~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to platform/windows-xul/plat/frontends/widgets/application.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Miro - an RSS based video player application
2
 
# Copyright (C) 2005-2010 Participatory Culture Foundation
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17
 
#
18
 
# In addition, as a special exception, the copyright holders give
19
 
# permission to link the code of portions of this program with the OpenSSL
20
 
# library.
21
 
#
22
 
# You must obey the GNU General Public License in all respects for all of
23
 
# the code used other than OpenSSL. If you modify file(s) with this
24
 
# exception, you may extend this exception to your version of the file(s),
25
 
# but you are not obligated to do so. If you do not wish to do so, delete
26
 
# this exception statement from your version. If you delete this exception
27
 
# statement from all source files in the program, then also delete it here.
28
 
 
29
 
import logging
30
 
import os
31
 
import sys
32
 
import traceback
33
 
import webbrowser
34
 
from urlparse import urlparse
35
 
import _winreg
36
 
 
37
 
import gtk
38
 
 
39
 
from miro.gtcache import gettext as _
40
 
from miro import app
41
 
from miro import config
42
 
from miro import eventloop
43
 
from miro import prefs
44
 
from miro.frontends.widgets import dialogs
45
 
from miro.frontends.widgets.application import Application
46
 
from miro.plat import migrateappname
47
 
from miro.plat import clipboard
48
 
from miro.plat import options
49
 
from miro.plat import resources
50
 
from miro.plat.renderers.vlc import VLCRenderer, get_item_type
51
 
from miro.plat.frontends.widgets import xulrunnerbrowser
52
 
from miro.frontends.widgets.gtk import trayicon
53
 
from miro.frontends.widgets.gtk import persistentwindow
54
 
from miro.frontends.widgets.gtk import widgets
55
 
from miro.plat.frontends.widgets import flash
56
 
from miro.plat.frontends.widgets import update
57
 
from miro.plat.frontends.widgets.threads import call_on_ui_thread
58
 
 
59
 
class WindowsApplication(Application):
60
 
    def run(self):
61
 
        logging.info("Python version:    %s", sys.version)
62
 
        logging.info("Gtk+ version:      %s", gtk.gtk_version)
63
 
        logging.info("PyGObject version: %s", gtk.ver)
64
 
        logging.info("PyGtk version:     %s", gtk.pygtk_version)
65
 
 
66
 
        app.video_renderer = app.audio_renderer = VLCRenderer()
67
 
        app.get_item_type = get_item_type
68
 
        self.initXULRunner()
69
 
        gtk.gdk.threads_init()
70
 
        self.startup()
71
 
        gtk.gdk.threads_enter()
72
 
        settings = gtk.settings_get_default()
73
 
        settings.set_property('gtk-theme-name', "MS-Windows")
74
 
        settings.set_property('gtk-tooltip-browse-mode-timeout', 0)
75
 
        # I'm not exactly sure why, but if the previous statement is not
76
 
        # present, we can get segfaults when displaying tooltips
77
 
        try:
78
 
            gtk.main()
79
 
        finally:
80
 
            gtk.gdk.threads_leave()
81
 
        xulrunnerbrowser.shutdown()
82
 
        app.controller.on_shutdown()
83
 
 
84
 
    def on_pref_changed(self, key, value):
85
 
        """Any time a preference changes, this gets notified so that we
86
 
        can adjust things.
87
 
        """
88
 
        if key == options.SHOW_TRAYICON.key:
89
 
            self.trayicon.set_visible(value)
90
 
 
91
 
        elif key == prefs.RUN_AT_STARTUP.key:
92
 
            self.set_run_at_startup(value)
93
 
 
94
 
    def _set_default_icon(self):
95
 
        # we set the icon first (if available) so that it doesn't flash
96
 
        # on when the window is realized in Application.build_window()
97
 
        icopath = os.path.join(resources.appRoot(), "Miro.ico")
98
 
        if config.get(prefs.THEME_NAME) and config.get(options.WINDOWS_ICON):
99
 
            themeIcoPath = resources.theme_path(config.get(prefs.THEME_NAME),
100
 
                                                config.get(options.WINDOWS_ICON))
101
 
            if os.path.exists(themeIcoPath):
102
 
                icopath = themeIcoPath
103
 
                gtk.window_set_default_icon_from_file(icopath)
104
 
        return icopath
105
 
 
106
 
    def build_window(self):
107
 
        icopath = self._set_default_icon()
108
 
        Application.build_window(self)
109
 
        self.window.connect('save-dimensions', self.set_main_window_dimensions)
110
 
        self.window.connect('save-maximized', self.set_main_window_maximized)
111
 
 
112
 
        maximized = self.get_main_window_maximized()
113
 
        if maximized != None:
114
 
            if maximized:
115
 
                self.window._window.maximize()
116
 
            else:
117
 
                self.window._window.unmaximize()
118
 
 
119
 
        config.add_change_callback(self.on_pref_changed)
120
 
 
121
 
        if trayicon.trayicon_is_supported:
122
 
            self.trayicon = trayicon.Trayicon(icopath)
123
 
            if config.get(options.SHOW_TRAYICON):
124
 
                self.trayicon.set_visible(True)
125
 
            else:
126
 
                self.trayicon.set_visible(False)
127
 
        else:
128
 
            logging.info("trayicon is not supported.")
129
 
 
130
 
    def on_close(self):
131
 
        if config.get(prefs.MINIMIZE_TO_TRAY_ASK_ON_CLOSE):
132
 
            ret = dialogs.show_choice_dialog(
133
 
                _("Close to tray?"),
134
 
                _("When you click the red close button, would you like %(appname)s to "
135
 
                  "close to the system tray or quit?  You can change this "
136
 
                  "setting later in the Options.",
137
 
                  {"appname": config.get(prefs.SHORT_APP_NAME)}),
138
 
                (dialogs.BUTTON_QUIT, dialogs.BUTTON_CLOSE_TO_TRAY))
139
 
            config.set(prefs.MINIMIZE_TO_TRAY_ASK_ON_CLOSE, False)
140
 
            if ret == dialogs.BUTTON_CLOSE_TO_TRAY:
141
 
                config.set(prefs.MINIMIZE_TO_TRAY, True)
142
 
            else:
143
 
                config.set(prefs.MINIMIZE_TO_TRAY, False)
144
 
 
145
 
        if config.get(prefs.MINIMIZE_TO_TRAY):
146
 
            self.trayicon.on_click(None)
147
 
        else:
148
 
            self.quit()
149
 
 
150
 
    def quit_ui(self):
151
 
        app.video_renderer.shutdown()
152
 
        for widget in persistentwindow.get_widgets():
153
 
            widget.destroy()
154
 
        if hasattr(self, "trayicon"):
155
 
            self.trayicon.set_visible(False)
156
 
        gtk.main_quit()
157
 
 
158
 
    def get_clipboard_text(self):
159
 
        return clipboard.get_text()
160
 
 
161
 
    def copy_text_to_clipboard(self, text):
162
 
        clipboard.set_text(text)
163
 
 
164
 
    def initXULRunner(self):
165
 
        app_dir = os.path.dirname(sys.executable)
166
 
        xul_dir = os.path.join(app_dir, 'xulrunner')
167
 
        xulrunnerbrowser.initialize(xul_dir, app_dir)
168
 
        xulrunnerbrowser.setup_user_agent(config.get(prefs.LONG_APP_NAME),
169
 
                config.get(prefs.APP_VERSION), config.get(prefs.PROJECT_URL))
170
 
        xulrunnerbrowser.install_window_creator(self)
171
 
 
172
 
    def on_new_window(self, uri):
173
 
        self.open_url(uri)
174
 
 
175
 
    def startup_ui(self):
176
 
        Application.startup_ui(self)
177
 
        call_on_ui_thread(migrateappname.migrateVideos, 'Democracy', 'Miro')
178
 
        call_on_ui_thread(flash.check_flash_install)
179
 
 
180
 
    def open_url(self, url):
181
 
        # It looks like the maximum URL length is about 2k. I can't
182
 
        # seem to find the exact value
183
 
        if len(url) > 2047:
184
 
            url = url[:2047]
185
 
        try:
186
 
            webbrowser.get("windows-default").open_new(url)
187
 
        except:
188
 
            logging.warn("Error opening URL: %r\n%s", url,
189
 
                    traceback.format_exc())
190
 
            recommendURL = config.get(prefs.RECOMMEND_URL)
191
 
 
192
 
            if url.startswith(config.get(prefs.VIDEOBOMB_URL)):
193
 
                title = _('Error Bombing Item')
194
 
            elif url.startswith(recommendURL):
195
 
                title = _('Error Recommending Item')
196
 
            else:
197
 
                title = _("Error Opening Website")
198
 
 
199
 
            scheme, host, path, params, query, fragment = urlparse(url)
200
 
            shortURL = '%s:%s%s' % (scheme, host, path)
201
 
            msg = _(
202
 
                "There was an error opening %(url)s.  Please try again in a few "
203
 
                "seconds",
204
 
                {"url": shortURL}
205
 
            )
206
 
            dialogs.show_message(title, msg, dialogs.WARNING_MESSAGE)
207
 
 
208
 
    def reveal_file(self, fn):
209
 
        if not os.path.isdir(fn):
210
 
            fn = os.path.dirname(fn)
211
 
        os.startfile(fn)
212
 
 
213
 
    def open_file(self, fn):
214
 
        os.startfile(fn)
215
 
 
216
 
    def get_main_window_dimensions(self):
217
 
        max_width = gtk.gdk.screen_width()
218
 
        max_height = gtk.gdk.screen_height()
219
 
        rect = widgets.Rect.from_string(config.get(options.WINDOW_DIMENSIONS))
220
 
        rect.x = max(min(rect.x, max_width - 20), 0)
221
 
        rect.y = max(min(rect.y, max_height - 20), 0)
222
 
        rect.width = max(min(rect.width, max_width), 800)
223
 
        rect.height = max(min(rect.height, max_height - 20), 480)
224
 
        return rect
225
 
 
226
 
    def get_main_window_maximized(self):
227
 
        return config.get(options.WINDOW_MAXIMIZED)
228
 
 
229
 
    def set_main_window_dimensions(self, window, x, y, width, height):
230
 
        config.set(options.WINDOW_DIMENSIONS, "%s,%s,%s,%s" % (x, y, width, height))
231
 
 
232
 
    def set_main_window_maximized(self, window, maximized):
233
 
        config.set(options.WINDOW_MAXIMIZED, maximized)
234
 
 
235
 
    def send_notification(self, title, body,
236
 
                          timeout=5000, attach_trayicon=True):
237
 
        print '--- %s ---' % title
238
 
        print body
239
 
 
240
 
    def set_run_at_startup(self, value):
241
 
        runSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
242
 
        try:
243
 
            folder = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, runSubkey, 0,
244
 
                                     _winreg.KEY_SET_VALUE)
245
 
        except WindowsError, e:
246
 
            if e.errno == 2:
247
 
                # registry key doesn't exist
248
 
                folder = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,
249
 
                                           runSubkey)
250
 
            else:
251
 
                raise
252
 
 
253
 
        if value:
254
 
            # We don't use the app name for the .exe, so branded
255
 
            # versions work
256
 
            filename = os.path.join(resources.root(), "..", "Miro.exe")
257
 
            filename = os.path.normpath(filename)
258
 
            themeName = config.get(prefs.THEME_NAME)
259
 
            if themeName is not None:
260
 
                themeName = themeName.replace("\\", "\\\\").replace('"', '\\"')
261
 
                filename = "%s --theme \"%s\"" % (filename, themeName)
262
 
 
263
 
            _winreg.SetValueEx(folder, config.get(prefs.LONG_APP_NAME), 0,
264
 
                               _winreg.REG_SZ, filename)
265
 
 
266
 
        else:
267
 
            try:
268
 
                _winreg.DeleteValue(folder, config.get(prefs.LONG_APP_NAME))
269
 
            except WindowsError, e:
270
 
                if e.errno == 2:
271
 
                    # registry key doesn't exist, user must have deleted it
272
 
                    # manual
273
 
                    pass
274
 
                else:
275
 
                    raise
276
 
 
277
 
    def handle_first_time(self, callback):
278
 
        self._set_default_icon()
279
 
        Application.handle_first_time(self, callback)
280
 
        
281
 
    def handle_update_available(self, obj, item):
282
 
        call_on_ui_thread(self.show_update_available, item)
283
 
 
284
 
    def show_update_available(self, item):
285
 
        releaseNotes = item.get('description', '')
286
 
        dialog = update.UpdateAvailableDialog(releaseNotes)
287
 
        try:
288
 
            ret = dialog.run()
289
 
 
290
 
            if ret == dialogs.BUTTON_YES:
291
 
                enclosures = [enclosure for enclosure in item['enclosures']
292
 
                              if enclosure['type'] == 'application/octet-stream']
293
 
                downloadURL = enclosures[0]['href']
294
 
                self.open_url(downloadURL)
295
 
        finally:
296
 
            dialog.destroy()