From 50bf96b0738d085dd931cec0573cda89e0f627fa Mon Sep 17 00:00:00 2001 From: Julien Cochuyt Date: Fri, 28 Jun 2019 16:34:55 +0200 Subject: [PATCH 1/5] Uniformize all "Copied to clipboard" messages (#6757) --- source/api.py | 36 ++++++++++++++++++++++++++------ source/cursorManager.py | 4 +--- source/globalCommands.py | 18 ++++------------ source/textInfos/__init__.py | 6 ++++-- source/treeInterceptorHandler.py | 4 ++-- 5 files changed, 41 insertions(+), 27 deletions(-) diff --git a/source/api.py b/source/api.py index 361cdd259db..9340110400d 100644 --- a/source/api.py +++ b/source/api.py @@ -26,6 +26,7 @@ import appModuleHandler import cursorManager from typing import Any +import speech #User functions @@ -292,21 +293,44 @@ def processPendingEvents(processEventQueue=True): if processEventQueue: queueHandler.flushQueue(queueHandler.eventQueue) -def copyToClip(text): + +def copyToClip(text, notify=False): """Copies the given text to the windows clipboard. @returns: True if it succeeds, False otherwise. @rtype: boolean @param text: the text which will be copied to the clipboard @type text: string +@param notify: whether to emit a confirmation message +@type notify: boolean """ if not isinstance(text,str) or len(text)==0: return False import gui - with winUser.openClipboard(gui.mainFrame.Handle): - winUser.emptyClipboard() - winUser.setClipboardData(winUser.CF_UNICODETEXT,text) - got=getClipData() - return got == text + try: + with winUser.openClipboard(gui.mainFrame.Handle): + winUser.emptyClipboard() + winUser.setClipboardData(winUser.CF_UNICODETEXT, text) + got = getClipData() + except ctypes.WinError: + if notify: + # Translators: Presented when unable to copy to the clipboard + # because of an error. + ui.message(_("Unable to copy")) + return False + if got == text: + if notify: + # Translators: Announced when a text has been copied to clipboard. + # %s is replaced by the copied text. + speech.speakMessage(_("Copied to clipboard: %s" % text)) + # Translators: Displayed in braille when a text has been copied to clipboard. + # %s is replaced by the copied text. + braille.handler.message(_("Copied: %s" % text)) + return True + if notify: + # Translators: Presented when the clipboard content did not match what was just copied. + ui.message(_("Unable to copy")) + return False + def getClipData(): """Receives text from the windows clipboard. diff --git a/source/cursorManager.py b/source/cursorManager.py index 2b3ad58293d..37a9f363bfd 100644 --- a/source/cursorManager.py +++ b/source/cursorManager.py @@ -370,9 +370,7 @@ def script_copyToClipboard(self,gesture): # Translators: Reported when there is no text selected (for copying). ui.message(_("No selection")) return - if info.copyToClipboard(): - # Translators: Message presented when text has been copied to clipboard. - ui.message(_("Copied to clipboard")) + info.copyToClipboard(notify=True) def reportSelectionChange(self, oldTextInfo): newInfo=self.makeTextInfo(textInfos.POSITION_SELECTION) diff --git a/source/globalCommands.py b/source/globalCommands.py index f1357c4e136..069f94e6ccd 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -875,9 +875,7 @@ def script_navigatorObject_current(self,gesture): if scriptHandler.getLastScriptRepeatCount()==1: speech.speakSpelling(text) else: - if api.copyToClip(text): - # Translators: Indicates something has been copied to clipboard (example output: title text copied to clipboard). - speech.speakMessage(_("%s copied to clipboard")%text) + api.copyToClip(text, notify=True) else: speech.speakObject(curObject,reason=controlTypes.REASON_QUERY) # Translators: Input help mode message for report current navigator object command. @@ -1615,9 +1613,7 @@ def script_reportStatusLine(self,gesture): # Translators: Reported when user attempts to copy content of the empty status line. ui.message(_("unable to copy status bar content to clipboard")) else: - if api.copyToClip(text): - # Translators: The message presented when the status bar is copied to the clipboard. - ui.message(_("%s copied to clipboard")%text) + api.copyToClip(text, notify=True) # Translators: Input help mode message for report status line text command. script_reportStatusLine.__doc__ = _("Reads the current application status bar and moves the navigator to it. If pressed twice, spells the information. If pressed three times, copies the status bar to the clipboard") script_reportStatusLine.category=SCRCAT_FOCUS @@ -1669,8 +1665,7 @@ def script_title(self,gesture): elif repeatCount==1: speech.speakSpelling(title) else: - if api.copyToClip(title): - ui.message(_("%s copied to clipboard")%title) + api.copyToClip(title, notify=True) # Translators: Input help mode message for report title bar command. script_title.__doc__=_("Reports the title of the current application or foreground window. If pressed twice, spells the title. If pressed three times, copies the title to the clipboard") script_title.category=SCRCAT_FOCUS @@ -2218,12 +2213,7 @@ def script_review_copy(self, gesture): return elif scriptHandler.getLastScriptRepeatCount()==1: # the second call, try to copy the text copyMarker = pos.obj._selectThenCopyRange - if copyMarker.copyToClipboard(): - # Translators: Presented when some review text has been copied to clipboard. - ui.message(_("Review selection copied to clipboard")) - else: - # Translators: Presented when unable to copy to the clipboard because of an error. - ui.message(_("Unable to copy")) + copyMarker.copyToClipboard(notify=True) # on the second call always clean up the start marker api.getReviewPosition().obj._selectThenCopyRange = None api.getReviewPosition().obj._copyStartMarker = None diff --git a/source/textInfos/__init__.py b/source/textInfos/__init__.py index c908fdf0bf9..1efc60896bf 100755 --- a/source/textInfos/__init__.py +++ b/source/textInfos/__init__.py @@ -490,13 +490,15 @@ def _get_clipboardText(self): """Text suitably formatted for copying to the clipboard. E.g. crlf characters inserted between lines.""" return convertToCrlf(self.text) - def copyToClipboard(self): + def copyToClipboard(self, notify=False): """Copy the content of this instance to the clipboard. @return: C{True} if successful, C{False} otherwise. @rtype: bool + @param notify: whether to emit a confirmation message + @type notify: boolean """ import api - return api.copyToClip(self.clipboardText) + return api.copyToClip(self.clipboardText, notify) def getTextInChunks(self, unit): """Retrieve the text of this instance in chunks of a given unit. diff --git a/source/treeInterceptorHandler.py b/source/treeInterceptorHandler.py index eba67fb80ce..48365a1dca0 100644 --- a/source/treeInterceptorHandler.py +++ b/source/treeInterceptorHandler.py @@ -188,8 +188,8 @@ def _set__rangeObj(self,r): def _get_locationText(self): return self.innerTextInfo.locationText - def copyToClipboard(self): - return self.innerTextInfo.copyToClipboard() + def copyToClipboard(self, notify=False): + return self.innerTextInfo.copyToClipboard(notify) def find(self,text,caseSensitive=False,reverse=False): return self.innerTextInfo.find(text,caseSensitive,reverse) From 8f7ca6f2d3e1cf4eb4ca42c982d6ae0cb84597bd Mon Sep 17 00:00:00 2001 From: Julien Cochuyt Date: Fri, 1 May 2020 16:56:45 +0200 Subject: [PATCH 2/5] Do not bind `api` to `speech` / Move message literals to `ui` Review action: https://github.com/nvaccess/nvda/pull/9843#discussion_r417994985 --- source/api.py | 42 +++++++++++++++--------------------------- source/ui.py | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/source/api.py b/source/api.py index 9340110400d..a433033449b 100644 --- a/source/api.py +++ b/source/api.py @@ -1,8 +1,7 @@ -#api.py -#A part of NonVisual Desktop Access (NVDA) -#Copyright (C) 2006-2012 NVDA Contributors -#This file is covered by the GNU General Public License. -#See the file COPYING for more details. +# A part of NonVisual Desktop Access (NVDA) +# Copyright (C) 2006-2020 NV Access Limited, James Teh, Michael Curran, Peter Vagner, Derek Riemer, Davy Kager, Babbage B.V., Leonard de Ruijter, Joseph Lee, Accessolutions, Julien Cochuyt # noqa: E501 +# This file may be used under the terms of the GNU General Public License, version 2 or later. +# For more details see: https://www.gnu.org/licenses/gpl-2.0.html """General functions for NVDA""" @@ -25,8 +24,8 @@ import watchdog import appModuleHandler import cursorManager -from typing import Any -import speech +from typing import Any, Optional + #User functions @@ -294,16 +293,13 @@ def processPendingEvents(processEventQueue=True): queueHandler.flushQueue(queueHandler.eventQueue) -def copyToClip(text, notify=False): +def copyToClip(text: str, notify: Optional[bool] = False) -> bool: """Copies the given text to the windows clipboard. -@returns: True if it succeeds, False otherwise. -@rtype: boolean -@param text: the text which will be copied to the clipboard -@type text: string -@param notify: whether to emit a confirmation message -@type notify: boolean -""" - if not isinstance(text,str) or len(text)==0: + @returns: True if it succeeds, False otherwise. + @param text: the text which will be copied to the clipboard + @param notify: whether to emit a confirmation message + """ + if not isinstance(text, str) or len(text) == 0: return False import gui try: @@ -313,22 +309,14 @@ def copyToClip(text, notify=False): got = getClipData() except ctypes.WinError: if notify: - # Translators: Presented when unable to copy to the clipboard - # because of an error. - ui.message(_("Unable to copy")) + ui.reportTextCopiedToClipboard() # No argument reports a failure. return False if got == text: if notify: - # Translators: Announced when a text has been copied to clipboard. - # %s is replaced by the copied text. - speech.speakMessage(_("Copied to clipboard: %s" % text)) - # Translators: Displayed in braille when a text has been copied to clipboard. - # %s is replaced by the copied text. - braille.handler.message(_("Copied: %s" % text)) + ui.reportTextCopiedToClipboard(text) return True if notify: - # Translators: Presented when the clipboard content did not match what was just copied. - ui.message(_("Unable to copy")) + ui.reportTextCopiedToClipboard() # No argument reports a failure. return False diff --git a/source/ui.py b/source/ui.py index b963e9632e9..26b785eceeb 100644 --- a/source/ui.py +++ b/source/ui.py @@ -1,8 +1,7 @@ -#ui.py -#A part of NonVisual Desktop Access (NVDA) -#Copyright (C) 2008-2017 NV Access Limited, Dinesh Kaushal, Davy Kager, Babbage B.V. -#This file is covered by the GNU General Public License. -#See the file COPYING for more details. +# A part of NonVisual Desktop Access (NVDA) +# Copyright (C) 2008-2020 NV Access Limited, James Teh, Dinesh Kaushal, Davy Kager, André-Abush Clause, Babbage B.V., Leonard de Ruijter, Michael Curran, Accessolutions, Julien Cochuyt # noqa: E501 +# This file may be used under the terms of the GNU General Public License, version 2 or later. +# For more details see: https://www.gnu.org/licenses/gpl-2.0.html """User interface functionality. This refers to the user interface presented by the screen reader alone, not the graphical user interface. @@ -68,14 +67,19 @@ def browseableMessage(message,title=None,isHtml=False): gui.mainFrame.postPopup() -def message(text: str, speechPriority: Optional[speech.Spri] = None): +def message( + text: str, + speechPriority: Optional[speech.Spri] = None, + brailleText: Optional[str] = None, +): """Present a message to the user. The message will be presented in both speech and braille. @param text: The text of the message. @param speechPriority: The speech priority. + @param brailleText: If specified, present this alternative text on the braille display. """ speech.speakMessage(text, priority=speechPriority) - braille.handler.message(text) + braille.handler.message(brailleText if brailleText is not None else text) def reviewMessage(text: str, speechPriority: Optional[speech.Spri] = None): @@ -87,3 +91,23 @@ def reviewMessage(text: str, speechPriority: Optional[speech.Spri] = None): speech.speakMessage(text, priority=speechPriority) if braille.handler.shouldAutoTether or braille.handler.getTether() == braille.handler.TETHER_REVIEW: braille.handler.message(text) + + +def reportTextCopiedToClipboard(text: Optional[str] = None): + """Notify about the result of a "Copy to clipboard" operation. + @param text: The text that has been copied. Set to `None` to notify of a failed operation. + See: `api.copyToClip` + """ + if not text: + # Translators: Presented when unable to copy to the clipboard because of an error + # or the clipboard content did not match what was just copied. + message(_("Unable to copy")) + return + message( + # Translators: Announced when a text has been copied to clipboard. + # {text} is replaced by the copied text. + text=_("Copied to clipboard: {text}").format(text=text), + # Translators: Displayed in braille when a text has been copied to clipboard. + # {text} is replaced by the copied text. + brailleText=_("Copied: {text}").format(text=text) + ) From 2bd0c9058dc7cdd53aa0a3017ee7340f83502cc4 Mon Sep 17 00:00:00 2001 From: Julien Cochuyt Date: Fri, 1 May 2020 18:03:46 +0200 Subject: [PATCH 3/5] Declare encoding, as an alternative to PR #11081 --- source/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/ui.py b/source/ui.py index 26b785eceeb..cfdab8ad202 100644 --- a/source/ui.py +++ b/source/ui.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # A part of NonVisual Desktop Access (NVDA) # Copyright (C) 2008-2020 NV Access Limited, James Teh, Dinesh Kaushal, Davy Kager, André-Abush Clause, Babbage B.V., Leonard de Ruijter, Michael Curran, Accessolutions, Julien Cochuyt # noqa: E501 # This file may be used under the terms of the GNU General Public License, version 2 or later. From 1da6d1432d2a802b84c07de811131ce9faf380e0 Mon Sep 17 00:00:00 2001 From: Julien Cochuyt Date: Mon, 11 May 2020 15:10:31 +0200 Subject: [PATCH 4/5] Update copyright notice header lines and split instead of disabling E501 Re: https://github.com/nvaccess/nvda/pull/9844#discussion_r422956971 --- source/api.py | 3 ++- source/globalCommands.py | 3 ++- source/textInfos/__init__.py | 2 +- source/treeInterceptorHandler.py | 11 ++++++----- source/ui.py | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/source/api.py b/source/api.py index a433033449b..5328c05dddb 100644 --- a/source/api.py +++ b/source/api.py @@ -1,5 +1,6 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2006-2020 NV Access Limited, James Teh, Michael Curran, Peter Vagner, Derek Riemer, Davy Kager, Babbage B.V., Leonard de Ruijter, Joseph Lee, Accessolutions, Julien Cochuyt # noqa: E501 +# Copyright (C) 2006-2020 NV Access Limited, James Teh, Michael Curran, Peter Vagner, Derek Riemer, +# Davy Kager, Babbage B.V., Leonard de Ruijter, Joseph Lee, Accessolutions, Julien Cochuyt # This file may be used under the terms of the GNU General Public License, version 2 or later. # For more details see: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/source/globalCommands.py b/source/globalCommands.py index 069f94e6ccd..f3330b0bbfb 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -3,7 +3,8 @@ # This file is covered by the GNU General Public License. # See the file COPYING for more details. # Copyright (C) 2006-2020 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee, -# Leonard de Ruijter, Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger, Łukasz Golonka +# Leonard de Ruijter, Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger, Łukasz Golonka, Accessolutions, +# Julien Cochuyt import time import itertools diff --git a/source/textInfos/__init__.py b/source/textInfos/__init__.py index 1efc60896bf..e055a91624c 100755 --- a/source/textInfos/__init__.py +++ b/source/textInfos/__init__.py @@ -1,7 +1,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) 2006-2019 NV Access Limited, Babbage B.V. +# Copyright (C) 2006-2020 NV Access Limited, Babbage B.V., Accessolutions, Julien Cochuyt """Framework for accessing text content in widgets. The core component of this framework is the L{TextInfo} class. diff --git a/source/treeInterceptorHandler.py b/source/treeInterceptorHandler.py index 48365a1dca0..a1a4b32e450 100644 --- a/source/treeInterceptorHandler.py +++ b/source/treeInterceptorHandler.py @@ -1,8 +1,9 @@ -#treeInterceptorHandler.py -#A part of NonVisual Desktop Access (NVDA) -#Copyright (C) 2006-2017 NV Access Limited, Davy Kager -#This file is covered by the GNU General Public License. -#See the file COPYING for more details. +# treeInterceptorHandler.py +# A part of NonVisual Desktop Access (NVDA) +# Copyright (C) 2006-2020 NV Access Limited, Davy Kager, Accessolutions, Julien Cochuyt +# This file is covered by the GNU General Public License. +# See the file COPYING for more details. + from typing import Optional, Dict from logHandler import log diff --git a/source/ui.py b/source/ui.py index cfdab8ad202..56b94ae58cd 100644 --- a/source/ui.py +++ b/source/ui.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2008-2020 NV Access Limited, James Teh, Dinesh Kaushal, Davy Kager, André-Abush Clause, Babbage B.V., Leonard de Ruijter, Michael Curran, Accessolutions, Julien Cochuyt # noqa: E501 +# Copyright (C) 2008-2020 NV Access Limited, James Teh, Dinesh Kaushal, Davy Kager, André-Abush Clause, +# Babbage B.V., Leonard de Ruijter, Michael Curran, Accessolutions, Julien Cochuyt # This file may be used under the terms of the GNU General Public License, version 2 or later. # For more details see: https://www.gnu.org/licenses/gpl-2.0.html From b357c390fb56a100f5d60986275e9e3f478de633 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Tue, 17 Nov 2020 15:34:49 +0800 Subject: [PATCH 5/5] update changes file for PR #9843 --- user_docs/en/changes.t2t | 1 + 1 file changed, 1 insertion(+) diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index 18231e2f1e3..c5e10ea6b27 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -24,6 +24,7 @@ What's New in NVDA - Improvements to the UX of the "braille message timeout" and "Show messages indefinitely" options. (#11602) - In web browsers and other applications that support browse mode, the Elements List dialog (NVDA+F7) can now be invoked when in focus mode. (#10453) - Updates to ARIA live regions are now suppressed when reporting of dynamic content changes is disabled. (#9077) +- NVDA will now report "Copied to clipboard" before the copied text. (#6757) == Bug Fixes ==