LibreOffice Module sc (master)  1
checklistmenu.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <checklistmenu.hxx>
21 #include <o3tl/safeint.hxx>
22 #include <o3tl/string_view.hxx>
23 #include <globstr.hrc>
24 #include <scresid.hxx>
25 
26 #include <vcl/commandevent.hxx>
27 #include <vcl/decoview.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/virdev.hxx>
32 #include <rtl/math.hxx>
33 #include <unotools/charclass.hxx>
34 #include <comphelper/lok.hxx>
35 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
36 #include <tools/json_writer.hxx>
37 #include <svl/numformat.hxx>
38 
39 #include <document.hxx>
40 #include <viewdata.hxx>
41 
42 using namespace com::sun::star;
43 using ::com::sun::star::uno::Reference;
44 
46  : mbEnabled(true)
47 {
48 }
49 
51  : maTimer("sc SubMenuItemData maTimer")
52  , mpSubMenu(nullptr)
53  , mnMenuPos(MENU_NOT_SELECTED)
54  , mpParent(pParent)
55 {
57  maTimer.SetTimeout(Application::GetSettings().GetMouseSettings().GetMenuDelay());
58 }
59 
61 {
62  mpSubMenu = nullptr;
63  mnMenuPos = MENU_NOT_SELECTED;
64  maTimer.Stop();
65 }
66 
68 {
69  mpParent->handleMenuTimeout(this);
70 }
71 
73 {
74  executeMenuItem(mxMenu->get_selected_index());
75  return true;
76 }
77 
78 IMPL_LINK(ScCheckListMenuControl, MenuKeyInputHdl, const KeyEvent&, rKEvt, bool)
79 {
80  const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
81 
82  switch (rKeyCode.GetCode())
83  {
84  case KEY_RIGHT:
85  {
87  break;
88 
89  const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
90  if (!rMenu.mxSubMenuWin)
91  break;
92 
94  }
95  }
96 
97  return false;
98 }
99 
101 {
102  sal_uInt32 nSelectedMenu = MENU_NOT_SELECTED;
103  if (!mxMenu->get_selected(mxScratchIter.get()))
104  {
105  // reselect current item if its submenu is up and the launching item
106  // became unselected by mouse moving out of the top level menu
107  if (mnSelectedMenu < maMenuItems.size() &&
108  maMenuItems[mnSelectedMenu].mxSubMenuWin &&
109  maMenuItems[mnSelectedMenu].mxSubMenuWin->IsVisible())
110  {
111  mxMenu->select(mnSelectedMenu);
112  return;
113  }
114  }
115  else
116  nSelectedMenu = mxMenu->get_iter_index_in_parent(*mxScratchIter);
117 
118  setSelectedMenuItem(nSelectedMenu);
119 }
120 
121 void ScCheckListMenuControl::addMenuItem(const OUString& rText, Action* pAction)
122 {
123  MenuItemData aItem;
124  aItem.mbEnabled = true;
125  aItem.mxAction.reset(pAction);
126  maMenuItems.emplace_back(std::move(aItem));
127 
128  mxMenu->show();
129  mxMenu->append_text(rText);
130  mxMenu->set_image(mxMenu->n_children() - 1, css::uno::Reference<css::graphic::XGraphic>(), 1);
131 }
132 
134 {
135  MenuItemData aItem;
136  maMenuItems.emplace_back(std::move(aItem));
137 
138  mxMenu->append_separator("separator" + OUString::number(maMenuItems.size()));
139 }
140 
141 IMPL_LINK(ScCheckListMenuControl, TreeSizeAllocHdl, const Size&, rSize, void)
142 {
143  if (maAllocatedSize == rSize)
144  return;
145  maAllocatedSize = rSize;
146  SetDropdownPos();
147  if (!mnAsyncSetDropdownPosId && Application::GetToolkitName().startsWith("gtk"))
148  {
149  // for gtk retry again later in case it didn't work (wayland)
151  }
152 }
153 
155 {
156  std::vector<int> aWidths
157  {
158  o3tl::narrowing<int>(maAllocatedSize.Width() - (mxMenu->get_text_height() * 3) / 4 - 6)
159  };
160  mxMenu->set_column_fixed_widths(aWidths);
161 }
162 
163 IMPL_LINK_NOARG(ScCheckListMenuControl, SetDropdownPosHdl, void*, void)
164 {
165  mnAsyncSetDropdownPosId = nullptr;
166  SetDropdownPos();
167  mxMenu->queue_resize();
168 }
169 
171 {
172  int nWidth = (mxMenu->get_text_height() * 3) / 4;
173  mxDropDown->SetOutputSizePixel(Size(nWidth, nWidth));
174  DecorationView aDecoView(mxDropDown.get());
175  aDecoView.DrawSymbol(tools::Rectangle(Point(0, 0), Size(nWidth, nWidth)),
176  SymbolType::SPIN_RIGHT, mxDropDown->GetTextColor(),
177  DrawSymbolFlags::NONE);
178 }
179 
180 ScListSubMenuControl* ScCheckListMenuControl::addSubMenuItem(const OUString& rText, bool bEnabled, bool bColorMenu)
181 {
182  MenuItemData aItem;
183  aItem.mbEnabled = bEnabled;
184 
185  aItem.mxSubMenuWin.reset(new ScListSubMenuControl(mxMenu.get(), *this, bColorMenu));
186  maMenuItems.emplace_back(std::move(aItem));
187 
188  mxMenu->show();
189  mxMenu->append_text(rText);
190  mxMenu->set_image(mxMenu->n_children() - 1, *mxDropDown, 1);
191  return maMenuItems.back().mxSubMenuWin.get();
192 }
193 
195 {
196  if (nPos >= maMenuItems.size())
197  return;
198 
199  const MenuItemData& rMenu = maMenuItems[nPos];
200  if (rMenu.mxSubMenuWin)
201  {
202  if (rMenu.mbEnabled)
203  {
204  maOpenTimer.mnMenuPos = nPos;
205  maOpenTimer.mpSubMenu = rMenu.mxSubMenuWin.get();
206  launchSubMenu();
207  }
208  return;
209  }
210 
211  if (!maMenuItems[nPos].mxAction)
212  // no action is defined.
213  return;
214 
215  const bool bClosePopup = maMenuItems[nPos].mxAction->execute();
216  if (bClosePopup)
218 }
219 
221 {
222  if (mnSelectedMenu == nPos)
223  // nothing to do.
224  return;
225 
226  selectMenuItem(nPos, /*bSubMenuTimer*/true);
227 }
228 
230 {
231  if (pTimer == &maOpenTimer)
232  {
233  // Close any open submenu immediately.
235  {
237  maCloseTimer.mpSubMenu = nullptr;
239  }
240 
241  launchSubMenu();
242  }
243  else if (pTimer == &maCloseTimer)
244  {
245  // end submenu.
247  {
249  maCloseTimer.mpSubMenu = nullptr;
250 
251  // EndPopup sends a user event, and we want this focus to be set after that has done its conflicting focus-setting work
254  }
255  }
256 }
257 
259 {
260  if (!pMenu)
261  return;
262 
263  // Set the submenu on launch queue.
265  {
266  if (maOpenTimer.mpSubMenu != pMenu)
267  {
268  // new submenu is being requested.
270  }
271  else
272  {
273  if (pMenu == maCloseTimer.mpSubMenu)
275  }
276  }
277 
278  maOpenTimer.mpSubMenu = pMenu;
279  maOpenTimer.mnMenuPos = nPos;
282  else
284 }
285 
287 {
288  if (!maOpenTimer.mpSubMenu)
289  // There is no submenu to close.
290  return;
291 
292  // Stop any submenu on queue for opening.
294 
295  // Flush any pending close so it doesn't get skipped
297  {
299  }
300 
303  maOpenTimer.mpSubMenu = nullptr;
305 
308  else
310 }
311 
313 {
314  if (!mxMenu->get_selected(mxScratchIter.get()))
315  return tools::Rectangle();
316  return mxMenu->get_row_area(*mxScratchIter);
317 }
318 
320 {
322  if (!pSubMenu)
323  return;
324 
325  if (!mxMenu->get_selected(mxScratchIter.get()))
326  return;
327 
329  pSubMenu->StartPopupMode(mxMenu.get(), aRect);
330 
331  mxMenu->select(*mxScratchIter);
332  pSubMenu->GrabFocus();
333 }
334 
335 IMPL_LINK_NOARG(ScCheckListMenuControl, PostPopdownHdl, void*, void)
336 {
337  mnAsyncPostPopdownId = nullptr;
338  mxMenu->grab_focus();
339 }
340 
341 IMPL_LINK(ScCheckListMenuControl, MouseEnterHdl, const MouseEvent&, rMEvt, bool)
342 {
343  if (!rMEvt.IsEnterWindow())
344  return false;
346  return false;
347 }
348 
350 {
351  rSubMenu.EndPopupMode();
352  maOpenTimer.reset();
353 
354  // EndPopup sends a user event, and we want this focus to be set after that has done its conflicting focus-setting work
357 
358  size_t nMenuPos = getSubMenuPos(&rSubMenu);
359  if (nMenuPos != MENU_NOT_SELECTED)
360  {
361  mnSelectedMenu = nMenuPos;
362  mxMenu->select(mnSelectedMenu);
363  }
364 }
365 
366 void ScCheckListMenuControl::selectMenuItem(size_t nPos, bool bSubMenuTimer)
367 {
368  mxMenu->select(nPos == MENU_NOT_SELECTED ? -1 : nPos);
369  mnSelectedMenu = nPos;
370 
371  if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
372  {
374  return;
375  }
376 
377  if (!maMenuItems[nPos].mbEnabled)
378  {
380  return;
381  }
382 
383  if (bSubMenuTimer)
384  {
385  if (maMenuItems[nPos].mxSubMenuWin && mxMenu->changed_by_hover())
386  {
387  ScListSubMenuControl* pSubMenu = maMenuItems[nPos].mxSubMenuWin.get();
388  queueLaunchSubMenu(nPos, pSubMenu);
389  }
390  else
392  }
393 }
394 
396 {
398 }
399 
401 {
402  size_t n = maMenuItems.size();
403  for (size_t i = 0; i < n; ++i)
404  {
405  if (maMenuItems[i].mxSubMenuWin.get() == pSubMenu)
406  return i;
407  }
408  return MENU_NOT_SELECTED;
409 }
410 
412 {
414  size_t nMenuPos = getSubMenuPos(pSubMenu);
415  if (mnSelectedMenu != nMenuPos)
416  {
417  mnSelectedMenu = nMenuPos;
418  mxMenu->select(mnSelectedMenu);
419  }
420 }
421 
423 {
424  if (!mbIsPoppedUp)
425  return;
426  mxPopover->connect_closed(Link<weld::Popover&, void>());
427  mxPopover->popdown();
428  PopupModeEndHdl(*mxPopover);
429  assert(mbIsPoppedUp == false);
430 }
431 
433 {
434  mxPopover->connect_closed(LINK(this, ScCheckListMenuControl, PopupModeEndHdl));
435  mbIsPoppedUp = true;
436  mxPopover->popup_at_rect(pParent, rRect);
437  GrabFocus();
438 }
439 
441 {
442  EndPopupMode();
443 }
444 
446  mbAllowEmptySet(true), mbRTL(false)
447 {
448 }
449 
451  : mnValue(0.0)
452  , mbVisible(true)
453  , mbDate(false)
454  , mbLeaf(false)
455  , mbValue(false)
456  , meDatePartType(YEAR)
457 {
458 }
459 
460 // the value of border-width of FilterDropDown
461 constexpr int nBorderWidth = 4;
462 // number of rows visible in checklist
463 constexpr int nCheckListVisibleRows = 9;
464 // number of rows visible in colorlist
465 constexpr int nColorListVisibleRows = 9;
466 
468  bool bHasDates, int nWidth)
469  : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterdropdown.ui"))
470  , mxPopover(mxBuilder->weld_popover("FilterDropDown"))
471  , mxContainer(mxBuilder->weld_container("container"))
472  , mxMenu(mxBuilder->weld_tree_view("menu"))
473  , mxScratchIter(mxMenu->make_iterator())
474  , mxNonMenu(mxBuilder->weld_widget("nonmenu"))
475  , mxEdSearch(mxBuilder->weld_entry("search_edit"))
476  , mxBox(mxBuilder->weld_widget("box"))
477  , mxListChecks(mxBuilder->weld_tree_view("check_list_box"))
478  , mxTreeChecks(mxBuilder->weld_tree_view("check_tree_box"))
479  , mxChkToggleAll(mxBuilder->weld_check_button("toggle_all"))
480  , mxBtnSelectSingle(mxBuilder->weld_button("select_current"))
481  , mxBtnUnselectSingle(mxBuilder->weld_button("unselect_current"))
482  , mxButtonBox(mxBuilder->weld_box("buttonbox"))
483  , mxBtnOk(mxBuilder->weld_button("ok"))
484  , mxBtnCancel(mxBuilder->weld_button("cancel"))
485  , mxContextMenu(mxBuilder->weld_menu("contextmenu"))
486  , mxDropDown(mxMenu->create_virtual_device())
487  , mnCheckWidthReq(-1)
488  , mnWndWidth(0)
489  , mnCheckListVisibleRows(nCheckListVisibleRows)
490  , mePrevToggleAllState(TRISTATE_INDET)
491  , mnSelectedMenu(MENU_NOT_SELECTED)
492  , mrViewData(rViewData)
493  , mnAsyncPostPopdownId(nullptr)
494  , mnAsyncSetDropdownPosId(nullptr)
495  , mbHasDates(bHasDates)
496  , mbIsPoppedUp(false)
497  , maOpenTimer(this)
498  , maCloseTimer(this)
499 {
500  mxTreeChecks->set_clicks_to_toggle(1);
501  mxListChecks->set_clicks_to_toggle(1);
502 
503  mxNonMenu->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
504  mxEdSearch->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
505  mxListChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
506  mxTreeChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
507  mxListChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
508  mxTreeChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
509  mxChkToggleAll->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
510  mxBtnSelectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
511  mxBtnUnselectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
512  mxBtnOk->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
513  mxBtnCancel->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
514 
515  /*
516  tdf#136559 If we have no dates we don't need a tree
517  structure, just a list. GtkListStore can be then
518  used which is much faster than a GtkTreeStore, so
519  with no dates switch to the treeview which uses the
520  faster GtkListStore
521  */
522  if (mbHasDates)
523  mpChecks = mxTreeChecks.get();
524  else
525  {
526  mxTreeChecks->hide();
527  mxListChecks->show();
528  mpChecks = mxListChecks.get();
529  }
530 
531  int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
532  if (nWidth != -1)
533  {
534  mnCheckWidthReq = nWidth - nBorderWidth * 2 - 4;
535  mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
536  mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
537  }
538 
539  // sort ok/cancel into native order, if this was a dialog they would be auto-sorted, but this
540  // popup isn't a true dialog
541  mxButtonBox->sort_native_button_order();
542 
543  mxTreeChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
544  mxListChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
545 
546  mxBox->show();
547  mxEdSearch->show();
548  mxButtonBox->show();
549 
550  mxMenu->connect_row_activated(LINK(this, ScCheckListMenuControl, RowActivatedHdl));
551  mxMenu->connect_changed(LINK(this, ScCheckListMenuControl, SelectHdl));
552  mxMenu->connect_key_press(LINK(this, ScCheckListMenuControl, MenuKeyInputHdl));
553 
554  mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
555  mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
556  mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl));
557  mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl));
558  mxTreeChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
559  mxTreeChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
560  mxListChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
561  mxListChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
562  mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, TriStateHdl));
563  mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
564  mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
565 
566  CreateDropDown();
567  mxMenu->connect_size_allocate(LINK(this, ScCheckListMenuControl, TreeSizeAllocHdl));
568 
569  // determine what width the checklist will end up with
570  mnCheckWidthReq = mxContainer->get_preferred_size().Width();
571  // make that size fixed now, we can now use mnCheckWidthReq to speed up
572  // bulk_insert_for_each
573  mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
574  mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
575 }
576 
578 {
579  if (mxEdSearch->get_visible())
580  mxEdSearch->grab_focus();
581  else
582  {
583  mxMenu->set_cursor(0);
584  mxMenu->grab_focus();
585  }
586 }
587 
589 {
591  {
593  mnAsyncPostPopdownId = nullptr;
594  }
596  {
598  mnAsyncSetDropdownPosId = nullptr;
599  }
600 }
601 
603 {
604  EndPopupMode();
605  for (auto& rMenuItem : maMenuItems)
606  rMenuItem.mxSubMenuWin.reset();
608 }
609 
611 {
612  mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height() + 2);
614  if (mxMenu->n_children())
615  {
616  mxMenu->set_cursor(0);
617  mxMenu->unselect_all();
618  }
619 
620  mnWndWidth = mxContainer->get_preferred_size().Width() + nBorderWidth * 2 + 4;
621 }
622 
624 {
625  mpChecks->all_foreach([this, bSet](weld::TreeIter& rEntry){
626  mpChecks->set_toggle(rEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE);
627  return false;
628  });
629 
631  {
632  // We need to have at least one member selected.
633  mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
634  }
635 }
636 
638 {
639  setAllMemberState(!bSet);
640  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
641  if (!mpChecks->get_cursor(xEntry.get()))
642  return;
643  mpChecks->set_toggle(*xEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE);
644 }
645 
646 IMPL_LINK(ScCheckListMenuControl, CommandHdl, const CommandEvent&, rCEvt, bool)
647 {
648  if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
649  return false;
650 
651  mxContextMenu->set_sensitive("less", mnCheckListVisibleRows > 4);
652  mxContextMenu->set_sensitive("more", mnCheckListVisibleRows < 42);
653 
654  OString sCommand = mxContextMenu->popup_at_rect(mpChecks, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
655  if (sCommand.isEmpty())
656  return true;
657 
658  if (sCommand == "more")
659  ++mnCheckListVisibleRows;
660  else if (sCommand == "less")
661  --mnCheckListVisibleRows;
662  ResizeToRequest();
663 
664  return true;
665 }
666 
668 {
669  int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
670  mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
671  mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
672  mxPopover->resize_to_request();
673 }
674 
676 {
677  if (&rBtn == mxBtnOk.get())
678  close(true);
679  else if (&rBtn == mxBtnCancel.get())
680  close(false);
681  else if (&rBtn == mxBtnSelectSingle.get() || &rBtn == mxBtnUnselectSingle.get())
682  {
683  selectCurrentMemberOnly(&rBtn == mxBtnSelectSingle.get());
684  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
685  if (!mpChecks->get_cursor(xEntry.get()))
686  xEntry.reset();
687  Check(xEntry.get());
688  }
689 }
690 
692 {
693  switch (mePrevToggleAllState)
694  {
695  case TRISTATE_FALSE:
696  mxChkToggleAll->set_state(TRISTATE_TRUE);
697  setAllMemberState(true);
698  break;
699  case TRISTATE_TRUE:
700  mxChkToggleAll->set_state(TRISTATE_FALSE);
701  setAllMemberState(false);
702  break;
703  case TRISTATE_INDET:
704  default:
705  mxChkToggleAll->set_state(TRISTATE_TRUE);
706  setAllMemberState(true);
707  break;
708  }
709 
710  mePrevToggleAllState = mxChkToggleAll->get_state();
711 }
712 
713 namespace
714 {
715  void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, const ScCheckListMember& rMember, bool bChecked)
716  {
717  OUString aLabel = rMember.maName;
718  if (aLabel.isEmpty())
719  aLabel = ScResId(STR_EMPTYDATA);
720  rView.set_toggle(rIter, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
721  rView.set_text(rIter, aLabel, 0);
722  }
723 }
724 
726 {
727  OUString aSearchText = mxEdSearch->get_text();
728  aSearchText = ScGlobal::getCharClass().lowercase( aSearchText );
729  bool bSearchTextEmpty = aSearchText.isEmpty();
730  size_t n = maMembers.size();
731  size_t nSelCount = 0;
732 
733  // This branch is the general case, the other is an optimized variant of
734  // this one where we can take advantage of knowing we have no hierarchy
735  if (mbHasDates)
736  {
737  mpChecks->freeze();
738 
739  bool bSomeDateDeletes = false;
740 
741  for (size_t i = 0; i < n; ++i)
742  {
743  bool bIsDate = maMembers[i].mbDate;
744  bool bPartialMatch = false;
745 
746  OUString aLabelDisp = maMembers[i].maName;
747  if ( aLabelDisp.isEmpty() )
748  aLabelDisp = ScResId( STR_EMPTYDATA );
749 
750  if ( !bSearchTextEmpty )
751  {
752  if ( !bIsDate )
753  bPartialMatch = ( ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1 );
754  else if ( maMembers[i].meDatePartType == ScCheckListMember::DAY ) // Match with both numerical and text version of month
755  bPartialMatch = (ScGlobal::getCharClass().lowercase( OUString(
756  maMembers[i].maRealName + maMembers[i].maDateParts[1] )).indexOf( aSearchText ) != -1);
757  else
758  continue;
759  }
760  else if ( bIsDate && maMembers[i].meDatePartType != ScCheckListMember::DAY )
761  continue;
762 
763  if ( bSearchTextEmpty )
764  {
765  auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i], true, maMembers[i].mbVisible);
766  updateMemberParents(xLeaf.get(), i);
767  if ( maMembers[i].mbVisible )
768  ++nSelCount;
769  continue;
770  }
771 
772  if ( bPartialMatch )
773  {
774  auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i]);
775  updateMemberParents(xLeaf.get(), i);
776  ++nSelCount;
777  }
778  else
779  {
780  ShowCheckEntry(aLabelDisp, maMembers[i], false, false);
781  if( bIsDate )
782  bSomeDateDeletes = true;
783  }
784  }
785 
786  if ( bSomeDateDeletes )
787  {
788  for (size_t i = 0; i < n; ++i)
789  {
790  if (!maMembers[i].mbDate)
791  continue;
792  if (maMembers[i].meDatePartType != ScCheckListMember::DAY)
793  continue;
794  updateMemberParents(nullptr, i);
795  }
796  }
797 
798  mpChecks->thaw();
799  }
800  else
801  {
802  mpChecks->freeze();
803 
804  // when there are a lot of rows, it is cheaper to simply clear the tree and either
805  // re-initialise or just insert the filtered lines
806  mpChecks->clear();
807 
808  mpChecks->thaw();
809 
810  if (bSearchTextEmpty)
811  nSelCount = initMembers();
812  else
813  {
814  std::vector<size_t> aShownIndexes;
815 
816  for (size_t i = 0; i < n; ++i)
817  {
818  assert(!maMembers[i].mbDate);
819 
820  OUString aLabelDisp = maMembers[i].maName;
821  if ( aLabelDisp.isEmpty() )
822  aLabelDisp = ScResId( STR_EMPTYDATA );
823 
824  bool bPartialMatch = ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1;
825 
826  if (!bPartialMatch)
827  continue;
828 
829  aShownIndexes.push_back(i);
830  }
831 
832  std::vector<int> aFixedWidths { mnCheckWidthReq };
833  // tdf#122419 insert in the fastest order, this might be backwards.
834  mpChecks->bulk_insert_for_each(aShownIndexes.size(), [this, &aShownIndexes, &nSelCount](weld::TreeIter& rIter, int i) {
835  size_t nIndex = aShownIndexes[i];
836  insertMember(*mpChecks, rIter, maMembers[nIndex], true);
837  ++nSelCount;
838  }, nullptr, &aFixedWidths);
839  }
840  }
841 
842  if ( nSelCount == n )
843  mxChkToggleAll->set_state( TRISTATE_TRUE );
844  else if ( nSelCount == 0 )
845  mxChkToggleAll->set_state( TRISTATE_FALSE );
846  else
847  mxChkToggleAll->set_state( TRISTATE_INDET );
848 
849  if ( !maConfig.mbAllowEmptySet )
850  {
851  const bool bEmptySet( nSelCount == 0 );
852  mpChecks->set_sensitive(!bEmptySet);
853  mxChkToggleAll->set_sensitive(!bEmptySet);
854  mxBtnSelectSingle->set_sensitive(!bEmptySet);
855  mxBtnUnselectSingle->set_sensitive(!bEmptySet);
856  mxBtnOk->set_sensitive(!bEmptySet);
857  }
858 }
859 
861 {
862  if (mxBtnOk->get_sensitive())
863  close(true);
864  return true;
865 }
866 
867 IMPL_LINK( ScCheckListMenuControl, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void )
868 {
869  Check(&rRowCol.first);
870 }
871 
873 {
874  if (pEntry)
875  CheckEntry(*pEntry, mpChecks->get_toggle(*pEntry) == TRISTATE_TRUE);
876  size_t nNumChecked = GetCheckedEntryCount();
877  if (nNumChecked == maMembers.size())
878  // all members visible
879  mxChkToggleAll->set_state(TRISTATE_TRUE);
880  else if (nNumChecked == 0)
881  // no members visible
882  mxChkToggleAll->set_state(TRISTATE_FALSE);
883  else
884  mxChkToggleAll->set_state(TRISTATE_INDET);
885 
887  // We need to have at least one member selected.
888  mxBtnOk->set_sensitive(nNumChecked != 0);
889 
890  mePrevToggleAllState = mxChkToggleAll->get_state();
891 }
892 
894 {
895  if ( !maMembers[nIdx].mbDate || maMembers[nIdx].meDatePartType != ScCheckListMember::DAY )
896  return;
897 
898  OUString aYearName = maMembers[nIdx].maDateParts[0];
899  OUString aMonthName = maMembers[nIdx].maDateParts[1];
900  auto aItr = maYearMonthMap.find(aYearName + aMonthName);
901 
902  if ( pLeaf )
903  {
904  std::unique_ptr<weld::TreeIter> xYearEntry;
905  std::unique_ptr<weld::TreeIter> xMonthEntry = mpChecks->make_iterator(pLeaf);
906  if (!mpChecks->iter_parent(*xMonthEntry))
907  xMonthEntry.reset();
908  else
909  {
910  xYearEntry = mpChecks->make_iterator(xMonthEntry.get());
911  if (!mpChecks->iter_parent(*xYearEntry))
912  xYearEntry.reset();
913  }
914 
915  maMembers[nIdx].mxParent = std::move(xMonthEntry);
916  if ( aItr != maYearMonthMap.end() )
917  {
918  size_t nMonthIdx = aItr->second;
919  maMembers[nMonthIdx].mxParent = std::move(xYearEntry);
920  }
921  }
922  else
923  {
924  std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
925  if (aItr != maYearMonthMap.end() && !xYearEntry)
926  {
927  size_t nMonthIdx = aItr->second;
928  maMembers[nMonthIdx].mxParent.reset();
929  maMembers[nIdx].mxParent.reset();
930  }
931  else if (xYearEntry && !FindEntry(xYearEntry.get(), aMonthName))
932  maMembers[nIdx].mxParent.reset();
933  }
934 }
935 
937 {
938  maMembers.reserve(n);
939 }
940 
941 void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, bool bVisible)
942 {
944 
945  // Convert the numeric date value to a date object.
946  Date aDate = pFormatter->GetNullDate();
947  aDate.AddDays(rtl::math::approxFloor(nVal));
948 
949  sal_Int16 nYear = aDate.GetYear();
950  sal_uInt16 nMonth = aDate.GetMonth();
951  sal_uInt16 nDay = aDate.GetDay();
952 
953  // Get the localized month name list.
954  CalendarWrapper& rCalendar = ScGlobal::GetCalendar();
955  uno::Sequence<i18n::CalendarItem2> aMonths = rCalendar.getMonths();
956  if (aMonths.getLength() < nMonth)
957  return;
958 
959  OUString aYearName = OUString::number(nYear);
960  OUString aMonthName = aMonths[nMonth-1].FullName;
961  OUString aDayName = OUString::number(nDay);
962 
963  if ( aDayName.getLength() == 1 )
964  aDayName = "0" + aDayName;
965 
966  mpChecks->freeze();
967 
968  std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
969  if (!xYearEntry)
970  {
971  xYearEntry = mpChecks->make_iterator();
972  mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
973  mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
974  mpChecks->set_text(*xYearEntry, aYearName, 0);
975  ScCheckListMember aMemYear;
976  aMemYear.maName = aYearName;
977  aMemYear.maRealName = rsName;
978  aMemYear.mbDate = true;
979  aMemYear.mbLeaf = false;
980  aMemYear.mbVisible = bVisible;
981  aMemYear.mxParent.reset();
983  maMembers.emplace_back(std::move(aMemYear));
984  }
985 
986  std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), aMonthName);
987  if (!xMonthEntry)
988  {
989  xMonthEntry = mpChecks->make_iterator();
990  mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
991  mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
992  mpChecks->set_text(*xMonthEntry, aMonthName, 0);
993  ScCheckListMember aMemMonth;
994  aMemMonth.maName = aMonthName;
995  aMemMonth.maRealName = rsName;
996  aMemMonth.mbDate = true;
997  aMemMonth.mbLeaf = false;
998  aMemMonth.mbVisible = bVisible;
999  aMemMonth.mxParent = std::move(xYearEntry);
1001  maMembers.emplace_back(std::move(aMemMonth));
1002  maYearMonthMap[aYearName + aMonthName] = maMembers.size() - 1;
1003  }
1004 
1005  std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), aDayName);
1006  if (!xDayEntry)
1007  {
1008  xDayEntry = mpChecks->make_iterator();
1009  mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
1010  mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
1011  mpChecks->set_text(*xDayEntry, aDayName, 0);
1012  ScCheckListMember aMemDay;
1013  aMemDay.maName = aDayName;
1014  aMemDay.maRealName = rsName;
1015  aMemDay.maDateParts.resize(2);
1016  aMemDay.maDateParts[0] = aYearName;
1017  aMemDay.maDateParts[1] = aMonthName;
1018  aMemDay.mbDate = true;
1019  aMemDay.mbLeaf = true;
1020  aMemDay.mbVisible = bVisible;
1021  aMemDay.mxParent = std::move(xMonthEntry);
1023  maMembers.emplace_back(std::move(aMemDay));
1024  }
1025 
1026  mpChecks->thaw();
1027 }
1028 
1029 void ScCheckListMenuControl::addMember(const OUString& rName, const double nVal, bool bVisible, bool bValue)
1030 {
1031  ScCheckListMember aMember;
1032  // tdf#46062 - indicate hidden whitespaces using quotes
1033  aMember.maName = o3tl::trim(rName) != rName ? "\"" + rName + "\"" : rName;
1034  aMember.maRealName = rName;
1035  aMember.mnValue = nVal;
1036  aMember.mbDate = false;
1037  aMember.mbLeaf = true;
1038  aMember.mbValue = bValue;
1039  aMember.mbVisible = bVisible;
1040  aMember.mxParent.reset();
1041  maMembers.emplace_back(std::move(aMember));
1042 }
1043 
1044 std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode)
1045 {
1046  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(pParent);
1047  bool bEntry = pParent ? mpChecks->iter_children(*xEntry) : mpChecks->get_iter_first(*xEntry);
1048  while (bEntry)
1049  {
1050  if (sNode == mpChecks->get_text(*xEntry, 0))
1051  return xEntry;
1052  bEntry = mpChecks->iter_next_sibling(*xEntry);
1053  }
1054  return nullptr;
1055 }
1056 
1057 void ScCheckListMenuControl::GetRecursiveChecked(const weld::TreeIter* pEntry, std::unordered_set<OUString>& vOut,
1058  OUString& rLabel)
1059 {
1060  if (mpChecks->get_toggle(*pEntry) != TRISTATE_TRUE)
1061  return;
1062 
1063  // We have to hash parents and children together.
1064  // Per convention for easy access in getResult()
1065  // "child;parent;grandparent" while descending.
1066  if (rLabel.isEmpty())
1067  rLabel = mpChecks->get_text(*pEntry, 0);
1068  else
1069  rLabel = mpChecks->get_text(*pEntry, 0) + ";" + rLabel;
1070 
1071  // Prerequisite: the selection mechanism guarantees that if a child is
1072  // selected then also the parent is selected, so we only have to
1073  // inspect the children in case the parent is selected.
1074  if (!mpChecks->iter_has_child(*pEntry))
1075  return;
1076 
1077  std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(pEntry));
1078  bool bChild = mpChecks->iter_children(*xChild);
1079  while (bChild)
1080  {
1081  OUString aLabel = rLabel;
1082  GetRecursiveChecked(xChild.get(), vOut, aLabel);
1083  if (!aLabel.isEmpty() && aLabel != rLabel)
1084  vOut.insert(aLabel);
1085  bChild = mpChecks->iter_next_sibling(*xChild);
1086  }
1087  // Let the caller not add the parent alone.
1088  rLabel.clear();
1089 }
1090 
1091 std::unordered_set<OUString> ScCheckListMenuControl::GetAllChecked()
1092 {
1093  std::unordered_set<OUString> vResults(0);
1094 
1095  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1096  bool bEntry = mpChecks->get_iter_first(*xEntry);
1097  while (bEntry)
1098  {
1099  OUString aLabel;
1100  GetRecursiveChecked(xEntry.get(), vResults, aLabel);
1101  if (!aLabel.isEmpty())
1102  vResults.insert(aLabel);
1103  bEntry = mpChecks->iter_next_sibling(*xEntry);
1104  }
1105 
1106  return vResults;
1107 }
1108 
1109 bool ScCheckListMenuControl::IsChecked(std::u16string_view sName, const weld::TreeIter* pParent)
1110 {
1111  std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
1112  return xEntry && mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1113 }
1114 
1115 void ScCheckListMenuControl::CheckEntry(std::u16string_view sName, const weld::TreeIter* pParent, bool bCheck)
1116 {
1117  std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
1118  if (xEntry)
1119  CheckEntry(*xEntry, bCheck);
1120 }
1121 
1122 // Recursively check all children of rParent
1124 {
1125  mpChecks->set_toggle(rParent, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1126  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(&rParent);
1127  bool bEntry = mpChecks->iter_children(*xEntry);
1128  while (bEntry)
1129  {
1130  CheckAllChildren(*xEntry, bCheck);
1131  bEntry = mpChecks->iter_next_sibling(*xEntry);
1132  }
1133 }
1134 
1135 void ScCheckListMenuControl::CheckEntry(const weld::TreeIter& rParent, bool bCheck)
1136 {
1137  // recursively check all items below rParent
1138  CheckAllChildren(rParent, bCheck);
1139  // checking rParent can affect ancestors, e.g. if ancestor is unchecked and rParent is
1140  // now checked then the ancestor needs to be checked also
1141  if (!mpChecks->get_iter_depth(rParent))
1142  return;
1143 
1144  std::unique_ptr<weld::TreeIter> xAncestor(mpChecks->make_iterator(&rParent));
1145  bool bAncestor = mpChecks->iter_parent(*xAncestor);
1146  while (bAncestor)
1147  {
1148  // if any first level children checked then ancestor
1149  // needs to be checked, similarly if no first level children
1150  // checked then ancestor needs to be unchecked
1151  std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(xAncestor.get()));
1152  bool bChild = mpChecks->iter_children(*xChild);
1153  bool bChildChecked = false;
1154 
1155  while (bChild)
1156  {
1157  if (mpChecks->get_toggle(*xChild) == TRISTATE_TRUE)
1158  {
1159  bChildChecked = true;
1160  break;
1161  }
1162  bChild = mpChecks->iter_next_sibling(*xChild);
1163  }
1164  mpChecks->set_toggle(*xAncestor, bChildChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
1165  bAncestor = mpChecks->iter_parent(*xAncestor);
1166  }
1167 }
1168 
1169 std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::ShowCheckEntry(const OUString& sName, ScCheckListMember& rMember, bool bShow, bool bCheck)
1170 {
1171  std::unique_ptr<weld::TreeIter> xEntry;
1172  if (!rMember.mbDate || rMember.mxParent)
1173  xEntry = FindEntry(rMember.mxParent.get(), sName);
1174 
1175  if ( bShow )
1176  {
1177  if (!xEntry)
1178  {
1179  if (rMember.mbDate)
1180  {
1181  if (rMember.maDateParts.empty())
1182  return nullptr;
1183 
1184  std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, rMember.maDateParts[0]);
1185  if (!xYearEntry)
1186  {
1187  xYearEntry = mpChecks->make_iterator();
1188  mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
1189  mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
1190  mpChecks->set_text(*xYearEntry, rMember.maDateParts[0], 0);
1191  }
1192  std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), rMember.maDateParts[1]);
1193  if (!xMonthEntry)
1194  {
1195  xMonthEntry = mpChecks->make_iterator();
1196  mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
1197  mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
1198  mpChecks->set_text(*xMonthEntry, rMember.maDateParts[1], 0);
1199  }
1200  std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), rMember.maName);
1201  if (!xDayEntry)
1202  {
1203  xDayEntry = mpChecks->make_iterator();
1204  mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
1205  mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
1206  mpChecks->set_text(*xDayEntry, rMember.maName, 0);
1207  }
1208  return xDayEntry; // Return leaf node
1209  }
1210 
1211  xEntry = mpChecks->make_iterator();
1212  mpChecks->append(xEntry.get());
1213  mpChecks->set_toggle(*xEntry, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1214  mpChecks->set_text(*xEntry, sName, 0);
1215  }
1216  else
1217  CheckEntry(*xEntry, bCheck);
1218  }
1219  else if (xEntry)
1220  {
1221  mpChecks->remove(*xEntry);
1222  if (rMember.mxParent)
1223  {
1224  std::unique_ptr<weld::TreeIter> xParent(mpChecks->make_iterator(rMember.mxParent.get()));
1225  while (xParent && !mpChecks->iter_has_child(*xParent))
1226  {
1227  std::unique_ptr<weld::TreeIter> xTmp(mpChecks->make_iterator(xParent.get()));
1228  if (!mpChecks->iter_parent(*xParent))
1229  xParent.reset();
1230  mpChecks->remove(*xTmp);
1231  }
1232  }
1233  }
1234  return nullptr;
1235 }
1236 
1238 {
1239  int nRet = 0;
1240 
1241  mpChecks->all_foreach([this, &nRet](weld::TreeIter& rEntry){
1242  if (mpChecks->get_toggle(rEntry) == TRISTATE_TRUE)
1243  ++nRet;
1244  return false;
1245  });
1246 
1247  return nRet;
1248 }
1249 
1250 IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1251 {
1252  const vcl::KeyCode& rKey = rKEvt.GetKeyCode();
1253 
1254  if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
1255  {
1256  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1257  bool bEntry = mpChecks->get_cursor(xEntry.get());
1258  if (bEntry)
1259  {
1260  bool bOldCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1261  CheckEntry(*xEntry, !bOldCheck);
1262  bool bNewCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1263  if (bOldCheck != bNewCheck)
1264  Check(xEntry.get());
1265  }
1266  return true;
1267  }
1268 
1269  return false;
1270 }
1271 
1272 size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth)
1273 {
1274  size_t n = maMembers.size();
1275  size_t nVisMemCount = 0;
1276 
1277  if (nMaxMemberWidth == -1)
1278  nMaxMemberWidth = mnCheckWidthReq;
1279 
1280  if (!mpChecks->n_children() && !mbHasDates)
1281  {
1282  std::vector<int> aFixedWidths { nMaxMemberWidth };
1283  // tdf#134038 insert in the fastest order, this might be backwards so only do it for
1284  // the !mbHasDates case where no entry depends on another to exist before getting
1285  // inserted. We cannot retain pre-existing treeview content, only clear and fill it.
1286  mpChecks->bulk_insert_for_each(n, [this, &nVisMemCount](weld::TreeIter& rIter, int i) {
1287  assert(!maMembers[i].mbDate);
1288  insertMember(*mpChecks, rIter, maMembers[i], maMembers[i].mbVisible);
1289  if (maMembers[i].mbVisible)
1290  ++nVisMemCount;
1291  }, nullptr, &aFixedWidths);
1292  }
1293  else
1294  {
1295  mpChecks->freeze();
1296 
1297  std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1298  std::vector<std::unique_ptr<weld::TreeIter>> aExpandRows;
1299 
1300  for (size_t i = 0; i < n; ++i)
1301  {
1302  if (maMembers[i].mbDate)
1303  {
1304  CheckEntry(maMembers[i].maName, maMembers[i].mxParent.get(), maMembers[i].mbVisible);
1305  // Expand first node of checked dates
1306  if (!maMembers[i].mxParent && IsChecked(maMembers[i].maName, maMembers[i].mxParent.get()))
1307  {
1308  std::unique_ptr<weld::TreeIter> xDateEntry = FindEntry(nullptr, maMembers[i].maName);
1309  if (xDateEntry)
1310  aExpandRows.emplace_back(std::move(xDateEntry));
1311  }
1312  }
1313  else
1314  {
1315  mpChecks->append(xEntry.get());
1316  insertMember(*mpChecks, *xEntry, maMembers[i], maMembers[i].mbVisible);
1317  }
1318 
1319  if (maMembers[i].mbVisible)
1320  ++nVisMemCount;
1321  }
1322 
1323  mpChecks->thaw();
1324 
1325  for (const auto& rRow : aExpandRows)
1326  mpChecks->expand_row(*rRow);
1327  }
1328 
1329  if (nVisMemCount == n)
1330  {
1331  // all members visible
1332  mxChkToggleAll->set_state(TRISTATE_TRUE);
1334  }
1335  else if (nVisMemCount == 0)
1336  {
1337  // no members visible
1338  mxChkToggleAll->set_state(TRISTATE_FALSE);
1340  }
1341  else
1342  {
1343  mxChkToggleAll->set_state(TRISTATE_INDET);
1345  }
1346 
1347  if (nVisMemCount)
1348  mpChecks->select(0);
1349 
1350  return nVisMemCount;
1351 }
1352 
1354 {
1355  maConfig = rConfig;
1356 }
1357 
1359 {
1360  return mxChkToggleAll->get_state() == TRISTATE_TRUE;
1361 }
1362 
1364 {
1365  ResultType aResult;
1366  std::unordered_set<OUString> vCheckeds = GetAllChecked();
1367  size_t n = maMembers.size();
1368  for (size_t i = 0; i < n; ++i)
1369  {
1370  if ( maMembers[i].mbLeaf )
1371  {
1372  OUStringBuffer aLabel(maMembers[i].maName);
1373  if (aLabel.isEmpty())
1374  aLabel = ScResId(STR_EMPTYDATA);
1375 
1376  /* TODO: performance-wise this looks suspicious, concatenating to
1377  * do the lookup for each leaf item seems wasteful. */
1378  // Checked labels are in the form "child;parent;grandparent".
1379  if (maMembers[i].mxParent)
1380  {
1381  std::unique_ptr<weld::TreeIter> xIter(mpChecks->make_iterator(maMembers[i].mxParent.get()));
1382  do
1383  {
1384  aLabel.append(";" + mpChecks->get_text(*xIter));
1385  }
1386  while (mpChecks->iter_parent(*xIter));
1387  }
1388 
1389  bool bState = vCheckeds.find(aLabel.makeStringAndClear()) != vCheckeds.end();
1390 
1391  ResultEntry aResultEntry;
1392  aResultEntry.bValid = bState;
1393  aResultEntry.aName = maMembers[i].maRealName;
1394  aResultEntry.nValue = maMembers[i].mnValue;
1395  aResultEntry.bDate = maMembers[i].mbDate;
1396  aResultEntry.bValue = maMembers[i].mbValue;
1397  aResult.insert(aResultEntry);
1398  }
1399  }
1400  rResult.swap(aResult);
1401 }
1402 
1404 {
1405  prepWindow();
1407  // We need to have at least one member selected.
1408  mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
1409 
1410  tools::Rectangle aRect(rRect);
1411  if (maConfig.mbRTL)
1412  {
1413  // In RTL mode, the logical "left" is visual "right".
1415  {
1416  tools::Long nLeft = aRect.Left() - aRect.GetWidth();
1417  aRect.SetLeft( nLeft );
1418  }
1419  else
1420  {
1421  // in LOK mode, rRect is in document pixel coordinates, so width has to be added
1422  // to place the popup next to the (visual) left aligned button.
1423  aRect.Move(aRect.GetWidth(), 0);
1424  }
1425  }
1426  else if (mnWndWidth < aRect.GetWidth())
1427  {
1428  // Target rectangle (i.e. cell width) is wider than the window.
1429  // Simulate right-aligned launch by modifying the target rectangle
1430  // size.
1431  tools::Long nDiff = aRect.GetWidth() - mnWndWidth;
1432  aRect.AdjustLeft(nDiff );
1433  }
1434 
1435  StartPopupMode(pWidget, aRect);
1436 }
1437 
1439 {
1440  if (bOK && mxOKAction)
1441  mxOKAction->execute();
1442  EndPopupMode();
1443 }
1444 
1445 void ScCheckListMenuControl::setExtendedData(std::unique_ptr<ExtendedData> p)
1446 {
1447  mxExtendedData = std::move(p);
1448 }
1449 
1451 {
1452  return mxExtendedData.get();
1453 }
1454 
1456 {
1457  mxOKAction.reset(p);
1458 }
1459 
1461 {
1462  mxPopupEndAction.reset(p);
1463 }
1464 
1466 {
1467  mbIsPoppedUp = false;
1468  clearSelectedMenuItem();
1469  if (mxPopupEndAction)
1470  mxPopupEndAction->execute();
1471 
1472  DropPendingEvents();
1473 }
1474 
1475 int ScCheckListMenuControl::GetTextWidth(const OUString& rsName) const
1476 {
1477  return mxDropDown->GetTextWidth(rsName);
1478 }
1479 
1481 {
1482  int nBorder = nBorderWidth * 2 + 4;
1483  int nNewWidth = nMaxTextWidth - nBorder;
1484  if (nNewWidth > mnCheckWidthReq)
1485  {
1486  mnCheckWidthReq = nNewWidth;
1487  int nChecksHeight = mpChecks->get_height_rows(nCheckListVisibleRows);
1488  mpChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
1489  }
1490  return mnCheckWidthReq + nBorder;
1491 }
1492 
1494  : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filtersubdropdown.ui"))
1495  , mxPopover(mxBuilder->weld_popover("FilterSubDropDown"))
1496  , mxContainer(mxBuilder->weld_container("container"))
1497  , mxMenu(mxBuilder->weld_tree_view("menu"))
1498  , mxBackColorMenu(mxBuilder->weld_tree_view("background"))
1499  , mxTextColorMenu(mxBuilder->weld_tree_view("textcolor"))
1500  , mxScratchIter(mxMenu->make_iterator())
1501  , mrParentControl(rParentControl)
1502  , mnBackColorMenuPrefHeight(-1)
1503  , mnTextColorMenuPrefHeight(-1)
1504  , mbColorMenu(bColorMenu)
1505 {
1506  mxMenu->hide();
1507  mxBackColorMenu->hide();
1508  mxTextColorMenu->hide();
1509 
1510  if (!bColorMenu)
1511  {
1512  SetupMenu(*mxMenu);
1513  mxMenu->show();
1514  }
1515  else
1516  {
1517  mxBackColorMenu->set_clicks_to_toggle(1);
1518  mxBackColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
1519  mxBackColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
1520  mxTextColorMenu->set_clicks_to_toggle(1);
1521  mxTextColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
1522  mxTextColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
1525  }
1526 }
1527 
1529 {
1530  rMenu.connect_row_activated(LINK(this, ScListSubMenuControl, RowActivatedHdl));
1531  rMenu.connect_key_press(LINK(this, ScListSubMenuControl, MenuKeyInputHdl));
1532 }
1533 
1535 {
1536  if (mxPopupStartAction)
1537  mxPopupStartAction->execute();
1538 
1539  mxPopover->popup_at_rect(pParent, rRect, weld::Placement::End);
1540 
1541  weld::TreeView& rFirstMenu = mbColorMenu ? *mxBackColorMenu : *mxMenu;
1542  rFirstMenu.set_cursor(0);
1543  rFirstMenu.select(0);
1544 
1546 }
1547 
1549 {
1550  mxPopover->popdown();
1551 }
1552 
1554 {
1555  weld::TreeView& rFirstMenu = mbColorMenu ? *mxBackColorMenu : *mxMenu;
1556  rFirstMenu.grab_focus();
1557 }
1558 
1560 {
1561  return mxPopover->get_visible();
1562 }
1563 
1565 {
1566  if (!mbColorMenu)
1567  mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height());
1568  else
1569  {
1570  int nBackColorMenuPrefHeight = mnBackColorMenuPrefHeight;
1571  if (nBackColorMenuPrefHeight == -1)
1572  nBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
1573  mxBackColorMenu->set_size_request(-1, nBackColorMenuPrefHeight);
1574  int nTextColorMenuPrefHeight = mnTextColorMenuPrefHeight;
1575  if (nTextColorMenuPrefHeight == -1)
1576  nTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
1577  mxTextColorMenu->set_size_request(-1, nTextColorMenuPrefHeight);
1578  }
1579 }
1580 
1582 {
1584  aItem.mbEnabled = true;
1585  aItem.mxAction.reset(pAction);
1586  maMenuItems.emplace_back(std::move(aItem));
1587 }
1588 
1590 {
1591  addItem(pAction);
1592  mxMenu->append(weld::toId(pAction), rText);
1593 }
1594 
1595 void ScListSubMenuControl::addMenuColorItem(const OUString& rText, bool bActive, VirtualDevice& rImage,
1596  int nMenu, ScCheckListMenuControl::Action* pAction)
1597 {
1598  addItem(pAction);
1599 
1600  weld::TreeView& rColorMenu = nMenu == 0 ? *mxBackColorMenu : *mxTextColorMenu;
1601  rColorMenu.show();
1602 
1603  OUString sId = weld::toId(pAction);
1604  rColorMenu.insert(nullptr, -1, &rText, &sId, nullptr, nullptr, false, mxScratchIter.get());
1605  rColorMenu.set_toggle(*mxScratchIter, bActive ? TRISTATE_TRUE : TRISTATE_FALSE);
1606  rColorMenu.set_image(*mxScratchIter, rImage);
1607 
1608  if (mnTextColorMenuPrefHeight == -1 &&
1609  &rColorMenu == mxTextColorMenu.get() &&
1610  mxTextColorMenu->n_children() == nColorListVisibleRows)
1611  {
1612  mnTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
1613  }
1614 
1615  if (mnBackColorMenuPrefHeight == -1 &&
1616  &rColorMenu == mxBackColorMenu.get() &&
1617  mxBackColorMenu->n_children() == nColorListVisibleRows)
1618  {
1619  mnBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
1620  }
1621 }
1622 
1624 {
1626  maMenuItems.emplace_back(std::move(aItem));
1627 
1628  mxMenu->append_separator("separator" + OUString::number(maMenuItems.size()));
1629 }
1630 
1632 {
1633  maMenuItems.clear();
1634  mxMenu->clear();
1635  mxBackColorMenu->clear();
1637  mxTextColorMenu->clear();
1639 }
1640 
1641 IMPL_LINK(ScListSubMenuControl, MenuKeyInputHdl, const KeyEvent&, rKEvt, bool)
1642 {
1643  bool bConsumed = false;
1644  const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1645 
1646  switch (rKeyCode.GetCode())
1647  {
1648  case KEY_ESCAPE:
1649  case KEY_LEFT:
1650  {
1651  mrParentControl.endSubMenu(*this);
1652  bConsumed = true;
1653  break;
1654  }
1655  case KEY_SPACE:
1656  case KEY_RETURN:
1657  {
1658  weld::TreeView& rMenu = !mbColorMenu ? *mxMenu :
1659  (mxBackColorMenu->has_focus() ? *mxBackColorMenu : *mxTextColorMenu);
1660  // don't toggle checkbutton, go straight to activating entry
1661  bConsumed = RowActivatedHdl(rMenu);
1662  break;
1663  }
1664  case KEY_DOWN:
1665  {
1666  if (mxTextColorMenu->get_visible() &&
1667  mxBackColorMenu->has_focus() &&
1668  mxBackColorMenu->get_selected_index() == mxBackColorMenu->n_children() - 1)
1669  {
1670  mxBackColorMenu->unselect_all();
1671  mxTextColorMenu->select(0);
1672  mxTextColorMenu->set_cursor(0);
1673  mxTextColorMenu->grab_focus();
1674  bConsumed = true;
1675  }
1676  break;
1677  }
1678  case KEY_UP:
1679  {
1680  if (mxBackColorMenu->get_visible() &&
1681  mxTextColorMenu->has_focus() &&
1682  mxTextColorMenu->get_selected_index() == 0)
1683  {
1684  mxTextColorMenu->unselect_all();
1685  int nIndex = mxBackColorMenu->n_children() - 1;
1686  mxBackColorMenu->select(nIndex);
1687  mxBackColorMenu->set_cursor(nIndex);
1688  mxBackColorMenu->grab_focus();
1689  bConsumed = true;
1690  }
1691  break;
1692  }
1693  }
1694 
1695  return bConsumed;
1696 }
1697 
1698 IMPL_LINK(ScListSubMenuControl, ColorSelChangedHdl, weld::TreeView&, rMenu, void)
1699 {
1700  if (rMenu.get_selected_index() == -1)
1701  return;
1702  if (&rMenu != mxTextColorMenu.get())
1703  mxTextColorMenu->unselect_all();
1704  else
1705  mxBackColorMenu->unselect_all();
1706  rMenu.grab_focus();
1707 }
1708 
1709 IMPL_LINK(ScListSubMenuControl, RowActivatedHdl, weld::TreeView&, rMenu, bool)
1710 {
1711  executeMenuItem(weld::fromId<ScCheckListMenuControl::Action*>(rMenu.get_selected_id()));
1712  return true;
1713 }
1714 
1716 {
1717  // if no action is defined.
1718  if (!pAction)
1719  return;
1720 
1721  const bool bClosePopup = pAction->execute();
1722  if (bClosePopup)
1724 }
1725 
1727 {
1728  mxPopupStartAction.reset(p);
1729 }
1730 
1732 {
1733  EndPopupMode();
1735 }
1736 
1737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void set_text(int row, const OUString &rText, int col=-1)=0
IMPL_LINK_NOARG(ScCheckListMenuControl::SubMenuItemData, TimeoutHdl, Timer *, void)
constexpr int nCheckListVisibleRows
std::set< ResultEntry > ResultType
int mnCheckListVisibleRows
whole window width.
sal_Int32 nIndex
std::unique_ptr< weld::Button > mxBtnUnselectSingle
void updateMemberParents(const weld::TreeIter *pLeaf, size_t nIdx)
void setOKAction(Action *p)
OUString ScResId(TranslateId aId)
Definition: scdll.cxx:90
std::vector< MenuItemData > maMenuItems
void prepWindow()
Calculate the appropriate window size based on the menu items.
virtual bool iter_parent(TreeIter &rIter) const =0
std::unique_ptr< weld::TreeIter > mxParent
std::unique_ptr< weld::Button > mxBtnSelectSingle
virtual void insert(const TreeIter *pParent, int pos, const OUString *pStr, const OUString *pId, const OUString *pIconName, VirtualDevice *pImageSurface, bool bChildrenOnDemand, TreeIter *pRet)=0
void executeMenuItem(size_t nPos)
constexpr tools::Long Left() const
int mnWndWidth
matching width request for mxChecks
void CheckEntry(std::u16string_view sName, const weld::TreeIter *pParent, bool bCheck)
long Long
WeakReference< XInterface > mxParent
static const AllSettings & GetSettings()
sal_Int64 n
void addMenuColorItem(const OUString &rText, bool bActive, VirtualDevice &rImage, int nMenu, ScCheckListMenuControl::Action *pAction)
Reference< container::XNameAccess > mxContainer
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
std::unique_ptr< weld::TreeView > mxMenu
sal_uInt16 GetCode() const
ImplSVEvent * mnAsyncPostPopdownId
std::unordered_set< OUString > GetAllChecked()
ScDocument & GetDocument() const
Definition: viewdata.hxx:380
void selectMenuItem(size_t nPos, bool bSubMenuTimer)
This class implements a popup window for the auto filter dropdown.
constexpr sal_uInt16 KEY_SPACE
std::unique_ptr< ScCheckListMenuControl::Action > mxPopupStartAction
std::unique_ptr< weld::TreeIter > FindEntry(const weld::TreeIter *pParent, std::u16string_view sNode)
constexpr int nBorderWidth
TRISTATE_TRUE
void CheckAllChildren(const weld::TreeIter &rEntry, bool bCheck)
virtual TriState get_toggle(int row, int col=-1) const =0
constexpr sal_uInt16 KEY_UP
virtual std::unique_ptr< TreeIter > make_iterator(const TreeIter *pOrig=nullptr) const =0
virtual void thaw()=0
IMPL_LINK(ScCheckListMenuControl, MenuKeyInputHdl, const KeyEvent &, rKEvt, bool)
constexpr tools::Long Width() const
std::unique_ptr< weld::TreeView > mxMenu
ScListSubMenuControl * addSubMenuItem(const OUString &rText, bool bEnabled, bool bColorMenu)
void setPopupEndAction(Action *p)
Action to perform when an event takes place.
Configuration options for this popup window.
virtual void show()=0
std::unique_ptr< weld::TreeIter > mxScratchIter
std::unique_ptr< weld::Widget > mxNonMenu
sal_uInt16 GetMonth() const
void StartPopupMode(weld::Widget *pParent, const tools::Rectangle &rRect)
std::unique_ptr< weld::TreeIter > ShowCheckEntry(const OUString &sName, ScCheckListMember &rMember, bool bShow=true, bool bCheck=true)
std::pair< const TreeIter &, int > iter_col
virtual int n_children() const =0
std::unique_ptr< weld::TreeView > mxTextColorMenu
constexpr tools::Long GetWidth() const
void executeMenuItem(ScCheckListMenuControl::Action *pAction)
void launch(weld::Widget *pWidget, const tools::Rectangle &rRect)
void setAllMemberState(bool bSet)
std::unique_ptr< weld::TreeIter > mxScratchIter
static constexpr size_t MENU_NOT_SELECTED
void setPopupStartAction(ScCheckListMenuControl::Action *p)
TRISTATE_INDET
void connect_row_activated(const Link< TreeView &, bool > &rLink)
constexpr void SetLeft(tools::Long v)
bool IsChecked(std::u16string_view sName, const weld::TreeIter *pParent)
void addMenuItem(const OUString &rText, Action *pAction)
sal_Int16 GetYear() const
void handleMenuTimeout(const SubMenuItemData *pTimer)
constexpr sal_uInt16 KEY_DOWN
static void RemoveUserEvent(ImplSVEvent *nUserEvent)
SubMenuItemData maOpenTimer
std::unique_ptr< Action > mxOKAction
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:459
void getResult(ResultType &rResult)
int IncreaseWindowWidthToFitText(int nMaxTextWidth)
void addMenuItem(const OUString &rText, ScCheckListMenuControl::Action *pAction)
std::vector< OUString > maDateParts
size_t getSubMenuPos(const ScListSubMenuControl *pSubMenu)
void setMemberSize(size_t n)
std::unique_ptr< weld::TreeView > mxTreeChecks
int i
ScCheckListMenuControl & mrParentControl
virtual void set_toggle(int row, TriState eState, int col=-1)=0
ScListSubMenuControl(weld::Widget *pParent, ScCheckListMenuControl &rParentControl, bool bColorMenu)
int GetCheckedEntryCount() const
std::shared_ptr< Action > mxAction
virtual void freeze()=0
std::unique_ptr< weld::CheckButton > mxChkToggleAll
std::vector< ScCheckListMember > maMembers
void terminateAllPopupMenus()
Dismiss all visible popup menus and set focus back to the application window.
static SC_DLLPUBLIC const CharClass & getCharClass()
Definition: global.cxx:1024
TRISTATE_FALSE
std::map< OUString, size_t > maYearMonthMap
virtual bool iter_has_child(const TreeIter &rIter) const =0
std::unique_ptr< weld::Box > mxButtonBox
static OUString GetToolkitName()
virtual void Start(bool bStartTimer=true) override
std::unique_ptr< weld::Button > mxBtnOk
size_t initMembers(int nMaxMemberWidth=-1)
void endSubMenu(ScListSubMenuControl &rSubMenu)
ExtendedData * getExtendedData()
Get the store auxiliary data, or NULL if no such data is stored.
sal_uInt16 GetDay() const
void setExtendedData(std::unique_ptr< ExtendedData > p)
Set auxiliary data that the client code might need.
virtual void Invoke() override
void AddDays(sal_Int32 nAddDays)
virtual OUString get_text(int row, int col=-1) const =0
void setConfig(const Config &rConfig)
OUString lowercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
tools::Rectangle GetSubMenuParentRect()
Get the area of the active row.
constexpr int nColorListVisibleRows
virtual void expand_row(const TreeIter &rIter)=0
void SetTimeout(sal_uInt64 nTimeoutMs)
DatePartType meDatePartType
virtual bool get_cursor(TreeIter *pIter) const =0
virtual void connect_key_press(const Link< const KeyEvent &, bool > &rLink)
void StartPopupMode(weld::Widget *pParent, const tools::Rectangle &rRect)
std::unique_ptr< ExtendedData > mxExtendedData
constexpr sal_uInt16 KEY_RETURN
SubMenuItemData(ScCheckListMenuControl *pParent)
bool close
constexpr sal_uInt16 KEY_RIGHT
virtual bool iter_children(TreeIter &rIter) const =0
virtual void remove(int pos)=0
tools::Long const nBorder
void addMember(const OUString &rName, const double nVal, bool bVisible, bool bValue=false)
RegionData_Impl * mpParent
ScopedVclPtr< VirtualDevice > mxDropDown
OUString toId(const void *pValue)
SubMenuItemData maCloseTimer
virtual void select(int pos)=0
virtual void all_foreach(const std::function< bool(TreeIter &)> &func)=0
weld::TreeView * mpChecks
bool mbVisible
std::unique_ptr< weld::Popover > mxPopover
void Stop()
ScCheckListMenuControl(weld::Widget *pParent, ScViewData &rViewData, bool bTreeMode, int nWidth)
int GetTextWidth(const OUString &rsName) const
std::unique_ptr< weld::Widget > mxBox
constexpr sal_uInt16 KEY_ESCAPE
virtual void grab_focus()=0
std::unique_ptr< ScListSubMenuControl > mxSubMenuWin
void terminateAllPopupMenus()
Dismiss all visible popup menus and set focus back to the application window.
OUString maName
void addItem(ScCheckListMenuControl::Action *pAction)
static CalendarWrapper & GetCalendar()
Definition: global.cxx:1033
css::uno::Sequence< css::i18n::CalendarItem2 > getMonths() const
std::u16string_view trim(std::u16string_view str)
virtual bool get_iter_first(TreeIter &rIter) const =0
std::unique_ptr< weld::Container > mxContainer
virtual int get_height_rows(int nRows) const =0
OUString aLabel
virtual void set_cursor(int pos)=0
void SetInvokeHandler(const Link< Timer *, void > &rLink)
std::unique_ptr< Action > mxPopupEndAction
void selectCurrentMemberOnly(bool bSet)
void append(TreeIter *pRet=nullptr)
virtual void set_image(int row, const OUString &rImage, int col=-1)=0
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
void addDateMember(const OUString &rName, double nVal, bool bVisible)
VirtualDevice * get() const
void GetRecursiveChecked(const weld::TreeIter *pEntry, std::unordered_set< OUString > &vOut, OUString &rLabel)
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
virtual bool iter_next_sibling(TreeIter &rIter) const =0
void DrawSymbol(const tools::Rectangle &rRect, SymbolType eType, const Color &rColor, DrawSymbolFlags nStyle=DrawSymbolFlags::NONE)
void setSubMenuFocused(const ScListSubMenuControl *pSubMenu)
void setSelectedMenuItem(size_t nPos)
constexpr sal_uInt16 KEY_LEFT
void queueLaunchSubMenu(size_t nPos, ScListSubMenuControl *pMenu)
std::unique_ptr< weld::Popover > mxPopover
const Date & GetNullDate() const
virtual void set_size_request(int nWidth, int nHeight)=0
std::vector< ScCheckListMenuControl::MenuItemData > maMenuItems
std::unique_ptr< weld::TreeView > mxBackColorMenu
Extended data that the client code may need to store.
std::unique_ptr< weld::TreeView > mxListChecks
virtual void bulk_insert_for_each(int nSourceCount, const std::function< void(TreeIter &, int nSourceIndex)> &func, const weld::TreeIter *pParent=nullptr, const std::vector< int > *pFixedWidths=nullptr)=0
void SetupMenu(weld::TreeView &rMenu)
ImplSVEvent * mnAsyncSetDropdownPosId
void Check(const weld::TreeIter *pIter)
std::unique_ptr< weld::Button > mxBtnCancel
std::unique_ptr< weld::Entry > mxEdSearch
const double mnValue
virtual int get_iter_depth(const TreeIter &rIter) const =0
bool m_bDetectedRangeSegmentation false
OUString sId