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 
10 #ifndef INCLUDED_SC_SOURCE_UI_INC_CHECKLISTMENU_HXX
11 #define INCLUDED_SC_SOURCE_UI_INC_CHECKLISTMENU_HXX
12 
13 #include <vcl/popupmenuwindow.hxx>
14 #include <vcl/button.hxx>
15 #include <vcl/edit.hxx>
16 #include <vcl/timer.hxx>
17 #include <vcl/svlbitm.hxx>
18 
19 #include <memory>
20 #include <unordered_set>
21 #include <unordered_map>
22 #include <map>
23 #include <set>
24 
25 namespace com { namespace sun { namespace star {
26 
27     namespace accessibility {
28         class XAccessible;
29     }
30 
31 }}}
32 
33 class ScDocument;
34 class ScAccessibleFilterMenu;
35 
36 class ScMenuFloatingWindow : public PopupMenuFloatingWindow
37 {
38 public:
39     static constexpr size_t MENU_NOT_SELECTED = 999;
40 
41     /**
42      * Action to perform when an event takes place.  Create a sub-class of
43      * this to implement the desired action.
44      */
45     class Action
46     {
47     public:
~Action()48         virtual ~Action() {}
49         virtual void execute() = 0;
50     };
51 
52     explicit ScMenuFloatingWindow(vcl::Window* pParent, ScDocument* pDoc, sal_uInt16 nMenuStackLevel = 0);
53     virtual ~ScMenuFloatingWindow() override;
54      void dispose() override;
55 
56     virtual void PopupModeEnd() override;
57     virtual void MouseMove(const MouseEvent& rMEvt) override;
58     virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
59     virtual void MouseButtonUp(const MouseEvent& rMEvt) override;
60     virtual void KeyInput(const KeyEvent& rKEvt) override;
61     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
62     virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
63 
64     void addMenuItem(const OUString& rText, Action* pAction);
65     void addSeparator();
66 
67     ScMenuFloatingWindow* addSubMenuItem(const OUString& rText, bool bEnabled);
68     void setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu);
69     void selectMenuItem(size_t nPos, bool bSelected, bool bSubMenuTimer);
70     void clearSelectedMenuItem();
71     ScMenuFloatingWindow* getSubMenuWindow(size_t nPos) const;
72     bool isMenuItemSelected(size_t nPos) const;
getSelectedMenuItem() const73     size_t getSelectedMenuItem() const { return mnSelectedMenu;}
74 
75     void setName(const OUString& rName);
getName() const76     const OUString& getName() const { return maName;}
77 
78     void executeMenuItem(size_t nPos);
79     void getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const;
getParentMenuWindow() const80     ScMenuFloatingWindow* getParentMenuWindow() const { return mpParentMenu;}
81 
82 protected:
83     virtual void handlePopupEnd();
84 
85     Size getMenuSize() const;
86     void drawMenuItem(vcl::RenderContext& rRenderContext, size_t nPos);
87     void drawSeparator(vcl::RenderContext& rRenderContext, size_t nPos);
88     void drawAllMenuItems(vcl::RenderContext& rRenderContext);
getLabelFont() const89     const vcl::Font& getLabelFont() const
90     {
91         return maLabelFont;
92     }
93 
94     void queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu);
95     void queueCloseSubMenu();
96     void launchSubMenu(bool bSetMenuPos);
97     void endSubMenu(ScMenuFloatingWindow* pSubMenu);
98 
99     void fillMenuItemsToAccessible(ScAccessibleFilterMenu* pAccMenu) const;
100 
getDoc()101     ScDocument* getDoc() { return mpDoc;}
102 
103 protected:
104     css::uno::Reference<css::accessibility::XAccessible> mxAccessible;
105 
106 private:
107     struct SubMenuItemData;
108     void handleMenuTimeout(const SubMenuItemData* pTimer);
109 
110     void resizeToFitMenuItems();
111     void highlightMenuItem(vcl::RenderContext& rRenderContext, size_t nPos, bool bSelected);
112 
113     size_t getEnclosingMenuItem(const Point& rPos) const;
114     size_t getSubMenuPos(const ScMenuFloatingWindow* pSubMenu);
115 
116     /**
117      * Fire a menu highlight event since the accessibility framework needs
118      * this to track focus on menu items.
119      */
120     void fireMenuHighlightedEvent();
121 
122     /**
123      * Make sure that the specified submenu is permanently up, the submenu
124      * close timer is not active, and the correct menu item associated with
125      * the submenu is highlighted.
126      */
127     void setSubMenuFocused(const ScMenuFloatingWindow* pSubMenu);
128 
129     /**
130      * When a menu item of an invisible submenu is selected, we need to make
131      * sure that all its parent menu(s) are visible, with the right menu item
132      * highlighted in each of the parents.  Calling this method ensures it.
133      */
134     void ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu);
135 
136     /**
137      * Dismiss any visible child submenus when a menu item of a parent menu is
138      * selected.
139      */
140     void ensureSubMenuNotVisible();
141 
142     /**
143      * Dismiss all visible popup menus and set focus back to the application
144      * window.  This method is called e.g. when a menu action is fired.
145      */
146     void terminateAllPopupMenus();
147 
148 private:
149 
150     struct MenuItemData
151     {
152         OUString maText;
153         bool     mbEnabled:1;
154         bool     mbSeparator:1;
155 
156         std::shared_ptr<Action> mpAction;
157         VclPtr<ScMenuFloatingWindow> mpSubMenuWin;
158 
159         MenuItemData();
160     };
161 
162     std::vector<MenuItemData>         maMenuItems;
163 
164     struct SubMenuItemData
165     {
166         Timer                   maTimer;
167         VclPtr<ScMenuFloatingWindow>   mpSubMenu;
168         size_t                  mnMenuPos;
169 
170         DECL_LINK( TimeoutHdl, Timer*, void );
171 
172         SubMenuItemData(ScMenuFloatingWindow* pParent);
173         void reset();
174 
175     private:
176         VclPtr<ScMenuFloatingWindow> mpParent;
177     };
178     SubMenuItemData   maOpenTimer;
179     SubMenuItemData   maCloseTimer;
180 
181     vcl::Font         maLabelFont;
182 
183     // Name of this menu window, taken from the menu item of the parent window
184     // that launches it (if this is a sub menu).  If this is a top-level menu
185     // window, then this name can be anything.
186     OUString maName;
187 
188     size_t  mnSelectedMenu;
189     size_t  mnClickedMenu;
190 
191     ScDocument* mpDoc;
192 
193     VclPtr<ScMenuFloatingWindow> mpParentMenu;
194 };
195 
196 class ScCheckListMenuWindow;
197 
198 template <class T> struct VclPtr_hash;
199 template <> struct VclPtr_hash< VclPtr<vcl::Window> >
200 {
operator ()VclPtr_hash201     size_t operator()( const VclPtr<vcl::Window>& r ) const
202     {
203         return reinterpret_cast<size_t>(r.get());
204     }
205 };
206 
207 class ScTabStops
208 {
209 private:
210     typedef std::unordered_map< VclPtr<vcl::Window>, size_t, VclPtr_hash<VclPtr<vcl::Window>> > ControlToPosMap;
211     VclPtr<ScCheckListMenuWindow> mpMenuWindow;
212     ControlToPosMap maControlToPos;
213     std::vector<VclPtr<vcl::Window>> maControls;
214     size_t mnCurTabStop;
215 public:
216     ScTabStops( ScCheckListMenuWindow* mpMenuWin );
217     ~ScTabStops();
218     void AddTabStop( vcl::Window* pWin );
219     void SetTabStop( vcl::Window* pWin );
220     void CycleFocus( bool bReverse = false );
221     void clear();
222 };
223 
224 struct ScCheckListMember;
225 
226 class ScCheckListBox : public SvTreeListBox
227 {
228     std::unique_ptr<SvLBoxButtonData> mpCheckButton;
229     ScTabStops*         mpTabStops;
230     bool                mbSeenMouseButtonDown;
231     void            CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const;
232     void            CheckAllChildren( SvTreeListEntry* pEntry, bool bCheck );
233 
234     public:
235 
236     ScCheckListBox( vcl::Window* pParent );
~ScCheckListBox()237     virtual ~ScCheckListBox() override { disposeOnce(); }
dispose()238     virtual void dispose() override { mpCheckButton.reset(); SvTreeListBox::dispose(); }
239     void Init();
240     void CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck );
241     void CheckEntry( SvTreeListEntry* pEntry, bool bCheck );
242     SvTreeListEntry* ShowCheckEntry( const OUString& sName, ScCheckListMember& rMember, bool bShow = true, bool bCheck = true );
243     void GetRecursiveChecked( SvTreeListEntry* pEntry, std::unordered_set<OUString>& vOut, OUString& rLabel );
244     std::unordered_set<OUString> GetAllChecked();
245     bool IsChecked( const OUString& sName, SvTreeListEntry* pParent );
246     SvTreeListEntry* FindEntry( SvTreeListEntry* pParent, const OUString& sNode );
247     sal_uInt16 GetCheckedEntryCount() const;
248     virtual void KeyInput( const KeyEvent& rKEvt ) override;
249     virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
250     virtual void MouseButtonUp(const MouseEvent& rMEvt) override;
SetTabStopsContainer(ScTabStops * pTabStops)251     void SetTabStopsContainer( ScTabStops* pTabStops ) { mpTabStops = pTabStops; }
252 };
253 
254 class ScSearchEdit : public Edit
255 {
256 private:
257     ScTabStops*         mpTabStops;
258 public:
ScSearchEdit(Window * pParent)259     ScSearchEdit(Window* pParent)
260         : Edit(pParent)
261         , mpTabStops(nullptr)
262     {
263         set_id("search_edit");
264     }
265 
266     virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
SetTabStopsContainer(ScTabStops * pTabStops)267     void SetTabStopsContainer( ScTabStops* pTabStops )  { mpTabStops = pTabStops; }
268 };
269 
270 struct ScCheckListMember
271 {
272     enum DatePartType
273     {
274         YEAR,
275         MONTH,
276         DAY,
277     };
278 
279     OUString                 maName; // node name
280     OUString                 maRealName;
281     bool                     mbVisible;
282     bool                     mbDate;
283     bool                     mbLeaf;
284     DatePartType             meDatePartType;
285     // To store Year and Month if the member if DAY type
286     std::vector<OUString>    maDateParts;
287     ScCheckListMember();
288     SvTreeListEntry* mpParent;
289 };
290 
291 /**
292  * This class implements a popup window for field button, for quick access
293  * of hide-item list, and possibly more stuff related to field options.
294  */
295 class ScCheckListMenuWindow : public ScMenuFloatingWindow
296 {
297 public:
298     struct ResultEntry
299     {
300         OUString aName;
301         bool bValid;
302         bool bDate;
303 
operator <ScCheckListMenuWindow::ResultEntry304         bool operator<(const ResultEntry& rhs) const
305         {
306             return aName < rhs.aName;
307         }
308 
operator ==ScCheckListMenuWindow::ResultEntry309         bool operator == (const ResultEntry& rhs) const
310         {
311             return aName == rhs.aName &&
312                    bValid == rhs.bValid &&
313                    bDate == rhs.bDate;
314         }
315     };
316     typedef std::set<ResultEntry> ResultType;
317 
318     /**
319      * Extended data that the client code may need to store.  Create a
320      * sub-class of this and store data there.
321      */
322     struct ExtendedData {
323 
~ExtendedDataScCheckListMenuWindow::ExtendedData324     virtual ~ExtendedData() {}
325 
326     };
327 
328     /**
329      * Configuration options for this popup window.
330      */
331     struct Config
332     {
333         bool mbAllowEmptySet;
334         bool mbRTL;
335         Config();
336     };
337 
338     explicit ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc, int nWidth = -1);
339     virtual ~ScCheckListMenuWindow() override;
340     virtual void dispose() override;
341 
342     virtual void MouseMove(const MouseEvent& rMEvt) override;
343     virtual bool EventNotify(NotifyEvent& rNEvt) override;
344     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
345     virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
346 
347     void setMemberSize(size_t n);
348     void setHasDates(bool bHasDates);
349     void addDateMember(const OUString& rName, double nVal, bool bVisible);
350     void addMember(const OUString& rName, bool bVisible);
351     void initMembers();
352     void setConfig(const Config& rConfig);
353 
354     bool isAllSelected() const;
355     void getResult(ResultType& rResult);
356     void launch(const tools::Rectangle& rRect);
357     void close(bool bOK);
358 
359     /**
360      * Set auxiliary data that the client code might need.  Note that this
361      * popup window class manages its life time; no explicit deletion of the
362      * instance is needed in the client code.
363      */
364     void setExtendedData(std::unique_ptr<ExtendedData> p);
365 
366     /**
367      * Get the store auxiliary data, or NULL if no such data is stored.
368      */
369     ExtendedData* getExtendedData();
370 
371     void setOKAction(Action* p);
372     void setPopupEndAction(Action* p);
373 
374 protected:
375     virtual void handlePopupEnd() override;
376 
377 private:
378 
379     class CancelButton : public ::CancelButton
380     {
381     public:
382         CancelButton(ScCheckListMenuWindow* pParent);
383         virtual ~CancelButton() override;
384         virtual void dispose() override;
385 
386         virtual void Click() override;
387 
388     private:
389         VclPtr<ScCheckListMenuWindow> mpParent;
390     };
391 
392     enum SectionType {
393         WHOLE,                // entire window
394         LISTBOX_AREA_OUTER,   // box enclosing the check box items.
395         LISTBOX_AREA_INNER,   // box enclosing the check box items.
396         SINGLE_BTN_AREA,      // box enclosing the single-action buttons.
397         CHECK_TOGGLE_ALL,     // check box for toggling all items.
398         BTN_SINGLE_SELECT,
399         BTN_SINGLE_UNSELECT,
400         BTN_OK,               // OK button
401         BTN_CANCEL,           // Cancel button
402         EDIT_SEARCH,          // Search box
403     };
404     void getSectionPosSize(Point& rPos, Size& rSize, SectionType eType) const;
405 
406     /**
407      * Calculate the appropriate window size, the position and size of each
408      * control based on the menu items.
409      */
410     void packWindow();
411     void setAllMemberState(bool bSet);
412     void selectCurrentMemberOnly(bool bSet);
413     void updateMemberParents( const SvTreeListEntry* pLeaf, size_t nIdx );
414 
415     DECL_LINK( ButtonHdl, Button*, void );
416     DECL_LINK( TriStateHdl, Button*, void );
417     DECL_LINK( CheckHdl, SvTreeListBox*, void );
418     DECL_LINK( EdModifyHdl, Edit&, void );
419 
420 private:
421     VclPtr<ScSearchEdit>   maEdSearch;
422     VclPtr<ScCheckListBox> maChecks;
423 
424     VclPtr<TriStateBox>     maChkToggleAll;
425     VclPtr<ImageButton>     maBtnSelectSingle;
426     VclPtr<ImageButton>     maBtnUnselectSingle;
427 
428     VclPtr<OKButton>        maBtnOk;
429     VclPtr<CancelButton>    maBtnCancel;
430 
431     std::vector<ScCheckListMember> maMembers;
432     // For Dates
433     std::map<OUString, size_t>    maYearMonthMap;
434 
435     std::unique_ptr<ExtendedData> mpExtendedData;
436     std::unique_ptr<Action>       mpOKAction;
437     std::unique_ptr<Action>       mpPopupEndAction;
438 
439     Config maConfig;
440     Size maWndSize;  /// whole window size.
441     Size maMenuSize; /// size of all menu items combined.
442     TriState mePrevToggleAllState;
443     ScTabStops maTabStops;
444 };
445 
446 #endif
447 
448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
449