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::addFields(const std::vector<OUString>& aFields)
377{
378 if (!mbIsMultiField)
379 return;
380
381 mxFieldsCombo->clear();
382
383 for (auto& aField: aFields)
384 mxFieldsCombo->append_text(aField);
385
386 mxFieldsCombo->set_active(0);
387}
388
390{
391 if (!mbIsMultiField)
392 return -1;
393
394 return mxFieldsCombo->get_active();
395}
396
397void ScCheckListMenuControl::selectMenuItem(size_t nPos, bool bSubMenuTimer)
398{
399 mxMenu->select(nPos == MENU_NOT_SELECTED ? -1 : nPos);
401
402 if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
403 {
405 return;
406 }
407
408 if (!maMenuItems[nPos].mbEnabled)
409 {
411 return;
412 }
413
414 if (bSubMenuTimer)
415 {
416 if (maMenuItems[nPos].mxSubMenuWin && mxMenu->changed_by_hover())
417 {
418 ScListSubMenuControl* pSubMenu = maMenuItems[nPos].mxSubMenuWin.get();
419 queueLaunchSubMenu(nPos, pSubMenu);
420 }
421 else
423 }
424}
425
427{
429}
430
432{
433 size_t n = maMenuItems.size();
434 for (size_t i = 0; i < n; ++i)
435 {
436 if (maMenuItems[i].mxSubMenuWin.get() == pSubMenu)
437 return i;
438 }
439 return MENU_NOT_SELECTED;
440}
441
443{
445 size_t nMenuPos = getSubMenuPos(pSubMenu);
446 if (mnSelectedMenu != nMenuPos)
447 {
448 mnSelectedMenu = nMenuPos;
449 mxMenu->select(mnSelectedMenu);
450 }
451}
452
454{
455 if (!mbIsPoppedUp)
456 return;
457 mxPopover->connect_closed(Link<weld::Popover&, void>());
458 mxPopover->popdown();
459 PopupModeEndHdl(*mxPopover);
460 assert(mbIsPoppedUp == false);
461}
462
464{
465 mxPopover->connect_closed(LINK(this, ScCheckListMenuControl, PopupModeEndHdl));
466 mbIsPoppedUp = true;
467 mxPopover->popup_at_rect(pParent, rRect);
468 GrabFocus();
469}
470
472{
473 EndPopupMode();
474}
475
477 mbAllowEmptySet(true), mbRTL(false)
478{
479}
480
482 : mnValue(0.0)
483 , mbVisible(true)
484 , mbHiddenByOtherFilter(false)
485 , mbDate(false)
486 , mbLeaf(false)
487 , mbValue(false)
488 , meDatePartType(YEAR)
489{
490}
491
492// the value of border-width of FilterDropDown
493constexpr int nBorderWidth = 4;
494// number of rows visible in checklist
495constexpr int nCheckListVisibleRows = 9;
496// number of rows visible in colorlist
497constexpr int nColorListVisibleRows = 9;
498
500 bool bHasDates, int nWidth, bool bIsMultiField)
501 : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterdropdown.ui"))
502 , mxPopover(mxBuilder->weld_popover("FilterDropDown"))
503 , mxContainer(mxBuilder->weld_container("container"))
504 , mxMenu(mxBuilder->weld_tree_view("menu"))
505 , mxScratchIter(mxMenu->make_iterator())
506 , mxNonMenu(mxBuilder->weld_widget("nonmenu"))
507 , mxFieldsComboLabel(mxBuilder->weld_label("select_field_label"))
508 , mxFieldsCombo(mxBuilder->weld_combo_box("multi_field_combo"))
509 , mxEdSearch(mxBuilder->weld_entry("search_edit"))
510 , mxBox(mxBuilder->weld_widget("box"))
511 , mxListChecks(mxBuilder->weld_tree_view("check_list_box"))
512 , mxTreeChecks(mxBuilder->weld_tree_view("check_tree_box"))
513 , mxChkToggleAll(mxBuilder->weld_check_button("toggle_all"))
514 , mxBtnSelectSingle(mxBuilder->weld_button("select_current"))
515 , mxBtnUnselectSingle(mxBuilder->weld_button("unselect_current"))
516 , mxButtonBox(mxBuilder->weld_box("buttonbox"))
517 , mxBtnOk(mxBuilder->weld_button("ok"))
518 , mxBtnCancel(mxBuilder->weld_button("cancel"))
519 , mxContextMenu(mxBuilder->weld_menu("contextmenu"))
520 , mxDropDown(mxMenu->create_virtual_device())
521 , mnCheckWidthReq(-1)
522 , mnWndWidth(0)
523 , mnCheckListVisibleRows(nCheckListVisibleRows)
524 , mePrevToggleAllState(TRISTATE_INDET)
525 , mnSelectedMenu(MENU_NOT_SELECTED)
526 , mrViewData(rViewData)
527 , mnAsyncPostPopdownId(nullptr)
528 , mnAsyncSetDropdownPosId(nullptr)
529 , mbHasDates(bHasDates)
530 , mbIsPoppedUp(false)
531 , maOpenTimer(this)
532 , maCloseTimer(this)
533 , maSearchEditTimer("ScCheckListMenuControl maSearchEditTimer")
534 , mbIsMultiField(bIsMultiField)
535{
536 mxTreeChecks->set_clicks_to_toggle(1);
537 mxListChecks->set_clicks_to_toggle(1);
538
539 mxNonMenu->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
540 mxEdSearch->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
541 mxListChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
542 mxTreeChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
543 mxListChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
544 mxTreeChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
545 mxChkToggleAll->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
546 mxBtnSelectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
547 mxBtnUnselectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
548 mxBtnOk->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
549 mxBtnCancel->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
550
551 /*
552 tdf#136559 If we have no dates we don't need a tree
553 structure, just a list. GtkListStore can be then
554 used which is much faster than a GtkTreeStore, so
555 with no dates switch to the treeview which uses the
556 faster GtkListStore
557 */
558 if (mbHasDates)
559 mpChecks = mxTreeChecks.get();
560 else
561 {
562 mxTreeChecks->hide();
563 mxListChecks->show();
564 mpChecks = mxListChecks.get();
565 }
566
567 int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
568 if (nWidth != -1)
569 {
570 mnCheckWidthReq = nWidth - nBorderWidth * 2 - 4;
571 mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
572 mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
573 }
574
575 // sort ok/cancel into native order, if this was a dialog they would be auto-sorted, but this
576 // popup isn't a true dialog
577 mxButtonBox->sort_native_button_order();
578
579 mxTreeChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
580 mxListChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
581
582 mxBox->show();
583 if (mbIsMultiField)
584 {
585 mxFieldsComboLabel->show();
586 mxFieldsCombo->show();
587 }
588 else
589 {
590 mxFieldsComboLabel->hide();
591 mxFieldsCombo->hide();
592 }
593 mxEdSearch->show();
594 mxButtonBox->show();
595
596 mxMenu->connect_row_activated(LINK(this, ScCheckListMenuControl, RowActivatedHdl));
597 mxMenu->connect_changed(LINK(this, ScCheckListMenuControl, SelectHdl));
598 mxMenu->connect_key_press(LINK(this, ScCheckListMenuControl, MenuKeyInputHdl));
599
600 mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
601 mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
602 if (mbIsMultiField)
603 mxFieldsCombo->connect_changed(LINK(this, ScCheckListMenuControl, ComboChangedHdl));
604 mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl));
605 mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl));
606 mxTreeChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
607 mxTreeChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
608 mxListChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
609 mxListChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
610 mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, TriStateHdl));
611 mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
612 mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
613
615 mxMenu->connect_size_allocate(LINK(this, ScCheckListMenuControl, TreeSizeAllocHdl));
616
617 // determine what width the checklist will end up with
618 mnCheckWidthReq = mxContainer->get_preferred_size().Width();
619 // make that size fixed now, we can now use mnCheckWidthReq to speed up
620 // bulk_insert_for_each
621 mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
622 mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
623
626}
627
629{
630 if (mxEdSearch->get_visible())
631 mxEdSearch->grab_focus();
632 else
633 {
634 mxMenu->set_cursor(0);
635 mxMenu->grab_focus();
636 }
637}
638
640{
642 {
644 mnAsyncPostPopdownId = nullptr;
645 }
647 {
649 mnAsyncSetDropdownPosId = nullptr;
650 }
651}
652
654{
656 EndPopupMode();
657 for (auto& rMenuItem : maMenuItems)
658 rMenuItem.mxSubMenuWin.reset();
660}
661
663{
664 mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height() + 2);
666 if (mxMenu->n_children())
667 {
668 mxMenu->set_cursor(0);
669 mxMenu->unselect_all();
670 }
671
672 mnWndWidth = mxContainer->get_preferred_size().Width() + nBorderWidth * 2 + 4;
673}
674
676{
677 mpChecks->all_foreach([this, bSet](weld::TreeIter& rEntry){
678 if (mpChecks->get_sensitive(rEntry, 0))
680 return false;
681 });
682
684 {
685 // We need to have at least one member selected.
686 mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
687 }
688}
689
691{
692 setAllMemberState(!bSet);
693 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
694 if (!mpChecks->get_cursor(xEntry.get()))
695 return;
697}
698
699IMPL_LINK(ScCheckListMenuControl, CommandHdl, const CommandEvent&, rCEvt, bool)
700{
701 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
702 return false;
703
704 mxContextMenu->set_sensitive("less", mnCheckListVisibleRows > 4);
705 mxContextMenu->set_sensitive("more", mnCheckListVisibleRows < 42);
706
707 OUString sCommand = mxContextMenu->popup_at_rect(mpChecks, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
708 if (sCommand.isEmpty())
709 return true;
710
711 if (sCommand == "more")
712 ++mnCheckListVisibleRows;
713 else if (sCommand == "less")
714 --mnCheckListVisibleRows;
715 ResizeToRequest();
716
717 return true;
718}
719
721{
722 int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
723 mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
724 mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
725 mxPopover->resize_to_request();
726}
727
729{
730 if (&rBtn == mxBtnOk.get())
731 close(true);
732 else if (&rBtn == mxBtnCancel.get())
733 close(false);
734 else if (&rBtn == mxBtnSelectSingle.get() || &rBtn == mxBtnUnselectSingle.get())
735 {
736 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
737 bool bEntry = mpChecks->get_cursor(xEntry.get());
738 if (!bEntry)
739 xEntry.reset();
740 if (bEntry && mpChecks->get_sensitive(*xEntry, 0))
741 {
742 selectCurrentMemberOnly(&rBtn == mxBtnSelectSingle.get());
743 Check(xEntry.get());
744 }
745 }
746}
747
749{
750 switch (mePrevToggleAllState)
751 {
752 case TRISTATE_FALSE:
753 mxChkToggleAll->set_state(TRISTATE_TRUE);
754 setAllMemberState(true);
755 break;
756 case TRISTATE_TRUE:
757 mxChkToggleAll->set_state(TRISTATE_FALSE);
758 setAllMemberState(false);
759 break;
760 case TRISTATE_INDET:
761 default:
762 mxChkToggleAll->set_state(TRISTATE_TRUE);
763 setAllMemberState(true);
764 break;
765 }
766
767 mePrevToggleAllState = mxChkToggleAll->get_state();
768}
769
770namespace
771{
772 void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, const ScCheckListMember& rMember, bool bChecked)
773 {
774 OUString aLabel = rMember.maName;
775 if (aLabel.isEmpty())
776 aLabel = ScResId(STR_EMPTYDATA);
777 rView.set_toggle(rIter, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
778 rView.set_text(rIter, aLabel, 0);
779 rView.set_sensitive(rIter, !rMember.mbHiddenByOtherFilter);
780 }
781}
782
784{
785 if (mbIsMultiField && mxFieldChangedAction)
786 mxFieldChangedAction->execute();
787}
788
789IMPL_LINK_NOARG(ScCheckListMenuControl, SearchEditTimeoutHdl, Timer*, void)
790{
791 OUString aSearchText = mxEdSearch->get_text();
792 aSearchText = ScGlobal::getCharClass().lowercase( aSearchText );
793 bool bSearchTextEmpty = aSearchText.isEmpty();
794 size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
795 [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
796 size_t nSelCount = 0;
797
798 // This branch is the general case, the other is an optimized variant of
799 // this one where we can take advantage of knowing we have no hierarchy
800 if (mbHasDates)
801 {
802 mpChecks->freeze();
803
804 bool bSomeDateDeletes = false;
805
806 for (size_t i = 0; i < nEnableMember; ++i)
807 {
808 bool bIsDate = maMembers[i].mbDate;
809 bool bPartialMatch = false;
810
811 OUString aLabelDisp = maMembers[i].maName;
812 if ( aLabelDisp.isEmpty() )
813 aLabelDisp = ScResId( STR_EMPTYDATA );
814
815 if ( !bSearchTextEmpty )
816 {
817 if ( !bIsDate )
818 bPartialMatch = ( ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1 );
819 else if ( maMembers[i].meDatePartType == ScCheckListMember::DAY ) // Match with both numerical and text version of month
820 bPartialMatch = (ScGlobal::getCharClass().lowercase( OUString(
821 maMembers[i].maRealName + maMembers[i].maDateParts[1] )).indexOf( aSearchText ) != -1);
822 else
823 continue;
824 }
825 else if ( bIsDate && maMembers[i].meDatePartType != ScCheckListMember::DAY )
826 continue;
827
828 if ( bSearchTextEmpty )
829 {
830 auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i], true, maMembers[i].mbVisible);
831 updateMemberParents(xLeaf.get(), i);
832 if ( maMembers[i].mbVisible )
833 ++nSelCount;
834 continue;
835 }
836
837 if ( bPartialMatch )
838 {
839 auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i]);
840 updateMemberParents(xLeaf.get(), i);
841 ++nSelCount;
842 }
843 else
844 {
845 ShowCheckEntry(aLabelDisp, maMembers[i], false, false);
846 if( bIsDate )
847 bSomeDateDeletes = true;
848 }
849 }
850
851 if ( bSomeDateDeletes )
852 {
853 for (size_t i = 0; i < nEnableMember; ++i)
854 {
855 if (!maMembers[i].mbDate)
856 continue;
857 if (maMembers[i].meDatePartType != ScCheckListMember::DAY)
858 continue;
859 updateMemberParents(nullptr, i);
860 }
861 }
862
863 mpChecks->thaw();
864 }
865 else
866 {
867 mpChecks->freeze();
868
869 // when there are a lot of rows, it is cheaper to simply clear the tree and either
870 // re-initialise or just insert the filtered lines
871 mpChecks->clear();
872
873 mpChecks->thaw();
874
875 if (bSearchTextEmpty)
876 nSelCount = initMembers();
877 else
878 {
879 std::vector<size_t> aShownIndexes;
880
881 for (size_t i = 0; i < nEnableMember; ++i)
882 {
883 assert(!maMembers[i].mbDate);
884
885 OUString aLabelDisp = maMembers[i].maName;
886 if ( aLabelDisp.isEmpty() )
887 aLabelDisp = ScResId( STR_EMPTYDATA );
888
889 bool bPartialMatch = ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1;
890
891 if (!bPartialMatch)
892 continue;
893
894 aShownIndexes.push_back(i);
895 }
896
897 std::vector<int> aFixedWidths { mnCheckWidthReq };
898 // tdf#122419 insert in the fastest order, this might be backwards.
899 mpChecks->bulk_insert_for_each(aShownIndexes.size(), [this, &aShownIndexes, &nSelCount](weld::TreeIter& rIter, int i) {
900 size_t nIndex = aShownIndexes[i];
901 insertMember(*mpChecks, rIter, maMembers[nIndex], true);
902 ++nSelCount;
903 }, nullptr, &aFixedWidths);
904 }
905 }
906
907 if ( nSelCount == nEnableMember )
908 mxChkToggleAll->set_state( TRISTATE_TRUE );
909 else if ( nSelCount == 0 )
910 mxChkToggleAll->set_state( TRISTATE_FALSE );
911 else
912 mxChkToggleAll->set_state( TRISTATE_INDET );
913
914 if ( !maConfig.mbAllowEmptySet )
915 {
916 const bool bEmptySet( nSelCount == 0 );
917 mpChecks->set_sensitive(!bEmptySet);
918 mxChkToggleAll->set_sensitive(!bEmptySet);
919 mxBtnSelectSingle->set_sensitive(!bEmptySet);
920 mxBtnUnselectSingle->set_sensitive(!bEmptySet);
921 mxBtnOk->set_sensitive(!bEmptySet);
922 }
923}
924
926{
927 maSearchEditTimer.Start();
928}
929
931{
932 if (mxBtnOk->get_sensitive())
933 close(true);
934 return true;
935}
936
938{
939 Check(&rRowCol.first);
940}
941
943{
944 if (pEntry)
945 CheckEntry(*pEntry, mpChecks->get_toggle(*pEntry) == TRISTATE_TRUE);
946 size_t nNumChecked = GetCheckedEntryCount();
947 size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
948 [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
949 if (nNumChecked == nEnableMember)
950 // all members visible
951 mxChkToggleAll->set_state(TRISTATE_TRUE);
952 else if (nNumChecked == 0)
953 // no members visible
954 mxChkToggleAll->set_state(TRISTATE_FALSE);
955 else
956 mxChkToggleAll->set_state(TRISTATE_INDET);
957
959 // We need to have at least one member selected.
960 mxBtnOk->set_sensitive(nNumChecked != 0);
961
963}
964
966{
967 if ( !maMembers[nIdx].mbDate || maMembers[nIdx].meDatePartType != ScCheckListMember::DAY )
968 return;
969
970 OUString aYearName = maMembers[nIdx].maDateParts[0];
971 OUString aMonthName = maMembers[nIdx].maDateParts[1];
972 auto aItr = maYearMonthMap.find(aYearName + aMonthName);
973
974 if ( pLeaf )
975 {
976 std::unique_ptr<weld::TreeIter> xYearEntry;
977 std::unique_ptr<weld::TreeIter> xMonthEntry = mpChecks->make_iterator(pLeaf);
978 if (!mpChecks->iter_parent(*xMonthEntry))
979 xMonthEntry.reset();
980 else
981 {
982 xYearEntry = mpChecks->make_iterator(xMonthEntry.get());
983 if (!mpChecks->iter_parent(*xYearEntry))
984 xYearEntry.reset();
985 }
986
987 maMembers[nIdx].mxParent = std::move(xMonthEntry);
988 if ( aItr != maYearMonthMap.end() )
989 {
990 size_t nMonthIdx = aItr->second;
991 maMembers[nMonthIdx].mxParent = std::move(xYearEntry);
992 }
993 }
994 else
995 {
996 std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
997 if (aItr != maYearMonthMap.end() && !xYearEntry)
998 {
999 size_t nMonthIdx = aItr->second;
1000 maMembers[nMonthIdx].mxParent.reset();
1001 maMembers[nIdx].mxParent.reset();
1002 }
1003 else if (xYearEntry && !FindEntry(xYearEntry.get(), aMonthName))
1004 maMembers[nIdx].mxParent.reset();
1005 }
1006}
1007
1009{
1010 maMembers.reserve(n);
1011}
1012
1013void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, bool bVisible, bool bHiddenByOtherFilter)
1014{
1016
1017 // Convert the numeric date value to a date object.
1018 Date aDate = pFormatter->GetNullDate();
1019 aDate.AddDays(rtl::math::approxFloor(nVal));
1020
1021 sal_Int16 nYear = aDate.GetYear();
1022 sal_uInt16 nMonth = aDate.GetMonth();
1023 sal_uInt16 nDay = aDate.GetDay();
1024
1025 // Get the localized month name list.
1027 uno::Sequence<i18n::CalendarItem2> aMonths = rCalendar.getMonths();
1028 if (aMonths.getLength() < nMonth)
1029 return;
1030
1031 OUString aYearName = OUString::number(nYear);
1032 OUString aMonthName = aMonths[nMonth-1].FullName;
1033 OUString aDayName = OUString::number(nDay);
1034
1035 if ( aDayName.getLength() == 1 )
1036 aDayName = "0" + aDayName;
1037
1038 mpChecks->freeze();
1039
1040 std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
1041 if (!xYearEntry)
1042 {
1043 xYearEntry = mpChecks->make_iterator();
1044 mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
1045 mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
1046 mpChecks->set_text(*xYearEntry, aYearName, 0);
1047 mpChecks->set_sensitive(*xYearEntry, !bHiddenByOtherFilter);
1048 ScCheckListMember aMemYear;
1049 aMemYear.maName = aYearName;
1050 aMemYear.maRealName = rsName;
1051 aMemYear.mbDate = true;
1052 aMemYear.mbLeaf = false;
1053 aMemYear.mbVisible = bVisible;
1054 aMemYear.mbHiddenByOtherFilter = bHiddenByOtherFilter;
1055 aMemYear.mxParent.reset();
1057 maMembers.emplace_back(std::move(aMemYear));
1058 }
1059
1060 std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), aMonthName);
1061 if (!xMonthEntry)
1062 {
1063 xMonthEntry = mpChecks->make_iterator();
1064 mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
1065 mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
1066 mpChecks->set_text(*xMonthEntry, aMonthName, 0);
1067 mpChecks->set_sensitive(*xMonthEntry, !bHiddenByOtherFilter);
1068 ScCheckListMember aMemMonth;
1069 aMemMonth.maName = aMonthName;
1070 aMemMonth.maRealName = rsName;
1071 aMemMonth.mbDate = true;
1072 aMemMonth.mbLeaf = false;
1073 aMemMonth.mbVisible = bVisible;
1074 aMemMonth.mbHiddenByOtherFilter = bHiddenByOtherFilter;
1075 aMemMonth.mxParent = std::move(xYearEntry);
1077 maMembers.emplace_back(std::move(aMemMonth));
1078 maYearMonthMap[aYearName + aMonthName] = maMembers.size() - 1;
1079 }
1080
1081 std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), aDayName);
1082 if (!xDayEntry)
1083 {
1084 xDayEntry = mpChecks->make_iterator();
1085 mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
1086 mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
1087 mpChecks->set_text(*xDayEntry, aDayName, 0);
1088 mpChecks->set_sensitive(*xDayEntry, !bHiddenByOtherFilter);
1089 ScCheckListMember aMemDay;
1090 aMemDay.maName = aDayName;
1091 aMemDay.maRealName = rsName;
1092 aMemDay.maDateParts.resize(2);
1093 aMemDay.maDateParts[0] = aYearName;
1094 aMemDay.maDateParts[1] = aMonthName;
1095 aMemDay.mbDate = true;
1096 aMemDay.mbLeaf = true;
1097 aMemDay.mbVisible = bVisible;
1098 aMemDay.mbHiddenByOtherFilter = bHiddenByOtherFilter;
1099 aMemDay.mxParent = std::move(xMonthEntry);
1101 maMembers.emplace_back(std::move(aMemDay));
1102 }
1103
1104 mpChecks->thaw();
1105}
1106
1107void ScCheckListMenuControl::addMember(const OUString& rName, const double nVal, bool bVisible, bool bHiddenByOtherFilter, bool bValue)
1108{
1109 ScCheckListMember aMember;
1110 // tdf#46062 - indicate hidden whitespaces using quotes
1111 aMember.maName = o3tl::trim(rName) != rName ? "\"" + rName + "\"" : rName;
1112 aMember.maRealName = rName;
1113 aMember.mnValue = nVal;
1114 aMember.mbDate = false;
1115 aMember.mbLeaf = true;
1116 aMember.mbValue = bValue;
1117 aMember.mbVisible = bVisible;
1118 aMember.mbHiddenByOtherFilter = bHiddenByOtherFilter;
1119 aMember.mxParent.reset();
1120 maMembers.emplace_back(std::move(aMember));
1121}
1122
1124{
1125 maMembers.clear();
1126
1127 mpChecks->freeze();
1128 mpChecks->clear();
1129 mpChecks->thaw();
1130}
1131
1132std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode)
1133{
1134 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(pParent);
1135 bool bEntry = pParent ? mpChecks->iter_children(*xEntry) : mpChecks->get_iter_first(*xEntry);
1136 while (bEntry)
1137 {
1138 if (sNode == mpChecks->get_text(*xEntry, 0))
1139 return xEntry;
1140 bEntry = mpChecks->iter_next_sibling(*xEntry);
1141 }
1142 return nullptr;
1143}
1144
1145void ScCheckListMenuControl::GetRecursiveChecked(const weld::TreeIter* pEntry, std::unordered_set<OUString>& vOut,
1146 OUString& rLabel)
1147{
1148 if (mpChecks->get_toggle(*pEntry) != TRISTATE_TRUE)
1149 return;
1150
1151 // We have to hash parents and children together.
1152 // Per convention for easy access in getResult()
1153 // "child;parent;grandparent" while descending.
1154 if (rLabel.isEmpty())
1155 rLabel = mpChecks->get_text(*pEntry, 0);
1156 else
1157 rLabel = mpChecks->get_text(*pEntry, 0) + ";" + rLabel;
1158
1159 // Prerequisite: the selection mechanism guarantees that if a child is
1160 // selected then also the parent is selected, so we only have to
1161 // inspect the children in case the parent is selected.
1162 if (!mpChecks->iter_has_child(*pEntry))
1163 return;
1164
1165 std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(pEntry));
1166 bool bChild = mpChecks->iter_children(*xChild);
1167 while (bChild)
1168 {
1169 OUString aLabel = rLabel;
1170 GetRecursiveChecked(xChild.get(), vOut, aLabel);
1171 if (!aLabel.isEmpty() && aLabel != rLabel)
1172 vOut.insert(aLabel);
1173 bChild = mpChecks->iter_next_sibling(*xChild);
1174 }
1175 // Let the caller not add the parent alone.
1176 rLabel.clear();
1177}
1178
1179std::unordered_set<OUString> ScCheckListMenuControl::GetAllChecked()
1180{
1181 std::unordered_set<OUString> vResults(0);
1182
1183 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1184 bool bEntry = mpChecks->get_iter_first(*xEntry);
1185 while (bEntry)
1186 {
1187 OUString aLabel;
1188 GetRecursiveChecked(xEntry.get(), vResults, aLabel);
1189 if (!aLabel.isEmpty())
1190 vResults.insert(aLabel);
1191 bEntry = mpChecks->iter_next_sibling(*xEntry);
1192 }
1193
1194 return vResults;
1195}
1196
1197bool ScCheckListMenuControl::IsChecked(std::u16string_view sName, const weld::TreeIter* pParent)
1198{
1199 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
1200 return xEntry && mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1201}
1202
1203void ScCheckListMenuControl::CheckEntry(std::u16string_view sName, const weld::TreeIter* pParent, bool bCheck)
1204{
1205 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
1206 if (xEntry)
1207 CheckEntry(*xEntry, bCheck);
1208}
1209
1210// Recursively check all children of rParent
1212{
1213 mpChecks->set_toggle(rParent, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1214 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(&rParent);
1215 bool bEntry = mpChecks->iter_children(*xEntry);
1216 while (bEntry)
1217 {
1218 CheckAllChildren(*xEntry, bCheck);
1219 bEntry = mpChecks->iter_next_sibling(*xEntry);
1220 }
1221}
1222
1224{
1225 // recursively check all items below rParent
1226 CheckAllChildren(rParent, bCheck);
1227 // checking rParent can affect ancestors, e.g. if ancestor is unchecked and rParent is
1228 // now checked then the ancestor needs to be checked also
1229 if (!mpChecks->get_iter_depth(rParent))
1230 return;
1231
1232 std::unique_ptr<weld::TreeIter> xAncestor(mpChecks->make_iterator(&rParent));
1233 bool bAncestor = mpChecks->iter_parent(*xAncestor);
1234 while (bAncestor)
1235 {
1236 // if any first level children checked then ancestor
1237 // needs to be checked, similarly if no first level children
1238 // checked then ancestor needs to be unchecked
1239 std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(xAncestor.get()));
1240 bool bChild = mpChecks->iter_children(*xChild);
1241 bool bChildChecked = false;
1242
1243 while (bChild)
1244 {
1245 if (mpChecks->get_toggle(*xChild) == TRISTATE_TRUE)
1246 {
1247 bChildChecked = true;
1248 break;
1249 }
1250 bChild = mpChecks->iter_next_sibling(*xChild);
1251 }
1252 mpChecks->set_toggle(*xAncestor, bChildChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
1253 bAncestor = mpChecks->iter_parent(*xAncestor);
1254 }
1255}
1256
1257std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::ShowCheckEntry(const OUString& sName, ScCheckListMember& rMember, bool bShow, bool bCheck)
1258{
1259 std::unique_ptr<weld::TreeIter> xEntry;
1260 if (!rMember.mbDate || rMember.mxParent)
1261 xEntry = FindEntry(rMember.mxParent.get(), sName);
1262
1263 if ( bShow )
1264 {
1265 if (!xEntry)
1266 {
1267 if (rMember.mbDate)
1268 {
1269 if (rMember.maDateParts.empty())
1270 return nullptr;
1271
1272 std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, rMember.maDateParts[0]);
1273 if (!xYearEntry)
1274 {
1275 xYearEntry = mpChecks->make_iterator();
1276 mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
1277 mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
1278 mpChecks->set_text(*xYearEntry, rMember.maDateParts[0], 0);
1279 }
1280 std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), rMember.maDateParts[1]);
1281 if (!xMonthEntry)
1282 {
1283 xMonthEntry = mpChecks->make_iterator();
1284 mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
1285 mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
1286 mpChecks->set_text(*xMonthEntry, rMember.maDateParts[1], 0);
1287 }
1288 std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), rMember.maName);
1289 if (!xDayEntry)
1290 {
1291 xDayEntry = mpChecks->make_iterator();
1292 mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
1293 mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
1294 mpChecks->set_text(*xDayEntry, rMember.maName, 0);
1295 }
1296 return xDayEntry; // Return leaf node
1297 }
1298
1299 xEntry = mpChecks->make_iterator();
1300 mpChecks->append(xEntry.get());
1301 mpChecks->set_toggle(*xEntry, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1302 mpChecks->set_text(*xEntry, sName, 0);
1303 }
1304 else
1305 CheckEntry(*xEntry, bCheck);
1306 }
1307 else if (xEntry)
1308 {
1309 mpChecks->remove(*xEntry);
1310 if (rMember.mxParent)
1311 {
1312 std::unique_ptr<weld::TreeIter> xParent(mpChecks->make_iterator(rMember.mxParent.get()));
1313 while (xParent && !mpChecks->iter_has_child(*xParent))
1314 {
1315 std::unique_ptr<weld::TreeIter> xTmp(mpChecks->make_iterator(xParent.get()));
1316 if (!mpChecks->iter_parent(*xParent))
1317 xParent.reset();
1318 mpChecks->remove(*xTmp);
1319 }
1320 }
1321 }
1322 return nullptr;
1323}
1324
1326{
1327 int nRet = 0;
1328
1329 mpChecks->all_foreach([this, &nRet](weld::TreeIter& rEntry){
1330 if (mpChecks->get_toggle(rEntry) == TRISTATE_TRUE)
1331 ++nRet;
1332 return false;
1333 });
1334
1335 return nRet;
1336}
1337
1338IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1339{
1340 const vcl::KeyCode& rKey = rKEvt.GetKeyCode();
1341
1342 if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
1343 {
1344 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1345 bool bEntry = mpChecks->get_cursor(xEntry.get());
1346 if (bEntry && mpChecks->get_sensitive(*xEntry, 0))
1347 {
1348 bool bOldCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1349 CheckEntry(*xEntry, !bOldCheck);
1350 bool bNewCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
1351 if (bOldCheck != bNewCheck)
1352 Check(xEntry.get());
1353 }
1354 return true;
1355 }
1356
1357 return false;
1358}
1359
1360size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth)
1361{
1362 size_t n = maMembers.size();
1363 size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
1364 [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
1365 size_t nVisMemCount = 0;
1366
1367 if (nMaxMemberWidth == -1)
1368 nMaxMemberWidth = mnCheckWidthReq;
1369
1370 if (!mpChecks->n_children() && !mbHasDates)
1371 {
1372 std::vector<int> aFixedWidths { nMaxMemberWidth };
1373 // tdf#134038 insert in the fastest order, this might be backwards so only do it for
1374 // the !mbHasDates case where no entry depends on another to exist before getting
1375 // inserted. We cannot retain pre-existing treeview content, only clear and fill it.
1376 mpChecks->bulk_insert_for_each(n, [this, &nVisMemCount](weld::TreeIter& rIter, int i) {
1377 assert(!maMembers[i].mbDate);
1378 insertMember(*mpChecks, rIter, maMembers[i], maMembers[i].mbVisible);
1379 if (maMembers[i].mbVisible)
1380 ++nVisMemCount;
1381 }, nullptr, &aFixedWidths);
1382 }
1383 else
1384 {
1385 mpChecks->freeze();
1386
1387 std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
1388 std::vector<std::unique_ptr<weld::TreeIter>> aExpandRows;
1389
1390 for (size_t i = 0; i < n; ++i)
1391 {
1392 if (maMembers[i].mbDate)
1393 {
1394 CheckEntry(maMembers[i].maName, maMembers[i].mxParent.get(), maMembers[i].mbVisible);
1395 // Expand first node of checked dates
1397 {
1398 std::unique_ptr<weld::TreeIter> xDateEntry = FindEntry(nullptr, maMembers[i].maName);
1399 if (xDateEntry)
1400 aExpandRows.emplace_back(std::move(xDateEntry));
1401 }
1402 }
1403 else
1404 {
1405 mpChecks->append(xEntry.get());
1406 insertMember(*mpChecks, *xEntry, maMembers[i], maMembers[i].mbVisible);
1407 }
1408
1409 if (maMembers[i].mbVisible)
1410 ++nVisMemCount;
1411 }
1412
1413 mpChecks->thaw();
1414
1415 for (const auto& rRow : aExpandRows)
1416 mpChecks->expand_row(*rRow);
1417 }
1418
1419 if (nVisMemCount == nEnableMember)
1420 {
1421 // all members visible
1422 mxChkToggleAll->set_state(TRISTATE_TRUE);
1424 }
1425 else if (nVisMemCount == 0)
1426 {
1427 // no members visible
1428 mxChkToggleAll->set_state(TRISTATE_FALSE);
1430 }
1431 else
1432 {
1433 mxChkToggleAll->set_state(TRISTATE_INDET);
1435 }
1436
1437 if (nVisMemCount)
1438 mpChecks->set_cursor(0);
1439
1440 return nVisMemCount;
1441}
1442
1444{
1445 maConfig = rConfig;
1446}
1447
1449{
1450 return mxChkToggleAll->get_state() == TRISTATE_TRUE;
1451}
1452
1454{
1455 ResultType aResult;
1456 std::unordered_set<OUString> vCheckeds = GetAllChecked();
1457 size_t n = maMembers.size();
1458 for (size_t i = 0; i < n; ++i)
1459 {
1460 if ( maMembers[i].mbLeaf )
1461 {
1462 OUStringBuffer aLabel(maMembers[i].maName);
1463 if (aLabel.isEmpty())
1464 aLabel = ScResId(STR_EMPTYDATA);
1465
1466 /* TODO: performance-wise this looks suspicious, concatenating to
1467 * do the lookup for each leaf item seems wasteful. */
1468 // Checked labels are in the form "child;parent;grandparent".
1469 if (maMembers[i].mxParent)
1470 {
1471 std::unique_ptr<weld::TreeIter> xIter(mpChecks->make_iterator(maMembers[i].mxParent.get()));
1472 do
1473 {
1474 aLabel.append(";" + mpChecks->get_text(*xIter));
1475 }
1476 while (mpChecks->iter_parent(*xIter));
1477 }
1478
1479 bool bState = vCheckeds.find(aLabel.makeStringAndClear()) != vCheckeds.end();
1480
1481 ResultEntry aResultEntry;
1482 aResultEntry.bValid = bState && !maMembers[i].mbHiddenByOtherFilter;
1483 aResultEntry.aName = maMembers[i].maRealName;
1484 aResultEntry.nValue = maMembers[i].mnValue;
1485 aResultEntry.bDate = maMembers[i].mbDate;
1486 aResultEntry.bValue = maMembers[i].mbValue;
1487 aResult.insert(aResultEntry);
1488 }
1489 }
1490 rResult.swap(aResult);
1491}
1492
1494{
1495 prepWindow();
1497 // We need to have at least one member selected.
1498 mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
1499
1500 tools::Rectangle aRect(rRect);
1501 if (maConfig.mbRTL)
1502 {
1503 // In RTL mode, the logical "left" is visual "right".
1505 {
1506 tools::Long nLeft = aRect.Left() - aRect.GetWidth();
1507 aRect.SetLeft( nLeft );
1508 }
1509 else
1510 {
1511 // in LOK mode, rRect is in document pixel coordinates, so width has to be added
1512 // to place the popup next to the (visual) left aligned button.
1513 aRect.Move(aRect.GetWidth(), 0);
1514 }
1515 }
1516 else if (mnWndWidth < aRect.GetWidth())
1517 {
1518 // Target rectangle (i.e. cell width) is wider than the window.
1519 // Simulate right-aligned launch by modifying the target rectangle
1520 // size.
1521 tools::Long nDiff = aRect.GetWidth() - mnWndWidth;
1522 aRect.AdjustLeft(nDiff );
1523 }
1524
1525 StartPopupMode(pWidget, aRect);
1526}
1527
1529{
1530 if (bOK && mxOKAction)
1531 mxOKAction->execute();
1532 EndPopupMode();
1533}
1534
1535void ScCheckListMenuControl::setExtendedData(std::unique_ptr<ExtendedData> p)
1536{
1537 mxExtendedData = std::move(p);
1538}
1539
1541{
1542 return mxExtendedData.get();
1543}
1544
1546{
1547 mxOKAction.reset(p);
1548}
1549
1551{
1552 mxPopupEndAction.reset(p);
1553}
1554
1556{
1557 mxFieldChangedAction.reset(p);
1558}
1559
1561{
1562 mbIsPoppedUp = false;
1563 clearSelectedMenuItem();
1564 if (mxPopupEndAction)
1565 mxPopupEndAction->execute();
1566
1567 DropPendingEvents();
1568}
1569
1570int ScCheckListMenuControl::GetTextWidth(const OUString& rsName) const
1571{
1572 return mxDropDown->GetTextWidth(rsName);
1573}
1574
1576{
1577 int nBorder = nBorderWidth * 2 + 4;
1578 int nNewWidth = nMaxTextWidth - nBorder;
1579 if (nNewWidth > mnCheckWidthReq)
1580 {
1581 mnCheckWidthReq = nNewWidth;
1582 int nChecksHeight = mpChecks->get_height_rows(nCheckListVisibleRows);
1583 mpChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
1584 }
1585 return mnCheckWidthReq + nBorder;
1586}
1587
1589 : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filtersubdropdown.ui"))
1590 , mxPopover(mxBuilder->weld_popover("FilterSubDropDown"))
1591 , mxContainer(mxBuilder->weld_container("container"))
1592 , mxMenu(mxBuilder->weld_tree_view("menu"))
1593 , mxBackColorMenu(mxBuilder->weld_tree_view("background"))
1594 , mxTextColorMenu(mxBuilder->weld_tree_view("textcolor"))
1595 , mxScratchIter(mxMenu->make_iterator())
1596 , mrParentControl(rParentControl)
1597 , mnBackColorMenuPrefHeight(-1)
1598 , mnTextColorMenuPrefHeight(-1)
1599 , mbColorMenu(bColorMenu)
1600{
1601 mxMenu->hide();
1602 mxBackColorMenu->hide();
1603 mxTextColorMenu->hide();
1604
1605 if (!bColorMenu)
1606 {
1607 SetupMenu(*mxMenu);
1608 mxMenu->show();
1609 }
1610 else
1611 {
1612 mxBackColorMenu->set_clicks_to_toggle(1);
1613 mxBackColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
1614 mxBackColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
1615 mxTextColorMenu->set_clicks_to_toggle(1);
1616 mxTextColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
1617 mxTextColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
1620 }
1621}
1622
1624{
1625 rMenu.connect_row_activated(LINK(this, ScListSubMenuControl, RowActivatedHdl));
1626 rMenu.connect_key_press(LINK(this, ScListSubMenuControl, MenuKeyInputHdl));
1627}
1628
1630{
1632 mxPopupStartAction->execute();
1633
1634 mxPopover->popup_at_rect(pParent, rRect, weld::Placement::End);
1635
1637 rFirstMenu.set_cursor(0);
1638 rFirstMenu.select(0);
1639
1641}
1642
1644{
1645 mxPopover->popdown();
1646}
1647
1649{
1651 rFirstMenu.grab_focus();
1652}
1653
1655{
1656 return mxPopover->get_visible();
1657}
1658
1660{
1661 if (!mbColorMenu)
1662 mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height());
1663 else
1664 {
1665 int nBackColorMenuPrefHeight = mnBackColorMenuPrefHeight;
1666 if (nBackColorMenuPrefHeight == -1)
1667 nBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
1668 mxBackColorMenu->set_size_request(-1, nBackColorMenuPrefHeight);
1669 int nTextColorMenuPrefHeight = mnTextColorMenuPrefHeight;
1670 if (nTextColorMenuPrefHeight == -1)
1671 nTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
1672 mxTextColorMenu->set_size_request(-1, nTextColorMenuPrefHeight);
1673 }
1674}
1675
1677{
1679 aItem.mbEnabled = true;
1680 aItem.mxAction.reset(pAction);
1681 maMenuItems.emplace_back(std::move(aItem));
1682}
1683
1685{
1686 addItem(pAction);
1687 mxMenu->append(weld::toId(pAction), rText);
1688}
1689
1690void ScListSubMenuControl::addMenuColorItem(const OUString& rText, bool bActive, VirtualDevice& rImage,
1691 int nMenu, ScCheckListMenuControl::Action* pAction)
1692{
1693 addItem(pAction);
1694
1695 weld::TreeView& rColorMenu = nMenu == 0 ? *mxBackColorMenu : *mxTextColorMenu;
1696 rColorMenu.show();
1697
1698 OUString sId = weld::toId(pAction);
1699 rColorMenu.insert(nullptr, -1, &rText, &sId, nullptr, nullptr, false, mxScratchIter.get());
1700 rColorMenu.set_toggle(*mxScratchIter, bActive ? TRISTATE_TRUE : TRISTATE_FALSE);
1701 rColorMenu.set_image(*mxScratchIter, rImage);
1702
1703 if (mnTextColorMenuPrefHeight == -1 &&
1704 &rColorMenu == mxTextColorMenu.get() &&
1705 mxTextColorMenu->n_children() == nColorListVisibleRows)
1706 {
1707 mnTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
1708 }
1709
1710 if (mnBackColorMenuPrefHeight == -1 &&
1711 &rColorMenu == mxBackColorMenu.get() &&
1712 mxBackColorMenu->n_children() == nColorListVisibleRows)
1713 {
1714 mnBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
1715 }
1716}
1717
1719{
1721 maMenuItems.emplace_back(std::move(aItem));
1722
1723 mxMenu->append_separator("separator" + OUString::number(maMenuItems.size()));
1724}
1725
1727{
1728 maMenuItems.clear();
1729 mxMenu->clear();
1730 mxBackColorMenu->clear();
1732 mxTextColorMenu->clear();
1734}
1735
1736IMPL_LINK(ScListSubMenuControl, MenuKeyInputHdl, const KeyEvent&, rKEvt, bool)
1737{
1738 bool bConsumed = false;
1739 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1740
1741 switch (rKeyCode.GetCode())
1742 {
1743 case KEY_ESCAPE:
1744 case KEY_LEFT:
1745 {
1746 mrParentControl.endSubMenu(*this);
1747 bConsumed = true;
1748 break;
1749 }
1750 case KEY_SPACE:
1751 case KEY_RETURN:
1752 {
1753 weld::TreeView& rMenu = !mbColorMenu ? *mxMenu :
1754 (mxBackColorMenu->has_focus() ? *mxBackColorMenu : *mxTextColorMenu);
1755 // don't toggle checkbutton, go straight to activating entry
1756 bConsumed = RowActivatedHdl(rMenu);
1757 break;
1758 }
1759 case KEY_DOWN:
1760 {
1761 if (mxTextColorMenu->get_visible() &&
1762 mxBackColorMenu->has_focus() &&
1763 mxBackColorMenu->get_selected_index() == mxBackColorMenu->n_children() - 1)
1764 {
1765 mxBackColorMenu->unselect_all();
1766 mxTextColorMenu->select(0);
1767 mxTextColorMenu->set_cursor(0);
1768 mxTextColorMenu->grab_focus();
1769 bConsumed = true;
1770 }
1771 break;
1772 }
1773 case KEY_UP:
1774 {
1775 if (mxBackColorMenu->get_visible() &&
1776 mxTextColorMenu->has_focus() &&
1777 mxTextColorMenu->get_selected_index() == 0)
1778 {
1779 mxTextColorMenu->unselect_all();
1780 int nIndex = mxBackColorMenu->n_children() - 1;
1781 mxBackColorMenu->select(nIndex);
1782 mxBackColorMenu->set_cursor(nIndex);
1783 mxBackColorMenu->grab_focus();
1784 bConsumed = true;
1785 }
1786 break;
1787 }
1788 }
1789
1790 return bConsumed;
1791}
1792
1793IMPL_LINK(ScListSubMenuControl, ColorSelChangedHdl, weld::TreeView&, rMenu, void)
1794{
1795 if (rMenu.get_selected_index() == -1)
1796 return;
1797 if (&rMenu != mxTextColorMenu.get())
1798 mxTextColorMenu->unselect_all();
1799 else
1800 mxBackColorMenu->unselect_all();
1801 rMenu.grab_focus();
1802}
1803
1804IMPL_LINK(ScListSubMenuControl, RowActivatedHdl, weld::TreeView&, rMenu, bool)
1805{
1806 executeMenuItem(weld::fromId<ScCheckListMenuControl::Action*>(rMenu.get_selected_id()));
1807 return true;
1808}
1809
1811{
1812 // if no action is defined.
1813 if (!pAction)
1814 return;
1815
1816 const bool bClosePopup = pAction->execute();
1817 if (bClosePopup)
1819}
1820
1822{
1823 mxPopupStartAction.reset(p);
1824}
1825
1827{
1828 EndPopupMode();
1830}
1831
1832/* 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)
std::unique_ptr< Action > mxFieldChangedAction
void CheckEntry(std::u16string_view sName, const weld::TreeIter *pParent, bool bCheck)
std::unique_ptr< weld::Button > mxBtnUnselectSingle
ScCheckListMenuControl(weld::Widget *pParent, ScViewData &rViewData, bool bTreeMode, int nWidth, bool bIsMultiField=false)
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
void setFieldChangedAction(Action *p)
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
std::unique_ptr< weld::Label > mxFieldsComboLabel
tools::Rectangle GetSubMenuParentRect()
Get the area of the active row.
std::unique_ptr< weld::ComboBox > mxFieldsCombo
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
void addFields(const std::vector< OUString > &aFields)
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:1064
static CalendarWrapper & GetCalendar()
Definition: global.cxx:1073
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
virtual void clear()=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
OUString sName
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
tools::Long const nBorder
int i
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > 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
#define EDIT_UPDATEDATA_TIMEOUT
bool bVisible
OUString aLabel
OUString sId