Skip to content

Commit

Permalink
Add ability to set the default folder for place markers
Browse files Browse the repository at this point in the history
  • Loading branch information
nvdaes committed Jan 13, 2025
1 parent 212b129 commit 80b3c13
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 16 deletions.
138 changes: 123 additions & 15 deletions addon/globalPlugins/placeMarkers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# -*- coding: UTF-8 -*-
# placeMarkers: Plugin to manage place markers based on positions or strings in specific documents
# Copyright (C) 2012-2023 Noelia Ruiz Martínez, other contributors
# Copyright (C) 2012-2025 Noelia Ruiz Martínez, other contributors
# Released under GPL 2
# Converted to Python 3 by Joseph Lee in 2017
# UIA support added by Abdel in 2022

import pickle
import re
import os
from pathlib import Path
import NVDAObjects
import UIAHandler
import shutil
Expand All @@ -33,6 +34,7 @@
from speech import sayAll
from scriptHandler import willSayAllResume, script
from logHandler import log
import config

from .skipTranslation import translate
from .securityUtils import secureBrowseableMessage # Created by Cyrille (@CyrilleB79)
Expand All @@ -46,15 +48,26 @@
PLACE_MARKERS_PATH = os.path.join(
CONFIG_PATH, "addons", "placeMarkers", "globalPlugins", "placeMarkers", "savedPlaceMarkers"
)
SEARCH_FOLDER = os.path.join(PLACE_MARKERS_PATH, "search")
BOOKMARKS_FOLDER = os.path.join(PLACE_MARKERS_PATH, "bookmarks")

ADDON_SUMMARY = addonHandler.getCodeAddon().manifest["summary"]

# Globals
lastFindText = ""
lastCaseSensitivity = False

confspec = {
"defaultFolder": "string(default='')"
}
config.conf.spec["placeMarkers"] = confspec


def getDefaultFolder() -> str:
if defaultFolder := config.conf["placeMarkers"]["defaultFolder"]:
return Path(defaultFolder)
return PLACE_MARKERS_PATH

searchFolder = os.path.join(getDefaultFolder(), "search")
bookmarksFolder = os.path.join(getDefaultFolder(), "bookmarks")

def goToUIABookmark(treeInterceptor: chromium.ChromiumUIATreeInterceptor, startOffset: int) -> bool:
"""
Expand Down Expand Up @@ -172,20 +185,20 @@ def disableInSecureMode(decoratedCls):


def createSearchFolder():
if os.path.isdir(SEARCH_FOLDER):
if os.path.isdir(searchFolder):
return
try:
os.makedirs(SEARCH_FOLDER)
os.makedirs(searchFolder)
except Exception as e:
log.debugWarning("Error creating search folder", exc_info=True)
raise e


def createBookmarksFolder():
if os.path.isdir(BOOKMARKS_FOLDER):
if os.path.isdir(bookmarksFolder):
return
try:
os.makedirs(BOOKMARKS_FOLDER)
os.makedirs(bookmarksFolder)
except Exception as e:
log.debugWarning("Error creating bookmarks folder", exc_info=True)
raise e
Expand Down Expand Up @@ -302,18 +315,17 @@ def getFile(folder, ext=""):
file = f"uia_{file}" if uia else file
file += nameToAdd
file = api.filterFileName(standardFileName(file))
folderPath = os.path.join(PLACE_MARKERS_PATH, folder)
maxLenFileName = 232 - len(folderPath)
maxLenFileName = 232 - len(folder)
if maxLenFileName <= 0:
return ""
file = file[:maxLenFileName]
file = file + ext
path = os.path.join(folderPath, file)
path = os.path.join(folder, file)
return path


def getFileSearch():
return getFile("search", ".txt")
return getFile(searchFolder, ".txt")


def getSavedTexts():
Expand All @@ -334,11 +346,11 @@ def getLastSpecificFindText():


def getFileBookmarks():
return getFile("bookmarks", ".pickle")
return getFile(bookmarksFolder, ".pickle")


def getFileTempBookmark():
return getFile("bookmarks", ".txt")
return getFile(bookmarksFolder, ".txt")


def getSavedBookmarks():
Expand Down Expand Up @@ -679,6 +691,81 @@ def onCancel(self, evt):
self.Destroy()


class SetDefaultFolderDialog(wx.Dialog):

def __init__(self, parent):
# Translators: The title of the Set default folder dialog.
super().__init__(parent, title=_("Set default folder for place markers"))

mainSizer = wx.BoxSizer(wx.VERTICAL)
sHelper = gui.guiHelper.BoxSizerHelper(self, orientation=wx.VERTICAL)

# Translators: An informational message displayed in the Set default folder dialog.
dialogCaption = _("Select the folder where your place markers will be searched.")
sHelper.addItem(wx.StaticText(self, label=dialogCaption))

# Translators: The label of a grouping with controls to select the destination directory in the Set default folder dialog.
directoryGroupText = _("default directory for place markers:")
groupHelper = sHelper.addItem(
gui.guiHelper.BoxSizerHelper(
self, sizer=wx.StaticBoxSizer(wx.StaticBox(
self, label=directoryGroupText), wx.VERTICAL
)
)
)
# Message translated in NVDA core.
browseText = translate("Browse...")
# Translators: The title of the dialog to browse for the default directory for place markers.
dirDialogTitle = _("Select default directory")
directoryEntryControl = groupHelper.addItem(
gui.guiHelper.PathSelectionHelper(self, browseText, dirDialogTitle)
)
self.defaultDirectoryEdit = directoryEntryControl.pathControl
self.defaultDirectoryEdit.Value = PLACE_MARKERS_PATH
bHelper = sHelper.addDialogDismissButtons(gui.guiHelper.ButtonHelper(wx.HORIZONTAL))
# Message translated in NVDA core.
continueButton = bHelper.addButton(self, label=translate("&Continue"), id=wx.ID_OK)
continueButton.SetDefault()
continueButton.Bind(wx.EVT_BUTTON, self.onSetDefaultFolder)
bHelper.addButton(self, id=wx.ID_CANCEL)
mainSizer.Add(sHelper.sizer, border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
self.Sizer = mainSizer
mainSizer.Fit(self)
self.CentreOnScreen()

def onSetDefaultFolder(self, evt):
if not self.defaultDirectoryEdit.Value:
# Message translated in NVDA core.
gui.messageBox(
translate("Please specify a directory."),
# Message translated in NVDA core.
translate("Error"),
wx.OK | wx.ICON_ERROR
)
return
drv = os.path.splitdrive(self.defaultDirectoryEdit.Value)[0]
if drv and not os.path.isdir(drv):
# Message translated in NVDA core.
gui.messageBox(
translate("Invalid drive %s") % drv,
# Message translated in NVDA core.
translate("Error"),
wx.OK | wx.ICON_ERROR
)
return
self.Hide()
config.conf["placeMarkers"]["defaultFolder"] = self.defaultDirectoryEdit.Value
globals
searchFolder = os.path.join(getDefaultFolder(), "search")
bookmarksFolder = os.path.join(getDefaultFolder(), "bookmarks")
createSearchFolder()
createBookmarksFolder()
self.Destroy()

def onCancel(self, evt):
self.Destroy()


class PathSelectionWithoutNewDir(gui.guiHelper.PathSelectionHelper):

def __init__(self, parent, buttonText, browseForDirectoryTitle):
Expand Down Expand Up @@ -820,6 +907,14 @@ def __init__(self):
_("Restore previously saved place markers")
)
gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onRestore, self.restoreItem)
self.setDefaultFolderItem = self.BSMenu.Append(
wx.ID_ANY,
# Translators: the name for an item of addon submenu.
_("Set &default place markers folder..."),
# Translators: the tooltip text for an item of addon submenu.
_("Sets default folder for place markers")
)
gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onSetDefaultFolder, self.setDefaultFolderItem)

def terminate(self):
try:
Expand All @@ -828,7 +923,7 @@ def terminate(self):
pass

def onSpecificSearch(self, evt):
os.startfile(SEARCH_FOLDER)
os.startfile(searchFolder)

@script(
# Translators: message presented in input mode, when a keystroke of an addon script is pressed.
Expand All @@ -838,7 +933,7 @@ def script_openSpecificSearchFolder(self, gesture):
wx.CallAfter(self.onSpecificSearch, None)

def onBookmarks(self, evt):
os.startfile(BOOKMARKS_FOLDER)
os.startfile(bookmarksFolder)

@script(
# Translators: message presented in input mode, when a keystroke of an addon script is pressed.
Expand Down Expand Up @@ -873,6 +968,19 @@ def onRestore(self, evt):
def script_activateRestoreDialog(self, gesture):
wx.CallAfter(self.onRestore, None)

def onSetDefaultFolder(self, evt):
gui.mainFrame.prePopup()
d = SetDefaultFolderDialog(gui.mainFrame)
d.Show()
gui.mainFrame.postPopup()

@script(
# Translators: message presented in input mode, when a keystroke of an addon script is pressed.
description=_("Activates the Set default folder dialog of %s.") % ADDON_SUMMARY
)
def script_activateSetDefaultFolderDialog(self, gesture):
wx.CallAfter(self.onSetDefaultFolder, None)

@script(
# Translators: message presented in input mode, when a keystroke of an addon script is pressed.
description=_("finds a text string from the current cursor position for a specific document."),
Expand Down
2 changes: 1 addition & 1 deletion buildVars.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# Documentation file name
"addon_docFileName": "readme.html",
# Minimum NVDA version supported (e.g. "2018.3")
"addon_minimumNVDAVersion": "2022.1",
"addon_minimumNVDAVersion": "2024.4",
# Last NVDA version supported/tested (e.g. "2018.4", ideally more recent than minimum version)
"addon_lastTestedNVDAVersion": "2024.4",
# Add-on update channel (default is stable or None)
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Using the PlaceMarkers submenu under NVDA's Preferences menu, you can access:
* Bookmarks folder: Opens a folder of the saved placemarkers.
* Copy placeMarkers folder: You can save a copy of the placeMarkers folder.
* Restore placeMarkers: You can restore your placeMarkers from a previously saved placeMarkers folder.
* Set default place markers folder: the default folder for place markers can be set from this dialog.

Note: The placemarker position is based on the number of characters; and therefore in dynamic pages it is better to use the specific search, not placemarkers.

Expand Down

0 comments on commit 80b3c13

Please sign in to comment.