Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions source/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#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
# 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"""

Expand All @@ -25,7 +25,8 @@
import watchdog
import appModuleHandler
import cursorManager
from typing import Any
from typing import Any, Optional


#User functions

Expand Down Expand Up @@ -302,21 +303,33 @@ def processPendingEvents(processEventQueue=True):
if processEventQueue:
queueHandler.flushQueue(queueHandler.eventQueue)

def copyToClip(text):

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
"""
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
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:
ui.reportTextCopiedToClipboard() # No argument reports a failure.
return False
if got == text:
if notify:
ui.reportTextCopiedToClipboard(text)
return True
if notify:
ui.reportTextCopiedToClipboard() # No argument reports a failure.
return False


def getClipData():
"""Receives text from the windows clipboard.
Expand Down
4 changes: 1 addition & 3 deletions source/cursorManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,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)
Expand Down
18 changes: 4 additions & 14 deletions source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,9 +892,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.
Expand Down Expand Up @@ -1638,9 +1636,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
Expand Down Expand Up @@ -1692,8 +1688,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
Expand Down Expand Up @@ -2277,12 +2272,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
Expand Down
8 changes: 5 additions & 3 deletions source/textInfos/__init__.py
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -503,13 +503,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.
Expand Down
15 changes: 8 additions & 7 deletions source/treeInterceptorHandler.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -188,8 +189,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)
Expand Down
40 changes: 33 additions & 7 deletions source/ui.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#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.
# -*- 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
# 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.
Expand Down Expand Up @@ -69,14 +70,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):
Expand All @@ -88,3 +94,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)
)
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -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 ==
Expand Down