Repository: nvaccess/nvda

Issue:
AddonsDialog: some add-ons are marked as Enabled when NVDA is launched with --disable-addons flag
<!--
Please thoroughly read NVDA's wiki article on how to fill in this template, including how to provide the required files.
Issues may be closed if the required information is not present.
https://github.com/nvaccess/nvda/wiki/Github-issue-template-explanation-and-examples
-->

### Steps to reproduce:

1. Launch NVDA.
2. Make sure you have at least one add-on installed.
3. Close NVDA.
4. Launch NVDA from the command line with the `--disable-addons` flag.
5. Open the add-on manager dialog.

### Actual behavior:

Add-ons that are enabled on your profile will appear as enabled on the dialog.

### Expected behavior:

Since NVDA has been told to start with all add-ons disabled, and since they are effectively not running, they shouldn't appear as enabled.

I found this when looking at #5785.

Since a label indicates that you must restart NVDA to re-enable your add-ons, and because the `--disable-addons` will likely be used by people who know what they're doing, I am wondering if we shouldn't disable this dialog and its menu entry all together.

### System configuration
#### NVDA installed/portable/running from source:

NVDA Installed.

#### NVDA version:

NVDA 2019.1.

#### Windows version:

Windows 10 Education version 1809.

### Other questions
#### Does the issue still occur after restarting your PC?

Yes.

#### Have you tried any other versions of NVDA?

No.


Discussion:
Confirmed.

My recommended action/fix would be to:

1. Leave the add-on dialog enabled.
2. Each add-on should be marked as disabled.
3. No option to enable or disable them should be present.
I agree. Will try to implement this soon!

Resolving patch:
diff --git a/source/addonHandler/__init__.py b/source/addonHandler/__init__.py
--- a/source/addonHandler/__init__.py
+++ b/source/addonHandler/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: UTF-8 -*-
 #addonHandler.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2012-2018 Rui Batista, NV Access Limited, Noelia Ruiz Martínez, Joseph Lee, Babbage B.V.
+#Copyright (C) 2012-2019 Rui Batista, NV Access Limited, Noelia Ruiz Martínez, Joseph Lee, Babbage B.V., Arnold Loubriat
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -16,6 +16,7 @@
 import shutil
 from six.moves import cStringIO as StringIO, cPickle
 from six import string_types
+import globalVars
 import zipfile
 from configobj import ConfigObj
 from configobj.validate import Validator
@@ -397,7 +398,7 @@ def enable(self, shouldEnable):
 
 	@property
 	def isRunning(self):
-		return not (self.isPendingInstall or self.isDisabled or self.isBlocked)
+		return not (globalVars.appArgs.disableAddons or self.isPendingInstall or self.isDisabled or self.isBlocked)
 
 	@property
 	def isDisabled(self):
diff --git a/source/gui/addonGui.py b/source/gui/addonGui.py
--- a/source/gui/addonGui.py
+++ b/source/gui/addonGui.py
@@ -2,7 +2,7 @@
 #A part of NonVisual Desktop Access (NVDA)
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
-#Copyright (C) 2012-2018 NV Access Limited, Beqa Gozalishvili, Joseph Lee, Babbage B.V., Ethan Holliger
+#Copyright (C) 2012-2019 NV Access Limited, Beqa Gozalishvili, Joseph Lee, Babbage B.V., Ethan Holliger, Arnold Loubriat
 
 import os
 import weakref
@@ -157,16 +157,28 @@ def __init__(self, parent):
 		AddonsDialog._instance = weakref.ref(self)
 		# Translators: The title of the Addons Dialog
 		title = _("Add-ons Manager")
-		wx.Dialog.__init__(self, parent, title=title)
+		# Translators: The title of the Addons Dialog when add-ons are disabled
+		titleWhenAddonsAreDisabled = _("Add-ons Manager (add-ons disabled)")
+		wx.Dialog.__init__(
+			self,
+			parent,
+			title=title if not globalVars.appArgs.disableAddons else titleWhenAddonsAreDisabled
+		)
 		DpiScalingHelperMixin.__init__(self, self.GetHandle())
 
 		mainSizer = wx.BoxSizer(wx.VERTICAL)
 		firstTextSizer = wx.BoxSizer(wx.VERTICAL)
 		listAndButtonsSizerHelper = guiHelper.BoxSizerHelper(self, sizer=wx.BoxSizer(wx.HORIZONTAL))
 		if globalVars.appArgs.disableAddons:
-			# Translators: A message in the add-ons manager shown when all add-ons are disabled.
-			label = _("All add-ons are currently disabled. To enable add-ons you must restart NVDA.")
-			firstTextSizer.Add(wx.StaticText(self, label=label))
+			# Translators: A message in the add-ons manager shown when add-ons are globally disabled.
+			label = _(
+				"NVDA was started with all add-ons disabled. "
+				"You may modify the enabled / disabled state, and install or uninstall add-ons. "
+				"Changes will not take effect until after NVDA is restarted."
+			)
+			addonsDisabledText = wx.StaticText(self, label=label)
+			addonsDisabledText.Wrap(self.scaleSize(670))
+			firstTextSizer.Add(addonsDisabledText)
 		# Translators: the label for the installed addons list in the addons manager.
 		entriesLabel = _("Installed Add-ons")
 		firstTextSizer.Add(wx.StaticText(self, label=entriesLabel))
@@ -303,16 +315,29 @@ def getAddonStatus(self, addon):
 			# Translators: The status shown for a newly installed addon before NVDA is restarted.
 			statusList.append(_("Install"))
 		# in some cases an addon can be expected to be disabled after install, so we want "install" to take precedence here
-		elif globalVars.appArgs.disableAddons or addon.isDisabled:
+		# If add-ons are globally disabled, don't show this status.
+		elif addon.isDisabled and not globalVars.appArgs.disableAddons:
 			# Translators: The status shown for an addon when its currently suspended do to addons being disabled.
 			statusList.append(_("Disabled"))
 		if addon.isPendingRemove:
 			# Translators: The status shown for an addon that has been marked as removed, before NVDA has been restarted.
 			statusList.append(_("Removed after restart"))
-		elif addon.isPendingDisable or (not addon.isPendingEnable and addon.isPendingInstall and addon.isDisabled):
+		elif addon.isPendingDisable or (
+				# yet to be installed, disabled after install
+				not addon.isPendingEnable and addon.isPendingInstall and addon.isDisabled
+			) or (
+				# addons globally disabled, disabled after restart
+				globalVars.appArgs.disableAddons and addon.isDisabled and not addon.isPendingEnable
+			):
 			# Translators: The status shown for an addon when it requires a restart to become disabled
 			statusList.append(_("Disabled after restart"))
-		elif addon.isPendingEnable or (addon.isPendingInstall and not addon.isDisabled):
+		elif addon.isPendingEnable or (
+				# yet to be installed, enabled after install
+				addon.isPendingInstall and not addon.isDisabled
+			) or (
+				# addons globally disabled, enabled after restart
+				globalVars.appArgs.disableAddons and not addon.isDisabled
+			):
 			# Translators: The status shown for an addon when it requires a restart to become enabled
 			statusList.append(_("Enabled after restart"))
 		return ", ".join(statusList)
@@ -378,7 +403,8 @@ def onClose(self,evt):
 		for addon in self.curAddons:
 			if (addon.isPendingInstall or addon.isPendingRemove
 				or addon.isDisabled and addon.isPendingEnable
-				or addon.isRunning and addon.isPendingDisable):
+				or addon.isRunning and addon.isPendingDisable
+				or not addon.isDisabled and addon.isPendingDisable):
 				needsRestart = True
 				break
 		if needsRestart:


Test patch: