diff --git a/.github/scripts/langCodes.py b/.github/scripts/langCodes.py index 044e5b5..ea2780a 100644 --- a/.github/scripts/langCodes.py +++ b/.github/scripts/langCodes.py @@ -2,59 +2,57 @@ # Mapping between Crowdin language IDs (keys) and standard NVDA directory names (values). # This dictionary acts as the symmetrical counterpart to 'languageMappings.json' implemented by @nvdaes. -# It ensures that translations exported from Crowdin are stored in the correct +# It ensures that translations exported from Crowdin are stored in the correct # local paths (e.g., 'es-ES' from Crowdin goes into the 'es' folder). CROWDIN_TO_NVDA = { - # Arabic variants - "ar-SA": "ar_SA", - - # Spanish variants - "es-ES": "es", - "es-CO": "es_CO", - - # Portuguese variants - "pt-BR": "pt_BR", - "pt-PT": "pt_PT", - - # Chinese variants - "zh-CN": "zh_CN", - "zh-HK": "zh_HK", - "zh-TW": "zh_TW", - - # Other specific mappings from the NVDA ecosystem - "af": "af_ZA", - "de-CH": "de_CH", - "nb": "nb_NO", - "nn-NO": "nn_NO", - "sr-CS": "sr" + # Arabic variants + "ar-SA": "ar_SA", + # Spanish variants + "es-ES": "es", + "es-CO": "es_CO", + # Portuguese variants + "pt-BR": "pt_BR", + "pt-PT": "pt_PT", + # Chinese variants + "zh-CN": "zh_CN", + "zh-HK": "zh_HK", + "zh-TW": "zh_TW", + # Other specific mappings from the NVDA ecosystem + "af": "af_ZA", + "de-CH": "de_CH", + "nb": "nb_NO", + "nn-NO": "nn_NO", + "sr-CS": "sr", } + def get_nvda_code(crowdin_code): - """ - Returns the appropriate local directory name for a given Crowdin language ID. - - Args: - crowdin_code (str): The language identifier from Crowdin (e.g., 'pt-BR', 'fr'). - - Returns: - str: The corresponding NVDA locale folder name (e.g., 'pt_BR', 'fr'). - """ - # 1. Direct check in our verified map (Priority) - if crowdin_code in CROWDIN_TO_NVDA: - return CROWDIN_TO_NVDA[crowdin_code] - - # 2. Automated conversion for regional variants: Crowdin "xx-YY" -> NVDA "xx_YY" - # This handles regional codes not explicitly defined in the map. - if "-" in crowdin_code: - return crowdin_code.replace("-", "_") - - # 3. Default: Return as is. - # This covers base languages that don't use regional folders in NVDA - # (e.g., 'fr', 'tr', 'bg', 'fi', 'fa'). - return crowdin_code + """ + Returns the appropriate local directory name for a given Crowdin language ID. + + Args: + crowdin_code (str): The language identifier from Crowdin (e.g., 'pt-BR', 'fr'). + + Returns: + str: The corresponding NVDA locale folder name (e.g., 'pt_BR', 'fr'). + """ + # 1. Direct check in our verified map (Priority) + if crowdin_code in CROWDIN_TO_NVDA: + return CROWDIN_TO_NVDA[crowdin_code] + + # 2. Automated conversion for regional variants: Crowdin "xx-YY" -> NVDA "xx_YY" + # This handles regional codes not explicitly defined in the map. + if "-" in crowdin_code: + return crowdin_code.replace("-", "_") + + # 3. Default: Return as is. + # This covers base languages that don't use regional folders in NVDA + # (e.g., 'fr', 'tr', 'bg', 'fi', 'fa'). + return crowdin_code + if __name__ == "__main__": - # Ensure a language code was provided as a command-line argument - if len(sys.argv) > 1: - # Standardize input and output the mapped code for PowerShell to capture - print(get_nvda_code(sys.argv[1])) \ No newline at end of file + # Ensure a language code was provided as a command-line argument + if len(sys.argv) > 1: + # Standardize input and output the mapped code for PowerShell to capture + print(get_nvda_code(sys.argv[1])) diff --git a/.github/workflows/auto-update-translations.yaml b/.github/workflows/auto-update-translations.yaml index 61d57c6..c4fd48c 100644 --- a/.github/workflows/auto-update-translations.yaml +++ b/.github/workflows/auto-update-translations.yaml @@ -11,4 +11,3 @@ on: jobs: auto_update_translations: uses: abdel792/autoUpdateTranslations/.github/workflows/l10n-updates.yaml@8ac89c644395cf2aad6e03a4bb2be5c36526d12a - \ No newline at end of file diff --git a/.github/workflows/manual-release.yaml b/.github/workflows/manual-release.yaml index dad952d..21a1688 100644 --- a/.github/workflows/manual-release.yaml +++ b/.github/workflows/manual-release.yaml @@ -119,7 +119,7 @@ jobs: f.seek(0) f.write(text) f.truncate() - shell: python + shell: python - name: Check if there are any changes diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 207177d..65cff7f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: - id: check-hooks-apply - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: # Prevents commits to certain branches - id: no-commit-to-branch @@ -60,7 +60,7 @@ repos: # Avoids using reserved Windows filenames. - id: check-illegal-windows-names - repo: https://github.com/asottile/add-trailing-comma - rev: v3.2.0 + rev: v4.0.0 hooks: # Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables, # if a trailing comma is added. @@ -69,7 +69,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Matches Ruff version in pyproject. - rev: v0.12.7 + rev: v0.15.12 hooks: - id: ruff name: lint with ruff diff --git a/addon/appModules/mp3directcut.py b/addon/appModules/mp3directcut.py index c15d99b..9a9fd77 100644 --- a/addon/appModules/mp3directcut.py +++ b/addon/appModules/mp3directcut.py @@ -12,6 +12,7 @@ import windowUtils from typing import Callable import controlTypes + if hasattr(controlTypes, "ROLE_PANE"): from controlTypes import ROLE_PANE, ROLE_EDITABLETEXT else: @@ -29,112 +30,120 @@ setFocus, mouse_event, MOUSEEVENTF_LEFTDOWN, - MOUSEEVENTF_LEFTUP + MOUSEEVENTF_LEFTUP, ) from ui import message import sys from NVDAObjects.IAccessible import IAccessible, getNVDAObjectFromEvent import addonHandler + addonHandler.initTranslation() _: Callable[[str], str] # Constants -PROGRAM_NAME = 'mp3DirectCut' +PROGRAM_NAME = "mp3DirectCut" ADDON_SUMMARY = addonHandler.getCodeAddon().manifest["summary"] -hr, min, sec, hun, th = _('hours'), _('minutes'), _('seconds'), _('hundredths'), _('thousandths') +hr, min, sec, hun, th = _("hours"), _("minutes"), _("seconds"), _("hundredths"), _("thousandths") # For support of speak on demand feature. speakOnDemand = {"speakOnDemand": True} if buildVersion.version_year > 2023 else {} announce = ( # Translators: Message to inform that no selection has been realized. - _('No selection'), + _("No selection"), # Translators: Message to inform the user that the playback cursor is at the top of the file. - _('Beginning of the file.'), + _("Beginning of the file."), # Translators: Message to inform the user that the playback cursor is at the end of the file. - _('End of the file.'), + _("End of the file."), # Translators: Message to inform the user that not file is loaded. - _('Not file is loaded. Please check that you are in a file, open one with Control O,\ - or R to start recording.'), + _( + "Not file is loaded. Please check that you are in a file, open one with Control O,\ + or R to start recording." + ), # Translators: Message to inform the user that the current command is not available in a recording mode. - _('This command is not available in a recording mode, it is available only in a reading mode !'), + _("This command is not available in a recording mode, it is available only in a reading mode !"), # Translators: Message to indicate the position of the selection start marker. - _('The marker of the beginning of selection B is at'), + _("The marker of the beginning of selection B is at"), # Translators: Message to indicate the position of the selection end marker. - _('The marker of the End of selection N is at'), + _("The marker of the End of selection N is at"), # Translators: Message to indicate the level of the vu-meter. - _('The level of the vu-meter is at'), + _("The level of the vu-meter is at"), # Translators: Message to indicate the total duration of the current file. - _('Total time: '), + _("Total time: "), # Translators: Message to indicate the duration of the selection. - _('Duration of selection'), + _("Duration of selection"), # Translators: Message to indicate the actual part. - _('Actual part'), + _("Actual part"), # Translators: Message to inform that no selection was found. - _('Selection not found.'), + _("Selection not found."), # Translators: Message to indicate that the information of the current part is not available. - _('Information specific to the actual part is not available.'), + _("Information specific to the actual part is not available."), # Translators: Message to prompt the user to verify that no selection has been made. - _('Please check that no selection has been made.'), + _("Please check that no selection has been made."), # Translators: Message to prompt the user to verify that it is not in recording mode. - _('Please chek that you are not in a recording mode.'), + _("Please chek that you are not in a recording mode."), # Translators: Message to inform the user that the recording is ready. - _('The recording is ready ! It remains only to press spacebar for begin the recording.\ - This same spacebar will stop the recording !'), + _( + "The recording is ready ! It remains only to press spacebar for begin the recording.\ + This same spacebar will stop the recording !" + ), # Translators: Message to inform the user that a recording is in progress. - _('A recording is in progress, please press spacebar for stop it and start a new one.'), + _("A recording is in progress, please press spacebar for stop it and start a new one."), # Translators: Message to inform the user that the recording is not ready. - _('The recording is not ready !'), + _("The recording is not ready !"), # Translators: Message to indicate that the vu-meter is not available. - _('The vu-meter is not available. Please verify if a recording is in progress,\ - and that the checkbox enable the margin button is checked in the options of {0}.').format(PROGRAM_NAME), + _( + "The vu-meter is not available. Please verify if a recording is in progress,\ + and that the checkbox enable the margin button is checked in the options of {0}." + ).format(PROGRAM_NAME), # Translators: Message to confirm that the level of the vu-meter has been reset. - _('The level of the vu-meter has been reset !'), + _("The level of the vu-meter has been reset !"), # Translators: Message to confirm the placement of the selection start marker. _("Start selection marker set."), # Translators: Message to confirm the placement of the selection end marker. _("End selection marker set."), # Translators: Message to confirm that the selection has been canceled. - _('Selection canceled.') + _("Selection canceled."), ) def timeSplitter(time): # noqa: C901 - hours = minutes = seconds = hundredths = thousandths = '' - if ':' in time: - hrs = time.split(':') - if hrs[0] != '00' and hrs[0] != '0': - hours = '{0} {1}, '.format(hrs[0], hr) - if hrs[1].split("'")[0] != '00': + hours = minutes = seconds = hundredths = thousandths = "" + if ":" in time: + hrs = time.split(":") + if hrs[0] != "00" and hrs[0] != "0": + hours = "{0} {1}, ".format(hrs[0], hr) + if hrs[1].split("'")[0] != "00": minutes = hrs[1].split("'")[0] else: mnts = time.split("'") - if mnts[0] != '00' and mnts[0] != '0': + if mnts[0] != "00" and mnts[0] != "0": minutes = mnts[0] if minutes: if len(minutes) > 1: - if minutes[0] == '0': + if minutes[0] == "0": minutes = minutes[1] - minutes = '{0} {1}, '.format(minutes, min) + minutes = "{0} {1}, ".format(minutes, min) try: - scnds = time.split("'")[1].split('.')[0] + scnds = time.split("'")[1].split(".")[0] except IndexError: - scnds = '' - if scnds != '00' and scnds != '0': + scnds = "" + if scnds != "00" and scnds != "0": seconds = scnds if seconds: if len(seconds) > 1: - if seconds[0] == '0': + if seconds[0] == "0": seconds = seconds[1] - seconds = '{0} {1}, '.format(seconds, sec) - hd = time.split('.')[1].split()[0] - if hd != '00' and hd != '000': + seconds = "{0} {1}, ".format(seconds, sec) + hd = time.split(".")[1].split()[0] + if hd != "00" and hd != "000": if len(hd) == 3: - thousandths = '{0} {1}.'.format(hd, th) + thousandths = "{0} {1}.".format(hd, th) else: - hundredths = '{0} {1}.'.format(hd, hun) - timeSplitter = hours + minutes + seconds + hundredths if not thousandths\ - else hours + minutes + seconds + thousandths + hundredths = "{0} {1}.".format(hd, hun) + timeSplitter = ( + hours + minutes + seconds + hundredths if not thousandths else hours + minutes + seconds + thousandths + ) return timeSplitter @@ -150,19 +159,21 @@ def isRecordingReady(): def sayMessage(msg, space=None, marker=None): import config + if space: - if config.conf['mp3DCReport']['space']: + if config.conf["mp3DCReport"]["space"]: message(msg) elif marker: - if config.conf['mp3DCReport']['marker']: + if config.conf["mp3DCReport"]["marker"]: message(msg) else: - if config.conf['mp3DCReport']['other']: + if config.conf["mp3DCReport"]["other"]: message(msg) def isReading(): import time + firstValue = actualDuration() time.sleep(0.2) secondValue = actualDuration() @@ -191,7 +202,7 @@ def getTextFromWindow(hwnd): def isStarting(): focus = api.getFocusObject() - if focus.role == ROLE_PANE and focus.name == 'mp3DirectCut': + if focus.role == ROLE_PANE and focus.name == "mp3DirectCut": hwnd = readingWindow() starting = getTextFromWindow(hwnd) if not starting: @@ -220,7 +231,7 @@ def checkPart(): text = getTextFromWindow(hwnd) if text and not text.isspace(): text = text.split() - if text[1].startswith('('): + if text[1].startswith("("): return True return False @@ -237,18 +248,18 @@ def checkSelection(): def part(flag=None): hwnd = recAndSelWindow() text = getTextFromWindow(hwnd) - text = text.split('(') + text = text.split("(") text = text[1] - text = text.split(')') + text = text.split(")") text = text[0] - text = text.replace('/', ' {0} '.format(_('of'))) - return '{0} {1}'.format(announce[10], text) if not flag else '{0} {1}'.format(_('Part'), text) + text = text.replace("/", " {0} ".format(_("of"))) + return "{0} {1}".format(announce[10], text) if not flag else "{0} {1}".format(_("Part"), text) def selectionDuration(): hwnd = recAndSelWindow() text = getTextFromWindow(hwnd) - selectionDuration = text.split('(') + selectionDuration = text.split("(") selectionDuration = selectionDuration[1] selectionDuration = selectionDuration[:-1] selectionDuration = timeSplitter(selectionDuration) if timeSplitter(selectionDuration) else announce[0] @@ -258,7 +269,7 @@ def selectionDuration(): def beginSelection(): hwnd = recAndSelWindow() text = getTextFromWindow(hwnd) - beginSelection = text.split(' - ') + beginSelection = text.split(" - ") beginSelection = beginSelection[0] beginSelection = beginSelection.split() if len(beginSelection) > 2: @@ -272,9 +283,9 @@ def beginSelection(): def endSelection(): hwnd = recAndSelWindow() text = getTextFromWindow(hwnd) - endSelection = text.split(' - ') + endSelection = text.split(" - ") endSelection = endSelection[1] - endSelection = endSelection.split(' ') + endSelection = endSelection.split(" ") endSelection = endSelection[0] endSelection = timeSplitter(endSelection) if timeSplitter(endSelection) else announce[1] return endSelection @@ -283,8 +294,8 @@ def endSelection(): def actualDuration(): hwnd = readingWindow() actual = getTextFromWindow(hwnd) - if actual and not actual.isspace() and ' ' in actual: - actual = actual.split(': ') + if actual and not actual.isspace() and " " in actual: + actual = actual.split(": ") actual = actual[2].split() actual = actual[0] actual = timeSplitter(actual) @@ -294,8 +305,8 @@ def actualDuration(): def actualDurationPercentage(): hwnd = readingWindow() actual = getTextFromWindow(hwnd) - if '(' in actual: - actual = actual.split('(') + if "(" in actual: + actual = actual.split("(") actual = actual[1] actual = actual[:-1] return actual @@ -305,8 +316,8 @@ def totalTime(): if checkPart() or checkSelection(): hwnd = readingWindow() time = getTextFromWindow(hwnd) - time = time.split(': ') - time = time[1].split(' ')[0] + time = time.split(": ") + time = time[1].split(" ")[0] total = timeSplitter(time) return total @@ -314,27 +325,27 @@ def totalTime(): def timeRemaining(): hwnd = readingWindow() actual = getTextFromWindow(hwnd) - if actual != '' and not actual.isspace() and ' ' in actual: - actual = actual.split(': ') + if actual != "" and not actual.isspace() and " " in actual: + actual = actual.split(": ") actual = actual[2].split() actual = actual[0] total = getTextFromWindow(hwnd) - total = total.split(': ') + total = total.split(": ") total = total[1] - total = total.split(' ')[0] + total = total.split(" ")[0] if total == actual: # Translators: Message to inform the user that there is no remaining time. - return _('No time remaining !') - hORm = len(total.split('.')[1]) + return _("No time remaining !") + hORm = len(total.split(".")[1]) fmt = "%H:%M'%S.%f" - if ':' not in actual: - actual = '0:{0}'.format(actual) - if ':' not in total: - total = '0:{0}'.format(total) + if ":" not in actual: + actual = "0:{0}".format(actual) + if ":" not in total: + total = "0:{0}".format(total) result = datetime.strptime(total, fmt) - datetime.strptime(actual, fmt) - result = str(result).decode('utf-8') if sys.version_info.major == 2 else str(result) - result = result.replace(':', "'") - result = result.replace("'", ':', 1) + result = str(result).decode("utf-8") if sys.version_info.major == 2 else str(result) + result = result.replace(":", "'") + result = result.replace("'", ":", 1) result = result[:-4] if hORm == 2 else result[:-3] try: return timeSplitter(result) @@ -342,8 +353,7 @@ def timeRemaining(): return totalTime() -class SoundManager (IAccessible): - +class SoundManager(IAccessible): scriptCategory = ADDON_SUMMARY keys = ( "kb:1", @@ -366,7 +376,7 @@ def script_readFromSelection(self, gesture): kig.fromName("downArrow").send() @script( - gesture='kb:r', + gesture="kb:r", **speakOnDemand, ) def script_checkRecording(self, gesture): @@ -379,7 +389,7 @@ def script_checkRecording(self, gesture): sayMessage(announce[17]) @script( - gesture='kb:control+r', + gesture="kb:control+r", **speakOnDemand, ) def script_cancelSelection(self, gesture): @@ -392,7 +402,7 @@ def script_cancelSelection(self, gesture): message(text) @script( - gesture='kb:space', + gesture="kb:space", **speakOnDemand, ) def script_space(self, gesture): @@ -414,13 +424,16 @@ def script_space(self, gesture): if not actual: sayMessage(announce[1], space=True) return - actual = actual + ' ' + actualDurationPercentage() if not actual == totalTime()\ - else announce[2] + ' ' + actual + ' ' + actualDurationPercentage() + actual = ( + actual + " " + actualDurationPercentage() + if not actual == totalTime() + else announce[2] + " " + actual + " " + actualDurationPercentage() + ) sayMessage(actual, space=True) self.appModule.runValueChange = True @script( - gesture='kb:control+rightArrow', + gesture="kb:control+rightArrow", **speakOnDemand, ) def script_nextSplittingPoint(self, gesture): @@ -431,11 +444,17 @@ def script_nextSplittingPoint(self, gesture): if checkPart() or checkSelection(): actual = actualDuration() if actual == totalTime(): - actual = announce[2] + ' ' + actualDuration() + ' ' + actualDurationPercentage() if checkSelection()\ - else announce[2] + ' ' + actualDuration() + ' ' + part(flag=True) + actual = ( + announce[2] + " " + actualDuration() + " " + actualDurationPercentage() + if checkSelection() + else announce[2] + " " + actualDuration() + " " + part(flag=True) + ) else: - actual = actual + ' ' + actualDurationPercentage() if checkSelection()\ - else actual + ' ' + part(flag=True) + actual = ( + actual + " " + actualDurationPercentage() + if checkSelection() + else actual + " " + part(flag=True) + ) if not isReading(): self.appModule.runValueChange = False api.processPendingEvents() @@ -443,7 +462,7 @@ def script_nextSplittingPoint(self, gesture): self.appModule.runValueChange = True @script( - gesture='kb:control+leftArrow', + gesture="kb:control+leftArrow", **speakOnDemand, ) def script_previousSplittingPoint(self, gesture): @@ -454,11 +473,17 @@ def script_previousSplittingPoint(self, gesture): if checkSelection() or checkPart(): actual = actualDuration() if not actual: - actual = announce[1] + ' ' + actualDurationPercentage() if checkSelection()\ - else announce[1] + ' ' + part(flag=True) + actual = ( + announce[1] + " " + actualDurationPercentage() + if checkSelection() + else announce[1] + " " + part(flag=True) + ) else: - actual = actual + ' ' + actualDurationPercentage() if checkSelection()\ - else actual + ' ' + part(flag=True) + actual = ( + actual + " " + actualDurationPercentage() + if checkSelection() + else actual + " " + part(flag=True) + ) if not isReading(): self.appModule.runValueChange = False api.processPendingEvents() @@ -466,7 +491,7 @@ def script_previousSplittingPoint(self, gesture): self.appModule.runValueChange = True @script( - gesture='kb:upArrow', + gesture="kb:upArrow", **speakOnDemand, ) def script_up(self, gesture): @@ -485,22 +510,28 @@ def script_up(self, gesture): if not actual: actual = announce[1] if actual == totalTime(): - actual = '{0} {1}'.format(actual, announce[2]) - sayMessage(announce[5] + ' : ' + actual + ' ' + actualDurationPercentage()) + actual = "{0} {1}".format(actual, announce[2]) + sayMessage(announce[5] + " : " + actual + " " + actualDurationPercentage()) else: actual = actualDuration() if not actual: - sayMessage('{0}, {1}'.format(announce[0], announce[1])) + sayMessage("{0}, {1}".format(announce[0], announce[1])) return if actual == totalTime(): - actual = '{0} {1}'.format(actual, announce[2]) + actual = "{0} {1}".format(actual, announce[2]) # Translators: Message to indicate the elapsed time. - sayMessage('{0}, {1} {2} {3}'.format( - announce[0], _('Elapsed time: '), actual, actualDurationPercentage())) + sayMessage( + "{0}, {1} {2} {3}".format( + announce[0], + _("Elapsed time: "), + actual, + actualDurationPercentage(), + ), + ) self.appModule.runValueChange = True @script( - gesture='kb:downArrow', + gesture="kb:downArrow", **speakOnDemand, ) def script_down(self, gesture): @@ -519,25 +550,33 @@ def script_down(self, gesture): if not actual: actual = announce[1] if actual == totalTime(): - actual = '{0} {1}'.format(actual, announce[2]) - sayMessage(announce[6] + ' : ' + actual + ' ' + actualDurationPercentage()) + actual = "{0} {1}".format(actual, announce[2]) + sayMessage(announce[6] + " : " + actual + " " + actualDurationPercentage()) else: actual = actualDuration() if not actual: - sayMessage('{0}, {1}'.format(announce[0], announce[1])) + sayMessage("{0}, {1}".format(announce[0], announce[1])) return if actual == totalTime(): - actual = '{0} {1}'.format(actual, announce[2]) + actual = "{0} {1}".format(actual, announce[2]) # Translators: Message to indicate the elapsed time. - sayMessage('{0}, {1} {2} {3}'.format( - announce[0], _('Elapsed time: '), actual, actualDurationPercentage())) + sayMessage( + "{0}, {1} {2} {3}".format( + announce[0], + _("Elapsed time: "), + actual, + actualDurationPercentage(), + ), + ) self.appModule.runValueChange = True @script( - gesture='kb:control+shift+d', + gesture="kb:control+shift+d", # Translators, Message presented in input help mode. - description=_('Gives the duration from the beginning of the file to the current position of the playback cursor.\ - If pressed twice, gives the total duration.'), + description=_( + "Gives the duration from the beginning of the file to the current position of the playback cursor.\ + If pressed twice, gives the total duration." + ), **speakOnDemand, ) def script_elapsedTime(self, gesture): @@ -551,22 +590,28 @@ def script_elapsedTime(self, gesture): if not actualDuration(): text = announce[1] elif actualDuration() == totalTime(): - text = '{0} {1} {2} {3}'.format( - _('Elapsed time: '), actualDuration(), announce[2], actualDurationPercentage()) + text = "{0} {1} {2} {3}".format( + _("Elapsed time: "), + actualDuration(), + announce[2], + actualDurationPercentage(), + ) else: # Translators: Message to indicate the elapsed time. - text = '{0} {1} {2}'.format(_('Elapsed time: '), actualDuration(), actualDurationPercentage()) + text = "{0} {1} {2}".format(_("Elapsed time: "), actualDuration(), actualDurationPercentage()) repeat = getLastScriptRepeatCount() if repeat == 0: message(text) elif repeat == 1: - message('{0} {1}'.format(announce[8], totalTime())) + message("{0} {1}".format(announce[8], totalTime())) @script( - gesture='kb:control+shift+r', + gesture="kb:control+shift+r", # Translators: Message presented in input help mode. - description=_('Gives the time remaining from the current position of the playback cursor ' - 'to the end of the file.'), + description=_( + "Gives the time remaining from the current position of the playback cursor " + "to the end of the file.", + ), **speakOnDemand, ) def script_timeRemaining(self, gesture): @@ -578,13 +623,15 @@ def script_timeRemaining(self, gesture): return if checkSelection() or checkPart(): # Translators: Message to indicate the remaining time. - message('{0} {1}'.format(_('Remaining time: '), timeRemaining())) + message("{0} {1}".format(_("Remaining time: "), timeRemaining())) @script( - gesture='kb:control+shift+space', + gesture="kb:control+shift+space", # Translators: Message presented in input help mode. - description=_('Used to determine the current level of the vu-meter, ' - 'during recording, double pressure reset it.'), + description=_( + "Used to determine the current level of the vu-meter, " + "during recording, double pressure reset it.", + ), **speakOnDemand, ) def script_vuMeter(self, gesture): @@ -597,7 +644,7 @@ def script_vuMeter(self, gesture): level = getTextFromWindow(hwnd) if level: if repeat == 0: - sayMessage(announce[7] + ' : ' + level) + sayMessage(announce[7] + " : " + level) elif repeat == 1: setFocus(hwnd) mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, None, None) @@ -607,7 +654,7 @@ def script_vuMeter(self, gesture): sayMessage(announce[18]) @script( - gesture='kb:b', + gesture="kb:b", **speakOnDemand, ) def script_bPosition(self, gesture): @@ -619,7 +666,7 @@ def script_bPosition(self, gesture): sayMessage(announce[20], marker=True) @script( - gesture='kb:n', + gesture="kb:n", **speakOnDemand, ) def script_nPosition(self, gesture): @@ -631,10 +678,12 @@ def script_nPosition(self, gesture): sayMessage(announce[21], marker=True) @script( - gesture='kb:control+shift+b', + gesture="kb:control+shift+b", # Translators: Message presented in input help mode. - description=_('Used to indicate the position of the marker of the beginning of selection B.\ - Double pressure lets give you the duration of the selection.'), + description=_( + "Used to indicate the position of the marker of the beginning of selection B.\ + Double pressure lets give you the duration of the selection." + ), **speakOnDemand, ) def script_beginningOfSelection(self, gesture): @@ -645,17 +694,19 @@ def script_beginningOfSelection(self, gesture): if checkSelection(): bSelection = beginSelection() if repeat == 0: - sayMessage(announce[5] + ' : ' + bSelection) + sayMessage(announce[5] + " : " + bSelection) elif repeat == 1: - sayMessage(announce[9] + ' : ' + selectionDuration()) + sayMessage(announce[9] + " : " + selectionDuration()) else: sayMessage(announce[11]) @script( - gesture='kb:control+shift+e', + gesture="kb:control+shift+e", # Translators: Message presented in input help mode. - description=_('Used to indicate the position of the marker of the end of selection N.\ - Double pressure gives recapitulatif positions B and N, and the duration of the selection.'), + description=_( + "Used to indicate the position of the marker of the end of selection N.\ + Double pressure gives recapitulatif positions B and N, and the duration of the selection." + ), **speakOnDemand, ) def script_endOfSelection(self, gesture): @@ -667,18 +718,20 @@ def script_endOfSelection(self, gesture): bSelection = beginSelection() eSelection = endSelection() if repeat == 0: - sayMessage(announce[6] + ' : ' + eSelection) + sayMessage(announce[6] + " : " + eSelection) elif repeat == 1: - sayMessage(announce[5] + ' : ' + bSelection) - sayMessage(announce[6] + ' : ' + eSelection) - sayMessage(announce[9] + ' : ' + selectionDuration()) + sayMessage(announce[5] + " : " + bSelection) + sayMessage(announce[6] + " : " + eSelection) + sayMessage(announce[9] + " : " + selectionDuration()) else: sayMessage(announce[11]) @script( - gesture='kb:control+shift+p', + gesture="kb:control+shift+p", # Translators: Message presented in input help mode. - description=_('Give the reference of the actual part and the total number of parts in the current file.'), + description=_( + "Give the reference of the actual part and the total number of parts in the current file." + ), **speakOnDemand, ) def script_actualPart(self, gesture): @@ -688,30 +741,29 @@ def script_actualPart(self, gesture): if checkPart(): message(part()) elif isRecording(): - message(announce[12] + ' ' + announce[14]) + message(announce[12] + " " + announce[14]) elif checkSelection(): - message(announce[12] + ' ' + announce[13]) + message(announce[12] + " " + announce[13]) else: message(announce[12]) -class AppModule (appModuleHandler.AppModule): - +class AppModule(appModuleHandler.AppModule): scriptCategory = ADDON_SUMMARY runValueChange = True def event_valueChange(self, obj, nextHandler): if not self.runValueChange: return - if obj.role == ROLE_EDITABLETEXT and obj.value and all(x in obj.value for x in [' ', ':']): + if obj.role == ROLE_EDITABLETEXT and obj.value and all(x in obj.value for x in [" ", ":"]): if checkSelection() or checkPart(): actual = actualDuration() if actual == totalTime(): - actual = announce[2] + ' ' + actual + actual = announce[2] + " " + actual elif not actual: actual = announce[1] else: - actual = actual + ' ' + actualDurationPercentage() + actual = actual + " " + actualDurationPercentage() if not isReading(): sayMessage(actual) return @@ -720,19 +772,19 @@ def event_valueChange(self, obj, nextHandler): def event_NVDAObject_init(self, obj): if obj and obj.firstChild and obj.firstChild.windowControlID == 641: obj.name = obj.firstChild.name - if self.productVersion < '2.2.1': + if self.productVersion < "2.2.1": if "#" in obj.displayText: match = re.search(r"(^.+?#[0-9]+)", obj.displayText) if match: obj.name = match.group(1) def chooseNVDAObjectOverlayClasses(self, obj, clsList): - if obj.role == ROLE_PANE and obj.name and any(x in obj.name for x in ['mp3DirectCut', '.mp3']): + if obj.role == ROLE_PANE and obj.name and any(x in obj.name for x in ["mp3DirectCut", ".mp3"]): clsList.insert(0, SoundManager) @script( gesture="kb:nvda+h", - description=_('Lets open the help of the current add-on.'), + description=_("Lets open the help of the current add-on."), **speakOnDemand, ) def script_openHelp(self, gesture): diff --git a/addon/doc/pt_PT/readme.md b/addon/doc/pt_PT/readme.md index 7d3fa06..4227ef1 100644 --- a/addon/doc/pt_PT/readme.md +++ b/addon/doc/pt_PT/readme.md @@ -83,7 +83,7 @@ Este extra oferece os seguintes comandos: * Página para baixo - * Dá um avanço de dez segundos na reprodução, dizendo a duração. + * Dá um avanço de dez segundos na reprodução, dizendo a duração. * Esta duração é configurável nas opções do programa. * Página para cima diff --git a/addon/doc/sr/readme.md b/addon/doc/sr/readme.md index 6cd42b4..3b4d333 100644 --- a/addon/doc/sr/readme.md +++ b/addon/doc/sr/readme.md @@ -67,7 +67,7 @@ This addon offers the following commands: * Shift+Right Arrow * Lets do a brief forward of four hundredths of seconds during playback, while giving the current duration. * Shift+Left Arrow - * Lets do a brief backwards of four hundredths of seconds during playback, while giving the current duration. + * Lets do a brief backwards of four hundredths of seconds during playback, while giving the current duration. * S * Used to stop the reading and give the current duration. * Space diff --git a/addon/globalPlugins/mp3DirectCut/__init__.py b/addon/globalPlugins/mp3DirectCut/__init__.py index 40fdc99..2941a46 100644 --- a/addon/globalPlugins/mp3DirectCut/__init__.py +++ b/addon/globalPlugins/mp3DirectCut/__init__.py @@ -16,6 +16,7 @@ import gui import wx import config + addonHandler.initTranslation() _: Callable[[str], str] # Constants @@ -25,15 +26,14 @@ speakOnDemand = {"speakOnDemand": True} if buildVersion.version_year > 2023 else {} confSpec = { - 'space': 'boolean(default = True)', - 'marker': 'boolean(default = True)', - 'other': 'boolean(default = True)' + "space": "boolean(default = True)", + "marker": "boolean(default = True)", + "other": "boolean(default = True)", } -config.conf.spec['mp3DCReport'] = confSpec - +config.conf.spec["mp3DCReport"] = confSpec -class GlobalPlugin (globalPluginHandler.GlobalPlugin): +class GlobalPlugin(globalPluginHandler.GlobalPlugin): scriptCategory = ADDON_SUMMARY def __init__(self, *args, **kwargs): @@ -43,6 +43,7 @@ def __init__(self, *args, **kwargs): # This block ensures compatibility with NVDA versions prior to 2018.2 which includes the settings panel. if hasattr(gui, "NVDASettingsDialog"): from gui import NVDASettingsDialog + NVDASettingsDialog.categoryClasses.append(Mp3DirectCutPanel) else: self.createSubMenu() @@ -52,7 +53,8 @@ def createSubMenu(self): self.mp3DirectCut = self.preferencesMenu.Append( wx.ID_ANY, # Translators: name of the option in the menu. - _("{0} addon configuration").format("mp3DirectCut"), "" + _("{0} addon configuration").format("mp3DirectCut"), + "", ) gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onMp3DirectCutDialog, self.mp3DirectCut) @@ -77,14 +79,19 @@ def onMp3DirectCutDialog(self, evt): def script_activateMP3DirectCutConfigurationDialog(self, gesture): if not hasattr( gui.settingsDialogs, - "SettingsPanel" + "SettingsPanel", ) and not hasattr( gui, - "SettingsPanel" + "SettingsPanel", ): wx.CallAfter(self.onMP3DirectCutDialog, None) return wx.CallAfter( - (gui.mainFrame.popupSettingsDialog if hasattr(gui.mainFrame, "popupSettingsDialog") - else gui.mainFrame._popupSettingsDialog), - gui.settingsDialogs.NVDASettingsDialog, Mp3DirectCutPanel) + ( + gui.mainFrame.popupSettingsDialog + if hasattr(gui.mainFrame, "popupSettingsDialog") + else gui.mainFrame._popupSettingsDialog + ), + gui.settingsDialogs.NVDASettingsDialog, + Mp3DirectCutPanel, + ) diff --git a/addon/globalPlugins/mp3DirectCut/mp3DirectCutDialog.py b/addon/globalPlugins/mp3DirectCut/mp3DirectCutDialog.py index ac60fbc..98fb80a 100644 --- a/addon/globalPlugins/mp3DirectCut/mp3DirectCutDialog.py +++ b/addon/globalPlugins/mp3DirectCut/mp3DirectCutDialog.py @@ -11,6 +11,7 @@ import wx from gui import guiHelper from typing import Callable + # This block ensures compatibility with NVDA versions prior to 2018.2 which includes the settings panel. try: from gui.settingsDialogs import SettingsPanel @@ -22,37 +23,36 @@ _: Callable[[str], str] -class Mp3DirectCutPanel (SettingsPanel): - +class Mp3DirectCutPanel(SettingsPanel): title = "mp3DirectCut" def makeSettings(self, settingsSizer): settingsSizerHelper = guiHelper.BoxSizerHelper(self, sizer=settingsSizer) # Translators: The label of the checkbox to enable or disable the spacebar announcements. self.reportSpaceCheckBox = wx.CheckBox(parent=self, label=_("Enable announcements of the space key")) - self.reportSpaceCheckBox.SetValue(config.conf['mp3DCReport']['space']) + self.reportSpaceCheckBox.SetValue(config.conf["mp3DCReport"]["space"]) settingsSizerHelper.addItem(self.reportSpaceCheckBox) # Translators: The label of the checkbox to enable or disable the announcements of the selection markers. self.reportMarkerCheckBox = wx.CheckBox( parent=self, - label=_("Announce the placement of the selection markers")) - self.reportMarkerCheckBox.SetValue(config.conf['mp3DCReport']['marker']) + label=_("Announce the placement of the selection markers"), + ) + self.reportMarkerCheckBox.SetValue(config.conf["mp3DCReport"]["marker"]) settingsSizerHelper.addItem(self.reportMarkerCheckBox) # Translators: The label of the checkbox to enable or disable the other announcements. self.reportOtherCheckBox = wx.CheckBox(parent=self, label=_("Enable the other announces")) - self.reportOtherCheckBox.SetValue(config.conf['mp3DCReport']['other']) + self.reportOtherCheckBox.SetValue(config.conf["mp3DCReport"]["other"]) settingsSizerHelper.addItem(self.reportOtherCheckBox) def onSave(self): - config.conf['mp3DCReport']['space'] = self.reportSpaceCheckBox.GetValue() - config.conf['mp3DCReport']['marker'] = self.reportMarkerCheckBox.GetValue() - config.conf['mp3DCReport']['other'] = self.reportOtherCheckBox.GetValue() - + config.conf["mp3DCReport"]["space"] = self.reportSpaceCheckBox.GetValue() + config.conf["mp3DCReport"]["marker"] = self.reportMarkerCheckBox.GetValue() + config.conf["mp3DCReport"]["other"] = self.reportOtherCheckBox.GetValue() -class Mp3DirectCutDialog (SettingsDialog): +class Mp3DirectCutDialog(SettingsDialog): # Translators: The title of the add-on configuration dialog box. title = _("Configuration of the addon {0}").format("mp3DirectCut") @@ -60,26 +60,27 @@ def makeSettings(self, settingsSizer): settingsSizerHelper = guiHelper.BoxSizerHelper(self, sizer=settingsSizer) # Translators: The label of the checkbox to enable or disable the spacebar announcements. self.reportSpaceCheckBox = wx.CheckBox(parent=self, label=_("Enable announcements of the space key")) - self.reportSpaceCheckBox.SetValue(config.conf['mp3DCReport']['space']) + self.reportSpaceCheckBox.SetValue(config.conf["mp3DCReport"]["space"]) settingsSizerHelper.addItem(self.reportSpaceCheckBox) # Translators: The label of the checkbox to enable or disable the announcements of the selection markers. self.reportMarkerCheckBox = wx.CheckBox( parent=self, - label=_("Announce the placement of the selection markers")) - self.reportMarkerCheckBox.SetValue(config.conf['mp3DCReport']['marker']) + label=_("Announce the placement of the selection markers"), + ) + self.reportMarkerCheckBox.SetValue(config.conf["mp3DCReport"]["marker"]) settingsSizerHelper.addItem(self.reportMarkerCheckBox) # Translators: The label of the checkbox to enable or disable the other announcements. self.reportOtherCheckBox = wx.CheckBox(parent=self, label=_("Enable the other announces")) - self.reportOtherCheckBox.SetValue(config.conf['mp3DCReport']['other']) + self.reportOtherCheckBox.SetValue(config.conf["mp3DCReport"]["other"]) settingsSizerHelper.addItem(self.reportOtherCheckBox) def postInit(self): self.reportSpaceCheckBox.SetFocus() def onOk(self, evt): - config.conf['mp3DCReport']['space'] = self.reportSpaceCheckBox.GetValue() - config.conf['mp3DCReport']['marker'] = self.reportMarkerCheckBox.GetValue() - config.conf['mp3DCReport']['other'] = self.reportOtherCheckBox.GetValue() + config.conf["mp3DCReport"]["space"] = self.reportSpaceCheckBox.GetValue() + config.conf["mp3DCReport"]["marker"] = self.reportMarkerCheckBox.GetValue() + config.conf["mp3DCReport"]["other"] = self.reportOtherCheckBox.GetValue() super(Mp3DirectCutDialog, self).onOk(evt) diff --git a/addon/installTasks.py b/addon/installTasks.py index 50f022d..08aa9a4 100644 --- a/addon/installTasks.py +++ b/addon/installTasks.py @@ -10,6 +10,7 @@ import gui from typing import Callable import wx + addonHandler.initTranslation() _: Callable[[str], str] @@ -18,12 +19,17 @@ def onInstall(): addon = [x for x in addonHandler.getAvailableAddons() if x.manifest["name"] == "mp3directcut"] if len(addon) > 0: addon = addon[0] - if gui.messageBox( - # Translators: A message informing the user that he has installed an incompatible version. - _("You have installed the mp3DirectCut-1.1 add-on which is incompatible with this one.\ - Do you want to uninstall this old version?"), - # Translators: The title of the dialogbox. - _("Uninstall incompatible version"), - wx.YES | wx.NO | wx.ICON_WARNING - ) == wx.YES: + if ( + gui.messageBox( + # Translators: A message informing the user that he has installed an incompatible version. + _( + "You have installed the mp3DirectCut-1.1 add-on which is incompatible with this one.\ + Do you want to uninstall this old version?" + ), + # Translators: The title of the dialogbox. + _("Uninstall incompatible version"), + wx.YES | wx.NO | wx.ICON_WARNING, + ) + == wx.YES + ): addon.requestRemove() diff --git a/mp3DirectCut.xliff b/mp3DirectCut.xliff index 721a514..724af24 100644 --- a/mp3DirectCut.xliff +++ b/mp3DirectCut.xliff @@ -1,6 +1,6 @@ - + $(ID:15cc7ab7-30a6-488d-bdbc-7022256a7573) diff --git a/readme.md b/readme.md index fc867ed..8fa618c 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# mp3DirectCut +# mp3DirectCut * Author(s) : Abdel, Rémy, Abdellah zineddine, Jean-François COLAS. diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index a71857d..7de9357 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -34,12 +34,14 @@ def generate(env: Environment): env.SetDefault(excludePatterns=tuple()) addonAction = env.Action( - lambda target, source, env: createAddonBundleFromPath( - source[0].abspath, - target[0].abspath, - env["excludePatterns"], - ) - and None, + lambda target, source, env: ( + createAddonBundleFromPath( + source[0].abspath, + target[0].abspath, + env["excludePatterns"], + ) + and None + ), lambda target, source, env: f"Generating Addon {target[0]}", ) env["BUILDERS"]["NVDAAddon"] = Builder( @@ -53,15 +55,17 @@ def generate(env: Environment): env.SetDefault(speechDictionaries={}) manifestAction = env.Action( - lambda target, source, env: generateManifest( - source[0].abspath, - target[0].abspath, - addon_info=env["addon_info"], - brailleTables=env["brailleTables"], - symbolDictionaries=env["symbolDictionaries"], - speechDictionaries=env["speechDictionaries"], - ) - and None, + lambda target, source, env: ( + generateManifest( + source[0].abspath, + target[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], + ) + and None + ), lambda target, source, env: f"Generating manifest {target[0]}", ) env["BUILDERS"]["NVDAManifest"] = Builder( @@ -71,16 +75,18 @@ def generate(env: Environment): ) translatedManifestAction = env.Action( - lambda target, source, env: generateTranslatedManifest( - source[1].abspath, - target[0].abspath, - mo=source[0].abspath, - addon_info=env["addon_info"], - brailleTables=env["brailleTables"], - symbolDictionaries=env["symbolDictionaries"], - speechDictionaries=env["speechDictionaries"], - ) - and None, + lambda target, source, env: ( + generateTranslatedManifest( + source[1].abspath, + target[0].abspath, + mo=source[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], + ) + and None + ), lambda target, source, env: f"Generating translated manifest {target[0]}", ) @@ -93,14 +99,16 @@ def generate(env: Environment): env.SetDefault(mdExtensions={}) mdAction = env.Action( - lambda target, source, env: md2html( - source[0].path, - target[0].path, - moFile=env["moFile"].path if env["moFile"] else None, - mdExtensions=env["mdExtensions"], - addon_info=env["addon_info"], - ) - and None, + lambda target, source, env: ( + md2html( + source[0].path, + target[0].path, + moFile=env["moFile"].path if env["moFile"] else None, + mdExtensions=env["mdExtensions"], + addon_info=env["addon_info"], + ) + and None + ), lambda target, source, env: f"Generating {target[0]}", ) env["BUILDERS"]["md2html"] = env.Builder(