diff options
author | Caolán McNamara <caolanm@redhat.com> | 2018-11-05 21:01:23 +0000 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2018-11-19 12:09:25 +0100 |
commit | 97bbffc917deba872090667e9dc096ecec99d557 (patch) | |
tree | 672bff54843461ff30270a11b726c1a48eea50cd /vcl | |
parent | 5b3592a42cc88a225237efadcc4d110be307303e (diff) |
weld TreeView
a) use GtkTreeStores for GtkTreeViews
b) ironically can't store GtkTreeStore contents in .ui apparently
c) set show_expanders for all non-trees and unconverted cases
d) on-demand subtrees
Change-Id: I3c1036a222daba2c129b1a22ffeb3fe35005ae31
Reviewed-on: https://gerrit.libreoffice.org/63336
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/source/app/salvtables.cxx | 227 | ||||
-rw-r--r-- | vcl/source/treelist/treelistbox.cxx | 16 | ||||
-rw-r--r-- | vcl/source/window/builder.cxx | 15 | ||||
-rw-r--r-- | vcl/uiconfig/ui/printdialog.ui | 1 | ||||
-rw-r--r-- | vcl/uiconfig/ui/printerdevicepage.ui | 4 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 389 |
6 files changed, 613 insertions, 39 deletions
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index ade8338f9946..67ac7ed97986 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -1750,6 +1750,17 @@ IMPL_LINK(SalInstanceEntry, CursorListener, VclWindowEvent&, rEvent, void) signal_cursor_position(); } +struct SalInstanceTreeIter : public weld::TreeIter +{ + SalInstanceTreeIter(const SalInstanceTreeIter* pOrig) + { + if (!pOrig) + return; + iter = pOrig->iter; + } + SvTreeListEntry* iter; +}; + class SalInstanceTreeView : public SalInstanceContainer, public virtual weld::TreeView { private: @@ -1759,19 +1770,26 @@ private: DECL_LINK(SelectHdl, SvTreeListBox*, void); DECL_LINK(DoubleClickHdl, SvTreeListBox*, bool); + DECL_LINK(ExpandingHdl, SvTreeListBox*, bool); public: SalInstanceTreeView(SvTreeListBox* pTreeView, bool bTakeOwnership) : SalInstanceContainer(pTreeView, bTakeOwnership) , m_xTreeView(pTreeView) { + m_xTreeView->SetNodeDefaultImages(); m_xTreeView->SetSelectHdl(LINK(this, SalInstanceTreeView, SelectHdl)); m_xTreeView->SetDoubleClickHdl(LINK(this, SalInstanceTreeView, DoubleClickHdl)); + m_xTreeView->SetExpandingHdl(LINK(this, SalInstanceTreeView, ExpandingHdl)); } - virtual void insert(int pos, const OUString& rStr, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) override + virtual void insert(weld::TreeIter* pParent, int pos, const OUString& rStr, const OUString* pId, + const OUString* pIconName, VirtualDevice* pImageSurface, const OUString* pExpanderName, + bool bChildrenOnDemand) override { - auto nInsertPos = pos == -1 ? COMBOBOX_APPEND : pos; + SalInstanceTreeIter* pVclIter = static_cast<SalInstanceTreeIter*>(pParent); + SvTreeListEntry* iter = pVclIter ? pVclIter->iter : nullptr; + auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos; void* pUserData; if (pId) { @@ -1781,8 +1799,9 @@ public: else pUserData = nullptr; + SvTreeListEntry* pResult; if (!pIconName && !pImageSurface) - m_xTreeView->InsertEntry(rStr, nullptr, false, nInsertPos, pUserData); + pResult = m_xTreeView->InsertEntry(rStr, iter, false, nInsertPos, pUserData); else { SvTreeListEntry* pEntry = new SvTreeListEntry; @@ -1790,7 +1809,20 @@ public: pEntry->AddItem(o3tl::make_unique<SvLBoxContextBmp>(aImage, aImage, false)); pEntry->AddItem(o3tl::make_unique<SvLBoxString>(rStr)); pEntry->SetUserData(pUserData); - m_xTreeView->Insert(pEntry, nInsertPos); + m_xTreeView->Insert(pEntry, iter, nInsertPos); + pResult = pEntry; + } + + if (pExpanderName) + { + Image aImage(createImage(*pExpanderName)); + m_xTreeView->SetExpandedEntryBmp(pResult, aImage); + m_xTreeView->SetCollapsedEntryBmp(pResult, aImage); + } + + if (bChildrenOnDemand) + { + m_xTreeView->InsertEntry("<dummy>", pResult, false, 0, nullptr); } } @@ -1914,6 +1946,157 @@ public: return m_xTreeView->GetAbsPos(pEntry); } + virtual std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pOrig) const override + { + return std::unique_ptr<weld::TreeIter>(new SalInstanceTreeIter(static_cast<const SalInstanceTreeIter*>(pOrig))); + } + + virtual void copy_iterator(const weld::TreeIter& rSource, weld::TreeIter& rDest) const override + { + const SalInstanceTreeIter& rVclSource(static_cast<const SalInstanceTreeIter&>(rSource)); + SalInstanceTreeIter& rVclDest(static_cast<SalInstanceTreeIter&>(rDest)); + rVclDest.iter = rVclSource.iter; + } + + virtual bool get_selected(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xTreeView->FirstSelected(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual bool get_cursor(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetCurEntry(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual void set_cursor(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->SetCurEntry(rVclIter.iter); + } + + virtual bool get_iter_first(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->GetEntry(0); + return rVclIter.iter != nullptr; + } + + virtual bool iter_next_sibling(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = rVclIter.iter->NextSibling(); + return rVclIter.iter != nullptr; + } + + virtual bool iter_next(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->Next(rVclIter.iter); + if (rVclIter.iter && m_xTreeView->GetEntryText(rVclIter.iter) == "<dummy>") + return iter_next(rVclIter); + return rVclIter.iter != nullptr; + } + + virtual bool iter_children(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->FirstChild(rVclIter.iter); + bool bRet = rVclIter.iter != nullptr; + if (bRet) + { + //on-demand dummy entry doesn't count + return m_xTreeView->GetEntryText(rVclIter.iter) != "<dummy>"; + } + return bRet; + } + + virtual bool iter_parent(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->GetParent(rVclIter.iter); + return rVclIter.iter != nullptr; + } + + virtual void remove(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->RemoveEntry(rVclIter.iter); + } + + virtual void select(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->Select(rVclIter.iter, true); + enable_notify_events(); + } + + virtual void unselect(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't unselect when frozen"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->Select(rVclIter.iter, false); + enable_notify_events(); + } + + virtual int get_iter_depth(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->GetModel()->GetDepth(rVclIter.iter); + } + + virtual bool iter_has_child(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return rVclIter.iter->HasChildren(); + } + + virtual bool get_row_expanded(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->IsExpanded(rVclIter.iter); + } + + virtual void expand_row(weld::TreeIter& rIter) override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + if (!m_xTreeView->IsExpanded(rVclIter.iter) && signal_expanding(rIter)) + m_xTreeView->Expand(rVclIter.iter); + } + + virtual OUString get_text(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->GetEntryText(rVclIter.iter); + } + + virtual OUString get_id(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + const OUString* pStr = static_cast<const OUString*>(rVclIter.iter->GetUserData()); + if (pStr) + return *pStr; + return OUString(); + } + + virtual void set_expander_image(const weld::TreeIter& rIter, const OUString& rImage) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + Image aImage(createImage(rImage)); + m_xTreeView->SetExpandedEntryBmp(rVclIter.iter, aImage); + m_xTreeView->SetCollapsedEntryBmp(rVclIter.iter, aImage); + } + virtual void set_selection_mode(bool bMultiple) override { m_xTreeView->SetSelectionMode(bMultiple ? SelectionMode::Multiple : SelectionMode::Single); @@ -1941,6 +2124,7 @@ public: virtual ~SalInstanceTreeView() override { + m_xTreeView->SetExpandingHdl(Link<SvTreeListBox*, bool>()); m_xTreeView->SetDoubleClickHdl(Link<SvTreeListBox*, bool>()); m_xTreeView->SetSelectHdl(Link<SvTreeListBox*, void>()); } @@ -1961,6 +2145,41 @@ IMPL_LINK_NOARG(SalInstanceTreeView, DoubleClickHdl, SvTreeListBox*, bool) return false; } +IMPL_LINK_NOARG(SalInstanceTreeView, ExpandingHdl, SvTreeListBox*, bool) +{ + SvTreeListEntry* pEntry = m_xTreeView->GetHdlEntry(); + if (m_xTreeView->IsExpanded(pEntry)) + { + //collapsing; + return true; + } + + // if there's a preexisting placeholder child, required to make this + // potentially expandable in the first place, now we remove it + bool bPlaceHolder = false; + if (pEntry->HasChildren()) + { + auto pChild = m_xTreeView->FirstChild(pEntry); + if (m_xTreeView->GetEntryText(pChild) == "<dummy>") + { + m_xTreeView->RemoveEntry(pChild); + bPlaceHolder = true; + } + } + + SalInstanceTreeIter aIter(nullptr); + aIter.iter = pEntry; + bool bRet = signal_expanding(aIter); + + //expand disallowed, restore placeholder + if (!bRet && bPlaceHolder) + { + m_xTreeView->InsertEntry("<dummy>", pEntry, false, 0, nullptr); + } + + return bRet; +} + class SalInstanceSpinButton : public SalInstanceEntry, public virtual weld::SpinButton { private: diff --git a/vcl/source/treelist/treelistbox.cxx b/vcl/source/treelist/treelistbox.cxx index a581fd5172e6..e51363bd2861 100644 --- a/vcl/source/treelist/treelistbox.cxx +++ b/vcl/source/treelist/treelistbox.cxx @@ -2137,9 +2137,11 @@ bool SvTreeListBox::Expand( SvTreeListEntry* pParent ) if( pParent->HasChildrenOnDemand() ) RequestingChildren( pParent ); - if( pParent->HasChildren() ) + bool bExpandAllowed = pParent->HasChildren() && ExpandingHdl(); + // double check if the expander callback ended up removing all children + if (pParent->HasChildren()) { - if( ExpandingHdl() ) + if (bExpandAllowed) { bExpanded = true; ExpandListEntry( pParent ); @@ -3649,7 +3651,7 @@ bool SvTreeListBox::set_property(const OString &rKey, const OUString &rValue) { set_min_width_in_chars(rValue.toInt32()); } - if (rKey == "enable-tree-lines") + else if (rKey == "enable-tree-lines") { auto nStyle = GetStyle(); nStyle &= ~(WB_HASLINES | WB_HASLINESATROOT); @@ -3657,6 +3659,14 @@ bool SvTreeListBox::set_property(const OString &rKey, const OUString &rValue) nStyle |= (WB_HASLINES | WB_HASLINESATROOT); SetStyle(nStyle); } + else if (rKey == "show-expanders") + { + auto nStyle = GetStyle(); + nStyle &= ~(WB_HASBUTTONS | WB_HASBUTTONSATROOT); + if (toBool(rValue)) + nStyle |= (WB_HASBUTTONS | WB_HASBUTTONSATROOT); + SetStyle(nStyle); + } else return Control::set_property(rKey, rValue); return true; diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index b2dbeba41233..edb7398b69a3 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -1892,7 +1892,7 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString & xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle); else { - VclPtrInstance<SvTreeListBox> xBox(pRealParent, nWinStyle); + VclPtrInstance<SvTreeListBox> xBox(pRealParent, nWinStyle | WB_HASBUTTONS | WB_HASBUTTONSATROOT); xBox->SetNoAutoCurEntry(true); xBox->SetHighlightRange(); // select over the whole width xWindow = xBox; @@ -2909,10 +2909,9 @@ void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID) m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow); } -void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID) +void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID, const OString &rClass) { int nLevel = 1; - sal_Int32 nRowIndex = 0; while(true) { @@ -2929,8 +2928,10 @@ void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rI { if (name.equals("row")) { - handleRow(reader, rID); - nRowIndex++; + bool bNotTreeStore = rClass != "GtkTreeStore"; + if (bNotTreeStore) + handleRow(reader, rID); + assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization"); } else ++nLevel; @@ -3433,9 +3434,9 @@ VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, xmlreader::Xm } } - if (sClass == "GtkListStore") + if (sClass == "GtkListStore" || sClass == "GtkTreeStore") { - handleListStore(reader, sID); + handleListStore(reader, sID, sClass); return nullptr; } else if (sClass == "GtkMenu") diff --git a/vcl/uiconfig/ui/printdialog.ui b/vcl/uiconfig/ui/printdialog.ui index 2c2061c22c43..d832944052cc 100644 --- a/vcl/uiconfig/ui/printdialog.ui +++ b/vcl/uiconfig/ui/printdialog.ui @@ -456,6 +456,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="vexpand">True</property> + <property name="show_expanders">False</property> <child internal-child="selection"> <object class="GtkTreeSelection" id="treeview-selection"/> </child> diff --git a/vcl/uiconfig/ui/printerdevicepage.ui b/vcl/uiconfig/ui/printerdevicepage.ui index 1dc801f78ea9..48ea728fb201 100644 --- a/vcl/uiconfig/ui/printerdevicepage.ui +++ b/vcl/uiconfig/ui/printerdevicepage.ui @@ -2,7 +2,7 @@ <!-- Generated with glade 3.20.4 --> <interface domain="vcl"> <requires lib="gtk+" version="3.18"/> - <object class="GtkListStore" id="liststore1"> + <object class="GtkTreeStore" id="liststore1"> <columns> <!-- column-name text --> <column type="gchararray"/> @@ -10,7 +10,7 @@ <column type="gchararray"/> </columns> </object> - <object class="GtkListStore" id="liststore2"> + <object class="GtkTreeStore" id="liststore2"> <columns> <!-- column-name text --> <column type="gchararray"/> diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index 2ffe595cb223..7e613bd202d1 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -4023,6 +4023,89 @@ namespace } } } + + GdkPixbuf* getPixbuf(const OUString& rIconName) + { + GdkPixbuf* pixbuf = nullptr; + + if (rIconName.lastIndexOf('.') != rIconName.getLength() - 4) + { + assert((rIconName== "dialog-warning" || rIconName== "dialog-error" || rIconName== "dialog-information") && + "unknown stock image"); + + GError *error = nullptr; + GtkIconTheme *icon_theme = gtk_icon_theme_get_default(); + pixbuf = gtk_icon_theme_load_icon(icon_theme, OUStringToOString(rIconName, RTL_TEXTENCODING_UTF8).getStr(), + 16, GTK_ICON_LOOKUP_USE_BUILTIN, &error); + } + else + { + const AllSettings& rSettings = Application::GetSettings(); + pixbuf = load_icon_by_name(rIconName, + rSettings.GetStyleSettings().DetermineIconTheme(), + rSettings.GetUILanguageTag().getBcp47()); + } + + return pixbuf; + } + + void insert_row(GtkTreeStore* pTreeStore, GtkTreeIter& iter, GtkTreeIter* parent, int pos, const OUString* pId, const OUString& rText, + const OUString* pIconName, VirtualDevice* pDevice, const OUString* pExpanderName) + { + if (!pIconName && !pDevice) + { + gtk_tree_store_insert_with_values(pTreeStore, &iter, parent, pos, + 0, OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr(), + 1, !pId ? nullptr : OUStringToOString(*pId, RTL_TEXTENCODING_UTF8).getStr(), + -1); + } + else + { + if (pIconName) + { + GdkPixbuf* pixbuf = getPixbuf(*pIconName); + + gtk_tree_store_insert_with_values(pTreeStore, &iter, parent, pos, + 0, OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr(), + 1, !pId ? nullptr : OUStringToOString(*pId, RTL_TEXTENCODING_UTF8).getStr(), + 2, pixbuf, + -1); + + if (pixbuf) + g_object_unref(pixbuf); + } + else + { + cairo_surface_t* surface = get_underlying_cairo_surface(*pDevice); + + Size aSize(pDevice->GetOutputSizePixel()); + cairo_surface_t* target = cairo_surface_create_similar(surface, + cairo_surface_get_content(surface), + aSize.Width(), + aSize.Height()); + + cairo_t* cr = cairo_create(target); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + + gtk_tree_store_insert_with_values(pTreeStore, &iter, parent, pos, + 0, OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr(), + 1, !pId ? nullptr : OUStringToOString(*pId, RTL_TEXTENCODING_UTF8).getStr(), + 3, target, + -1); + cairo_surface_destroy(target); + } + } + + if (pExpanderName) + { + GdkPixbuf* pixbuf = getPixbuf(*pExpanderName); + gtk_tree_store_set(pTreeStore, &iter, 4, pixbuf, -1); + if (pixbuf) + g_object_unref(pixbuf); + } + } } namespace @@ -4042,14 +4125,26 @@ namespace } } +struct GtkInstanceTreeIter : public weld::TreeIter +{ + GtkInstanceTreeIter(const GtkInstanceTreeIter* pOrig) + { + if (!pOrig) + return; + iter = pOrig->iter; + } + GtkTreeIter iter; +}; + class GtkInstanceTreeView : public GtkInstanceContainer, public virtual weld::TreeView { private: GtkTreeView* m_pTreeView; - GtkListStore* m_pListStore; + GtkTreeStore* m_pTreeStore; std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter; gulong m_nChangedSignalId; gulong m_nRowActivatedSignalId; + gulong m_nTestExpandRowSignalId; DECL_LINK(async_signal_changed, void*, void); @@ -4075,7 +4170,7 @@ private: OUString get(int pos, int col) const { OUString sRet; - GtkTreeModel *pModel = GTK_TREE_MODEL(m_pListStore); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); GtkTreeIter iter; if (gtk_tree_model_iter_nth_child(pModel, &iter, nullptr, pos)) { @@ -4087,53 +4182,99 @@ private: return sRet; } + static gboolean signalTestExpandRow(GtkTreeView*, GtkTreeIter* iter, GtkTreePath*, gpointer widget) + { + GtkInstanceTreeView* pThis = static_cast<GtkInstanceTreeView*>(widget); + return !pThis->signal_test_expand_row(*iter); + } + + bool signal_test_expand_row(GtkTreeIter& iter) + { + GtkInstanceTreeIter aIter(nullptr); + + // if there's a preexisting placeholder child, required to make this + // potentially expandable in the first place, now we remove it + bool bPlaceHolder = false; + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreeIter tmp; + if (gtk_tree_model_iter_children(pModel, &tmp, &iter)) + { + aIter.iter = tmp; + if (get_text(aIter) == "<dummy>") + { + gtk_tree_store_remove(m_pTreeStore, &tmp); + bPlaceHolder = true; + } + } + + aIter.iter = iter; + bool bRet = signal_expanding(aIter); + + //expand disallowed, restore placeholder + if (!bRet && bPlaceHolder) + { + GtkTreeIter subiter; + insert_row(m_pTreeStore, subiter, &iter, -1, nullptr, "<dummy>", nullptr, nullptr, nullptr); + } + + return bRet; + } + public: GtkInstanceTreeView(GtkTreeView* pTreeView, bool bTakeOwnership) : GtkInstanceContainer(GTK_CONTAINER(pTreeView), bTakeOwnership) , m_pTreeView(pTreeView) - , m_pListStore(GTK_LIST_STORE(gtk_tree_view_get_model(m_pTreeView))) + , m_pTreeStore(GTK_TREE_STORE(gtk_tree_view_get_model(m_pTreeView))) , m_nChangedSignalId(g_signal_connect(gtk_tree_view_get_selection(pTreeView), "changed", G_CALLBACK(signalChanged), this)) , m_nRowActivatedSignalId(g_signal_connect(pTreeView, "row-activated", G_CALLBACK(signalRowActivated), this)) + , m_nTestExpandRowSignalId(g_signal_connect(pTreeView, "test-expand-row", G_CALLBACK(signalTestExpandRow), this)) { } - virtual void insert(int pos, const OUString& rText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) override + virtual void insert(weld::TreeIter* pParent, int pos, const OUString& rText, const OUString* pId, const OUString* pIconName, + VirtualDevice* pImageSurface, const OUString* pExpanderName, bool bChildrenOnDemand) override { disable_notify_events(); GtkTreeIter iter; - insert_row(m_pListStore, iter, pos, pId, rText, pIconName, pImageSurface); + GtkInstanceTreeIter* pGtkIter = static_cast<GtkInstanceTreeIter*>(pParent); + insert_row(m_pTreeStore, iter, pGtkIter ? &pGtkIter->iter : nullptr, pos, pId, rText, pIconName, pImageSurface, pExpanderName); + if (bChildrenOnDemand) + { + GtkTreeIter subiter; + insert_row(m_pTreeStore, subiter, &iter, -1, nullptr, "<dummy>", nullptr, nullptr, nullptr); + } enable_notify_events(); } virtual void set_font_color(int pos, const Color& rColor) const override { GtkTreeIter iter; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pListStore), &iter, nullptr, pos); + gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pTreeStore), &iter, nullptr, pos); GdkRGBA aColor{rColor.GetRed()/255.0, rColor.GetGreen()/255.0, rColor.GetBlue()/255.0, 0}; - gtk_list_store_set(m_pListStore, &iter, 4, &aColor, -1); + gtk_tree_store_set(m_pTreeStore, &iter, 4, &aColor, -1); } virtual void remove(int pos) override { disable_notify_events(); GtkTreeIter iter; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pListStore), &iter, nullptr, pos); - gtk_list_store_remove(m_pListStore, &iter); + gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pTreeStore), &iter, nullptr, pos); + gtk_tree_store_remove(m_pTreeStore, &iter); enable_notify_events(); } virtual int find_text(const OUString& rText) const override { Search aSearch(rText, 0); - gtk_tree_model_foreach(GTK_TREE_MODEL(m_pListStore), foreach_find, &aSearch); + gtk_tree_model_foreach(GTK_TREE_MODEL(m_pTreeStore), foreach_find, &aSearch); return aSearch.index; } virtual int find_id(const OUString& rId) const override { Search aSearch(rId, 1); - gtk_tree_model_foreach(GTK_TREE_MODEL(m_pListStore), foreach_find, &aSearch); + gtk_tree_model_foreach(GTK_TREE_MODEL(m_pTreeStore), foreach_find, &aSearch); return aSearch.index; } @@ -4142,14 +4283,16 @@ public: if (pos == before) return; + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + disable_notify_events(); GtkTreeIter iter; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pListStore), &iter, nullptr, pos); + gtk_tree_model_iter_nth_child(pModel, &iter, nullptr, pos); GtkTreeIter position; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pListStore), &position, nullptr, before); + gtk_tree_model_iter_nth_child(pModel, &position, nullptr, before); - gtk_list_store_move_before(m_pListStore, &iter, &position); + gtk_tree_store_move_before(m_pTreeStore, &iter, &position); enable_notify_events(); } @@ -4163,7 +4306,7 @@ public: virtual void clear() override { disable_notify_events(); - gtk_list_store_clear(m_pListStore); + gtk_tree_store_clear(m_pTreeStore); enable_notify_events(); } @@ -4172,14 +4315,14 @@ public: m_xSorter.reset(new comphelper::string::NaturalStringSorter( ::comphelper::getProcessComponentContext(), Application::GetSettings().GetUILanguageTag().getLocale())); - GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pListStore); + GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeStore); gtk_tree_sortable_set_sort_func(pSortable, 0, sort_func, m_xSorter.get(), nullptr); gtk_tree_sortable_set_sort_column_id(pSortable, 0, GTK_SORT_ASCENDING); } virtual int n_children() const override { - return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_pListStore), nullptr); + return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_pTreeStore), nullptr); } virtual void select(int pos) override @@ -4257,15 +4400,214 @@ public: return nRet; } + virtual std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pOrig) const override + { + return std::unique_ptr<weld::TreeIter>(new GtkInstanceTreeIter(static_cast<const GtkInstanceTreeIter*>(pOrig))); + } + + virtual void copy_iterator(const weld::TreeIter& rSource, weld::TreeIter& rDest) const override + { + const GtkInstanceTreeIter& rGtkSource(static_cast<const GtkInstanceTreeIter&>(rSource)); + GtkInstanceTreeIter& rGtkDest(static_cast<GtkInstanceTreeIter&>(rDest)); + rGtkDest.iter = rGtkSource.iter; + } + + virtual bool get_selected(weld::TreeIter* pIter) const override + { + GtkInstanceTreeIter* pGtkIter = static_cast<GtkInstanceTreeIter*>(pIter); + return gtk_tree_selection_get_selected(gtk_tree_view_get_selection(m_pTreeView), nullptr, pGtkIter ? &pGtkIter->iter : nullptr); + } + + virtual bool get_cursor(weld::TreeIter* pIter) const override + { + GtkInstanceTreeIter* pGtkIter = static_cast<GtkInstanceTreeIter*>(pIter); + GtkTreePath* path; + gtk_tree_view_get_cursor(m_pTreeView, &path, nullptr); + if (pGtkIter && path) + { + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + gtk_tree_model_get_iter(pModel, &pGtkIter->iter, path); + } + return path != nullptr; + } + + virtual void set_cursor(const weld::TreeIter& rIter) override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter)); + gtk_tree_view_set_cursor(m_pTreeView, path, nullptr, false); + gtk_tree_path_free(path); + } + + virtual bool get_iter_first(weld::TreeIter& rIter) const override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + return gtk_tree_model_get_iter_first(pModel, &rGtkIter.iter); + } + + virtual bool iter_next_sibling(weld::TreeIter& rIter) const override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + return gtk_tree_model_iter_next(pModel, &rGtkIter.iter); + } + + virtual bool iter_next(weld::TreeIter& rIter) const override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreeIter iter = rGtkIter.iter; + if (iter_children(rGtkIter)) + return true; + GtkTreeIter tmp = iter; + if (gtk_tree_model_iter_next(pModel, &tmp)) + { + rGtkIter.iter = tmp; + return true; + } + if (!gtk_tree_model_iter_parent(pModel, &tmp, &iter)) + return false; + tmp = iter; + if (gtk_tree_model_iter_next(pModel, &tmp)) + { + rGtkIter.iter = tmp; + return true; + } + return false; + } + + virtual bool iter_children(weld::TreeIter& rIter) const override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreeIter tmp; + gboolean ret = gtk_tree_model_iter_children(pModel, &tmp, &rGtkIter.iter); + rGtkIter.iter = tmp; + if (ret) + { + //on-demand dummy entry doesn't count + return get_text(rGtkIter) != "<dummy>"; + } + return ret; + } + + virtual bool iter_parent(weld::TreeIter& rIter) const override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreeIter tmp; + auto ret = gtk_tree_model_iter_parent(pModel, &tmp, &rGtkIter.iter); + rGtkIter.iter = tmp; + return ret; + } + + virtual void remove(const weld::TreeIter& rIter) override + { + disable_notify_events(); + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + gtk_tree_store_remove(m_pTreeStore, const_cast<GtkTreeIter*>(&rGtkIter.iter)); + enable_notify_events(); + } + + virtual void select(const weld::TreeIter& rIter) override + { + assert(gtk_tree_view_get_model(m_pTreeView) && "don't select when frozen"); + disable_notify_events(); + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + gtk_tree_selection_select_iter(gtk_tree_view_get_selection(m_pTreeView), const_cast<GtkTreeIter*>(&rGtkIter.iter)); + enable_notify_events(); + } + + virtual void unselect(const weld::TreeIter& rIter) override + { + assert(gtk_tree_view_get_model(m_pTreeView) && "don't select when frozen"); + disable_notify_events(); + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + gtk_tree_selection_unselect_iter(gtk_tree_view_get_selection(m_pTreeView), const_cast<GtkTreeIter*>(&rGtkIter.iter)); + enable_notify_events(); + } + + virtual int get_iter_depth(const weld::TreeIter& rIter) const override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter)); + int ret = gtk_tree_path_get_depth(path) - 1; + gtk_tree_path_free(path); + return ret; + } + + virtual bool iter_has_child(const weld::TreeIter& rIter) const override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + return gtk_tree_model_iter_has_child(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter)); + } + + virtual bool get_row_expanded(const weld::TreeIter& rIter) const override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter)); + bool ret = gtk_tree_view_row_expanded(m_pTreeView, path); + gtk_tree_path_free(path); + return ret; + } + + virtual void expand_row(weld::TreeIter& rIter) override + { + GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + GtkTreePath* path = gtk_tree_model_get_path(pModel, &rGtkIter.iter); + if (!gtk_tree_view_row_expanded(m_pTreeView, path)) + gtk_tree_view_expand_row(m_pTreeView, path, false); + gtk_tree_path_free(path); + } + + virtual OUString get_text(const weld::TreeIter& rIter) const override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + gchar* pStr; + gtk_tree_model_get(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter), 0, &pStr, -1); + OUString sRet(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); + g_free(pStr); + return sRet; + } + + virtual OUString get_id(const weld::TreeIter& rIter) const override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); + gchar* pStr; + gtk_tree_model_get(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter), 1, &pStr, -1); + OUString sRet(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); + g_free(pStr); + return sRet; + } + + virtual void set_expander_image(const weld::TreeIter& rIter, const OUString& rExpanderName) override + { + const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); + disable_notify_events(); + GdkPixbuf* pixbuf = getPixbuf(rExpanderName); + gtk_tree_store_set(m_pTreeStore, const_cast<GtkTreeIter*>(&rGtkIter.iter), 4, pixbuf, -1); + if (pixbuf) + g_object_unref(pixbuf); + enable_notify_events(); + } + virtual void freeze() override { disable_notify_events(); - g_object_ref(m_pListStore); + g_object_ref(m_pTreeStore); GtkInstanceContainer::freeze(); gtk_tree_view_set_model(m_pTreeView, nullptr); if (m_xSorter) { - GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pListStore); + GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeStore); gtk_tree_sortable_set_sort_column_id(pSortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING); } enable_notify_events(); @@ -4276,12 +4618,12 @@ public: disable_notify_events(); if (m_xSorter) { - GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pListStore); + GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeStore); gtk_tree_sortable_set_sort_column_id(pSortable, 0, GTK_SORT_ASCENDING); } - gtk_tree_view_set_model(m_pTreeView, GTK_TREE_MODEL(m_pListStore)); + gtk_tree_view_set_model(m_pTreeView, GTK_TREE_MODEL(m_pTreeStore)); GtkInstanceContainer::thaw(); - g_object_unref(m_pListStore); + g_object_unref(m_pTreeStore); enable_notify_events(); } @@ -4371,8 +4713,9 @@ public: virtual ~GtkInstanceTreeView() override { - g_signal_handler_disconnect(gtk_tree_view_get_selection(m_pTreeView), m_nChangedSignalId); + g_signal_handler_disconnect(m_pTreeView, m_nTestExpandRowSignalId); g_signal_handler_disconnect(m_pTreeView, m_nRowActivatedSignalId); + g_signal_handler_disconnect(gtk_tree_view_get_selection(m_pTreeView), m_nChangedSignalId); } }; |