diff options
Diffstat (limited to 'sc/source/ui/cctrl/checklistmenu.cxx')
-rw-r--r-- | sc/source/ui/cctrl/checklistmenu.cxx | 554 |
1 files changed, 303 insertions, 251 deletions
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx index 44b7607f6340..8c9317f9d5ab 100644 --- a/sc/source/ui/cctrl/checklistmenu.cxx +++ b/sc/source/ui/cctrl/checklistmenu.cxx @@ -25,7 +25,7 @@ #include <vcl/decoview.hxx> #include <vcl/event.hxx> -#include <vcl/floatwin.hxx> +#include <vcl/dockwin.hxx> #include <vcl/settings.hxx> #include <vcl/svapp.hxx> #include <vcl/virdev.hxx> @@ -40,7 +40,6 @@ using ::com::sun::star::uno::Reference; ScCheckListMenuControl::MenuItemData::MenuItemData() : mbEnabled(true) - , mbSeparator(false) { } @@ -120,13 +119,12 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, SelectHdl, weld::TreeView&, void) else nSelectedMenu = mxMenu->get_iter_index_in_parent(*mxScratchIter); - setSelectedMenuItem(nSelectedMenu, true, false); + setSelectedMenuItem(nSelectedMenu, true); } void ScCheckListMenuControl::addMenuItem(const OUString& rText, Action* pAction) { MenuItemData aItem; - aItem.maText = rText; aItem.mbEnabled = true; aItem.mxAction.reset(pAction); maMenuItems.emplace_back(std::move(aItem)); @@ -139,10 +137,9 @@ void ScCheckListMenuControl::addMenuItem(const OUString& rText, Action* pAction) void ScCheckListMenuControl::addSeparator() { MenuItemData aItem; - aItem.mbSeparator = true; maMenuItems.emplace_back(std::move(aItem)); - //mxMenu->append_separator("seperator" + OUString::number(maMenuItems.size())); + //mxMenu->append_separator("separator" + OUString::number(maMenuItems.size())); } IMPL_LINK(ScCheckListMenuControl, TreeSizeAllocHdl, const Size&, rSize, void) @@ -168,10 +165,10 @@ ScCheckListMenuWindow* ScCheckListMenuControl::addSubMenuItem(const OUString& rT assert(mbCanHaveSubMenu); MenuItemData aItem; - aItem.maText = rText; aItem.mbEnabled = bEnabled; vcl::Window *pContainer = mxFrame->GetWindow(GetWindowType::FirstChild); - aItem.mxSubMenuWin.reset(VclPtr<ScCheckListMenuWindow>::Create(pContainer, mpDoc, false, -1, mxFrame->GetMenuStackLevel()+1, mxFrame.get())); + aItem.mxSubMenuWin.reset(VclPtr<ScCheckListMenuWindow>::Create(pContainer, mpDoc, false, + false, -1, mxFrame.get())); maMenuItems.emplace_back(std::move(aItem)); mxMenu->append_text(rText); @@ -195,23 +192,12 @@ void ScCheckListMenuControl::executeMenuItem(size_t nPos) maMenuItems[nPos].mxAction->execute(); } -void ScCheckListMenuControl::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu) +void ScCheckListMenuControl::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer) { if (mnSelectedMenu == nPos) // nothing to do. return; - if (bEnsureSubMenu) - { - // Dismiss any child popup menu windows. - if (mnSelectedMenu < maMenuItems.size() && - maMenuItems[mnSelectedMenu].mxSubMenuWin && - maMenuItems[mnSelectedMenu].mxSubMenuWin->IsVisible()) - { - maMenuItems[mnSelectedMenu].mxSubMenuWin->get_widget().ensureSubMenuNotVisible(); - } - } - selectMenuItem(nPos, bSubMenuTimer); } @@ -346,23 +332,19 @@ void ScCheckListMenuControl::selectMenuItem(size_t nPos, bool bSubMenuTimer) return; } + ScCheckListMenuWindow* pParentMenu = mxFrame->GetParentMenu(); + if (pParentMenu) + pParentMenu->get_widget().setSubMenuFocused(this); - if (nPos != MENU_NOT_SELECTED) + if (bSubMenuTimer) { - ScCheckListMenuWindow* pParentMenu = mxFrame->GetParentMenu(); - if (pParentMenu) - pParentMenu->get_widget().setSubMenuFocused(this); - - if (bSubMenuTimer) + if (maMenuItems[nPos].mxSubMenuWin) { - if (maMenuItems[nPos].mxSubMenuWin) - { - ScCheckListMenuWindow* pSubMenu = maMenuItems[nPos].mxSubMenuWin.get(); - queueLaunchSubMenu(nPos, pSubMenu); - } - else - queueCloseSubMenu(); + ScCheckListMenuWindow* pSubMenu = maMenuItems[nPos].mxSubMenuWin.get(); + queueLaunchSubMenu(nPos, pSubMenu); } + else + queueCloseSubMenu(); } } @@ -401,24 +383,12 @@ void ScCheckListMenuControl::EndPopupMode() mxFrame->EnableDocking(false); } -void ScCheckListMenuControl::StartPopupMode(const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags) +void ScCheckListMenuControl::StartPopupMode(const tools::Rectangle& rRect, FloatWinPopupFlags eFlags) { mxFrame->EnableDocking(true); DockingManager* pDockingManager = vcl::Window::GetDockingManager(); pDockingManager->SetPopupModeEndHdl(mxFrame, LINK(this, ScCheckListMenuControl, PopupModeEndHdl)); - pDockingManager->StartPopupMode(mxFrame, rRect, nPopupModeFlags); -} - -void ScCheckListMenuControl::ensureSubMenuNotVisible() -{ - if (mnSelectedMenu < maMenuItems.size() && - maMenuItems[mnSelectedMenu].mxSubMenuWin && - maMenuItems[mnSelectedMenu].mxSubMenuWin->IsVisible()) - { - maMenuItems[mnSelectedMenu].mxSubMenuWin->get_widget().ensureSubMenuNotVisible(); - } - - EndPopupMode(); + pDockingManager->StartPopupMode(mxFrame, rRect, (eFlags | FloatWinPopupFlags::GrabFocus)); } void ScCheckListMenuControl::terminateAllPopupMenus() @@ -443,15 +413,17 @@ ScCheckListMember::ScCheckListMember() } ScCheckListMenuControl::ScCheckListMenuControl(ScCheckListMenuWindow* pParent, vcl::Window* pContainer, - ScDocument* pDoc, bool bCanHaveSubMenu, int nWidth) + ScDocument* pDoc, bool bCanHaveSubMenu, + bool bHasDates, int nWidth) : mxFrame(pParent) - , mxBuilder(Application::CreateInterimBuilder(pContainer, "modules/scalc/ui/filterdropdown.ui")) + , mxBuilder(Application::CreateInterimBuilder(pContainer, "modules/scalc/ui/filterdropdown.ui", false)) , mxContainer(mxBuilder->weld_container("FilterDropDown")) , mxMenu(mxBuilder->weld_tree_view("menu")) , mxScratchIter(mxMenu->make_iterator()) , mxEdSearch(mxBuilder->weld_entry("search_edit")) , mxBox(mxBuilder->weld_widget("box")) - , mxChecks(mxBuilder->weld_tree_view("check_list_box")) + , mxListChecks(mxBuilder->weld_tree_view("check_list_box")) + , mxTreeChecks(mxBuilder->weld_tree_view("check_tree_box")) , mxChkToggleAll(mxBuilder->weld_check_button("toggle_all")) , mxBtnSelectSingle(mxBuilder->weld_button("select_current")) , mxBtnUnselectSingle(mxBuilder->weld_button("unselect_current")) @@ -459,33 +431,92 @@ ScCheckListMenuControl::ScCheckListMenuControl(ScCheckListMenuWindow* pParent, v , mxBtnOk(mxBuilder->weld_button("ok")) , mxBtnCancel(mxBuilder->weld_button("cancel")) , mxDropDown(mxMenu->create_virtual_device()) - , mnWidthHint(nWidth) - , maWndSize() + , mnCheckWidthReq(-1) + , mnWndWidth(0) , mePrevToggleAllState(TRISTATE_INDET) , mnSelectedMenu(MENU_NOT_SELECTED) , mpDoc(pDoc) , mnAsyncPostPopdownId(nullptr) - , mbHasDates(false) + , mbHasDates(bHasDates) , mbCanHaveSubMenu(bCanHaveSubMenu) , maOpenTimer(this) , maCloseTimer(this) { + /* + tdf#136559 If we have no dates we don't need a tree + structure, just a list. GtkListStore can be then + used which is much faster than a GtkTreeStore, so + with no dates switch to the treeview which uses the + faster GtkListStore + */ + if (mbHasDates) + mpChecks = mxTreeChecks.get(); + else + { + mxTreeChecks->hide(); + mxListChecks->show(); + mpChecks = mxListChecks.get(); + } + + bool bIsSubMenu = pParent->GetParentMenu(); + + int nChecksHeight = mxTreeChecks->get_height_rows(9); + if (!bIsSubMenu && nWidth != -1) + { + mnCheckWidthReq = nWidth - mxFrame->get_border_width() * 2 - 4; + mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight); + mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight); + } + // sort ok/cancel into native order, if this was a dialog they would be auto-sorted, but this // popup isn't a true dialog //mxButtonBox->sort_native_button_order(); - //mxChecks->enable_toggle_buttons(weld::ColumnToggleType::Check); + if (!bIsSubMenu) + { + mxTreeChecks->enable_toggle_buttons(weld::ColumnToggleType::Check); + mxListChecks->enable_toggle_buttons(weld::ColumnToggleType::Check); + + mxBox->show(); + mxEdSearch->show(); + mxButtonBox->show(); + } mxContainer->connect_focus_in(LINK(this, ScCheckListMenuControl, FocusHdl)); mxMenu->connect_row_activated(LINK(this, ScCheckListMenuControl, RowActivatedHdl)); mxMenu->connect_changed(LINK(this, ScCheckListMenuControl, SelectHdl)); mxMenu->connect_key_press(LINK(this, ScCheckListMenuControl, MenuKeyInputHdl)); + if (!bIsSubMenu) + { + mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); + mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); + mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl)); + mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl)); + mxTreeChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl)); + mxTreeChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl)); + mxListChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl)); + mxListChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl)); + mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, TriStateHdl)); + mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); + mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); + } + if (mbCanHaveSubMenu) { CreateDropDown(); mxMenu->connect_size_allocate(LINK(this, ScCheckListMenuControl, TreeSizeAllocHdl)); } + + if (!bIsSubMenu) + { + // determine what width the checklist will end up with + mnCheckWidthReq = mxContainer->get_preferred_size().Width(); + // make that size fixed now, we can now use mnCheckWidthReq to speed up + // bulk_insert_for_each + mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight); + mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight); + } } IMPL_LINK_NOARG(ScCheckListMenuControl, FocusHdl, weld::Widget&, void) @@ -517,13 +548,13 @@ ScCheckListMenuControl::~ScCheckListMenuControl() } ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc, bool bCanHaveSubMenu, - int nWidth, sal_uInt16 nMenuStackLevel, ScCheckListMenuWindow* pParentMenu) + bool bTreeMode, int nWidth, ScCheckListMenuWindow* pParentMenu) : DockingWindow(pParent, "InterimDockParent", "svx/ui/interimdockparent.ui") , mxParentMenu(pParentMenu) , mxBox(get<vcl::Window>("box")) - , mxControl(new ScCheckListMenuControl(this, mxBox.get(), pDoc, bCanHaveSubMenu, nWidth)) - , mnMenuStackLevel(nMenuStackLevel) { + setDeferredProperties(); + mxControl.reset(new ScCheckListMenuControl(this, mxBox.get(), pDoc, bCanHaveSubMenu, bTreeMode, nWidth)); SetBackground(Application::GetSettings().GetStyleSettings().GetMenuColor()); set_id("check_list_menu"); } @@ -560,39 +591,22 @@ void ScCheckListMenuWindow::GetFocus() mxControl->GrabFocus(); } -void ScCheckListMenuControl::packWindow() +void ScCheckListMenuControl::prepWindow() { - mxBox->show(); - mxEdSearch->show(); - mxButtonBox->show(); - - mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); - mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); - mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl)); - mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl)); - //mxChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl)); - mxChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl)); - mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, TriStateHdl)); - mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); - mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl)); - - mxChecks->set_size_request(-1, mxChecks->get_height_rows(9)); mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height() + 2); mnSelectedMenu = 0; mxMenu->set_cursor(mnSelectedMenu); mxMenu->unselect_all(); - maWndSize = mxContainer->get_preferred_size(); - if (maWndSize.Width() < mnWidthHint) - { - mxContainer->set_size_request(mnWidthHint, -1); - maWndSize.setWidth(mnWidthHint); - } + mnWndWidth = mxContainer->get_preferred_size().Width() + mxFrame->get_border_width() * 2 + 4; } void ScCheckListMenuControl::setAllMemberState(bool bSet) { - CheckAllChildren(nullptr, bSet); + mpChecks->all_foreach([this, bSet](weld::TreeIter& rEntry){ + mpChecks->set_toggle(rEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE); + return false; + }); if (!maConfig.mbAllowEmptySet) { @@ -604,10 +618,10 @@ void ScCheckListMenuControl::setAllMemberState(bool bSet) void ScCheckListMenuControl::selectCurrentMemberOnly(bool bSet) { setAllMemberState(!bSet); - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(); - if (!mxChecks->get_cursor(xEntry.get())) + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(); + if (!mpChecks->get_cursor(xEntry.get())) return; - //mxChecks->set_toggle(*xEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE); + mpChecks->set_toggle(*xEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE); } IMPL_LINK(ScCheckListMenuControl, ButtonHdl, weld::Button&, rBtn, void) @@ -619,8 +633,8 @@ IMPL_LINK(ScCheckListMenuControl, ButtonHdl, weld::Button&, rBtn, void) else if (&rBtn == mxBtnSelectSingle.get() || &rBtn == mxBtnUnselectSingle.get()) { selectCurrentMemberOnly(&rBtn == mxBtnSelectSingle.get()); - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(); - if (!mxChecks->get_cursor(xEntry.get())) + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(); + if (!mpChecks->get_cursor(xEntry.get())) xEntry.reset(); Check(xEntry.get()); } @@ -657,12 +671,12 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, EdModifyHdl, weld::Entry&, void) size_t nSelCount = 0; bool bSomeDateDeletes = false; - mxChecks->freeze(); + mpChecks->freeze(); - if (bSearchTextEmpty) + if (bSearchTextEmpty && !mbHasDates) { // when there are a lot of rows, it is cheaper to simply clear the tree and re-initialise - mxChecks->clear(); + mpChecks->clear(); nSelCount = initMembers(); } else @@ -725,7 +739,7 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, EdModifyHdl, weld::Entry&, void) } } - mxChecks->thaw(); + mpChecks->thaw(); if ( nSelCount == n ) mxChkToggleAll->set_state( TRISTATE_TRUE ); @@ -737,7 +751,7 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, EdModifyHdl, weld::Entry&, void) if ( !maConfig.mbAllowEmptySet ) { const bool bEmptySet( nSelCount == 0 ); - mxChecks->set_sensitive(!bEmptySet); + mpChecks->set_sensitive(!bEmptySet); mxChkToggleAll->set_sensitive(!bEmptySet); mxBtnSelectSingle->set_sensitive(!bEmptySet); mxBtnUnselectSingle->set_sensitive(!bEmptySet); @@ -752,15 +766,15 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, EdActivateHdl, weld::Entry&, bool) return true; } -// IMPL_LINK( ScCheckListMenuControl, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void ) -// { -// Check(&rRowCol.first); -// } +IMPL_LINK( ScCheckListMenuControl, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void ) +{ + Check(&rRowCol.first); +} void ScCheckListMenuControl::Check(const weld::TreeIter* pEntry) { if (pEntry) - CheckEntry(pEntry, mxChecks->get_toggle(*pEntry) == TRISTATE_TRUE); + CheckEntry(*pEntry, mpChecks->get_toggle(*pEntry) == TRISTATE_TRUE); size_t nNumChecked = GetCheckedEntryCount(); if (nNumChecked == maMembers.size()) // all members visible @@ -790,13 +804,13 @@ void ScCheckListMenuControl::updateMemberParents(const weld::TreeIter* pLeaf, si if ( pLeaf ) { std::unique_ptr<weld::TreeIter> xYearEntry; - std::unique_ptr<weld::TreeIter> xMonthEntry = mxChecks->make_iterator(pLeaf); - if (!mxChecks->iter_parent(*xMonthEntry)) + std::unique_ptr<weld::TreeIter> xMonthEntry = mpChecks->make_iterator(pLeaf); + if (!mpChecks->iter_parent(*xMonthEntry)) xMonthEntry.reset(); else { - xYearEntry = mxChecks->make_iterator(xMonthEntry.get()); - if (!mxChecks->iter_parent(*xYearEntry)) + xYearEntry = mpChecks->make_iterator(xMonthEntry.get()); + if (!mpChecks->iter_parent(*xYearEntry)) xYearEntry.reset(); } @@ -851,15 +865,15 @@ void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, if ( aDayName.getLength() == 1 ) aDayName = "0" + aDayName; - mxChecks->freeze(); + mpChecks->freeze(); std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName); if (!xYearEntry) { - xYearEntry = mxChecks->make_iterator(); - mxChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get()); - mxChecks->set_toggle(*xYearEntry, TRISTATE_FALSE); - mxChecks->set_text(*xYearEntry, aYearName, 0); + xYearEntry = mpChecks->make_iterator(); + mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get()); + mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE); + mpChecks->set_text(*xYearEntry, aYearName, 0); ScCheckListMember aMemYear; aMemYear.maName = aYearName; aMemYear.maRealName = rsName; @@ -874,10 +888,10 @@ void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), aMonthName); if (!xMonthEntry) { - xMonthEntry = mxChecks->make_iterator(); - mxChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get()); - mxChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE); - mxChecks->set_text(*xMonthEntry, aMonthName, 0); + xMonthEntry = mpChecks->make_iterator(); + mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get()); + mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE); + mpChecks->set_text(*xMonthEntry, aMonthName, 0); ScCheckListMember aMemMonth; aMemMonth.maName = aMonthName; aMemMonth.maRealName = rsName; @@ -893,10 +907,10 @@ void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), aDayName); if (!xDayEntry) { - xDayEntry = mxChecks->make_iterator(); - mxChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get()); - mxChecks->set_toggle(*xDayEntry, TRISTATE_FALSE); - mxChecks->set_text(*xDayEntry, aDayName, 0); + xDayEntry = mpChecks->make_iterator(); + mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get()); + mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE); + mpChecks->set_text(*xDayEntry, aDayName, 0); ScCheckListMember aMemDay; aMemDay.maName = aDayName; aMemDay.maRealName = rsName; @@ -911,7 +925,7 @@ void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, maMembers.emplace_back(std::move(aMemDay)); } - mxChecks->thaw(); + mpChecks->thaw(); } void ScCheckListMenuControl::addMember(const OUString& rName, bool bVisible) @@ -927,13 +941,13 @@ void ScCheckListMenuControl::addMember(const OUString& rName, bool bVisible) std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, const OUString& sNode) { - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(pParent); - bool bEntry = pParent ? mxChecks->iter_children(*xEntry) : mxChecks->get_iter_first(*xEntry); + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(pParent); + bool bEntry = pParent ? mpChecks->iter_children(*xEntry) : mpChecks->get_iter_first(*xEntry); while (bEntry) { - if (sNode == mxChecks->get_text(*xEntry, 0)) + if (sNode == mpChecks->get_text(*xEntry, 0)) return xEntry; - bEntry = mxChecks->iter_next_sibling(*xEntry); + bEntry = mpChecks->iter_next_sibling(*xEntry); } return nullptr; } @@ -941,50 +955,50 @@ std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::Tr void ScCheckListMenuControl::GetRecursiveChecked(const weld::TreeIter* pEntry, std::unordered_set<OUString>& vOut, OUString& rLabel) { - if (mxChecks->get_toggle(*pEntry) == TRISTATE_TRUE) - { - // We have to hash parents and children together. - // Per convention for easy access in getResult() - // "child;parent;grandparent" while descending. - if (rLabel.isEmpty()) - rLabel = mxChecks->get_text(*pEntry, 0); - else - rLabel = mxChecks->get_text(*pEntry, 0) + ";" + rLabel; + if (mpChecks->get_toggle(*pEntry) != TRISTATE_TRUE) + return; - // Prerequisite: the selection mechanism guarantees that if a child is - // selected then also the parent is selected, so we only have to - // inspect the children in case the parent is selected. - if (mxChecks->iter_has_child(*pEntry)) - { - std::unique_ptr<weld::TreeIter> xChild(mxChecks->make_iterator(pEntry)); - bool bChild = mxChecks->iter_children(*xChild); - while (bChild) - { - OUString aLabel = rLabel; - GetRecursiveChecked(xChild.get(), vOut, aLabel); - if (!aLabel.isEmpty() && aLabel != rLabel) - vOut.insert(aLabel); - bChild = mxChecks->iter_next_sibling(*xChild); - } - // Let the caller not add the parent alone. - rLabel.clear(); - } + // We have to hash parents and children together. + // Per convention for easy access in getResult() + // "child;parent;grandparent" while descending. + if (rLabel.isEmpty()) + rLabel = mpChecks->get_text(*pEntry, 0); + else + rLabel = mpChecks->get_text(*pEntry, 0) + ";" + rLabel; + + // Prerequisite: the selection mechanism guarantees that if a child is + // selected then also the parent is selected, so we only have to + // inspect the children in case the parent is selected. + if (!mpChecks->iter_has_child(*pEntry)) + return; + + std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(pEntry)); + bool bChild = mpChecks->iter_children(*xChild); + while (bChild) + { + OUString aLabel = rLabel; + GetRecursiveChecked(xChild.get(), vOut, aLabel); + if (!aLabel.isEmpty() && aLabel != rLabel) + vOut.insert(aLabel); + bChild = mpChecks->iter_next_sibling(*xChild); } + // Let the caller not add the parent alone. + rLabel.clear(); } std::unordered_set<OUString> ScCheckListMenuControl::GetAllChecked() { std::unordered_set<OUString> vResults(0); - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(); - bool bEntry = mxChecks->get_iter_first(*xEntry); + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(); + bool bEntry = mpChecks->get_iter_first(*xEntry); while (bEntry) { OUString aLabel; GetRecursiveChecked(xEntry.get(), vResults, aLabel); if (!aLabel.isEmpty()) vResults.insert(aLabel); - bEntry = mxChecks->iter_next_sibling(*xEntry); + bEntry = mpChecks->iter_next_sibling(*xEntry); } return vResults; @@ -993,61 +1007,60 @@ std::unordered_set<OUString> ScCheckListMenuControl::GetAllChecked() bool ScCheckListMenuControl::IsChecked(const OUString& sName, const weld::TreeIter* pParent) { std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName); - return xEntry && mxChecks->get_toggle(*xEntry) == TRISTATE_TRUE; + return xEntry && mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE; } void ScCheckListMenuControl::CheckEntry(const OUString& sName, const weld::TreeIter* pParent, bool bCheck) { std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName); if (xEntry) - CheckEntry(xEntry.get(), bCheck); + CheckEntry(*xEntry, bCheck); } -// Recursively check all children of pParent -void ScCheckListMenuControl::CheckAllChildren(const weld::TreeIter* pParent, bool bCheck) +// Recursively check all children of rParent +void ScCheckListMenuControl::CheckAllChildren(const weld::TreeIter& rParent, bool bCheck) { - if (pParent) - mxChecks->set_toggle(*pParent, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE); - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(pParent); - bool bEntry = pParent ? mxChecks->iter_children(*xEntry) : mxChecks->get_iter_first(*xEntry); + mpChecks->set_toggle(rParent, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE); + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(&rParent); + bool bEntry = mpChecks->iter_children(*xEntry); while (bEntry) { - CheckAllChildren(xEntry.get(), bCheck); - bEntry = mxChecks->iter_next_sibling(*xEntry); + CheckAllChildren(*xEntry, bCheck); + bEntry = mpChecks->iter_next_sibling(*xEntry); } } -void ScCheckListMenuControl::CheckEntry(const weld::TreeIter* pParent, bool bCheck) +void ScCheckListMenuControl::CheckEntry(const weld::TreeIter& rParent, bool bCheck) { - // recursively check all items below pParent - CheckAllChildren(pParent, bCheck); - // checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is + // recursively check all items below rParent + CheckAllChildren(rParent, bCheck); + // checking rParent can affect ancestors, e.g. if ancestor is unchecked and rParent is // now checked then the ancestor needs to be checked also - if (pParent && mxChecks->get_iter_depth(*pParent)) + if (!mpChecks->get_iter_depth(rParent)) + return; + + std::unique_ptr<weld::TreeIter> xAncestor(mpChecks->make_iterator(&rParent)); + bool bAncestor = mpChecks->iter_parent(*xAncestor); + while (bAncestor) { - std::unique_ptr<weld::TreeIter> xAncestor(mxChecks->make_iterator(pParent)); - bool bAncestor = mxChecks->iter_parent(*xAncestor); - while (bAncestor) + // if any first level children checked then ancestor + // needs to be checked, similarly if no first level children + // checked then ancestor needs to be unchecked + std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(xAncestor.get())); + bool bChild = mpChecks->iter_children(*xChild); + bool bChildChecked = false; + + while (bChild) { - // if any first level children checked then ancestor - // needs to be checked, similarly if no first level children - // checked then ancestor needs to be unchecked - std::unique_ptr<weld::TreeIter> xChild(mxChecks->make_iterator(xAncestor.get())); - bool bChild = mxChecks->iter_children(*xChild); - bool bChildChecked = false; - - while (bChild) + if (mpChecks->get_toggle(*xChild) == TRISTATE_TRUE) { - if (mxChecks->get_toggle(*xChild) == TRISTATE_TRUE) - { - bChildChecked = true; - break; - } - bChild = mxChecks->iter_next_sibling(*xChild); + bChildChecked = true; + break; } - mxChecks->set_toggle(*xAncestor, bChildChecked ? TRISTATE_TRUE : TRISTATE_FALSE); - bAncestor = mxChecks->iter_parent(*xAncestor); + bChild = mpChecks->iter_next_sibling(*xChild); } + mpChecks->set_toggle(*xAncestor, bChildChecked ? TRISTATE_TRUE : TRISTATE_FALSE); + bAncestor = mpChecks->iter_parent(*xAncestor); } } @@ -1069,50 +1082,50 @@ std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::ShowCheckEntry(const OUS std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, rMember.maDateParts[0]); if (!xYearEntry) { - xYearEntry = mxChecks->make_iterator(); - mxChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get()); - mxChecks->set_toggle(*xYearEntry, TRISTATE_FALSE); - mxChecks->set_text(*xYearEntry, rMember.maDateParts[0], 0); + xYearEntry = mpChecks->make_iterator(); + mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get()); + mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE); + mpChecks->set_text(*xYearEntry, rMember.maDateParts[0], 0); } std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), rMember.maDateParts[1]); if (!xMonthEntry) { - xMonthEntry = mxChecks->make_iterator(); - mxChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get()); - mxChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE); - mxChecks->set_text(*xMonthEntry, rMember.maDateParts[1], 0); + xMonthEntry = mpChecks->make_iterator(); + mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get()); + mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE); + mpChecks->set_text(*xMonthEntry, rMember.maDateParts[1], 0); } std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), rMember.maName); if (!xDayEntry) { - xDayEntry = mxChecks->make_iterator(); - mxChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get()); - mxChecks->set_toggle(*xDayEntry, TRISTATE_FALSE); - mxChecks->set_text(*xDayEntry, rMember.maName, 0); + xDayEntry = mpChecks->make_iterator(); + mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get()); + mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE); + mpChecks->set_text(*xDayEntry, rMember.maName, 0); } return xDayEntry; // Return leaf node } - xEntry = mxChecks->make_iterator(); - mxChecks->append(xEntry.get()); - mxChecks->set_toggle(*xEntry, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE); - mxChecks->set_text(*xEntry, sName, 0); + xEntry = mpChecks->make_iterator(); + mpChecks->append(xEntry.get()); + mpChecks->set_toggle(*xEntry, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE); + mpChecks->set_text(*xEntry, sName, 0); } else - CheckEntry(xEntry.get(), bCheck); + CheckEntry(*xEntry, bCheck); } else if (xEntry) { - mxChecks->remove(*xEntry); + mpChecks->remove(*xEntry); if (rMember.mxParent) { - std::unique_ptr<weld::TreeIter> xParent(mxChecks->make_iterator(rMember.mxParent.get())); - while (xParent && !mxChecks->iter_has_child(*xParent)) + std::unique_ptr<weld::TreeIter> xParent(mpChecks->make_iterator(rMember.mxParent.get())); + while (xParent && !mpChecks->iter_has_child(*xParent)) { - std::unique_ptr<weld::TreeIter> xTmp(mxChecks->make_iterator(xParent.get())); - if (!mxChecks->iter_parent(*xParent)) + std::unique_ptr<weld::TreeIter> xTmp(mpChecks->make_iterator(xParent.get())); + if (!mpChecks->iter_parent(*xParent)) xParent.reset(); - mxChecks->remove(*xTmp); + mpChecks->remove(*xTmp); } } } @@ -1123,8 +1136,8 @@ int ScCheckListMenuControl::GetCheckedEntryCount() const { int nRet = 0; - mxChecks->all_foreach([this, &nRet](weld::TreeIter& rEntry){ - if (mxChecks->get_toggle(rEntry) == TRISTATE_TRUE) + mpChecks->all_foreach([this, &nRet](weld::TreeIter& rEntry){ + if (mpChecks->get_toggle(rEntry) == TRISTATE_TRUE) ++nRet; return false; }); @@ -1138,13 +1151,13 @@ IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const KeyEvent&, rKEvt, bool) if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE ) { - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(); - bool bEntry = mxChecks->get_cursor(xEntry.get()); + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(); + bool bEntry = mpChecks->get_cursor(xEntry.get()); if (bEntry) { - bool bOldCheck = mxChecks->get_toggle(*xEntry) == TRISTATE_TRUE; - CheckEntry(xEntry.get(), !bOldCheck); - bool bNewCheck = mxChecks->get_toggle(*xEntry) == TRISTATE_TRUE; + bool bOldCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE; + CheckEntry(*xEntry, !bOldCheck); + bool bNewCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE; if (bOldCheck != bNewCheck) Check(xEntry.get()); } @@ -1154,49 +1167,75 @@ IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const KeyEvent&, rKEvt, bool) return false; } -void ScCheckListMenuControl::setHasDates(bool bHasDates) +namespace { - mbHasDates = bHasDates; - mxChecks->set_show_expanders(mbHasDates); + void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, const ScCheckListMember& rMember) + { + OUString aLabel = rMember.maName; + if (aLabel.isEmpty()) + aLabel = ScResId(STR_EMPTYDATA); + rView.set_toggle(rIter, rMember.mbVisible ? TRISTATE_TRUE : TRISTATE_FALSE); + rView.set_text(rIter, aLabel, 0); + } } -size_t ScCheckListMenuControl::initMembers() +size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth) { size_t n = maMembers.size(); size_t nVisMemCount = 0; - mxChecks->freeze(); + if (nMaxMemberWidth == -1) + nMaxMemberWidth = mnCheckWidthReq; - std::unique_ptr<weld::TreeIter> xEntry = mxChecks->make_iterator(); - std::vector<std::unique_ptr<weld::TreeIter>> aExpandRows; - - for (size_t i = 0; i < n; ++i) + if (!mpChecks->n_children() && !mbHasDates) + { + std::vector<int> aFixedWidths { nMaxMemberWidth }; + // tdf#134038 insert in the fastest order, this might be backwards so only do it for + // the !mbHasDates case where no entry depends on another to exist before getting + // inserted. We cannot retain pre-existing treeview content, only clear and fill it. + mpChecks->bulk_insert_for_each(n, [this, &nVisMemCount](weld::TreeIter& rIter, int i) { + assert(!maMembers[i].mbDate); + insertMember(*mpChecks, rIter, maMembers[i]); + if (maMembers[i].mbVisible) + ++nVisMemCount; + }, &aFixedWidths); + } + else { - if (maMembers[i].mbDate) + mpChecks->freeze(); + + std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(); + std::vector<std::unique_ptr<weld::TreeIter>> aExpandRows; + + for (size_t i = 0; i < n; ++i) { - CheckEntry(maMembers[i].maName, maMembers[i].mxParent.get(), maMembers[i].mbVisible); - // Expand first node of checked dates - if (!maMembers[i].mxParent && IsChecked(maMembers[i].maName, maMembers[i].mxParent.get())) + if (maMembers[i].mbDate) { - std::unique_ptr<weld::TreeIter> xDateEntry = FindEntry(nullptr, maMembers[i].maName); - if (xDateEntry) - aExpandRows.emplace_back(std::move(xDateEntry)); + CheckEntry(maMembers[i].maName, maMembers[i].mxParent.get(), maMembers[i].mbVisible); + // Expand first node of checked dates + if (!maMembers[i].mxParent && IsChecked(maMembers[i].maName, maMembers[i].mxParent.get())) + { + std::unique_ptr<weld::TreeIter> xDateEntry = FindEntry(nullptr, maMembers[i].maName); + if (xDateEntry) + aExpandRows.emplace_back(std::move(xDateEntry)); + } + } + else + { + mpChecks->append(xEntry.get()); + insertMember(*mpChecks, *xEntry, maMembers[i]); } - } - else - { - OUString aLabel = maMembers[i].maName; - if (aLabel.isEmpty()) - aLabel = ScResId(STR_EMPTYDATA); - mxChecks->append(xEntry.get()); - mxChecks->set_toggle(*xEntry, maMembers[i].mbVisible ? TRISTATE_TRUE : TRISTATE_FALSE); - mxChecks->set_text(*xEntry, aLabel, 0); + if (maMembers[i].mbVisible) + ++nVisMemCount; } - if (maMembers[i].mbVisible) - ++nVisMemCount; + mpChecks->thaw(); + + for (auto& rRow : aExpandRows) + mpChecks->expand_row(*rRow); } + if (nVisMemCount == n) { // all members visible @@ -1215,13 +1254,8 @@ size_t ScCheckListMenuControl::initMembers() mePrevToggleAllState = TRISTATE_INDET; } - mxChecks->thaw(); - - for (auto& rRow : aExpandRows) - mxChecks->expand_row(*rRow); - if (nVisMemCount) - mxChecks->select(0); + mpChecks->select(0); return nVisMemCount; } @@ -1254,12 +1288,12 @@ void ScCheckListMenuControl::getResult(ResultType& rResult) // Checked labels are in the form "child;parent;grandparent". if (maMembers[i].mxParent) { - std::unique_ptr<weld::TreeIter> xIter(mxChecks->make_iterator(maMembers[i].mxParent.get())); + std::unique_ptr<weld::TreeIter> xIter(mpChecks->make_iterator(maMembers[i].mxParent.get())); do { - aLabel.append(";").append(mxChecks->get_text(*xIter)); + aLabel.append(";").append(mpChecks->get_text(*xIter)); } - while (mxChecks->iter_parent(*xIter)); + while (mpChecks->iter_parent(*xIter)); } bool bState = vCheckeds.find(aLabel.makeStringAndClear()) != vCheckeds.end(); @@ -1279,7 +1313,7 @@ void ScCheckListMenuControl::getResult(ResultType& rResult) void ScCheckListMenuControl::launch(const tools::Rectangle& rRect) { - packWindow(); + prepWindow(); if (!maConfig.mbAllowEmptySet) // We need to have at least one member selected. mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0); @@ -1291,16 +1325,16 @@ void ScCheckListMenuControl::launch(const tools::Rectangle& rRect) long nLeft = aRect.Left() - aRect.GetWidth(); aRect.SetLeft( nLeft ); } - else if (maWndSize.Width() < aRect.GetWidth()) + else if (mnWndWidth < aRect.GetWidth()) { // Target rectangle (i.e. cell width) is wider than the window. // Simulate right-aligned launch by modifying the target rectangle // size. - long nDiff = aRect.GetWidth() - maWndSize.Width(); + long nDiff = aRect.GetWidth() - mnWndWidth; aRect.AdjustLeft(nDiff ); } - StartPopupMode(aRect, (FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus)); + StartPopupMode(aRect, FloatWinPopupFlags::Down); } void ScCheckListMenuControl::close(bool bOK) @@ -1337,4 +1371,22 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, PopupModeEndHdl, FloatingWindow*, void) mxPopupEndAction->execute(); } +int ScCheckListMenuControl::GetTextWidth(const OUString& rsName) const +{ + return mxDropDown->GetTextWidth(rsName); +} + +int ScCheckListMenuControl::IncreaseWindowWidthToFitText(int nMaxTextWidth) +{ + int nBorder = mxFrame->get_border_width() * 2 + 4; + int nNewWidth = nMaxTextWidth - nBorder; + if (nNewWidth > mnCheckWidthReq) + { + mnCheckWidthReq = nNewWidth; + int nChecksHeight = mpChecks->get_height_rows(9); + mpChecks->set_size_request(mnCheckWidthReq, nChecksHeight); + } + return mnCheckWidthReq + nBorder; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |