33# This file is covered by the GNU General Public License.
44# See the file COPYING for more details.
55
6+ from abc import ABC , abstractmethod
67import functools
78from typing import (
89 Dict ,
10+ Generic ,
11+ Iterable ,
912 List ,
13+ TypeVar ,
1014)
1115
1216import wx
1317
18+ from _addonStore .models .status import _StatusFilterKey
1419from logHandler import log
20+ import ui
1521
16- from ..viewModels .action import AddonActionVM
22+ from ..viewModels .action import AddonActionVM , BatchAddonActionVM
23+ from ..viewModels .addonList import AddonListItemVM
1724from ..viewModels .store import AddonStoreVM
1825
1926
20- class _ActionsContextMenu :
21- def __init__ (self , storeVM : AddonStoreVM ):
22- self ._storeVM = storeVM
23- self ._actionMenuItemMap : Dict [AddonActionVM , wx .MenuItem ] = {}
24- self ._contextMenu = wx .Menu ()
27+ AddonActionT = TypeVar ("AddonActionT" , AddonActionVM , BatchAddonActionVM )
28+
29+
30+ class _ActionsContextMenuP (Generic [AddonActionT ], ABC ):
31+ _actions : List [AddonActionT ]
32+ _actionMenuItemMap : Dict [AddonActionT , wx .MenuItem ]
33+ _contextMenu : wx .Menu
34+
35+ @abstractmethod
36+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionT ):
37+ ...
2538
2639 def popupContextMenuFromPosition (
2740 self ,
@@ -31,14 +44,9 @@ def popupContextMenuFromPosition(
3144 self ._populateContextMenu ()
3245 targetWindow .PopupMenu (self ._contextMenu , pos = position )
3346
34- def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionVM ):
35- selectedAddon = actionVM .listItemVM
36- log .debug (f"action selected: actionVM: { actionVM } , selectedAddon: { selectedAddon } " )
37- actionVM .actionHandler (selectedAddon )
38-
3947 def _populateContextMenu (self ):
4048 prevActionIndex = - 1
41- for action in self ._storeVM . actionVMList :
49+ for action in self ._actions :
4250 menuItem = self ._actionMenuItemMap .get (action )
4351 menuItems : List [wx .MenuItem ] = list (self ._contextMenu .GetMenuItems ())
4452 isMenuItemInContextMenu = menuItem is not None and menuItem in menuItems
@@ -71,3 +79,68 @@ def _populateContextMenu(self):
7179 # Remove the menu item from the context menu.
7280 self ._contextMenu .RemoveItem (menuItem )
7381 del self ._actionMenuItemMap [action ]
82+
83+ menuItems : List [wx .MenuItem ] = list (self ._contextMenu .GetMenuItems ())
84+ for menuItem in menuItems :
85+ if menuItem not in self ._actionMenuItemMap .values ():
86+ # The menu item is not in the action menu item map.
87+ # It should be removed from the context menu.
88+ self ._contextMenu .RemoveItem (menuItem )
89+
90+
91+ class _MonoActionsContextMenu (_ActionsContextMenuP [AddonActionVM ]):
92+ """Context menu for actions for a single add-on"""
93+ def __init__ (self , storeVM : AddonStoreVM ):
94+ self ._storeVM = storeVM
95+ self ._actionMenuItemMap = {}
96+ self ._contextMenu = wx .Menu ()
97+
98+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionVM ):
99+ selectedAddon = actionVM .actionTarget
100+ log .debug (f"action selected: actionVM: { actionVM .displayName } , selectedAddon: { selectedAddon } " )
101+ actionVM .actionHandler (selectedAddon )
102+
103+ @property
104+ def _actions (self ) -> List [AddonActionVM ]:
105+ return self ._storeVM .actionVMList
106+
107+
108+ class _BatchActionsContextMenu (_ActionsContextMenuP [BatchAddonActionVM ]):
109+ """Context menu for actions for a group of add-ons"""
110+ def __init__ (self , storeVM : AddonStoreVM ):
111+ self ._storeVM = storeVM
112+ self ._actionMenuItemMap = {}
113+ self ._contextMenu = wx .Menu ()
114+ self ._selectedAddons : Iterable [AddonListItemVM ] = tuple ()
115+
116+ def _updateSelectedAddons (self , selectedAddons : Iterable [AddonListItemVM ]):
117+ # Reset the action menu as self._actions depends on the selected add-ons
118+ self ._actionMenuItemMap = {}
119+ self ._selectedAddons = selectedAddons
120+
121+ def popupContextMenuFromPosition (
122+ self ,
123+ targetWindow : wx .Window ,
124+ position : wx .Position = wx .DefaultPosition
125+ ):
126+ super ().popupContextMenuFromPosition (targetWindow , position )
127+ if self ._contextMenu .GetMenuItemCount () == 0 :
128+ # Translators: a message displayed when activating the context menu on multiple selected add-ons,
129+ # but no actions are available for the add-ons.
130+ ui .message (pgettext ("addonStore" , "No actions available for the selected add-ons" ))
131+
132+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : BatchAddonActionVM ):
133+ log .debug (f"Performing batch action for actionVM: { actionVM .displayName } " )
134+ actionVM .actionHandler (self ._selectedAddons )
135+
136+ @property
137+ def _actions (self ) -> List [BatchAddonActionVM ]:
138+ return [
139+ BatchAddonActionVM (
140+ # Translators: Label for an action that installs the selected add-ons
141+ displayName = pgettext ("addonStore" , "&Install selected add-ons" ),
142+ actionHandler = self ._storeVM .getAddons ,
143+ validCheck = lambda aVMs : self ._storeVM ._filteredStatusKey == _StatusFilterKey .AVAILABLE ,
144+ actionTarget = self ._selectedAddons
145+ ),
146+ ]
0 commit comments