--- /dev/null
+#updateCheck.py\r
+#A part of NonVisual Desktop Access (NVDA)\r
+#This file is covered by the GNU General Public License.\r
+#See the file COPYING for more details.\r
+#Copyright (C) 2012 NV Access Limited\r
+\r
+"""Update checking functionality.\r
+"""\r
+\r
+import sys\r
+import os\r
+import threading\r
+import urllib\r
+import wx\r
+import versionInfo\r
+import languageHandler\r
+import gui\r
+from logHandler import log\r
+import config\r
+\r
+CHECK_URL = "http://www.nvda-project.org/updateCheck"\r
+\r
+# FIXME\r
+state = {\r
+ "lastCheck": 0,\r
+ "dontRemindVersion": None,\r
+}\r
+\r
+def checkForUpdate(auto=False):\r
+ """Check for an updated version of NVDA.\r
+ This will block, so it generally shouldn't be called from the main thread.\r
+ @param auto: Whether this is an automatic check for updates.\r
+ @type auto: bool\r
+ @return: Information about the update or C{None} if there is no update.\r
+ @rtype: dict\r
+ @raise RuntimeError: If there is an error checking for an update.\r
+ """\r
+ winVer = sys.getwindowsversion()\r
+ params = {\r
+ "autoCheck": auto,\r
+ "version": versionInfo.version,\r
+ "versionType": "stable",\r
+ "osVersion": "{v.major}.{v.minor}.{v.build} {v.service_pack}".format(v=winVer),\r
+ "x64": os.environ.get("PROCESSOR_ARCHITEW6432") == "AMD64",\r
+ "language": languageHandler.getLanguage(),\r
+ }\r
+ res = urllib.urlopen("%s?%s" % (CHECK_URL, urllib.urlencode(params)))\r
+ if res.code != 200:\r
+ raise RuntimeError("Checking for update failed with code %d" % res.code)\r
+ info = {}\r
+ for line in res:\r
+ line = line.rstrip()\r
+ try:\r
+ key, val = line.split(": ", 1)\r
+ except ValueError:\r
+ raise RuntimeError("Error in update check output")\r
+ info[key] = val\r
+ if not info:\r
+ return None\r
+ return info\r
+\r
+class CheckForUpdateUi(object):\r
+ """Check for an updated version of NVDA, presenting appropriate user interface.\r
+ Checks are performed in the background.\r
+ """\r
+\r
+ def __init__(self, auto=False):\r
+ """Constructor.\r
+ @param auto: Whether this is an automatic check for updates.\r
+ @type auto: bool\r
+ """\r
+ self.auto = auto\r
+ t = threading.Thread(target=self._bg)\r
+ t.daemon = True\r
+ t.start()\r
+\r
+ def _bg(self):\r
+ try:\r
+ info = checkForUpdate(self.auto)\r
+ except:\r
+ if self.auto:\r
+ log.debugWarning("Error checking for update", exc_info=True)\r
+ else:\r
+ log.error("Error checking for update", exc_info=True)\r
+ wx.CallAfter(self._error)\r
+ return\r
+ if not info:\r
+ # No update.\r
+ if not self.auto:\r
+ wx.CallAfter(self._result, None)\r
+ return\r
+ version = info["version"]\r
+ if self.auto and state["dontRemindVersion"] == version:\r
+ # The user has already been automatically notified about this version.\r
+ return\r
+ state["dontRemindVersion"] = version\r
+ wx.CallAfter(self._result, info)\r
+\r
+ def _error(self):\r
+ gui.messageBox(\r
+ # Translators: A message indicating that an error occurred while checking for an update to NVDA.\r
+ _("Error checking for update."),\r
+ # Translators: The title of an error message dialog.\r
+ _("Error"),\r
+ wx.OK | wx.ICON_ERROR)\r
+\r
+ def _result(self, info):\r
+ UpdateResultDialog(gui.mainFrame, info, self.auto)\r
+\r
+class UpdateResultDialog(wx.Dialog):\r
+\r
+ def __init__(self, parent, updateInfo, auto):\r
+ # Translators: The title of the dialog informing the user about an NVDA update.\r
+ super(UpdateResultDialog, self).__init__(parent, title=_("NVDA Update"))\r
+ self.updateInfo = updateInfo\r
+ mainSizer = wx.BoxSizer(wx.VERTICAL)\r
+\r
+ if updateInfo:\r
+ # Translators: A message indicating that an updated version of NVDA is available.\r
+ # {version} will be replaced with the version; e.g. 2011.3.\r
+ message = _("NVDA version {version} is available.").format(**updateInfo)\r
+ else:\r
+ # Translators: A message indicating that no update to NVDA is available.\r
+ message = _("No update available.")\r
+ mainSizer.Add(wx.StaticText(self, label=message))\r
+\r
+ if updateInfo:\r
+ # Translators: The label of a button to download an NVDA update.\r
+ item = wx.Button(self, label=_("&Download update"))\r
+ item.Bind(wx.EVT_BUTTON, self.onDownloadButton)\r
+ mainSizer.Add(item)\r
+\r
+ if auto:\r
+ # Translators: The label of a button to remind the user later about performing some action.\r
+ item = wx.Button(self, label=_("Remind me &later"))\r
+ item.Bind(wx.EVT_BUTTON, self.onLaterButton)\r
+ mainSizer.Add(item)\r
+\r
+ # Translators: The label of a button to close a dialog.\r
+ item = wx.Button(self, wx.ID_CLOSE, label=_("&Close"))\r
+ item.Bind(wx.EVT_BUTTON, lambda evt: self.Close())\r
+ mainSizer.Add(item)\r
+ self.Bind(wx.EVT_CLOSE, lambda evt: self.Destroy())\r
+ self.EscapeId = wx.ID_CLOSE\r
+\r
+ self.Sizer = mainSizer\r
+ mainSizer.Fit(self)\r
+ self.Show()\r
+\r
+ def onDownloadButton(self, evt):\r
+ if config.isInstalledCopy():\r
+ url = self.updateInfo["installerUrl"]\r
+ else:\r
+ url = self.updateInfo["portableUrl"]\r
+ os.startfile(url)\r
+ self.Close()\r
+\r
+ def onLaterButton(self, evt):\r
+ state["dontRemindVersion"] = None\r
+ self.Close()\r