1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
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.
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.
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
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
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.
34
from urlparse import urlparse
39
from miro.gtcache import gettext as _
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
59
class WindowsApplication(Application):
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)
66
app.video_renderer = app.audio_renderer = VLCRenderer()
67
app.get_item_type = get_item_type
69
gtk.gdk.threads_init()
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
80
gtk.gdk.threads_leave()
81
xulrunnerbrowser.shutdown()
82
app.controller.on_shutdown()
84
def on_pref_changed(self, key, value):
85
"""Any time a preference changes, this gets notified so that we
88
if key == options.SHOW_TRAYICON.key:
89
self.trayicon.set_visible(value)
91
elif key == prefs.RUN_AT_STARTUP.key:
92
self.set_run_at_startup(value)
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)
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)
112
maximized = self.get_main_window_maximized()
113
if maximized != None:
115
self.window._window.maximize()
117
self.window._window.unmaximize()
119
config.add_change_callback(self.on_pref_changed)
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)
126
self.trayicon.set_visible(False)
128
logging.info("trayicon is not supported.")
131
if config.get(prefs.MINIMIZE_TO_TRAY_ASK_ON_CLOSE):
132
ret = dialogs.show_choice_dialog(
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)
143
config.set(prefs.MINIMIZE_TO_TRAY, False)
145
if config.get(prefs.MINIMIZE_TO_TRAY):
146
self.trayicon.on_click(None)
151
app.video_renderer.shutdown()
152
for widget in persistentwindow.get_widgets():
154
if hasattr(self, "trayicon"):
155
self.trayicon.set_visible(False)
158
def get_clipboard_text(self):
159
return clipboard.get_text()
161
def copy_text_to_clipboard(self, text):
162
clipboard.set_text(text)
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)
172
def on_new_window(self, uri):
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)
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
186
webbrowser.get("windows-default").open_new(url)
188
logging.warn("Error opening URL: %r\n%s", url,
189
traceback.format_exc())
190
recommendURL = config.get(prefs.RECOMMEND_URL)
192
if url.startswith(config.get(prefs.VIDEOBOMB_URL)):
193
title = _('Error Bombing Item')
194
elif url.startswith(recommendURL):
195
title = _('Error Recommending Item')
197
title = _("Error Opening Website")
199
scheme, host, path, params, query, fragment = urlparse(url)
200
shortURL = '%s:%s%s' % (scheme, host, path)
202
"There was an error opening %(url)s. Please try again in a few "
206
dialogs.show_message(title, msg, dialogs.WARNING_MESSAGE)
208
def reveal_file(self, fn):
209
if not os.path.isdir(fn):
210
fn = os.path.dirname(fn)
213
def open_file(self, fn):
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)
226
def get_main_window_maximized(self):
227
return config.get(options.WINDOW_MAXIMIZED)
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))
232
def set_main_window_maximized(self, window, maximized):
233
config.set(options.WINDOW_MAXIMIZED, maximized)
235
def send_notification(self, title, body,
236
timeout=5000, attach_trayicon=True):
237
print '--- %s ---' % title
240
def set_run_at_startup(self, value):
241
runSubkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
243
folder = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, runSubkey, 0,
244
_winreg.KEY_SET_VALUE)
245
except WindowsError, e:
247
# registry key doesn't exist
248
folder = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,
254
# We don't use the app name for the .exe, so branded
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)
263
_winreg.SetValueEx(folder, config.get(prefs.LONG_APP_NAME), 0,
264
_winreg.REG_SZ, filename)
268
_winreg.DeleteValue(folder, config.get(prefs.LONG_APP_NAME))
269
except WindowsError, e:
271
# registry key doesn't exist, user must have deleted it
277
def handle_first_time(self, callback):
278
self._set_default_icon()
279
Application.handle_first_time(self, callback)
281
def handle_update_available(self, obj, item):
282
call_on_ui_thread(self.show_update_available, item)
284
def show_update_available(self, item):
285
releaseNotes = item.get('description', '')
286
dialog = update.UpdateAvailableDialog(releaseNotes)
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)