From 683100d67368176875369617875a2da4889ee35f Mon Sep 17 00:00:00 2001 From: John Scipione Date: Sat, 28 Nov 2020 21:09:06 -0500 Subject: [PATCH] Tracker: Live updating shiftable menu items Live updating menu classes listen for B_MODIFIERS_CHANGED messages and update the item label and shortcut on Shift or Control. -- File -- Create link => Create relative link Move to Trash => Delete Cut => Cut more Copy => Copy more Paste => Paste links Identify => Force identify -- Window -- Close => Close all Open parent enable/disable on Control to open root window -- Window => Arrange by/Desktop -- Clean up => Clean up all Create TLiveMenu and TLivePopUpMenu classes that inherit from BMenu and BPopUpMenu respectively. Create TLiveFileMenu, TLivePosePopUpMenu, TLiveWindowMenu and TLiveWindowPopUpMenu subclasses for the File and Window menu and context menus respectively. Create TLiveMixin to share methods between menus. Pass in parent window and use window shortcuts to enable/disable "Open parent". Change-Id: I5dfbd4d468fad02894f1f31aa08d1abf630a4b5d Reviewed-on: https://review.haiku-os.org/c/haiku/+/2293 Reviewed-by: John Scipione --- src/kits/tracker/ContainerWindow.cpp | 15 +- src/kits/tracker/FilePanelPriv.cpp | 11 +- src/kits/tracker/Jamfile | 1 + src/kits/tracker/LiveMenu.cpp | 309 +++++++++++++++++++++++++++ src/kits/tracker/LiveMenu.h | 121 +++++++++++ 5 files changed, 445 insertions(+), 12 deletions(-) create mode 100644 src/kits/tracker/LiveMenu.cpp create mode 100644 src/kits/tracker/LiveMenu.h diff --git a/src/kits/tracker/ContainerWindow.cpp b/src/kits/tracker/ContainerWindow.cpp index f2007205db..2e7a672275 100644 --- a/src/kits/tracker/ContainerWindow.cpp +++ b/src/kits/tracker/ContainerWindow.cpp @@ -79,6 +79,7 @@ All rights reserved. #include "FavoritesMenu.h" #include "FindPanel.h" #include "IconMenuItem.h" +#include "LiveMenu.h" #include "MimeTypes.h" #include "Model.h" #include "MountMenu.h" @@ -634,13 +635,13 @@ void BContainerWindow::AddContextMenus() { // create context sensitive menus - fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); + fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false); AddPoseContextMenu(fPoseContextMenu); fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false); AddVolumeContextMenu(fVolumeContextMenu); - fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); + fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false); AddWindowContextMenu(fWindowContextMenu); fDropContextMenu = new BPopUpMenu("DropContext", false, false); @@ -714,11 +715,11 @@ BContainerWindow::RepopulateMenus() } delete fPoseContextMenu; - fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); + fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false); AddPoseContextMenu(fPoseContextMenu); delete fWindowContextMenu; - fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); + fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false); AddWindowContextMenu(fWindowContextMenu); } @@ -1695,11 +1696,11 @@ BContainerWindow::IsShowing(const entry_ref* entry) const void BContainerWindow::AddMenus() { - fFileMenu = new BMenu(B_TRANSLATE("File")); + fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this); AddFileMenu(fFileMenu); fMenuBar->AddItem(fFileMenu); - fWindowMenu = new BMenu(B_TRANSLATE("Window")); + fWindowMenu = new TLiveWindowMenu(B_TRANSLATE("Window"), this); fMenuBar->AddItem(fWindowMenu); AddWindowMenu(fWindowMenu); @@ -3458,7 +3459,7 @@ BContainerWindow::NewArrangeByMenu() ASSERT(fAttrMenu); // create a new "Arrange by >" menu - BMenu* menu = new BMenu(Shortcuts()->ArrangeByLabel()); + TLiveArrangeByMenu* menu = new TLiveArrangeByMenu(Shortcuts()->ArrangeByLabel(), this); // add Attributes items to "Arrange by >" BMenuItem* item; diff --git a/src/kits/tracker/FilePanelPriv.cpp b/src/kits/tracker/FilePanelPriv.cpp index 37399b6ae2..f20bd7b10c 100644 --- a/src/kits/tracker/FilePanelPriv.cpp +++ b/src/kits/tracker/FilePanelPriv.cpp @@ -75,6 +75,7 @@ All rights reserved. #include "FSUtils.h" #include "FavoritesMenu.h" #include "IconMenuItem.h" +#include "LiveMenu.h" #include "MimeTypes.h" #include "NavMenu.h" #include "Shortcuts.h" @@ -851,7 +852,7 @@ TFilePanel::AddMenus() { // File - fFileMenu = new BMenu(B_TRANSLATE("File")); + fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this); AddFileMenu(fFileMenu); fMenuBar->AddItem(fFileMenu); @@ -1124,19 +1125,19 @@ TFilePanel::RepopulateMenus() fMenuBar->RemoveItem(fFileMenu); delete fFileMenu; if (ShouldAddMenus()) { - fFileMenu = new BMenu(B_TRANSLATE("File")); - TFilePanel::AddFileMenu(fFileMenu); + fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this); + AddFileMenu(fFileMenu); fMenuBar->AddItem(fFileMenu, 0); } } delete fPoseContextMenu; - fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); + fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false); fPoseContextMenu->SetFont(be_plain_font); TFilePanel::AddPoseContextMenu(fPoseContextMenu); delete fWindowContextMenu; - fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); + fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false); fWindowContextMenu->SetFont(be_plain_font); TFilePanel::AddWindowContextMenu(fWindowContextMenu); } diff --git a/src/kits/tracker/Jamfile b/src/kits/tracker/Jamfile index f29fa14677..b60d34589b 100644 --- a/src/kits/tracker/Jamfile +++ b/src/kits/tracker/Jamfile @@ -52,6 +52,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { IconCache.cpp IconMenuItem.cpp InfoWindow.cpp + LiveMenu.cpp MimeTypeList.cpp MiniMenuField.cpp Model.cpp diff --git a/src/kits/tracker/LiveMenu.cpp b/src/kits/tracker/LiveMenu.cpp new file mode 100644 index 0000000000..aa24c9bf6d --- /dev/null +++ b/src/kits/tracker/LiveMenu.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2020-2024 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ + + +#include "LiveMenu.h" + +#include + +#include +#include +#include +#include + +#include "Commands.h" +#include "TrackerSettings.h" + + +// #pragma mark - TLiveMixin + + +TLiveMixin::TLiveMixin(const BContainerWindow* window) +{ + fWindow = window; +} + + +void +TLiveMixin::UpdateFileMenu(BMenu* menu) +{ + if (menu == NULL) + return; + + if (menu->Window()->LockLooper()) { + int32 itemCount = menu->CountItems(); + for (int32 index = 0; index < itemCount; index++) { + BMenuItem* item = menu->ItemAt(index); + if (item == NULL || item->Message() == NULL) + continue; + + switch (item->Message()->what) { + // Move to Trash/Delete + case kMoveSelectionToTrash: + case kDeleteSelection: + if (!fWindow->Shortcuts()->IsTrash()) + fWindow->Shortcuts()->UpdateMoveToTrashItem(item); + break; + + // Create link/Create relative link + case kCreateLink: + case kCreateRelativeLink: + fWindow->Shortcuts()->UpdateCreateLinkItem(item); + break; + + // Cut/Cut more + case B_CUT: + case kCutMoreSelectionToClipboard: + fWindow->Shortcuts()->UpdateCutItem(item); + break; + + // Copy/Copy more + case B_COPY: + case kCopyMoreSelectionToClipboard: + fWindow->Shortcuts()->UpdateCopyItem(item); + break; + + // Paste/Paste links + case B_PASTE: + case kPasteLinksFromClipboard: + fWindow->Shortcuts()->UpdatePasteItem(item); + break; + + // Identify/Force identify + case kIdentifyEntry: + fWindow->Shortcuts()->UpdateIdentifyItem(item); + break; + } + } + + menu->Window()->UnlockLooper(); + } +} + + +void +TLiveMixin::UpdateWindowMenu(BMenu* menu) +{ + if (menu == NULL) + return; + + // update using the window version of TShortcuts class + + if (menu->Window()->LockLooper()) { + int32 itemCount = menu->CountItems(); + for (int32 index = 0; index < itemCount; index++) { + BMenuItem* item = menu->ItemAt(index); + if (item == NULL || item->Message() == NULL) + continue; + + switch (item->Message()->what) { + // Clean up/Clean up all + case kCleanup: + case kCleanupAll: + fWindow->Shortcuts()->UpdateCleanupItem(item); + break; + + // Open parent + case kOpenParentDir: + fWindow->Shortcuts()->UpdateOpenParentItem(item); + break; + + // Close/Close all + case B_QUIT_REQUESTED: + case kCloseAllWindows: + fWindow->Shortcuts()->UpdateCloseItem(item); + break; + } + } + + menu->Window()->UnlockLooper(); + } +} + + +// #pragma mark - TLiveMenu + + +TLiveMenu::TLiveMenu(const char* label) + : + BMenu(label) +{ +} + + +TLiveMenu::~TLiveMenu() +{ +} + + +void +TLiveMenu::MessageReceived(BMessage* message) +{ + if (message != NULL && message->what == B_MODIFIERS_CHANGED) + Update(); + else + BMenu::MessageReceived(message); +} + + +void +TLiveMenu::Update() +{ + // hook method +} + + +// #pragma mark - TLivePopUpMenu + + +TLivePopUpMenu::TLivePopUpMenu(const char* label, bool radioMode, bool labelFromMarked, + menu_layout layout) + : + BPopUpMenu(label, radioMode, labelFromMarked, layout) +{ +} + + +TLivePopUpMenu::~TLivePopUpMenu() +{ +} + + +void +TLivePopUpMenu::MessageReceived(BMessage* message) +{ + if (message != NULL && message->what == B_MODIFIERS_CHANGED) + Update(); + else + BMenu::MessageReceived(message); +} + + +void +TLivePopUpMenu::Update() +{ + // hook method +} + + +// #pragma mark - TLiveArrangeByMenu + + +TLiveArrangeByMenu::TLiveArrangeByMenu(const char* label, const BContainerWindow* window) + : + TLiveMenu(label), + TLiveMixin(window) +{ +} + + +TLiveArrangeByMenu::~TLiveArrangeByMenu() +{ +} + + +void +TLiveArrangeByMenu::Update() +{ + // Clean up/Clean up all + TShortcuts().UpdateCleanupItem(TShortcuts().FindItem(this, kCleanup, kCleanupAll)); +} + + +// #pragma mark - TLiveFileMenu + + +TLiveFileMenu::TLiveFileMenu(const char* label, const BContainerWindow* window) + : + TLiveMenu(label), + TLiveMixin(window) +{ +} + + +TLiveFileMenu::~TLiveFileMenu() +{ +} + + +void +TLiveFileMenu::Update() +{ + UpdateFileMenu(this); +} + + +// #pragma mark - TLivePosePopUpMenu + + +TLivePosePopUpMenu::TLivePosePopUpMenu(const char* label, const BContainerWindow* window, + bool radioMode, bool labelFromMarked, menu_layout layout) + : + TLivePopUpMenu(label, radioMode, labelFromMarked, layout), + TLiveMixin(window) +{ +} + + +TLivePosePopUpMenu::~TLivePosePopUpMenu() +{ +} + + +void +TLivePosePopUpMenu::Update() +{ + UpdateFileMenu(this); +} + + +// #pragma mark - TLiveWindowMenu + + +TLiveWindowMenu::TLiveWindowMenu(const char* label, const BContainerWindow* window) + : + TLiveMenu(label), + TLiveMixin(window) +{ +} + + +TLiveWindowMenu::~TLiveWindowMenu() +{ +} + + +void +TLiveWindowMenu::Update() +{ + UpdateWindowMenu(this); +} + + +// #pragma mark - TLiveWindowPopUpMenu + + +TLiveWindowPopUpMenu::TLiveWindowPopUpMenu(const char* label, const BContainerWindow* window, + bool radioMode, bool labelFromMarked, menu_layout layout) + : + TLivePopUpMenu(label, radioMode, labelFromMarked, layout), + TLiveMixin(window) +{ +} + + +TLiveWindowPopUpMenu::~TLiveWindowPopUpMenu() +{ +} + + +void +TLiveWindowPopUpMenu::Update() +{ + UpdateWindowMenu(this); +} diff --git a/src/kits/tracker/LiveMenu.h b/src/kits/tracker/LiveMenu.h new file mode 100644 index 0000000000..52364c1c0a --- /dev/null +++ b/src/kits/tracker/LiveMenu.h @@ -0,0 +1,121 @@ +/* + * Copyright 2020-2024 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ +#ifndef _LIVE_MENU_H +#define _LIVE_MENU_H + + +#include +#include + +#include "ContainerWindow.h" +#include "Shortcuts.h" + + +namespace BPrivate { + + +// mixin class for sharing virtual methods +struct TLiveMixin { + TLiveMixin(const BContainerWindow* window); + + virtual void UpdateFileMenu(BMenu* menu); + virtual void UpdateWindowMenu(BMenu* menu); + +private: + const BContainerWindow* fWindow; +}; + + +class TLiveMenu : public BMenu { +public: + TLiveMenu(const char* label); + virtual ~TLiveMenu(); + + virtual void MessageReceived(BMessage* message); + +protected: + virtual void Update(); +}; + + +class TLivePopUpMenu : public BPopUpMenu { +public: + TLivePopUpMenu(const char* label, + bool radioMode = true, + bool labelFromMarked = true, + menu_layout layout = B_ITEMS_IN_COLUMN); + virtual ~TLivePopUpMenu(); + + virtual void MessageReceived(BMessage* message); + +protected: + virtual void Update(); + +private: + const BContainerWindow* fWindow; +}; + + +class TLiveArrangeByMenu: public TLiveMenu, public TLiveMixin { +public: + TLiveArrangeByMenu(const char* label, const BContainerWindow* window); + virtual ~TLiveArrangeByMenu(); + +protected: + virtual void Update(); +}; + + +class TLiveFileMenu : public TLiveMenu, public TLiveMixin { +public: + TLiveFileMenu(const char* label, const BContainerWindow* window); + virtual ~TLiveFileMenu(); + +protected: + virtual void Update(); +}; + +class TLivePosePopUpMenu : public TLivePopUpMenu, public TLiveMixin { +public: + TLivePosePopUpMenu(const char* label, const BContainerWindow* window, + bool radioMode = true, bool labelFromMarked = true, + menu_layout layout = B_ITEMS_IN_COLUMN); + virtual ~TLivePosePopUpMenu(); + +protected: + virtual void Update(); +}; + + +class TLiveWindowMenu : public TLiveMenu, public TLiveMixin { +public: + TLiveWindowMenu(const char* label, const BContainerWindow* window); + virtual ~TLiveWindowMenu(); + +protected: + virtual void Update(); +}; + +class TLiveWindowPopUpMenu : public TLivePopUpMenu, public TLiveMixin { +public: + TLiveWindowPopUpMenu(const char* label, const BContainerWindow* window, + bool radioMode = true, bool labelFromMarked = true, + menu_layout layout = B_ITEMS_IN_COLUMN); + virtual ~TLiveWindowPopUpMenu(); + +protected: + virtual void Update(); +}; + + +} // namespace BPrivate + +using namespace BPrivate; + + +#endif // _LIVE_MENU_H