|
1 | 1 | # A part of NonVisual Desktop Access (NVDA) |
2 | | -# Copyright (C) 2022-2023 NV Access Limited |
| 2 | +# Copyright (C) 2022-2023 NV Access Limited, Cyrille Bougot |
3 | 3 | # This file is covered by the GNU General Public License. |
4 | 4 | # See the file COPYING for more details. |
5 | 5 |
|
|
44 | 44 |
|
45 | 45 | from ..controls.messageDialogs import ( |
46 | 46 | _shouldEnableWhenAddonTooOldDialog, |
| 47 | + _shouldEnableWhenMultipleAddonsTooOldDialog, |
47 | 48 | _shouldProceedToRemoveAddonDialog, |
| 49 | + _shouldProceedToRemoveMultipleAddonDialog, |
48 | 50 | _shouldInstallWhenAddonTooOldDialog, |
49 | 51 | _shouldProceedWhenInstalledAddonVersionUnknown, |
50 | 52 | ) |
@@ -249,16 +251,26 @@ def helpAddon(self, listItemVM: AddonListItemVM) -> None: |
249 | 251 | assert path is not None |
250 | 252 | startfile(path) |
251 | 253 |
|
252 | | - def removeAddon(self, listItemVM: AddonListItemVM[_AddonGUIModel]) -> None: |
| 254 | + def removeAddon(self, listItemVM: AddonListItemVM[_AddonGUIModel], askConfirmation: bool = True) -> None: |
253 | 255 | assert addonDataManager |
254 | 256 | assert listItemVM.model |
255 | | - if _shouldProceedToRemoveAddonDialog(listItemVM.model): |
| 257 | + if not askConfirmation or _shouldProceedToRemoveAddonDialog(listItemVM.model): |
256 | 258 | addonDataManager._deleteCacheInstalledAddon(listItemVM.model.name) |
257 | 259 | assert listItemVM.model._addonHandlerModel is not None |
258 | 260 | listItemVM.model._addonHandlerModel.requestRemove() |
259 | 261 | self.refresh() |
260 | 262 | listItemVM.status = getStatus(listItemVM.model) |
261 | 263 |
|
| 264 | + def removeAddons(self, listItemVMs: Iterable[AddonListItemVM[_AddonStoreModel]]) -> None: |
| 265 | + if not _shouldProceedToRemoveMultipleAddonDialog(nAddons=len(listItemVMs)): |
| 266 | + log.debug("Aborting batch remove add-ons.") |
| 267 | + return |
| 268 | + for aVM in listItemVMs: |
| 269 | + if aVM.status == AvailableAddonStatus.PENDING_REMOVE: |
| 270 | + log.debug(f"Skipping {aVM.Id} as it is already pending remove") |
| 271 | + else: |
| 272 | + self.removeAddon(aVM, askConfirmation=False) |
| 273 | + |
262 | 274 | def installOverrideIncompatibilityForAddon(self, listItemVM: AddonListItemVM) -> None: |
263 | 275 | from gui import mainFrame |
264 | 276 | if _shouldInstallWhenAddonTooOldDialog(mainFrame, listItemVM.model): |
@@ -298,18 +310,70 @@ def _handleEnableDisable(self, listItemVM: AddonListItemVM[_AddonManifestModel], |
298 | 310 | listItemVM.status = getStatus(listItemVM.model) |
299 | 311 | self.refresh() |
300 | 312 |
|
301 | | - def enableOverrideIncompatibilityForAddon(self, listItemVM: AddonListItemVM[_AddonManifestModel]) -> None: |
| 313 | + def enableOverrideIncompatibilityForAddon( |
| 314 | + self, |
| 315 | + listItemVM: AddonListItemVM[_AddonManifestModel], |
| 316 | + askConfirmation: bool = True, |
| 317 | + ) -> None: |
302 | 318 | from ... import mainFrame |
303 | | - if _shouldEnableWhenAddonTooOldDialog(mainFrame, listItemVM.model): |
| 319 | + if not askConfirmation or _shouldEnableWhenAddonTooOldDialog(mainFrame, listItemVM.model): |
304 | 320 | listItemVM.model.enableCompatibilityOverride() |
305 | 321 | self._handleEnableDisable(listItemVM, True) |
306 | 322 |
|
307 | 323 | def enableAddon(self, listItemVM: AddonListItemVM) -> None: |
308 | 324 | self._handleEnableDisable(listItemVM, True) |
309 | 325 |
|
| 326 | + def enableAddons(self, listItemVMs: Iterable[AddonListItemVM[_AddonStoreModel]]) -> None: |
| 327 | + disabledIncompatibleAddons = [ |
| 328 | + aVM for aVM in listItemVMs |
| 329 | + if aVM.status in ( |
| 330 | + AvailableAddonStatus.INCOMPATIBLE_DISABLED, |
| 331 | + AvailableAddonStatus.PENDING_INCOMPATIBLE_DISABLED, |
| 332 | + ) |
| 333 | + and not aVM.model.isCompatible |
| 334 | + and aVM.model.canOverrideCompatibility |
| 335 | + ] |
| 336 | + if ( |
| 337 | + len(disabledIncompatibleAddons) > 0 |
| 338 | + and not _shouldEnableWhenMultipleAddonsTooOldDialog(len(disabledIncompatibleAddons)) |
| 339 | + ): |
| 340 | + log.debug("Override incompatibility has been declined. Aborting batch enable add-ons.") |
| 341 | + return |
| 342 | + for aVM in listItemVMs: |
| 343 | + if aVM.status in ( |
| 344 | + AvailableAddonStatus.INCOMPATIBLE_DISABLED, |
| 345 | + AvailableAddonStatus.PENDING_INCOMPATIBLE_DISABLED, |
| 346 | + ) and not aVM.model.isCompatible and aVM.model.canOverrideCompatibility: |
| 347 | + self.enableOverrideIncompatibilityForAddon(aVM, askConfirmation=False) |
| 348 | + elif aVM.status in ( |
| 349 | + AvailableAddonStatus.DISABLED, |
| 350 | + AvailableAddonStatus.PENDING_DISABLE, |
| 351 | + ): |
| 352 | + self.enableAddon(aVM) |
| 353 | + else: |
| 354 | + log.debug( |
| 355 | + f"Skipping {aVM.Id} as it is not disabled, pending disable, incompatible disabled " |
| 356 | + "or pending incompatible disabled" |
| 357 | + ) |
| 358 | + |
| 359 | + |
| 360 | + |
310 | 361 | def disableAddon(self, listItemVM: AddonListItemVM) -> None: |
311 | 362 | self._handleEnableDisable(listItemVM, False) |
312 | 363 |
|
| 364 | + def disableAddons(self, listItemVMs: Iterable[AddonListItemVM[_AddonStoreModel]]) -> None: |
| 365 | + for aVM in listItemVMs: |
| 366 | + if aVM.status in [ |
| 367 | + AvailableAddonStatus.DISABLED, |
| 368 | + AvailableAddonStatus.PENDING_DISABLE, |
| 369 | + AvailableAddonStatus.INCOMPATIBLE_DISABLED, |
| 370 | + AvailableAddonStatus.PENDING_INCOMPATIBLE_DISABLED, |
| 371 | + AvailableAddonStatus.PENDING_REMOVE, |
| 372 | + ]: |
| 373 | + log.debug(f"Skipping {aVM.Id} as it is already disabled, pending disabled or pending remove") |
| 374 | + else: |
| 375 | + self.disableAddon(aVM) |
| 376 | + |
313 | 377 | def replaceAddon(self, listItemVM: AddonListItemVM) -> None: |
314 | 378 | from ... import mainFrame |
315 | 379 | if _shouldProceedWhenInstalledAddonVersionUnknown(mainFrame, listItemVM.model): |
|
0 commit comments