Package gui
[hide private]
[frames] | no frames]

Source Code for Package gui

  1  #gui/__init__.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2011 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  import time 
  8  import os 
  9  import sys 
 10  import wx 
 11  import globalVars 
 12  import ui 
 13  from logHandler import log 
 14  import config 
 15  import versionInfo 
 16  import speech 
 17  import queueHandler 
 18  import core 
 19  from settingsDialogs import * 
 20  import speechDictHandler 
 21  import languageHandler 
 22  import logViewer 
 23  import speechViewer 
 24  import winUser 
 25  import api 
 26   
 27  ### Constants 
 28  NVDA_PATH = os.getcwdu() 
 29  ICON_PATH=os.path.join(NVDA_PATH, "images", "nvda.ico") 
 30   
 31  ### Globals 
 32  mainFrame = None 
 33  isInMessageBox = False 
34 35 -def getDocFilePath(fileName, localized=True):
36 if not getDocFilePath.rootPath: 37 if hasattr(sys, "frozen"): 38 getDocFilePath.rootPath = os.path.join(NVDA_PATH, "documentation") 39 else: 40 getDocFilePath.rootPath = os.path.abspath(os.path.join("..", "user_docs")) 41 42 if localized: 43 lang = languageHandler.getLanguage() 44 tryLangs = [lang] 45 if "_" in lang: 46 # This locale has a sub-locale, but documentation might not exist for the sub-locale, so try stripping it. 47 tryLangs.append(lang.split("_")[0]) 48 # If all else fails, use English. 49 tryLangs.append("en") 50 51 fileName, fileExt = os.path.splitext(fileName) 52 for tryLang in tryLangs: 53 tryDir = os.path.join(getDocFilePath.rootPath, tryLang) 54 if not os.path.isdir(tryDir): 55 continue 56 57 # Some out of date translations might include .txt files which are now .html files in newer translations. 58 # Therefore, ignore the extension and try both .html and .txt. 59 for tryExt in ("html", "txt"): 60 tryPath = os.path.join(tryDir, "%s.%s" % (fileName, tryExt)) 61 if os.path.isfile(tryPath): 62 return tryPath 63 64 else: 65 # Not localized. 66 if not hasattr(sys, "frozen") and fileName in ("copying.txt", "contributors.txt"): 67 # If running from source, these two files are in the root dir. 68 return os.path.join(NVDA_PATH, "..", fileName) 69 else: 70 return os.path.join(getDocFilePath.rootPath, fileName)
71 getDocFilePath.rootPath = None
72 73 -class MainFrame(wx.Frame):
74
75 - def __init__(self):
76 style = wx.DEFAULT_FRAME_STYLE ^ wx.MAXIMIZE_BOX ^ wx.MINIMIZE_BOX | wx.FRAME_NO_TASKBAR 77 super(MainFrame, self).__init__(None, wx.ID_ANY, versionInfo.name, size=(1,1), style=style) 78 self.Bind(wx.EVT_CLOSE, self.onExitCommand) 79 self.sysTrayIcon = SysTrayIcon(self) 80 # This makes Windows return to the previous foreground window and also seems to allow NVDA to be brought to the foreground. 81 self.Show() 82 self.Hide() 83 if winUser.isWindowVisible(self.Handle): 84 # HACK: Work around a wx bug where Hide() doesn't actually hide the window, 85 # but IsShown() returns False and Hide() again doesn't fix it. 86 # This seems to happen if the call takes too long. 87 self.Show() 88 self.Hide()
89
90 - def Destroy(self):
91 self.sysTrayIcon.Destroy() 92 super(MainFrame, self).Destroy()
93
94 - def prePopup(self):
95 """Prepare for a popup. 96 This should be called before any dialog or menu which should pop up for the user. 97 L{postPopup} should be called after the dialog or menu has been shown. 98 @postcondition: A dialog or menu may be shown. 99 """ 100 if winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != os.getpid(): 101 # This process is not the foreground process, so bring it to the foreground. 102 self.Raise()
103
104 - def postPopup(self):
105 """Clean up after a popup dialog or menu. 106 This should be called after a dialog or menu was popped up for the user. 107 """ 108 if not winUser.isWindowVisible(winUser.getForegroundWindow()): 109 # The current foreground window is invisible, so we want to return to the previous foreground window. 110 # Showing and hiding our main window seems to achieve this. 111 self.Show() 112 self.Hide()
113
114 - def showGui(self):
115 # The menu pops up at the location of the mouse, which means it pops up at an unpredictable location. 116 # Therefore, move the mouse to the centre of the screen so that the menu will always pop up there. 117 left, top, width, height = api.getDesktopObject().location 118 x = width / 2 119 y = height / 2 120 winUser.setCursorPos(x, y) 121 self.sysTrayIcon.onActivate(None)
122 126
127 - def onSaveConfigurationCommand(self,evt):
128 if globalVars.appArgs.secure: 129 queueHandler.queueFunction(queueHandler.eventQueue,ui.message,_("Cannot save configuration - NVDA in secure mode")) 130 return 131 try: 132 config.save() 133 queueHandler.queueFunction(queueHandler.eventQueue,ui.message,_("configuration saved")) 134 except: 135 messageBox(_("Could not save configuration - probably read only file system"),_("Error"),wx.OK | wx.ICON_ERROR)
136
137 - def _popupSettingsDialog(self, dialog, *args, **kwargs):
138 if isInMessageBox: 139 return 140 self.prePopup() 141 try: 142 dialog(self, *args, **kwargs).Show() 143 except SettingsDialog.MultiInstanceError: 144 messageBox(_("Please close the other NVDA settings dialog first"),_("Error"),style=wx.OK | wx.ICON_ERROR) 145 self.postPopup()
146
147 - def onDefaultDictionaryCommand(self,evt):
148 self._popupSettingsDialog(DictionaryDialog,_("Default dictionary"),speechDictHandler.dictionaries["default"])
149
150 - def onVoiceDictionaryCommand(self,evt):
151 self._popupSettingsDialog(DictionaryDialog,_("Voice dictionary (%s)")%speechDictHandler.dictionaries["voice"].fileName,speechDictHandler.dictionaries["voice"])
152
153 - def onTemporaryDictionaryCommand(self,evt):
154 self._popupSettingsDialog(DictionaryDialog,_("Temporary dictionary"),speechDictHandler.dictionaries["temp"])
155
156 - def onExitCommand(self, evt):
157 canExit=False 158 if config.conf["general"]["askToExit"]: 159 if isInMessageBox: 160 return 161 if messageBox(_("Are you sure you want to quit NVDA?"), _("Exit NVDA"), wx.YES_NO|wx.ICON_WARNING) == wx.YES: 162 canExit=True 163 else: 164 canExit=True 165 if canExit: 166 wx.GetApp().ExitMainLoop()
167
168 - def onGeneralSettingsCommand(self,evt):
170
171 - def onSynthesizerCommand(self,evt):
173
174 - def onVoiceCommand(self,evt):
176
177 - def onBrailleCommand(self,evt):
179
180 - def onKeyboardSettingsCommand(self,evt):
182
183 - def onMouseSettingsCommand(self,evt):
185
186 - def onReviewCursorCommand(self,evt):
188
189 - def onObjectPresentationCommand(self,evt):
191
192 - def onBrowseModeCommand(self,evt):
194
195 - def onDocumentFormattingCommand(self,evt):
197
198 - def onSpeechSymbolsCommand(self, evt):
200
201 - def onAboutCommand(self,evt):
202 messageBox(versionInfo.aboutMessage, _("About NVDA"), wx.OK)
203
204 - def onViewLogCommand(self, evt):
206
207 - def onToggleSpeechViewerCommand(self, evt):
208 if not speechViewer.isActive: 209 speechViewer.activate() 210 self.sysTrayIcon.menu_tools_toggleSpeechViewer.Check(True) 211 else: 212 speechViewer.deactivate() 213 self.sysTrayIcon.menu_tools_toggleSpeechViewer.Check(False)
214
215 - def onPythonConsoleCommand(self, evt):
220
221 - def onReloadPluginsCommand(self, evt):
227
228 -class SysTrayIcon(wx.TaskBarIcon):
229
230 - def __init__(self, frame):
231 super(SysTrayIcon, self).__init__() 232 icon=wx.Icon(ICON_PATH,wx.BITMAP_TYPE_ICO) 233 self.SetIcon(icon, versionInfo.name) 234 235 self.menu=wx.Menu() 236 menu_preferences=wx.Menu() 237 item = menu_preferences.Append(wx.ID_ANY,_("&General settings..."),_("General settings")) 238 self.Bind(wx.EVT_MENU, frame.onGeneralSettingsCommand, item) 239 item = menu_preferences.Append(wx.ID_ANY,_("&Synthesizer..."),_(" the synthesizer to use")) 240 self.Bind(wx.EVT_MENU, frame.onSynthesizerCommand, item) 241 item = menu_preferences.Append(wx.ID_ANY,_("&Voice settings..."),_("Choose the voice, rate, pitch and volume to use")) 242 self.Bind(wx.EVT_MENU, frame.onVoiceCommand, item) 243 item = menu_preferences.Append(wx.ID_ANY,_("B&raille settings...")) 244 self.Bind(wx.EVT_MENU, frame.onBrailleCommand, item) 245 item = menu_preferences.Append(wx.ID_ANY,_("&Keyboard Settings..."),_("Configure keyboard layout, speaking of typed characters, words or command keys")) 246 self.Bind(wx.EVT_MENU, frame.onKeyboardSettingsCommand, item) 247 item = menu_preferences.Append(wx.ID_ANY, _("&Mouse settings..."),_("Change reporting of mouse shape and object under mouse")) 248 self.Bind(wx.EVT_MENU, frame.onMouseSettingsCommand, item) 249 item = menu_preferences.Append(wx.ID_ANY,_("Review &cursor..."),_("Configure how and when the review cursor moves")) 250 self.Bind(wx.EVT_MENU, frame.onReviewCursorCommand, item) 251 item = menu_preferences.Append(wx.ID_ANY,_("&Object presentation..."),_("Change reporting of objects")) 252 self.Bind(wx.EVT_MENU, frame.onObjectPresentationCommand, item) 253 item = menu_preferences.Append(wx.ID_ANY,_("&Browse mode..."),_("Change virtual buffers specific settings")) 254 self.Bind(wx.EVT_MENU, frame.onBrowseModeCommand, item) 255 item = menu_preferences.Append(wx.ID_ANY,_("Document &formatting..."),_("Change Settings of document properties")) 256 self.Bind(wx.EVT_MENU, frame.onDocumentFormattingCommand, item) 257 subMenu_speechDicts = wx.Menu() 258 if not globalVars.appArgs.secure: 259 item = subMenu_speechDicts.Append(wx.ID_ANY,_("&Default dictionary..."),_("dialog where you can set default dictionary by adding dictionary entries to the list")) 260 self.Bind(wx.EVT_MENU, frame.onDefaultDictionaryCommand, item) 261 item = subMenu_speechDicts.Append(wx.ID_ANY,_("&Voice dictionary..."),_("dialog where you can set voice-specific dictionary by adding dictionary entries to the list")) 262 self.Bind(wx.EVT_MENU, frame.onVoiceDictionaryCommand, item) 263 item = subMenu_speechDicts.Append(wx.ID_ANY,_("&Temporary dictionary..."),_("dialog where you can set temporary dictionary by adding dictionary entries to the edit box")) 264 self.Bind(wx.EVT_MENU, frame.onTemporaryDictionaryCommand, item) 265 menu_preferences.AppendMenu(wx.ID_ANY,_("Speech &dictionaries"),subMenu_speechDicts) 266 if not globalVars.appArgs.secure: 267 item = menu_preferences.Append(wx.ID_ANY, _("&Punctuation/symbol pronunciation...")) 268 self.Bind(wx.EVT_MENU, frame.onSpeechSymbolsCommand, item) 269 self.menu.AppendMenu(wx.ID_ANY,_("&Preferences"),menu_preferences) 270 271 menu_tools = wx.Menu() 272 if not globalVars.appArgs.secure: 273 item = menu_tools.Append(wx.ID_ANY, _("View log")) 274 self.Bind(wx.EVT_MENU, frame.onViewLogCommand, item) 275 item=self.menu_tools_toggleSpeechViewer = menu_tools.AppendCheckItem(wx.ID_ANY, _("Speech viewer")) 276 self.Bind(wx.EVT_MENU, frame.onToggleSpeechViewerCommand, item) 277 if not globalVars.appArgs.secure: 278 item = menu_tools.Append(wx.ID_ANY, _("Python console")) 279 self.Bind(wx.EVT_MENU, frame.onPythonConsoleCommand, item) 280 item = menu_tools.Append(wx.ID_ANY, _("Reload plugins")) 281 self.Bind(wx.EVT_MENU, frame.onReloadPluginsCommand, item) 282 self.menu.AppendMenu(wx.ID_ANY, _("Tools"), menu_tools) 283 284 menu_help = wx.Menu() 285 if not globalVars.appArgs.secure: 286 item = menu_help.Append(wx.ID_ANY, _("User guide")) 287 self.Bind(wx.EVT_MENU, lambda evt: os.startfile(getDocFilePath("userGuide.html")), item) 288 item = menu_help.Append(wx.ID_ANY, _("Keyboard Command Quick Reference")) 289 self.Bind(wx.EVT_MENU, lambda evt: os.startfile(getDocFilePath("keyCommands.html")), item) 290 item = menu_help.Append(wx.ID_ANY, _("What's &new")) 291 self.Bind(wx.EVT_MENU, lambda evt: os.startfile(getDocFilePath("changes.html")), item) 292 item = menu_help.Append(wx.ID_ANY, _("Web site")) 293 self.Bind(wx.EVT_MENU, lambda evt: os.startfile("http://www.nvda-project.org/"), item) 294 item = menu_help.Append(wx.ID_ANY, _("License")) 295 self.Bind(wx.EVT_MENU, lambda evt: os.startfile(getDocFilePath("copying.txt", False)), item) 296 item = menu_help.Append(wx.ID_ANY, _("Contributors")) 297 self.Bind(wx.EVT_MENU, lambda evt: os.startfile(getDocFilePath("contributors.txt", False)), item) 298 item = menu_help.Append(wx.ID_ANY, _("We&lcome dialog")) 299 self.Bind(wx.EVT_MENU, lambda evt: WelcomeDialog.run(), item) 300 menu_help.AppendSeparator() 301 item = menu_help.Append(wx.ID_ABOUT, _("About..."), _("About NVDA")) 302 self.Bind(wx.EVT_MENU, frame.onAboutCommand, item) 303 self.menu.AppendMenu(wx.ID_ANY,_("&Help"),menu_help) 304 self.menu.AppendSeparator() 305 item = self.menu.Append(wx.ID_ANY, _("&Revert to saved configuration"),_("Reset all settings to saved state")) 306 self.Bind(wx.EVT_MENU, frame.onRevertToSavedConfigurationCommand, item) 307 if not globalVars.appArgs.secure: 308 item = self.menu.Append(wx.ID_SAVE, _("&Save configuration"), _("Write the current configuration to nvda.ini")) 309 self.Bind(wx.EVT_MENU, frame.onSaveConfigurationCommand, item) 310 if not globalVars.appArgs.secure: 311 self.menu.AppendSeparator() 312 item = self.menu.Append(wx.ID_ANY, _("Donate")) 313 self.Bind(wx.EVT_MENU, lambda evt: os.startfile("http://www.nvaccess.org/wiki/Donate"), item) 314 self.menu.AppendSeparator() 315 item = self.menu.Append(wx.ID_EXIT, _("E&xit"),_("Exit NVDA")) 316 self.Bind(wx.EVT_MENU, frame.onExitCommand, item) 317 318 self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)
319
320 - def Destroy(self):
321 self.menu.Destroy() 322 super(SysTrayIcon, self).Destroy()
323
324 - def onActivate(self, evt):
325 mainFrame.prePopup() 326 self.PopupMenu(self.menu) 327 mainFrame.postPopup()
328
329 -def initialize():
330 global mainFrame 331 mainFrame = MainFrame() 332 wx.GetApp().SetTopWindow(mainFrame)
333
334 -def terminate():
335 global mainFrame 336 mainFrame.Destroy()
337
338 -def showGui():
339 wx.CallAfter(mainFrame.showGui)
340
341 -def quit():
342 wx.CallAfter(mainFrame.onExitCommand, None)
343
344 -def messageBox(message, caption=wx.MessageBoxCaptionStr, style=wx.OK | wx.CENTER, parent=None):
345 """Display a message dialog. 346 This should be used for all message dialogs 347 rather than using C{wx.MessageDialog} and C{wx.MessageBox} directly. 348 @param message: The message text. 349 @type message: str 350 @param caption: The caption (title) of the dialog. 351 @type caption: str 352 @param style: Same as for wx.MessageBox. 353 @type style: int 354 @param parent: The parent window (optional). 355 @type parent: C{wx.Window} 356 @return: Same as for wx.MessageBox. 357 @rtype: int 358 """ 359 global isInMessageBox 360 wasAlready = isInMessageBox 361 isInMessageBox = True 362 if not parent: 363 mainFrame.prePopup() 364 res = wx.MessageBox(message, caption, style, parent or mainFrame) 365 if not parent: 366 mainFrame.postPopup() 367 if not wasAlready: 368 isInMessageBox = False 369 return res
370
371 -def runScriptModalDialog(dialog, callback=None):
372 """Run a modal dialog from a script. 373 This will not block the caller, 374 but will instead call C{callback} (if provided) with the result from the dialog. 375 The dialog will be destroyed once the callback has returned. 376 @param dialog: The dialog to show. 377 @type dialog: C{wx.Dialog} 378 @param callback: The optional callable to call with the result from the dialog. 379 @type callback: callable 380 """ 381 def run(): 382 mainFrame.prePopup() 383 res = dialog.ShowModal() 384 mainFrame.postPopup() 385 if callback: 386 callback(res) 387 dialog.Destroy()
388 wx.CallAfter(run) 389
390 -class WelcomeDialog(wx.Dialog):
391 """The NVDA welcome dialog. 392 This provides essential information for new users, such as a description of the NVDA key and instructions on how to activate the NVDA menu. 393 It also provides quick access to some important configuration options. 394 This dialog is displayed the first time NVDA is started with a new configuration. 395 """ 396 397 WELCOME_MESSAGE = _( 398 "Welcome to NVDA!\n" 399 "Most commands for controlling NVDA require you to hold down the NVDA key while pressing other keys.\n" 400 "By default, the numpad insert and main insert keys may both be used as the NVDA key.\n" 401 "You can also configure NVDA to use the CapsLock as the NVDA key.\n" 402 "Press NVDA+n at any time to activate the NVDA menu.\n" 403 "From this menu, you can configure NVDA, get help and access other NVDA functions.\n" 404 ) 405
406 - def __init__(self, parent):
407 super(WelcomeDialog, self).__init__(parent, wx.ID_ANY, _("Welcome to NVDA")) 408 mainSizer=wx.BoxSizer(wx.VERTICAL) 409 welcomeText = wx.StaticText(self, wx.ID_ANY, self.WELCOME_MESSAGE) 410 mainSizer.Add(welcomeText,border=20,flag=wx.LEFT|wx.RIGHT|wx.TOP) 411 optionsSizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Options")), wx.HORIZONTAL) 412 self.capsAsNVDAModifierCheckBox = wx.CheckBox(self, wx.ID_ANY, _("Use CapsLock as an NVDA modifier key")) 413 self.capsAsNVDAModifierCheckBox.SetValue(config.conf["keyboard"]["useCapsLockAsNVDAModifierKey"]) 414 optionsSizer.Add(self.capsAsNVDAModifierCheckBox,flag=wx.TOP|wx.RIGHT,border=10) 415 self.showWelcomeDialogAtStartupCheckBox = wx.CheckBox(self, wx.ID_ANY, _("Show this dialog when NVDA starts")) 416 self.showWelcomeDialogAtStartupCheckBox.SetValue(config.conf["general"]["showWelcomeDialogAtStartup"]) 417 optionsSizer.Add(self.showWelcomeDialogAtStartupCheckBox,flag=wx.TOP|wx.LEFT,border=10) 418 mainSizer.Add(optionsSizer,flag=wx.LEFT|wx.TOP|wx.RIGHT,border=20) 419 mainSizer.Add(self.CreateButtonSizer(wx.OK),flag=wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL,border=20) 420 self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK) 421 422 self.SetSizer(mainSizer) 423 mainSizer.Fit(self) 424 self.capsAsNVDAModifierCheckBox.SetFocus()
425
426 - def onOk(self, evt):
427 config.conf["keyboard"]["useCapsLockAsNVDAModifierKey"] = self.capsAsNVDAModifierCheckBox.IsChecked() 428 config.conf["general"]["showWelcomeDialogAtStartup"] = self.showWelcomeDialogAtStartupCheckBox.IsChecked() 429 try: 430 config.save() 431 except: 432 pass 433 self.Close()
434 435 @classmethod
436 - def run(cls):
437 """Prepare and display an instance of this dialog. 438 This does not require the dialog to be instantiated. 439 """ 440 mainFrame.prePopup() 441 d = cls(mainFrame) 442 d.ShowModal() 443 d.Destroy() 444 mainFrame.postPopup()
445
446 -class ConfigFileErrorDialog(wx.Dialog):
447 """A configuration file error dialog. 448 This dialog tells the user that their configuration file is broken. 449 """ 450 451 MESSAGE=_("""Your configuration file contains errors. 452 Press 'Ok' to fix these errors, or press 'Cancel' if you wish to manually edit your config file at a later stage to make corrections. More details about the errors can be found in the log file. 453 """) 454
455 - def __init__(self, parent):
456 super(ConfigFileErrorDialog, self).__init__(parent, wx.ID_ANY, _("Configuration File Error")) 457 mainSizer=wx.BoxSizer(wx.VERTICAL) 458 messageText = wx.StaticText(self, wx.ID_ANY, self.MESSAGE) 459 mainSizer.Add(messageText,border=20,flag=wx.LEFT|wx.RIGHT|wx.TOP) 460 mainSizer.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL),flag=wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL,border=20) 461 self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK) 462 self.SetSizer(mainSizer) 463 mainSizer.Fit(self)
464
465 - def onOk(self, evt):
466 globalVars.configFileError=None 467 config.save() 468 self.Close()
469 470 @classmethod
471 - def run(cls):
472 """Prepare and display an instance of this dialog. 473 This does not require the dialog to be instantiated. 474 """ 475 mainFrame.prePopup() 476 d = cls(mainFrame) 477 d.ShowModal() 478 d.Destroy() 479 mainFrame.postPopup()
480