Skip to content

Commit 76aca61

Browse files
authored
Merge c70ce1f into d85dae2
2 parents d85dae2 + c70ce1f commit 76aca61

5 files changed

Lines changed: 124 additions & 18 deletions

File tree

source/core.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,13 @@ def main():
806806
)
807807
elif globalVars.appArgs.portablePath and (globalVars.appArgs.createPortable or globalVars.appArgs.createPortableSilent):
808808
import gui.installerGui
809-
wx.CallAfter(gui.installerGui.doCreatePortable,portableDirectory=globalVars.appArgs.portablePath,
810-
silent=globalVars.appArgs.createPortableSilent,startAfterCreate=not globalVars.appArgs.createPortableSilent)
809+
wx.CallAfter(
810+
gui.installerGui.doCreatePortable,
811+
portableDirectory=globalVars.appArgs.portablePath,
812+
silent=globalVars.appArgs.createPortableSilent,
813+
startAfterCreate=not globalVars.appArgs.createPortableSilent,
814+
warnForNonEmptyDirectory=not globalVars.appArgs.createPortableSilent,
815+
)
811816
elif not globalVars.appArgs.minimal:
812817
try:
813818
# Translators: This is shown on a braille display (if one is connected) when NVDA starts.

source/gui/installerGui.py

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import gui.contextHelp
1919
from gui.dpiScalingHelper import DpiScalingHelperMixinWithoutInit
2020
import systemUtils
21+
import ui
2122
from NVDAState import WritePaths
2223
from .message import displayDialogAsModal
2324

@@ -340,6 +341,62 @@ def showInstallGui():
340341
gui.mainFrame.postPopup()
341342

342343

344+
def _warnAndConfirmForNonEmptyDirectory(portableDirectory: str) -> bool:
345+
"""
346+
Display a warning message if the specified directory is not empty.
347+
:param portableDirectory: The directory to check.
348+
:return: True if the user wants to continue, False if the user wants to cancel.
349+
"""
350+
if os.path.exists(portableDirectory):
351+
dirContents = os.listdir(portableDirectory)
352+
else:
353+
dirContents = []
354+
if len(dirContents) > 0:
355+
if "nvda.exe" in dirContents:
356+
if wx.NO == gui.messageBox(
357+
_(
358+
# Translators: The message displayed when the user has specified a destination directory
359+
# that already has a portable copy in the Create Portable NVDA dialog.
360+
f"A portable copy already exists in the directory '{portableDirectory}'. "
361+
"Do you want to update it?"
362+
),
363+
# Translators: The title of a dialog presented when the user has specified a destination directory
364+
# that already has a portable copy in the Create Portable NVDA dialog.
365+
_("Portable Copy Exists"),
366+
wx.YES_NO | wx.ICON_QUESTION
367+
):
368+
return False
369+
elif wx.NO == gui.messageBox(
370+
_(
371+
# Translators: The message displayed when the user has specified a destination directory
372+
# that already exists in the Create Portable NVDA dialog.
373+
f"The specified directory '{portableDirectory}' is not empty. "
374+
"Proceeding will delete and replace existing files in the directory. "
375+
"Do you want to overwrite the contents of this folder? "
376+
),
377+
# Translators: The title of a dialog presented when the user has specified a destination directory
378+
# that already exists in the Create Portable NVDA dialog.
379+
_("Directory Exists"),
380+
wx.YES_NO | wx.ICON_QUESTION
381+
):
382+
return False
383+
return True
384+
385+
386+
def _getUniqueNewPortableDirectory(basePath: str) -> str:
387+
"""
388+
Generate a new directory name for a portable copy of NVDA.
389+
:param basePath: The base path to use.
390+
:return: A new directory name.
391+
"""
392+
newPortableDirectory = os.path.join(basePath, "NVDA")
393+
i = 1
394+
while os.path.exists(newPortableDirectory):
395+
newPortableDirectory = os.path.join(basePath, f"NVDA_{i}")
396+
i += 1
397+
return newPortableDirectory
398+
399+
343400
class PortableCreaterDialog(
344401
gui.contextHelp.ContextHelpMixin,
345402
wx.Dialog, # wxPython does not seem to call base class initializer, put last in MRO
@@ -367,13 +424,18 @@ def __init__(self, parent):
367424
browseText = _("Browse...")
368425
# Translators: The title of the dialog presented when browsing for the
369426
# destination directory when creating a portable copy of NVDA.
370-
dirDialogTitle = _("Select portable directory")
427+
dirDialogTitle = _("Select portable directory")
371428
directoryPathHelper = guiHelper.PathSelectionHelper(groupBox, browseText, dirDialogTitle)
372429
directoryEntryControl = groupHelper.addItem(directoryPathHelper)
373430
self.portableDirectoryEdit = directoryEntryControl.pathControl
374431
if globalVars.appArgs.portablePath:
375432
self.portableDirectoryEdit.Value = globalVars.appArgs.portablePath
376433

434+
# Translators: The label of a checkbox option in the Create Portable NVDA dialog.
435+
newFolderText = _("Create a &new folder for the portable copy")
436+
self.newFolderCheckBox = sHelper.addItem(wx.CheckBox(self, label=newFolderText))
437+
self.newFolderCheckBox.Value = True
438+
377439
# Translators: The label of a checkbox option in the Create Portable NVDA dialog.
378440
copyConfText = _("Copy current &user configuration")
379441
self.copyUserConfigCheckbox = sHelper.addItem(wx.CheckBox(self, label=copyConfText))
@@ -430,12 +492,20 @@ def onCreatePortable(self, evt):
430492
# components to that path to make it absolute from other contexts, by adding a drive letter/share path if
431493
# needed. The OS's idea of the current drive is used, as in os.getcwd(). (#14681)
432494
expandedPortableDirectory = os.path.abspath(expandedPortableDirectory)
495+
if self.newFolderCheckBox.Value:
496+
expandedPortableDirectory = _getUniqueNewPortableDirectory(expandedPortableDirectory)
497+
498+
# Warn here so that users can update the directory
499+
if not _warnAndConfirmForNonEmptyDirectory(expandedPortableDirectory):
500+
return
501+
433502
self.Hide()
434503
doCreatePortable(
435504
expandedPortableDirectory,
436-
self.copyUserConfigCheckbox.Value,
437-
False,
438-
self.startAfterCreateCheckbox.Value
505+
copyUserConfig=self.copyUserConfigCheckbox.Value,
506+
silent=False,
507+
startAfterCreate=self.startAfterCreateCheckbox.Value,
508+
warnForNonEmptyDirectory=False,
439509
)
440510
self.Destroy()
441511

@@ -447,8 +517,21 @@ def doCreatePortable(
447517
portableDirectory: str,
448518
copyUserConfig: bool = False,
449519
silent: bool = False,
450-
startAfterCreate: bool = False
520+
startAfterCreate: bool = False,
521+
warnForNonEmptyDirectory: bool = True,
451522
) -> None:
523+
"""
524+
Create a portable copy of NVDA.
525+
:param portableDirectory: The directory in which to create the portable copy.
526+
:param copyUserConfig: Whether to copy the current user configuration.
527+
:param silent: Whether to suppress messages.
528+
:param startAfterCreate: Whether to start the new portable copy after creation.
529+
:param warnForNonEmptyDirectory: Whether to warn if the destination directory is not empty.
530+
"""
531+
if warnForNonEmptyDirectory and not _warnAndConfirmForNonEmptyDirectory(portableDirectory):
532+
# Translators: The message displayed when the user cancels the creation of a portable copy of NVDA.
533+
ui.message(_("Portable copy creation cancelled."))
534+
return
452535
d = gui.IndeterminateProgressDialog(
453536
gui.mainFrame,
454537
# Translators: The title of the dialog presented while a portable copy of NVDA is being created.

source/nvda.pyw

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,23 @@ parser.add_argument('--no-sr-flag',action="store_false",dest='changeScreenReader
230230
installGroup = parser.add_mutually_exclusive_group()
231231
installGroup.add_argument('--install',action="store_true",dest='install',default=False,help="Installs NVDA (starting the new copy after installation)")
232232
installGroup.add_argument('--install-silent',action="store_true",dest='installSilent',default=False,help="Installs NVDA silently (does not start the new copy after installation).")
233-
installGroup.add_argument('--create-portable',action="store_true",dest='createPortable',default=False,help="Creates a portable copy of NVDA (starting the new copy after installation)")
234-
installGroup.add_argument('--create-portable-silent',action="store_true",dest='createPortableSilent',default=False,help="Creates a portable copy of NVDA silently (does not start the new copy after installation).")
233+
installGroup.add_argument(
234+
"--create-portable",
235+
action="store_true",
236+
dest="createPortable",
237+
default=False,
238+
help="Creates a portable copy of NVDA (and starts the new copy).\n"
239+
"Requires `--portable-path` to be specified.\n"
240+
)
241+
installGroup.add_argument(
242+
"--create-portable-silent",
243+
action="store_true",
244+
dest="createPortableSilent",
245+
default=False,
246+
help="Creates a portable copy of NVDA (without starting the new copy).\n"
247+
"This option suppresses warnings when writing to non-empty directories and may overwrite files without warning.\n"
248+
"Requires --portable-path to be specified.\n"
249+
)
235250
parser.add_argument('--portable-path',dest='portablePath',default=None,type=str,help="The path where a portable copy will be created")
236251
parser.add_argument('--launcher',action="store_true",dest='launcher',default=False,help="Started from the launcher")
237252
parser.add_argument('--enable-start-on-logon',metavar="True|False",type=stringToBool,dest='enableStartOnLogon',default=None,

user_docs/en/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Unicode CLDR has also been updated.
4141
* NVDA will now report figures with no accessible children, but with a label or description. (#14514)
4242
* When reading by line in browse mode, "caption" is no longer reported on each line of a long figure or table caption. (#14874)
4343
* In the Python console, the last unexecuted command will no longer be lost when moving in the input history. (#16653, @CyrilleB79)
44+
* By default, a new folder will be created when making a portable copy. Warnings have been added when writing to a non-empty directory. (#16684)
4445

4546
### Bug Fixes
4647
* Windows 11 fixes:

user_docs/en/userGuide.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,16 @@ This option is only available when installing from a portable copy, not when ins
442442
If creating a portable copy directly from the NVDA download package, press the Create Portable Copy button.
443443
If you have already closed this dialog or you are running an installed copy of NVDA, choose the Create Portable copy menu item found under Tools in the NVDA menu.
444444

445-
The Dialog that appears allows you to choose where the portable copy should be created.
445+
The dialog that appears allows you to choose where the portable copy should be created.
446446
This can be a directory on your hard drive or a location on a USB thumb drive or other portable media.
447-
There is also an option to choose whether NVDA should copy the logged on user's current NVDA configuration for use with the newly created portable copy.
447+
By default, a new directory is created for the portable copy.
448+
You can also choose to use an existing directory, this will overwrite files in the directory.
449+
If the existing directory is a portable copy of NVDA, that copy will be updated.
450+
451+
There is also an option to choose whether NVDA should copy the logged on user's current NVDA configuration for use with the newly created portable copy.
452+
This also includes add-ons.
448453
This option is only available when creating a portable copy from an installed copy, not when creating from the download package.
454+
449455
Pressing Continue will create the portable copy.
450456
Once creation is complete, a message will appear telling you it was successful.
451457
Press OK to dismiss this dialog.
@@ -3650,14 +3656,10 @@ For more information, read the in-depth section: [Add-ons and the Add-on Store](
36503656
### Create portable copy {#CreatePortableCopy}
36513657

36523658
This will open a dialog which allows you to create a portable copy of NVDA out of the installed version.
3653-
Either way, when running a portable copy of NVDA, in the extra tool sub menu the menu item will be called "install NVDA on this PC" instead of "create portable copy).
36543659

36553660
The dialog to create a portable copy of NVDA or to install NVDA on this PC will prompt you to choose a folder path in which NVDA should create the portable copy or in which NVDA should be installed.
36563661

3657-
In this dialog you can enable or disable the following:
3658-
3659-
* Copy current user configuration (this includes the files in %appdata%\roaming\NVDA or in the user configuration of your portable copy and also includes add-ons and other modules)
3660-
* Start the new portable copy after creation or start NVDA after installation (starts NVDA automatically after the portable copy creation or the installation)
3662+
Follow the directions in [Creating a portable copy](#CreatingAPortableCopy) for more information.
36613663

36623664
### Run COM registration fixing tool... {#RunCOMRegistrationFixingTool}
36633665

@@ -5082,8 +5084,8 @@ Following are the command line options for NVDA:
50825084
|None |`--install-silent` |Silently installs NVDA (does not start the newly installed copy)|
50835085
|None |`--enable-start-on-logon=True|False` |When installing, enable NVDA's [Use NVDA during Windows sign-in](#StartAtWindowsLogon)|
50845086
|None |`--copy-portable-config` |When installing, copy the portable configuration from the provided path (`--config-path`, `-c`) to the current user account|
5085-
|None |`--create-portable` |Creates a portable copy of NVDA (starting the newly created copy). Requires `--portable-path` to be specified|
5086-
|None |`--create-portable-silent` |Creates a portable copy of NVDA (does not start the newly installed copy). Requires `--portable-path` to be specified|
5087+
|None |`--create-portable` |Creates a portable copy of NVDA (and starts the new copy). Requires `--portable-path` to be specified|
5088+
|None |`--create-portable-silent` |Creates a portable copy of NVDA (without starting the new copy). Requires `--portable-path` to be specified. This option suppresses warnings when writing to non-empty directories and may overwrite files without warning.|
50875089
|None |`--portable-path=PORTABLEPATH` |The path where a portable copy will be created|
50885090

50895091
### System Wide Parameters {#SystemWideParameters}

0 commit comments

Comments
 (0)