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