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