4
import sys, os, os.path
9
from sets import Set as set
16
#from trayicon.SonataNotification import TrayIconTips as tooltips
18
if utils.RUNNING_HILDON:
21
#status of the main window progress bar
32
S_MAJOR_DB_OPERATION = 3
45
import FeedList, PlanetView, DownloadView, EntryFormatter
48
import FeedFilterPropertiesDialog
49
import AddSearchTagDialog
50
import EditSearchesDialog
51
import FeedFilterDialog
53
if not utils.RUNNING_HILDON:
54
import SynchronizeDialog
55
import EntryList, EntryView
57
if utils.HAS_GSTREAMER:
58
import GStreamerPlayer
60
class MainWindow(gobject.GObject):
64
COLUMN_STICKY_FLAG = 3
67
'player-show': (gobject.SIGNAL_RUN_FIRST,
70
'player-hide': (gobject.SIGNAL_RUN_FIRST,
75
def __init__(self, app, use_internal_player=False, window=None, status_icon=None):
76
gobject.GObject.__init__(self)
78
self._window_inited = False
79
self._mm = self._app.mediamanager
80
self._glade_prefix = utils.get_glade_prefix()
81
self._widgetTree = None
82
self._menu_widgettree = None
83
self.window_maximized = False
84
self.changing_layout=False
85
self.layout='standard'
86
self._bar_owner = U_NOBODY
87
self._status_owner = U_NOBODY
88
self._state = S_DEFAULT
89
self._fullscreen = False
90
self._fullscreen_lock = False
92
self._use_internal_player = False
93
if utils.HAS_GSTREAMER and use_internal_player:
94
self._use_internal_player = True
96
self._status_icon = status_icon
98
self._active_filter_name = FeedList.BUILTIN_TAGS[FeedList.ALL]
99
self._active_filter_index = FeedList.ALL
100
self._active_filter_path = (0,)
102
if not utils.RUNNING_SUGAR and not utils.RUNNING_HILDON:
103
pixbuf = gtk.gdk.pixbuf_new_from_file(utils.get_image_path('ev_online.png'))
104
source = gtk.IconSource()
105
source.set_pixbuf(pixbuf)
106
source.set_size(gtk.ICON_SIZE_DIALOG)
107
source.set_size_wildcarded(False)
108
self._connected_iconset = gtk.IconSet()
109
self._connected_iconset.add_source(source)
111
pixbuf = gtk.gdk.pixbuf_new_from_file(utils.get_image_path('ev_offline.png'))
112
source = gtk.IconSource()
113
source.set_pixbuf(pixbuf)
114
source.set_size(gtk.ICON_SIZE_DIALOG)
115
source.set_size_wildcarded(False)
116
self._disconnected_iconset = gtk.IconSet()
117
self._disconnected_iconset.add_source(source)
119
##other WINDOWS we open
121
self._window_add_search = AddSearchTagDialog.AddSearchTagDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'extra_dialogs.glade'), "window_add_search_tag",'penguintv'),self._app)
122
self._feed_filter_properties_dialog = FeedFilterPropertiesDialog.FeedFilterPropertiesDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'extra_dialogs.glade'), "window_filter_properties",'penguintv'),self._app)
123
if not utils.RUNNING_SUGAR and not utils.RUNNING_HILDON:
124
self._sync_dialog = SynchronizeDialog.SynchronizeDialog(os.path.join(self._glade_prefix,'extra_dialogs.glade'), self._app)
126
self._window_add_feed = None
127
self._filter_selector_dialog = None
128
self._feed_properties_dialog = None
131
self._app.connect('feed-added', self.__feed_added_cb)
132
self._app.connect('feed-removed', self.__feed_removed_cb)
133
self._app.connect('feed-polled', self.__feed_polled_cb)
134
self._app.connect('download-finished', self.__download_finished_cb)
135
self._app.connect('setting-changed', self.__setting_changed_cb)
136
self._app.connect('tags-changed', self.__tags_changed_cb)
137
self._app.connect('app-loaded', self.__app_loaded_cb)
138
self._app.connect('online-status-changed', self.__online_status_changed_cb)
139
self._app.connect('state-changed', self.__state_changed_cb)
141
#most of the initialization is done on Show()
142
if utils.RUNNING_SUGAR:
143
gobject.idle_add(self.Show, window)
145
def __link_activated_cb(self, o, link):
146
self._app.activate_link(link)
148
def __feed_clicked_cb(self, o):
149
if utils.RUNNING_HILDON:
150
self.feed_tabs.set_current_page(1)
152
def __entrylistview_list_resized_cb(self, entrylistview, new_width):
153
if self.layout == "widescreen" and self.app_window is not None:
154
listnview_width = self.app_window.get_size()[0] - self.feed_pane.get_position()
155
if listnview_width - new_width < 400: #ie, entry view will be tiny
156
self.entry_pane.set_position(listnview_width-400) #MAGIC NUMBER
157
elif new_width > 20: #MAGIC NUMBER
158
self.entry_pane.set_position(new_width)
160
def __feed_added_cb(self, app, feed_id, success):
162
#HACK: we know it will already be selected
163
#self.select_feed(feed_id)
164
self.display_status_message(_("Feed Added"))
165
gobject.timeout_add(2000, self.display_status_message, "")
167
self.display_status_message(_("Error adding feed"))
168
self.select_feed(feed_id)
170
def __feed_polled_cb(self, app, feed_id, update_data):
171
if not update_data.has_key('polling_multiple'):
172
self.display_status_message(_("Feed Updated"))
173
gobject.timeout_add(2000, self.display_status_message, "")
175
def __feed_removed_cb(self, app, feed_id):
176
self.update_filters()
178
def __download_finished_cb(self, app, d):
179
self._download_view.update_downloads()
180
self.update_download_progress()
182
def __setting_changed_cb(self, app, typ, datum, value):
183
if datum == '/apps/penguintv/show_notifications':
184
show_notifs_item = self._menu_widgettree.get_widget('show_notifications')
185
if show_notifs_item.get_active() != value:
186
show_notifs_item.set_active(value)
188
def __tags_changed_cb(self, app, val):
189
self.update_filters()
191
def __app_loaded_cb(self, app):
192
if utils.RUNNING_SUGAR:
193
self._finish_sugar_toolbar()
195
def __online_status_changed_cb(self, app, connected):
197
if self._connection_button:
198
#p = utils.get_image_path('ev_online.png')
200
i.set_from_icon_set(self._connected_iconset, gtk.ICON_SIZE_DIALOG)
201
self._connection_button.set_image(i)
203
if self._connection_button:
204
#p = utils.get_image_path('ev_offline.png')
206
i.set_from_icon_set(self._disconnected_iconset, gtk.ICON_SIZE_DIALOG)
207
self._connection_button.set_image(i)
209
def update_downloads(self):
210
self._download_view.update_downloads()
212
# def __getitem__(self, key):
213
# return self.widgets.get_widget(key)
215
def Show(self, dock_widget = None):
216
"""shows the main window. if given a widget, it will put itself in the widget. otherwise load a regular
217
application window"""
218
#if not utils.HAS_MOZILLA and self.layout.endswith("planet"):
219
# logging.warning("requested planet layout, but can't use because gtkmozembed isn't installed correctly (won't import)")
220
# self.layout = "standard"
221
if utils.RUNNING_SUGAR: #if we are in OLPC mode and just have to supply a widget...
222
self._status_view = None
223
self._disk_usage_widget = None
224
self.app_window = None
227
self._layout_dock = self.load_notebook()
228
self._layout_dock.add(self.load_layout())
229
vbox.pack_start(self._notebook)
230
self._status_view = MainWindow._my_status_view()
231
vbox.pack_start(self._status_view, False, False)
232
dock_widget.set_canvas(vbox)
233
dock_widget.show_all()
235
self.window = dock_widget
237
self._connection_button = None
240
self._widgetTree = gtk.glade.XML(os.path.join(self._glade_prefix,'..','penguintv.glade'), 'toolbar_holder','penguintv')
241
self.toolbar = self._load_sugar_toolbar()
244
for key in dir(self.__class__): #python insaneness
246
self._widgetTree.signal_connect(key, getattr(self, key))
248
self.window.connect('key_press_event', self.on_app_key_press_event)
249
elif utils.RUNNING_HILDON:
250
logging.debug("Hildon: setting up UI")
251
self._h_app = hildon.Program()
252
self.window = hildon.Window()
253
#self.window.set_title("PenguinTV "+utils.VERSION)
254
self.window.set_wmclass("penguintv","penguintv")
256
gtk.set_application_name("PenguinTV "+utils.VERSION)
258
logging.warning("set application name failed, old pymaemo?")
259
self.window.set_icon_from_file(utils.get_image_path('penguintvicon.png'))
261
self._status_view = None
262
self._disk_usage_widget = None
263
self.app_window = None
266
self._layout_dock = self.load_notebook()
267
self._layout_dock.add(self.load_layout())
269
self._status_view = MainWindow._my_status_view()
271
self._connection_button = None
273
logging.debug("Hildon: getting toolbar")
274
self._widgetTree = gtk.glade.XML(os.path.join(self._glade_prefix,'hildon.glade'), 'hildon_toolbar_holder','penguintv')
275
self.toolbar = self._load_toolbar()
276
self.toolbar.unparent()
278
vbox.pack_start(self._notebook)
279
vbox.pack_start(self._status_view, False, False)
281
self.window.add(vbox)
282
self.window.add_toolbar(self.toolbar)
283
self._h_app.add_window(self.window)
285
self._menu_widgettree = gtk.glade.XML(os.path.join(self._glade_prefix,'hildon.glade'), 'hildon_menu','penguintv')
286
menu = self._build_hildon_menu(self._menu_widgettree)
287
self.window.set_menu(menu)
289
show_notifs_item = self._menu_widgettree.get_widget('show_notifications')
290
show_notifs_item.set_active(self._app.db.get_setting(ptvDB.BOOL,
291
'/apps/penguintv/show_notifications', True))
293
self.window.show_all()
295
if not utils.HAS_STATUS_ICON:
296
show_notifs_item.hide()
298
for key in dir(self.__class__): #python insaneness
300
self._widgetTree.signal_connect(key, getattr(self, key))
301
self._menu_widgettree.signal_connect(key, getattr(self, key))
303
self.window.connect('destroy', self.on_app_destroy_event)
304
self.window.connect('delete-event', self.on_app_delete_event)
305
self.window.connect('key_press_event', self.on_app_key_press_event)
306
else: #if we are loading in a regular window...
307
self._load_app_window()
308
if not utils.HAS_SEARCH:
309
#remove UI elements that don't apply without search
310
self._widgetTree.get_widget('saved_searches').hide()
311
self._widgetTree.get_widget('separator11').hide()
312
self._widgetTree.get_widget('reindex_searches').hide()
313
self._widgetTree.get_widget('add_feed_filter').hide()
314
#if not utils.HAS_MOZILLA:
315
#self._widgetTree.get_widget('planet_layout').hide()
316
if not utils.HAS_STATUS_ICON:
317
self._widgetTree.get_widget('show_notifications').hide()
318
self.window = self.app_window
320
if self._use_internal_player:
321
#From Sonata/dbus_plugin.py
325
bus = dbus.SessionBus()
326
dbusObj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
327
dbusInterface = dbus.Interface(dbusObj, 'org.freedesktop.DBus')
328
if dbusInterface.NameHasOwner('org.gnome.SettingsDaemon'):
330
# mmkeys for gnome 2.22+
331
settingsDaemonObj = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
332
settingsDaemonInterface = dbus.Interface(settingsDaemonObj, 'org.gnome.SettingsDaemon.MediaKeys')
333
settingsDaemonInterface.GrabMediaPlayerKeys('PenguinTV', 0)
335
# mmkeys for gnome 2.18+
336
settingsDaemonObj = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon')
337
settingsDaemonInterface = dbus.Interface(settingsDaemonObj, 'org.gnome.SettingsDaemon')
338
settingsDaemonInterface.GrabMediaPlayerKeys('PenguinTV', 0)
339
settingsDaemonInterface.connect_to_signal('MediaPlayerKeyPressed', lambda app, key:self._dbus_mmkeys_cb(app, key))
345
keys = mmkeys.MmKeys()
347
keys.connect("mm_prev", lambda x,y: self._gstreamer_player.prev())
348
keys.connect("mm_next", lambda x,y: self._gstreamer_player.next())
349
keys.connect("mm_playpause", lambda x,y: self._gstreamer_player.play_pause_toggle())
350
keys.connect("mm_stop", lambda x,y: self._gstreamer_player.stop())
352
logging.debug("Multimedia Key Support not found")
355
self._notebook.show_only(N_FEEDS)
356
if not utils.HAS_SEARCH:
357
self.search_container.hide_all()
358
if utils.RUNNING_HILDON:
359
self._layout_components.get_widget('filter_label').hide()
360
self._filter_unread_checkbox.hide()
361
#if not utils.USE_TAGGING:
362
# self._filter_container.hide_all()
363
if self._use_internal_player:
364
if self._gstreamer_player.get_queue_count() > 0:
365
self._notebook.show_page(N_PLAYER)
366
self.emit('player-show')
368
if not self._window_inited:
369
gobject.idle_add(self._app.post_show_init)
370
self._window_inited = True
374
def _dbus_mmkeys_cb(self, app, key):
375
if app == 'PenguinTV':
376
if key in ("Play", "PlayPause", "Pause"):
378
self._app.player.control_internal(key)
380
def _build_hildon_menu(self, widgets):
383
menuitem = widgets.get_widget('file_menu')
385
menu.append(menuitem)
387
menuitem = widgets.get_widget('edit_menu')
389
menu.append(menuitem)
391
menuitem = widgets.get_widget('view_menu')
393
menu.append(menuitem)
395
menuitem = widgets.get_widget('go_menu')
397
menu.append(menuitem)
399
menuitem = widgets.get_widget('feed_menu')
401
menu.append(menuitem)
403
menuitem = widgets.get_widget('help_menu')
405
menu.append(menuitem)
407
separator = gtk.SeparatorMenuItem()
408
menu.append(separator)
410
item = gtk.ImageMenuItem('gtk-close')
411
item.connect('activate', self.on_app_delete_event)
416
def _load_toolbar(self):
417
toolbar = self._widgetTree.get_widget('toolbar1')
419
#set up separator (see below)
420
vseparator = self._widgetTree.get_widget('vseparator1')
421
vseparator_toolitem = self._widgetTree.get_widget('toolitem1')
422
vseparator_toolitem.set_expand(True)
423
vseparator.set_draw(False)
425
self._disk_usage_widget = self._widgetTree.get_widget('disk_usage')
426
self._disk_usage_widget.set_use_markup(True)
430
def _load_sugar_toolbar(self):
431
from sugar.graphics.toolbutton import ToolButton
432
from sugar.graphics.palette import Palette
434
toolbar = gtk.Toolbar()
436
# Add Feed Palette (initialized later when the dialogs are set up)
437
self.sugar_add_button = ToolButton('gtk-add')
438
toolbar.insert(self.sugar_add_button, -1)
439
self.sugar_add_button.show()
442
self._sugar_remove_button = ToolButton('gtk-remove')
444
#vbox.set_size_request(300, 200)
445
label = gtk.Label(_('Really remove feed?'))
446
vbox.pack_start(label)
448
expander_label = gtk.Label(' ')
449
hbox.pack_start(expander_label)
450
b = gtk.Button('gtk-remove')
451
b.set_use_stock(True)
452
b.connect('clicked', self.on_remove_feed_activate, True)
453
hbox.pack_start(b, False)
454
vbox.pack_start(hbox)
455
palette = Palette(_('Remove Feed'))
456
palette.set_content(vbox)
458
self._sugar_remove_button.set_palette(palette)
459
toolbar.insert(self._sugar_remove_button, -1)
460
self._sugar_remove_button.show()
463
b = gtk.ToolButton('gtk-refresh')
464
b.connect('clicked', self.on_feeds_poll_clicked)
465
toolbar.insert(b, -1)
469
b = gtk.ToolButton('gtk-go-down')
470
b.connect('clicked', self.on_download_unviewed_clicked)
471
toolbar.insert(b, -1)
475
sep = gtk.SeparatorToolItem()
476
toolbar.insert(sep, -1)
480
self._sugar_prefs_button = ToolButton('gtk-preferences')
481
toolbar.insert(self._sugar_prefs_button, -1)
482
self._sugar_prefs_button.show()
486
def _finish_sugar_toolbar(self):
487
from sugar.graphics.toolbutton import ToolButton
488
from sugar.graphics.palette import Palette
490
if self._window_add_feed is None:
491
self.show_window_add_feed()
492
self.hide_window_add_feed()
493
content = self._window_add_feed.extract_content()
494
palette = Palette(_('Add Feed'))
495
palette.set_content(content)
496
self.sugar_add_button.set_palette(palette)
498
content = self._app.window_preferences.extract_content()
499
palette = Palette(_('Preferences'))
500
palette.set_content(content)
501
self._sugar_prefs_button.set_palette(palette)
503
class _my_status_view(gtk.HBox):
504
def __init__(self, homogeneous=False, spacing=0):
505
gtk.HBox.__init__(self, homogeneous=False, spacing=0)
506
self._progress = gtk.ProgressBar()
507
sep = gtk.VSeparator()
508
self._status = gtk.Label()
510
self.pack_start(self._progress, False, False)
511
self.pack_start(sep, False, False)
512
self.pack_start(self._status, False, False)
514
def get_status(self):
517
def set_status(self, m):
518
self._status.set_text(m)
519
if utils.RUNNING_HILDON:
525
def set_progress_percentage(self, p):
526
self._progress.set_fraction(p)
527
if utils.RUNNING_HILDON:
529
self._progress.hide()
531
self._progress.show()
533
def get_progress_percentage(self):
534
return self._progress.get_fraction()
536
def _load_app_window(self):
537
self._widgetTree = gtk.glade.XML(os.path.join(self._glade_prefix,'desktop.glade'), 'app','penguintv')
538
self._menu_widgettree = self._widgetTree
540
notebook_dock = self._widgetTree.get_widget('layout_dock')
541
self.app_window = self._widgetTree.get_widget('app')
543
fancy_feedlist_item = self._menu_widgettree.get_widget('fancy_feed_display')
544
fancy_feedlist_item.set_active(self._app.db.get_setting(ptvDB.BOOL,
545
'/apps/penguintv/fancy_feedlist', True))
546
show_notifs_item = self._menu_widgettree.get_widget('show_notifications')
547
show_notifs_item.set_active(self._app.db.get_setting(ptvDB.BOOL,
548
'/apps/penguintv/show_notifications', True))
549
self._widgetTree.get_widget(self.layout+"_layout").set_active(True)
550
self.app_window.set_icon_from_file(utils.get_image_path('penguintvicon.png'))
551
#status_box = self._widgetTree.get_widget("status_hbox")
552
#self._status_view = MainWindow._my_status_view()
553
#status_box.pack_start(self._status_view)
554
self._status_view = self._widgetTree.get_widget('appbar')
557
self._connection_button = self._widgetTree.get_widget('connection_button')
558
#p = utils.get_image_path('ev_online.png')
560
i.set_from_icon_set(self._connected_iconset, gtk.ICON_SIZE_DIALOG)
561
self._connection_button.set_image(i)
564
self._layout_dock = self.load_notebook()
565
notebook_dock.add(self._notebook)
566
self._layout_dock.add(self.load_layout())
568
#sizing for the window comes from gconf
569
x = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/app_window_position_x', 40)
570
y = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/app_window_position_y', 40)
573
self.app_window.move(x,y)
574
w = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/app_window_size_x', 800)
575
h = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/app_window_size_y', 500)
578
self.app_window.maximize()
579
self.window_maximized = True
582
if w<0 or h<0: #very cheesy. negative values really means "maximize"
583
self.app_window.resize(abs(w),abs(h)) #but be good and don't make assumptions about negativity
584
gobject.idle_add(do_maximize)
586
self.app_window.resize(w,h)
588
if self.layout.endswith("planet"):
589
self._menu_widgettree.get_widget('entry_menu_item').hide()
591
self._menu_widgettree.get_widget('entry_menu_item').show()
593
self.app_window.show_all()
595
for key in dir(self.__class__): #python insaneness
597
self._widgetTree.signal_connect(key, getattr(self, key))
599
def show_window_add_feed(self, autolocation=True):
601
if self._window_add_feed is None:
602
if utils.RUNNING_HILDON:
603
self._window_add_feed = AddFeedDialog.AddFeedDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'hildon_dialog_add_feed.glade'), "window_add_feed",'penguintv'),self._app) #MAGIC
605
self._window_add_feed = AddFeedDialog.AddFeedDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'dialog_add_feed.glade'), "window_add_feed",'penguintv'),self._app) #MAGIC
606
self._window_add_feed.show(autolocation)
608
def hide_window_add_feed(self):
609
if self._window_add_feed is None:
610
self.show_window_add_feed()
612
self._window_add_feed.hide()
614
def set_window_add_feed_location(self, url):
615
assert self._window_add_feed is not None
616
self._window_add_feed.set_location(url)
618
def load_notebook(self):
619
self._notebook = NotebookManager()
620
self._notebook.set_property('tab-border',0)
621
if utils.RUNNING_HILDON:
622
label = gtk.Label(_('Feeds'))
624
label = gtk.Label(_('<span size="small">Feeds</span>'))
625
label.set_property('use-markup',True)
627
self._notebook.append_page(vbox, label)
630
if self._use_internal_player:
631
self._gstreamer_player = GStreamerPlayer.GStreamerPlayer(p_vbox, os.path.join(utils.get_home(), "gst_playlist.pickle"), tick_interval=5)
632
self._gstreamer_player.connect('item-queued', self._on_player_item_queued)
633
self._gstreamer_player.connect('items-removed', self._on_player_items_removed)
634
self._gstreamer_player.Show()
635
self.emit('player-show')
636
if utils.RUNNING_HILDON:
637
self._player_label = gtk.Label(_('Player'))
639
self._player_label = gtk.Label('<span size="small">'+_('Player')+'</span>')
640
self._player_label.set_property('use-markup',True)
641
self._notebook.append_page(p_vbox, self._player_label)
643
if utils.RUNNING_HILDON:
644
self._downloads_label = gtk.Label(_('Downloads'))
646
self._downloads_label = gtk.Label(_('<span size="small">Downloads</span>'))
647
self._downloads_label.set_property('use-markup',True)
648
self._download_view = DownloadView.DownloadView(self._app, self._mm, self._app.db, os.path.join(self._glade_prefix,'dialogs.glade'))
649
self._notebook.append_page(self._download_view.get_widget(), self._downloads_label)
651
#self._notebook.set_show_tabs(False)
652
self._notebook.set_property('show-border', False)
653
self._notebook.connect('realize', self._on_notebook_realized)
654
self._notebook.connect('switch-page', self._on_notebook_page_selected)
656
self._notebook.show_all()
659
def get_gst_player(self):
661
return self._gstreamer_player
663
logging.warning("no gstreamer player to get")
666
def notebook_select_page(self, page):
667
self._notebook.set_current_page(page)
669
def load_layout(self):
670
components = gtk.glade.XML(os.path.join(self._glade_prefix,self.layout+'.glade'),
671
self.layout+'_layout_container','penguintv') #MAGIC
672
self._layout_components = components
673
self._layout_container = components.get_widget(self.layout+'_layout_container')
674
#dock_widget.add(self._layout_container)
676
fancy = self._app.db.get_setting(ptvDB.BOOL, '/apps/penguintv/fancy_feedlist', True)
677
if utils.RUNNING_SUGAR:
679
elif utils.RUNNING_HILDON:
682
self.feed_list_view = FeedList.FeedList(components,self._app, fancy)
683
#renderer = EntryFormatter.MOZILLA
684
#renderer = EntryFormatter.GTKHTML
685
if utils.RUNNING_HILDON:
686
renderer = EntryFormatter.GTKHTML
687
elif utils.HAS_MOZILLA:
688
renderer = EntryFormatter.MOZILLA
690
logging.warning("Mozilla (gtkmozembed) not found, falling back to GTKHTML")
691
renderer = EntryFormatter.GTKHTML
693
#if self.layout.endswith("planet") and renderer != EntryFormatter.MOZILLA:
694
# self.layout = "standard"
695
# return self.load_layout()
697
if not self.layout.endswith("planet"):
698
self.entry_list_view = EntryList.EntryList(components, self._app,
699
self.feed_list_view, self)
700
self.entry_view = EntryView.EntryView(components, self.feed_list_view,
701
self.entry_list_view, self._app, self, renderer)
703
#self.entry_view = PlanetView.PlanetView(components, self.feed_list_view,
704
# self._app, self, self._app.db, renderer)
705
self.entry_view = PlanetView.PlanetView(components.get_widget('html_dock'),
706
self, self._app.db, utils.get_share_prefix(),
707
self.feed_list_view, self._app,
709
self.entry_list_view = self.entry_view
711
for key in dir(self.__class__): #python insaneness
713
components.signal_connect(key, getattr(self, key))
716
self.feed_list_view.connect('link-activated', self.__link_activated_cb)
717
self.feed_list_view.connect('feed-clicked', self.__feed_clicked_cb)
719
if not self.layout.endswith("planet"):
720
self.entry_list_view.connect('entrylist-resized', self.__entrylistview_list_resized_cb)
721
#if we connected this in planetview, we'd activate links twice
722
self.entry_list_view.connect('link-activated', self.__link_activated_cb)
724
self.entry_view.connect('link-activated', self.__link_activated_cb)
727
if not utils.RUNNING_HILDON:
728
self.feed_pane = components.get_widget('feed_pane')
730
self.feed_tabs = components.get_widget('feed_tabs')
731
self.feed_tabs.set_current_page(0)
732
self.feed_pane = None
733
self._feedlist = components.get_widget('feedlistview')
734
if self.layout.endswith("planet"):
735
self.entry_pane = self.feed_pane #cheat
737
self.entry_pane = components.get_widget('entry_pane')
739
self._filter_container = components.get_widget('filter_container')
740
self._filter_unread_checkbox = components.get_widget('unread_filter')
741
self._filter_tree = gtk.TreeStore(str, #filter displayable
745
if utils.RUNNING_HILDON:
746
eventbox = components.get_widget('filter_selector_eventbox')
747
self._filter_selector_combo = gtk.ComboBox(self._filter_tree)
748
cell = gtk.CellRendererText()
749
cell.set_property("size-points", 24)
750
self._filter_selector_combo.pack_start(cell, True)
751
self._filter_selector_combo.add_attribute(cell, 'text', 0)
752
self._filter_selector_combo.connect('changed', self.on_filter_changed)
753
eventbox.add(self._filter_selector_combo)
755
self._filter_selector_combo = components.get_widget('filter_selector_combo')
758
filter_filter = self._filter_tree.filter_new()
759
filter_filter.set_visible_column(3)
761
#if not utils.RUNNING_HILDON:
762
self._filter_selector_combo.set_model(filter_filter)
763
self._filter_selector_combo.set_row_separator_func(lambda model,iter:model[iter][2]==1)
765
self._filters = [] #text, text to display, type, tree path
766
self._favorite_filters = [] #text, text to display, type
768
self.search_entry = components.get_widget('search_entry')
769
completion = gtk.EntryCompletion()
770
completion_model = gtk.ListStore(str, str, int) #name, display, index
771
completion.set_model(completion_model)
772
renderer = gtk.CellRendererText()
773
completion.pack_start(renderer)
774
completion.add_attribute(renderer, 'text', 1)
775
def match_func(comp, string, iter):
776
try: return comp.get_model()[iter][0].upper().startswith(string.upper())
778
completion.set_match_func(match_func)
779
#completion.set_text_column(0)
780
completion.connect('match-selected',self._on_completion_match_selected, 2)
781
self.search_entry.set_completion(completion)
782
self.search_container = components.get_widget('search_container')
784
self.update_filters()
787
self._TARGET_TYPE_TEXT = 80
788
self._TARGET_TYPE_URL = 81
789
drop_types = [ ('text/x-moz-url',0,self._TARGET_TYPE_URL),
790
('text/unicode',0,self._TARGET_TYPE_TEXT),
791
('text/plain',0,self._TARGET_TYPE_TEXT)]
792
self._feedlist.drag_dest_set(gtk.DEST_DEFAULT_ALL, drop_types, gtk.gdk.ACTION_COPY)
794
if not self.layout.endswith("planet"):
795
val = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/entry_pane_position', 370)
796
if val < 10: val = 50
797
self.entry_pane.set_position(val)
799
if not utils.RUNNING_HILDON:
801
val = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/feed_pane_position', f_p_default)
804
if self.feed_pane is not None:
805
self.feed_pane.connect('realize', self._on_feed_pane_realized, val)
807
if not self.changing_layout:
808
self.set_active_filter(FeedList.ALL)
810
val = self._app.db.get_setting(ptvDB.STRING, '/apps/penguintv/default_filter')
813
filter_index = [row[F_NAME] for row in self._filters].index(val)
814
cur_filter = self._filters[filter_index]
816
if cur_filter[F_TYPE] == ptvDB.T_SEARCH or filter_index==FeedList.SEARCH:
817
self.set_active_filter(FeedList.ALL)
819
self.set_active_filter(filter_index)
821
self.set_active_filter(filter_index)
822
except ValueError: #didn't find the item in the model (.index(val) fails)
823
self.set_active_filter(FeedList.ALL)
825
self.set_active_filter(FeedList.ALL)
827
self.set_active_filter(self._active_filter_index)
828
#sys.stderr.write("done")
829
return self._layout_container
833
self.app_window.hide()
835
del self.feed_list_view
836
del self.entry_list_view
840
if self.feed_pane is not None:
843
if not self.layout.endswith("planet"):
846
del self._status_view
847
del self._disk_usage_widget
849
def get_parent(self):
852
def on_toggle_fullscreen_activate(self, event=None):
853
self.toggle_fullscreen()
855
def toggle_fullscreen(self):
856
#don't fullscreen under these exceptions
857
if self._notebook.get_current_page() == N_PLAYER:
858
assert self._gstreamer_player is not None
859
#if self._notebook.get_current_page() == N_DOWNLOADS:
862
#maemo throws X Window System errors when doing this -- ignore them
863
#http://labs.morpheuz.eng.br/blog/14/08/2007/xv-and-mplayer-on-maemo/
864
if utils.RUNNING_HILDON:
865
gtk.gdk.error_trap_push()
867
self._fullscreen = not self._fullscreen
869
self._do_fullscreen()
871
self._do_unfullscreen()
873
if utils.RUNNING_HILDON:
874
while gtk.events_pending():
877
gtk.gdk.error_trap_pop()
879
def _do_fullscreen(self):
880
if self._notebook.get_current_page() == N_PLAYER:
881
pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
882
color = gtk.gdk.Color()
883
cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
884
self.window.window.set_cursor(cursor)
885
if self._use_internal_player:
886
if self._gstreamer_player:
887
self._gstreamer_player.toggle_controls(True)
889
self.search_container.hide_all()
891
#elif self._notebook.get_current_page() == N_FEEDS:
892
if self.feed_pane is not None:
893
self._app.db.set_setting(ptvDB.INT, '/apps/penguintv/feed_pane_position', self.feed_pane.get_position())
894
if self.entry_pane is not None:
895
self._app.db.set_setting(ptvDB.INT, '/apps/penguintv/entry_pane_position', self.entry_pane.get_position())
896
if not utils.RUNNING_HILDON:
897
if self.layout.endswith('planet'):
898
self.entry_pane.set_position(0)
900
self.feed_pane.set_position(0)
902
self._notebook.set_keep_hidden(True)
903
self._widgetTree.get_widget('toolbar1').hide()
904
if utils.RUNNING_SUGAR:
905
self._status_view.hide()
906
elif utils.RUNNING_HILDON:
907
self.window.fullscreen()
909
self._widgetTree.get_widget('menubar2').hide()
910
self._widgetTree.get_widget('status_hbox').hide()
911
self._filter_container.hide_all()
912
self.app_window.fullscreen()
914
def _do_unfullscreen(self):
915
if self._fullscreen_lock:
918
self._fullscreen_lock = True
919
#if self._notebook.get_current_page() == N_PLAYER:
920
self.window.window.set_cursor(None)
921
if self._use_internal_player:
922
if self._gstreamer_player is not None:
923
self._gstreamer_player.toggle_controls(False)
926
self.search_container.show_all()
928
#elif self._notebook.get_current_page() == N_FEEDS:
929
if not utils.RUNNING_HILDON:
930
if self.layout.endswith('planet'):
931
val = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/entry_pane_position', 370)
932
self.entry_pane.set_position(val)
934
val = self._app.db.get_setting(ptvDB.INT, '/apps/penguintv/feed_pane_position', 370)
935
self.feed_pane.set_position(val)
937
self._notebook.set_keep_hidden(False)
939
#don't show the toolbar if we are on hildon and we are in the player
940
pagenum = self._notebook.get_current_page()
941
if not utils.RUNNING_HILDON or not pagenum == N_PLAYER:
942
self._widgetTree.get_widget('toolbar1').show_all()
944
def _unfullscreen_finish():
945
self.app_window.unfullscreen()
946
self._fullscreen_lock = False
949
if utils.RUNNING_SUGAR:
950
self._status_view.show()
951
self._fullscreen_lock = False
952
elif utils.RUNNING_HILDON:
953
self.window.unfullscreen()
954
self._fullscreen_lock = False
956
self._widgetTree.get_widget('menubar2').show_all()
957
self._widgetTree.get_widget('status_hbox').show_all()
958
self._filter_container.show_all()
959
gobject.idle_add(_unfullscreen_finish)
961
def on_about_activate(self,event):
962
widgets = gtk.glade.XML(os.path.join(self._glade_prefix,'dialogs.glade'), "aboutdialog1",'penguintv')
963
about_box = widgets.get_widget('aboutdialog1')
964
about_box.set_name('PenguinTV')
965
about_box.set_version(utils.VERSION)
966
about_box.connect('response', self.on_about_response)
969
def on_about_response(self, widget, event):
972
def on_app_delete_event(self, event, data=None):
976
if not self._app.is_quit_complete():
978
logging.debug('hildon main_quit')
982
if utils.RUNNING_HILDON:
983
gobject.timeout_add(250, gtkquit)
985
return self.window.hide_on_delete()
987
def on_app_destroy_event(self,event,data=None):
988
if utils.RUNNING_HILDON:
993
def on_app_window_state_event(self, client, event):
994
if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED:
995
self.window_maximized = True
996
elif event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED == 0:
997
self.window_maximized = False
999
def on_add_feed_activate(self, event=None):
1000
if self._state == S_MAJOR_DB_OPERATION:
1001
logging.warning("Please wait until feeds have loaded before adding a new one")
1003
self._notebook.set_current_page(N_FEEDS)
1004
self.show_window_add_feed() #not modal / blocking
1006
def on_add_feed_filter_activate(self,event):
1007
selected = self.feed_list_view.get_selected()
1009
title = self._app.db.get_feed_title(selected)
1010
dialog = FeedFilterDialog.FeedFilterDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'extra_dialogs.glade'), "window_feed_filter",'penguintv'),self._app)
1012
dialog.set_pointed_feed(selected,title)
1013
d = { 'title':title }
1014
dialog.set_filter_name(_("%(title)s Filtered" % d))
1016
dialog = gtk.Dialog(title=_("No Feed Selected"), parent=None, flags=gtk.DIALOG_MODAL, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
1017
label = gtk.Label(_("Please select the feed you would like to filter"))
1018
dialog.vbox.pack_start(label, True, True, 0)
1020
dialog.set_transient_for(self._app.main_window.get_parent())
1021
response = dialog.run()
1025
def on_connection_button_clicked(self, event):
1026
self._app.toggle_net_connection()
1028
def pane_to_feeds(self):
1029
self.feed_tabs.set_current_page(0)
1031
def on_feed_add_clicked(self, event):
1032
if self._state == S_MAJOR_DB_OPERATION:
1033
logging.warning("Please wait until feeds have loaded before adding a new one")
1035
self.show_window_add_feed() #not modal / blocking
1037
#def on_feed_pane_expose_event(self, widget, event):
1038
# self.feed_list_view.resize_columns(self.feed_pane.get_position())
1040
def on_feed_properties_activate(self, event):
1041
import FeedPropertiesDialog
1042
selected = self.feed_list_view.get_selected()
1044
#title, description, url, link
1045
feed_info = self._app.db.get_feed_info(selected)
1046
if self._feed_properties_dialog is None:
1047
if utils.RUNNING_HILDON:
1048
self._feed_properties_dialog = FeedPropertiesDialog.FeedPropertiesDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'hildon_dialogs.glade'), "window_feed_properties",'penguintv'),self._app)
1050
self._feed_properties_dialog = FeedPropertiesDialog.FeedPropertiesDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'dialogs.glade'), "window_feed_properties",'penguintv'),self._app)
1051
self._feed_properties_dialog.set_feedid(selected)
1052
self._feed_properties_dialog.set_title(feed_info['title'])
1053
self._feed_properties_dialog.set_rss(feed_info['url'])
1054
self._feed_properties_dialog.set_description(feed_info['description'])
1055
self._feed_properties_dialog.set_link(feed_info['link'])
1056
self._feed_properties_dialog.set_last_poll(feed_info['lastpoll'])
1057
self._feed_properties_dialog.set_tags(self._app.db.get_tags_for_feed(selected))
1058
self._feed_properties_dialog.set_flags(self._app.db.get_flags_for_feed(selected))
1059
if self._app.feed_refresh_method == penguintv.REFRESH_AUTO:
1060
self._feed_properties_dialog.set_next_poll(feed_info['lastpoll']+feed_info['pollfreq'])
1062
self._feed_properties_dialog.set_next_poll(feed_info['lastpoll']+self._app.polling_frequency)
1063
self._feed_properties_dialog.show()
1065
def on_feed_filter_properties_activate(self, event):
1066
selected = self.feed_list_view.get_selected()
1068
#title, description, url, link
1069
feed_info = self._app.db.get_feed_info(selected)
1070
self._feed_filter_properties_dialog.set_feed_id(selected)
1071
self._feed_filter_properties_dialog.set_pointed_feed_id(feed_info['feed_pointer'])
1072
self._feed_filter_properties_dialog.set_filter_name(feed_info['title'])
1073
self._feed_filter_properties_dialog.set_query(feed_info['description'])
1074
self._feed_filter_properties_dialog.show()
1076
def on_download_entry_activate(self, event):
1077
entry = self.entry_list_view.get_selected()['entry_id']
1078
self._app.download_entry(entry)
1080
def on_download_unviewed_activate(self, event):
1081
self._app.download_unviewed()
1083
def on_download_unviewed_clicked(self,event):
1084
self._app.download_unviewed()
1086
def on_delete_entry_media_activate(self,event):
1087
selected = self.entry_list_view.get_selected()['entry_id']
1088
self._app.delete_entry_media(selected)
1090
def on_delete_feed_media_activate(self,event):
1091
selected = self.feed_list_view.get_selected()
1093
self._app.delete_feed_media(selected)
1095
def on_edit_tags_for_all_activate(self, event):
1096
"""Bring up mass tag creation window"""
1098
window_edit_tags_multi = TagEditorNG.TagEditorNG(gtk.glade.XML(os.path.join(self._glade_prefix,'dialogs.glade'), "dialog_tag_editor_ng",'penguintv'), self._app)
1099
window_edit_tags_multi.show()
1101
def on_export_opml_activate(self, event):
1102
self._app.export_opml()
1104
def _on_feed_pane_realized(self, widget, val):
1105
widget.set_position(val)
1107
def on_feedlistview_drag_data_received(self, widget, context, x, y, selection, targetType, time):
1108
widget.emit_stop_by_name('drag-data-received')
1109
if targetType == self._TARGET_TYPE_TEXT:
1111
for c in selection.data:
1112
if c != "\0": #for some reason ever other character is a null. what gives?
1114
if url.split(':')[0] == 'feed':
1115
url = url[url.find(':')+1:]
1116
self._app.add_feed(url)
1117
elif targetType == self._TARGET_TYPE_URL:
1119
for c in selection.data[0:selection.data.find('\n')]:
1122
if url.split(':')[0] == 'feed': #stupid wordpress does 'feed:http://url.com/whatever'
1123
url = url[url.find(':')+1:]
1124
self._app.add_feed(url, url)
1126
def on_feeds_poll_clicked(self,event):
1127
self._app.poll_feeds()
1129
def set_hide_entries_menuitem(self, state):
1130
self._menu_widgettree.get_widget('hide_viewed_entries_cb').set_active(state)
1132
def set_hide_entries_visibility(self, state):
1134
self._menu_widgettree.get_widget('hide_viewed_entries_cb').show()
1136
self._menu_widgettree.get_widget('hide_viewed_entries_cb').hide()
1138
def on_hide_entries_cb_toggled(self, event):
1139
self.entry_list_view.set_hide_viewed(self._menu_widgettree.get_widget('hide_viewed_entries_cb').get_active())
1141
def on_hide_feeds_cb_toggled(self, checkbox):
1142
status = checkbox.get_active()
1143
self.feed_list_view.set_unread_toggle(status)
1144
self._menu_widgettree.get_widget('hide_viewed_feeds_cb').set_active(status)
1145
self._filter_unread_checkbox.set_active(status)
1147
def on_synchronize_button_clicked(self,event):
1148
self._sync_dialog.hide()
1149
self._sync_dialog.on_sync_button_clicked(event)
1151
def on_edit_favorite_tags(self, o=None):
1152
import FilterSelectorDialog
1153
if self._filter_selector_dialog is None:
1154
self._filter_selector_dialog = FilterSelectorDialog.FilterSelectorDialog(gtk.glade.XML(os.path.join(self._glade_prefix,'dialogs.glade'), "dialog_tag_favorites",'penguintv'),self)
1155
self._filter_selector_dialog.set_taglists(self._filters, self._favorite_filters)
1156
self._filter_selector_dialog.Show()
1158
def on_filter_changed(self, widget):
1159
model = widget.get_model()
1161
it = widget.get_active_iter()
1165
#if this is the edit tags menu item...
1166
if model[it][2] == 2:
1167
self.on_edit_favorite_tags()
1168
self._filter_selector_combo.set_active_iter(model.get_iter(self._active_filter_path))
1171
if model[it][1] == _('All Tags'):
1174
names = [f[F_NAME] for f in self._filters]
1175
index = names.index(model[it][1])
1177
if self._active_filter_index == index and not self.changing_layout:
1179
self._active_filter_name = model[it][1]
1180
self._active_filter_index = index
1181
self._active_filter_path = model.get_path(it)
1183
if utils.HAS_SEARCH and index == FeedList.SEARCH:
1184
self._filter_tree[FeedList.SEARCH][3] = True
1186
self._filter_tree[FeedList.SEARCH][3] = False
1188
if utils.HAS_STATUS_ICON:
1189
self._filter_tree[FeedList.NOTIFY][3] = True
1191
self._filter_tree[FeedList.NOTIFY][3] = False
1195
self._activate_filter()
1197
def _find_path(self, index):
1198
model = self._filter_selector_combo.get_model()
1199
name = self._filters[index][F_NAME]
1200
self._active_filter_path = None
1201
#if utils.RUNNING_HILDON:
1202
# #not a tree, so some filters appear twice. Need to select first
1203
# #instance, which foreach does not do easily
1205
# if row[1] == name:
1206
# self._active_filter_path = row.path
1209
def hunt_path(model, p, it):
1210
if model[it][1] == name and self._active_filter_path is None:
1211
self._active_filter_path = p
1212
model.foreach(hunt_path)
1214
def set_active_filter(self, index):
1215
model = self._filter_selector_combo.get_model()
1216
if utils.HAS_SEARCH and index == FeedList.SEARCH:
1217
self._filter_tree[FeedList.SEARCH][3] = True
1219
self._filter_tree[FeedList.SEARCH][3] = False
1221
if utils.HAS_STATUS_ICON:
1222
self._filter_tree[FeedList.NOTIFY][3] = True
1224
self._filter_tree[FeedList.NOTIFY][3] = False
1228
self._find_path(index)
1229
it = model.get_iter(self._active_filter_path)
1230
self._filter_selector_combo.set_active_iter(it)
1232
def _activate_filter(self):
1233
current_filter = self._filters[self._active_filter_index]
1234
if current_filter[F_TYPE] == ptvDB.T_SEARCH and self._state == S_MAJOR_DB_OPERATION:
1235
self.set_active_filter(FeedList.ALL)
1237
self._app.change_filter(current_filter[F_NAME],current_filter[F_TYPE])
1239
def on_import_opml_activate(self, event):
1240
if utils.RUNNING_HILDON:
1241
dialog = hildon.FileChooserDialog(self.window, action=gtk.FILE_CHOOSER_ACTION_OPEN)
1243
dialog = gtk.FileChooserDialog(_('Select OPML...'),None, action=gtk.FILE_CHOOSER_ACTION_OPEN,
1244
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
1245
dialog.set_default_response(gtk.RESPONSE_OK)
1247
filter = gtk.FileFilter()
1248
filter.set_name("OPML files")
1249
filter.add_pattern("*.opml")
1250
dialog.add_filter(filter)
1252
filter = gtk.FileFilter()
1253
filter.set_name("All files")
1254
filter.add_pattern("*")
1255
dialog.add_filter(filter)
1257
dialog.set_transient_for(self._app.main_window.get_parent())
1259
response = dialog.run()
1260
if response == gtk.RESPONSE_OK:
1261
f = open(dialog.get_filename(), "r")
1262
self.display_status_message(_("Importing Feeds, please wait..."))
1263
self._app.import_subscriptions(f)
1264
elif response == gtk.RESPONSE_CANCEL:
1265
logging.info('Closed, no files selected')
1268
def on_app_key_press_event(self, widget, event):
1269
keyname = gtk.gdk.keyval_name(event.keyval)
1270
#if event.state & gtk.gdk.CONTROL_MASK:
1271
# if keyname == 'k':
1272
# self.search_entry.grab_focus()
1274
if event.state & gtk.gdk.MOD1_MASK:
1276
self._notebook.set_current_page(N_FEEDS)
1277
elif keyname == '2':
1278
if self._notebook.is_showing(N_PLAYER):
1279
self._notebook.set_current_page(N_PLAYER)
1280
elif keyname == '3':
1281
if self._notebook.is_showing(N_DOWNLOADS):
1282
self._notebook.set_current_page(N_DOWNLOADS)
1284
if utils.RUNNING_SUGAR:
1285
if keyname == 'KP_Left' or keyname == 'Left' or keyname == 'KP_4':
1286
self.feed_list_view.grab_focus()
1287
elif keyname == 'KP_Right' or keyname == 'Right' \
1288
or keyname == 'KP_6':
1289
self.entry_view.grab_focus()
1290
elif utils.RUNNING_HILDON:
1291
#Move up Arrow key up GDK_Up
1292
#Move down Arrow key down GDK_Down
1293
#Move left Arrow key left GDK_Left
1294
#Move right Arrow key right GDK_Right
1295
#Select, Confirm Return GDK_Return
1296
#Cancel, Close Esc GDK_Escape
1297
#Open menu F4 GDK_F4
1298
#Full screen F6 GDK_F6
1299
#Increase / Zoom in / Volume up F7 GDK_F7
1300
#Decrease / Zoom out / Volume down F8 GDK_F8
1302
if keyname == 'KP_Left' or keyname == 'Left':
1303
self.feed_list_view.grab_focus()
1304
elif keyname == 'KP_Right' or keyname == 'Right':
1305
self.entry_view.grab_focus()
1306
elif keyname == 'F6':
1307
self.toggle_fullscreen()
1308
elif keyname == 'F7':
1309
if self._gstreamer_player is not None:
1310
self._gstreamer_player.vol_up()
1311
elif keyname == 'F8':
1312
if self._gstreamer_player is not None:
1313
self._gstreamer_player.vol_down()
1314
elif keyname == 'Escape':
1315
self.feed_tabs.set_current_page(0)
1316
else: #regular desktop version..
1317
if keyname == 'F11':
1318
self.toggle_fullscreen()
1319
#the key press will also trigger the accelerator once the menu
1320
#comes back -- stop it
1321
widget.stop_emission("key-press-event")
1323
if self._use_internal_player and self._notebook.get_current_page() == N_PLAYER:
1324
#if gstreamer can do something with this key, stop further
1326
if self._gstreamer_player.handle_key(keyname):
1327
widget.stop_emission("key-press-event")
1328
if keyname == "F" and event.state & gtk.gdk.CONTROL_MASK:
1329
self._gstreamer_player.ff()
1330
elif keyname == "B" and event.state & gtk.gdk.CONTROL_MASK:
1331
self._gstreamer_player.rew()
1333
def on_mark_entry_as_viewed_activate(self,event):
1334
entry = self.entry_list_view.get_selected()
1335
self._app.mark_entry_as_viewed(entry['entry_id'], entry['feed_id'])
1337
def on_mark_entry_as_unviewed_activate(self,event):
1338
entry = self.entry_list_view.get_selected()['entry_id']
1339
self._app.mark_entry_as_unviewed(entry)
1341
def on_keep_entry_new_activate(self, event):
1342
entry = self.entry_list_view.get_selected()['entry_id']
1343
self._app.activate_link("keep:%i" % (entry,))
1345
def on_unkeep_entry_new_activate(self, event):
1346
entry = self.entry_list_view.get_selected()['entry_id']
1347
self._app.activate_link("unkeep:%i" % (entry,))
1349
def on_mark_feed_as_viewed_activate(self, button=None, event=None):
1350
feed = self.feed_list_view.get_selected()
1352
self._app.mark_feed_as_viewed(feed)
1354
def on_mark_all_viewed_activate(self, event):
1355
self._app.mark_all_viewed()
1357
def _on_notebook_realized(self, widget):
1358
self._notebook.show_page(N_FEEDS)
1359
if not utils.HAS_SEARCH:
1360
self.search_container.hide_all()
1361
#if utils.RUNNING_SUGAR:
1362
# self._filter_container.hide_all()
1364
if self._use_internal_player:
1365
self._gstreamer_player.load()
1366
if self._gstreamer_player.get_queue_count() > 0:
1367
self._notebook.show_page(N_PLAYER)
1368
self.emit('player-show')
1370
def _on_notebook_page_selected(self, widget, page, pagenum):
1371
if utils.RUNNING_HILDON:
1372
if pagenum == N_PLAYER:
1377
#def _on_gst_player_realized(self, widget):
1378
# print "seek seek seek"
1379
# self._gstreamer_player.seek_to_saved_position()
1381
def on_play_entry_activate(self, event):
1382
entry = self.entry_list_view.get_selected()['entry_id']
1383
self._app.play_entry(entry)
1385
def on_play_unviewed_activate(self, event):
1386
self._app.play_unviewed()
1388
def on_play_unviewed_clicked(self, event):
1389
self._app.play_unviewed()
1391
def _on_player_item_queued(self, player, filename, name, userdata):
1392
self._notebook.show_page(N_PLAYER)
1393
self.emit('player-show')
1394
#if player.get_queue_count() == 1:
1396
# self._notebook.set_current_page(N_PLAYER)
1399
# pass #fails while loading
1400
if utils.RUNNING_HILDON:
1401
self._player_label.set_markup(_('Player (%d)') % player.get_queue_count())
1403
self._player_label.set_markup(_('<span size="small">Player (%d)</span>') % player.get_queue_count())
1404
#if self._state != S_MAJOR_DB_OPERATION:
1405
# tip = tooltips(self._player_label)
1406
# tip.display_notification("title", "text")
1408
def _on_player_items_removed(self, player):
1409
if player.get_queue_count() == 0:
1410
self._notebook.hide_page(N_PLAYER)
1411
self.emit('player-hide')
1413
if utils.RUNNING_HILDON:
1414
self._player_label.set_markup(_('Player (%d)') % player.get_queue_count())
1416
self._player_label.set_markup(_('<span size="small">Player (%d)</span>') % player.get_queue_count())
1418
def on_preferences_activate(self, event):
1419
self._app.window_preferences.show()
1421
def on_quit2_activate(self,event):
1422
self._app.do_quit() #make the program quit, dumbass
1423
##DEBUG for exit_toolbutton
1424
#if utils.RUNNING_SUGAR:
1427
def on_refresh_activate(self, event):
1428
feed = self.feed_list_view.get_selected()
1429
self._app.refresh_feed(feed)
1431
def on_refresh_feeds_activate(self, event):
1432
self._app.poll_feeds()
1434
def on_refresh_feeds_with_errors_activate(self, event):
1435
self._app.poll_feeds(ptvDB.A_ERROR_FEEDS)
1437
def on_refresh_visible_feeds_activate(self, event):
1438
if self._active_filter_index > FeedList.SEARCH:
1439
feeds = self._app.db.get_feeds_for_tag(self._active_filter_name)
1440
self._app.do_poll_multiple(None, ptvDB.A_IGNORE_ETAG, feeds,
1441
message=_("Refreshing %s..." % self._active_filter_name))
1442
elif utils.RUNNING_HILDON:
1443
self._app.do_poll_multiple(None, ptvDB.A_IGNORE_ETAG)
1445
def on_reindex_searches_activate(self, event):
1446
self.search_container.set_sensitive(False)
1447
self._app.set_state(penguintv.DEFAULT)
1448
self.search_entry.set_text(_("Please wait..."))
1449
self._app.db.doindex(self._app._done_populating)
1451
def _sensitize_search(self):
1452
self.search_entry.set_text("")
1453
self.search_container.set_sensitive(True)
1455
def on_remove_feed_activate(self, event, override=False):
1456
assert self._state != S_MAJOR_DB_OPERATION
1458
selected = self.feed_list_view.get_selected()
1460
self._notebook.set_current_page(N_FEEDS)
1462
dialog = gtk.Dialog(title=_("Really Remove Feed?"), parent=None, flags=gtk.DIALOG_MODAL, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_REMOVE, gtk.RESPONSE_ACCEPT))
1463
label = gtk.Label(_("Are you sure you want to remove this feed, all its entries, and all its media? \nThis operation cannot be undone."))
1464
dialog.vbox.pack_start(label, True, True, 0)
1466
dialog.set_transient_for(self._app.main_window.get_parent())
1467
response = dialog.run()
1470
if response != gtk.RESPONSE_ACCEPT:
1472
self._app.remove_feed(selected)
1474
def on_resume_all_activate(self, event):
1475
self._app.resume_resumable()
1477
def on_save_search_clicked(self, event):
1478
query = self.search_entry.get_text()
1481
self._window_add_search.show()
1482
self._window_add_search.set_query(query)
1484
def on_search_clear_clicked(self, event):
1485
self._app.set_state(penguintv.DEFAULT)
1487
def on_saved_searches_activate(self, event):
1488
window_edit_saved_searches = EditSearchesDialog.EditSearchesDialog(os.path.join(self._glade_prefix,'extra_dialogs.glade'),self._app)
1489
window_edit_saved_searches.show()
1490
del window_edit_saved_searches
1492
def on_search_entry_activate(self, event):
1493
self._app.manual_search(self.search_entry.get_text())
1495
def on_search_entry_changed(self, widget):
1497
#self.search_entry.get_completion().complete()
1498
#if self.search_container.get_property("sensitive"):
1499
# self._app.threaded_search(self.search_entry.get_text())
1501
def on_show_downloads_activate(self, event):
1502
self._app.show_downloads()
1504
def on_stop_downloads_clicked(self, widget):
1505
self._app.stop_downloads()
1507
#def on_stop_downloads_toggled(self, widget):
1509
# self._app.stop_downloads_toggled(widget.get_active())
1511
def on_synchronize_activate(self, event):
1512
self._sync_dialog.Show()
1514
def on_standard_layout_activate(self, event):
1515
self._app.change_layout('standard')
1517
def on_horizontal_layout_activate(self, event):
1518
self._app.change_layout('widescreen')
1520
def on_vertical_layout_activate(self,event):
1521
self._app.change_layout('vertical')
1523
def on_planet_layout_activate(self, event):
1524
self._app.change_layout('planet')
1526
def on_fancy_feed_display_activate(self, menuitem):
1527
self.feed_list_view.set_fancy(menuitem.get_active())
1528
self._app.db.set_setting(ptvDB.BOOL, '/apps/penguintv/fancy_feedlist', menuitem.get_active())
1530
def on_show_notifications_activate(self, menuitem):
1531
self._app.db.set_setting(ptvDB.BOOL, '/apps/penguintv/show_notifications',
1532
menuitem.get_active())
1534
def activate_layout(self, layout):
1535
"""gets called by app when it's ready"""
1536
self.changing_layout = True
1538
self._app.save_settings()
1539
self._app.write_feed_cache()
1540
self._layout_dock.remove(self._layout_container)
1542
self._layout_dock.add(self.load_layout())
1543
self.entry_view.post_show_init()
1544
if self.layout.endswith("planet"):
1545
self._menu_widgettree.get_widget('entry_menu_item').hide()
1547
self._menu_widgettree.get_widget('entry_menu_item').show()
1549
self._notebook.show_only(N_FEEDS)
1550
if not utils.HAS_SEARCH:
1551
self.search_container.hide_all()
1552
if utils.RUNNING_HILDON:
1553
self._layout_components.get_widget('filter_label').hide()
1554
self._filter_unread_checkbox.hide()
1555
#if not utils.USE_TAGGING:
1556
# self._filter_container.hide_all()
1557
if self._use_internal_player:
1558
if self._gstreamer_player.get_queue_count() > 0:
1559
self._notebook.show_page(N_PLAYER)
1560
self.emit('player-show')
1561
#can't reset changing_layout because app hasn't updated pointers yet
1563
def is_changing_layout(self):
1564
return self.changing_layout
1566
def display_status_message(self, m, update_category=U_STANDARD):
1567
"""displays a status message on the main status bar. If this is a polling update or download
1568
update, we don't overwrite what's there."""
1569
if self._status_view is None:
1572
current_text = self._status_view.get_status().get_text()
1574
if current_text == "":
1575
self._status_owner = update_category
1576
self._status_view.set_status(m)
1577
if utils.HAS_STATUS_ICON:
1578
self._status_icon.set_tooltip(m)
1580
if update_category >= self._status_owner:
1581
self._status_view.set_status(m)
1582
if utils.HAS_STATUS_ICON:
1583
self._status_icon.set_tooltip(m)
1585
self._status_owner = U_NOBODY
1587
self._status_owner = update_category
1588
#if update_category==U_STANDARD: #only overwrite if this is not a poll or download
1589
# self._status_owner = update_category
1590
# self._status_view.set_status(m)
1591
#elif update_category == U_POLL and self._status_owner != U_STANDARD:
1592
# self._status_owner = update_category
1593
# self._status_view.set_status(m)
1594
#elif update_category == U_DOWNLOAD and self._status_owner == U_DOWNLOAD:
1595
# self._status_view.set_status(m)
1597
return False #in case of timeouts
1599
def update_progress_bar(self, p, update_category=U_STANDARD):
1600
"""Update the progress bar. if both downloading and polling, polling wins"""
1603
self._bar_owner = U_NOBODY
1604
self._status_view.set_progress_percentage(0)
1606
if update_category >= self._bar_owner:
1607
self._bar_owner = update_category
1608
self._status_view.set_progress_percentage(p)
1610
def _unset_state(self):
1611
"""gets app ready to display new state by unloading current state"""
1612
#bring state back to default
1613
if self._state == S_MANUAL_SEARCH:
1614
self.search_entry.set_text("")
1615
if self._state == S_MAJOR_DB_OPERATION:
1616
self._widgetTree.get_widget("feed_add_button").set_sensitive(True)
1617
self._widgetTree.get_widget("feed_remove").set_sensitive(True)
1618
if not utils.RUNNING_SUGAR:
1619
#these are menu items
1620
self._menu_widgettree.get_widget("add_feed").set_sensitive(True)
1621
self._menu_widgettree.get_widget("remove_feed").set_sensitive(True)
1622
self._menu_widgettree.get_widget("properties").set_sensitive(True)
1623
#elif not utils.USE_TAGGING:
1624
# self._menu_widgettree.get_widget("add_feed_filter").set_sensitive(True)
1626
self.display_status_message("")
1627
self.update_progress_bar(-1,U_LOADING)
1629
def __state_changed_cb(self, app, new_state, data=None):
1630
d = {penguintv.DEFAULT: S_DEFAULT,
1631
penguintv.MANUAL_SEARCH: S_MANUAL_SEARCH,
1632
penguintv.TAG_SEARCH: S_TAG_SEARCH,
1633
#penguintv.ACTIVE_DOWNLOADS: S_DEFAULT,
1634
penguintv.MAJOR_DB_OPERATION: S_MAJOR_DB_OPERATION}
1636
new_state = d[new_state]
1637
if self._state == new_state:
1641
if new_state == S_MANUAL_SEARCH:
1642
if self.get_active_filter()[1] != FeedList.SEARCH:
1643
self.set_active_filter(FeedList.SEARCH)
1644
if new_state == S_TAG_SEARCH:
1645
self.search_entry.set_text("")
1647
if new_state == S_MAJOR_DB_OPERATION:
1648
self._widgetTree.get_widget("feed_add_button").set_sensitive(False)
1649
self._widgetTree.get_widget("feed_remove").set_sensitive(False)
1651
if not utils.RUNNING_SUGAR:
1652
#these are menu items
1653
self._menu_widgettree.get_widget("add_feed").set_sensitive(False)
1654
self._menu_widgettree.get_widget("remove_feed").set_sensitive(False)
1655
self._menu_widgettree.get_widget("properties").set_sensitive(False)
1656
#elif not utils.USE_TAGGING:
1657
# self._menu_widgettree.get_widget("add_feed_filter").set_sensitive(False)
1659
self._state = new_state
1661
def update_filters(self):
1662
"""update the filter combo box with the current list of filters"""
1663
#get name of current filter, if a tag
1664
current_filter = self.get_active_filter()[0]
1666
self._favorite_filters = []
1667
self._filter_tree.clear()
1668
completion_model = self.search_entry.get_completion().get_model()
1669
completion_model.clear()
1671
i=0 #we set i here so that searches and regular tags have incrementing ids
1673
builtin = _("All Feeds")
1674
text = builtin+" ("+str(len(self._app.db.get_feedlist()))+")"
1675
self._filters.append([0,builtin,text,ptvDB.T_BUILTIN])
1676
self._filter_tree.append(None, [text, builtin, 0, True])
1679
builtin = _("Downloaded Media")
1680
self._filters.append([0,builtin,builtin,ptvDB.T_BUILTIN])
1681
self._filter_tree.append(None, [builtin, builtin, 0, True])
1684
builtin = _("Notifying Feeds")
1685
text = builtin+" ("+str(len(self._app.db.get_feeds_for_flag(ptvDB.FF_NOTIFYUPDATES)))+")"
1686
self._filters.append([0,builtin,text,ptvDB.T_BUILTIN])
1687
self._filter_tree.append(None, [text, builtin, 0, utils.HAS_STATUS_ICON])
1690
builtin = _("Search Results")
1691
self._filters.append([0,builtin,builtin,ptvDB.T_BUILTIN])
1692
self._search_iter = self._filter_tree.append(None, [builtin, builtin, 0, False])
1696
if utils.HAS_SEARCH:
1697
tags = self._app.db.get_all_tags(ptvDB.T_SEARCH)
1700
for tag,favorite in tags:
1702
self._filters.append([favorite, tag,tag,ptvDB.T_SEARCH])
1703
completion_model.append([tag,_('tag: %s') % (tag,), i - 1])
1705
self._favorite_filters.append([favorite, tag,tag, i])
1707
tags = self._app.db.get_all_tags(ptvDB.T_TAG)
1709
self._filter_tree.append(None, ["", "", 1, True])
1710
for tag,favorite in tags:
1712
self._filters.append([favorite, tag,tag+" ("+str(self._app.db.get_count_for_tag(tag))+")",ptvDB.T_TAG])
1713
completion_model.append([tag,_('tag: %s') % (tag,), i - 1])
1715
self._favorite_filters.append([favorite, tag,tag+" ("+str(self._app.db.get_count_for_tag(tag))+")", i])
1717
self._favorite_filters.sort()
1718
self._favorite_filters = [f[1:] for f in self._favorite_filters]
1720
for fav in self._favorite_filters:
1721
self._filter_tree.append(None, [fav[1], fav[0], 0, True])
1724
if utils.RUNNING_HILDON:
1725
all_tags_submenu = None
1726
self._filter_tree.append(None, ["", "", 1, True])
1728
all_tags_submenu = self._filter_tree.append(None, [_('All Tags'), _('All Tags'), 0, True])
1730
for f in self._filters:
1731
if f[F_TYPE] == ptvDB.T_SEARCH:
1732
self._filter_tree.append(all_tags_submenu, [f[F_DISPLAY], f[F_NAME], 0, True])
1733
self._filter_tree.append(all_tags_submenu, ["", "", 1, True])
1734
for f in self._filters:
1735
if f[F_TYPE] == ptvDB.T_TAG:
1736
self._filter_tree.append(all_tags_submenu, [f[F_DISPLAY], f[F_NAME], 0, True])
1738
if not utils.RUNNING_HILDON:
1739
self._filter_tree.append(None, [_('Edit Favorite Tags...'), _('Edit Favorite Tags...'), 2, True])
1741
#get index for our previously selected tag
1742
index = self.get_filter_index(current_filter)
1743
if not self.changing_layout:
1744
if index is not None:
1745
self.set_active_filter(index)
1747
self.set_active_filter(FeedList.ALL)
1749
def set_tag_favorites(self, tag_list):
1750
old_order = [f[0] for f in self._favorite_filters]
1752
for t in tag_list[:len(old_order)]:
1755
if t != old_order[i-1]:
1756
self._app.db.set_tag_favorite(t, i)
1758
if len(old_order) > 0:
1759
i = len(old_order)-1
1762
for t in tag_list[len(old_order):]:
1765
self._app.db.set_tag_favorite(t, i)
1767
old = set(old_order)
1769
removed = list(old.difference(new))
1771
self._app.db.set_tag_favorite(t, 0)
1772
self.update_filters()
1774
def _on_completion_match_selected(self, completion, model, iter, column):
1775
self.search_entry.set_text("")
1776
self.set_active_filter(model[iter][column])
1779
if self._use_internal_player:
1780
self._gstreamer_player.finish()
1783
def get_filter_name(self, filt):
1784
return self._filters[filt][F_NAME]
1786
def get_filter_index(self, string):
1787
names = [m[F_NAME] for m in self._filters]
1789
index = names.index(string)
1790
if names not in FeedList.BUILTIN_TAGS:
1796
def get_active_filter(self):
1797
return (self._active_filter_name,self._active_filter_index)
1799
def rename_filter(self, old_name, new_name):
1800
names = [m[F_NAME] for m in self._filters]
1801
index = names.index(old_name)
1802
self._filters[index][F_NAME] = new_name
1803
self._filters[index][F_DISPLAY] = new_name
1805
def select_feed(self, feed_id):
1806
#if we have a tag, pick the first one (really used just when adding
1808
tags = self._app.db.get_tags_for_feed(feed_id)
1810
if not self._active_filter_name in tags:
1811
self.set_active_filter(FeedList.ALL)
1813
self.set_active_filter(FeedList.ALL)
1814
self.feed_list_view.set_selected(feed_id)
1815
self.feed_list_view.resize_columns()
1817
def update_disk_usage(self, size):
1818
if self._disk_usage_widget is None:
1820
self._disk_usage_widget.set_markup(utils.format_size(size))
1822
def update_download_progress(self):
1823
progresses = self._mm.get_download_list(Downloader.DOWNLOADING)
1824
queued = self._mm.get_download_list(Downloader.QUEUED)
1825
paused = self._mm.get_download_list(Downloader.PAUSED)
1826
#print len(progresses)
1827
if len(progresses)+len(queued)==0:
1828
self.display_status_message("")
1829
self.update_progress_bar(-1,U_DOWNLOAD)
1830
self._download_view.update_downloads()
1831
total = len(progresses) + len(queued) + len(paused)
1832
self._update_notebook_tabs(total)
1836
for d in progresses+queued:
1840
total_size += d.total_size
1841
downloaded += (d.progress/100.0)*d.total_size
1844
dict = { 'percent': downloaded*100.0/total_size,
1845
'files': len(progresses)+len(queued),
1846
'total': total_size>1 and "("+utils.format_size(total_size)+")" or '', #ternary operator simulation
1847
's': len(progresses)>1 and 's' or '',
1848
'queued': len(queued)}
1849
if dict['queued']>0:
1850
message = _("Downloaded %(percent)d%% of %(files)d file%(s)s, %(queued)d queued %(total)s") % dict
1852
message = _("Downloaded %(percent)d%% of %(files)d file%(s)s %(total)s") % dict
1853
self.display_status_message(message , U_DOWNLOAD)
1854
self.update_progress_bar(dict['percent']/100.0,U_DOWNLOAD)
1856
self._download_view.update_downloads()
1857
self._update_notebook_tabs(len(progresses)+len(queued)+len(paused))
1859
def _update_notebook_tabs(self, number):
1860
#logging.debug("updating notebook tabs (%i)" % number)
1862
self._notebook.hide_page(N_DOWNLOADS)
1864
if utils.RUNNING_HILDON:
1865
self._downloads_label.set_markup(_('Downloads (%d)') % number)
1867
self._downloads_label.set_markup(_('<span size="small">Downloads (%d)</span>') % number)
1868
self._notebook.show_page(N_DOWNLOADS)
1870
def desensitize(self):
1872
self.app_window.set_sensitive(False)
1874
self._layout_container.set_sensitive(False)
1875
while gtk.events_pending(): #make sure the sensitivity change goes through
1876
gtk.main_iteration()
1878
def sensitize(self):
1880
self.app_window.set_sensitive(True)
1882
self._layout_container.set_sensitive(True)
1883
while gtk.events_pending(): #make sure the sensitivity change goes through
1884
gtk.main_iteration()
1886
class NotebookManager(gtk.Notebook):
1887
"""manages showing and hiding of tabs. Also, hides the whole tab bar if only one
1888
tab open, and selects a different tab if the one we are closing is selected"""
1890
gtk.Notebook.__init__(self)
1891
# pages_showing refers to tabs that would be visible.
1892
# It is overriden by keep_hidden
1893
self._pages_showing = {}
1894
self._default_page = 0
1895
self._keep_hidden = False
1897
def append_page(self, widget, label):
1898
self._pages_showing[len(self._pages_showing)] = False
1899
gtk.Notebook.append_page(self, widget, label)
1901
def show_page(self, n):
1902
if not self._pages_showing.has_key(n): return
1903
if self._pages_showing[n] == True: return
1904
self._pages_showing[n] = True
1905
self.get_nth_page(n).show_all()
1907
for key in self._pages_showing.keys():
1908
if self._pages_showing[key]:
1910
if showing_count > 1 and not self._keep_hidden:
1911
self.set_show_tabs(True)
1913
def hide_page(self, n):
1914
if not self._pages_showing.has_key(n): return
1915
if self._pages_showing[n] == False: return
1916
self._pages_showing[n] = False
1917
self.get_nth_page(n).hide()
1919
for key in self._pages_showing.keys():
1920
if self._pages_showing[key]:
1922
if showing_count == 1:
1923
for key in self._pages_showing.keys():
1924
if self._pages_showing[key]:
1925
self.set_current_page(key)
1926
self.set_show_tabs(False)
1927
if self.get_current_page() == n:
1928
self.set_current_page(self._default_page)
1930
def show_only(self, n):
1931
if not self._pages_showing.has_key(n): return
1932
self._default_page = n
1933
for i in range(0,self.get_n_pages()):
1934
self._pages_showing[i] = i==n
1936
self.get_nth_page(i).show_all()
1938
self.get_nth_page(i).hide_all()
1939
self.set_current_page(n)
1940
self.set_show_tabs(False)
1942
def set_keep_hidden(self, hide):
1943
"""For fullscreen mode, we never want to show tabs"""
1946
self.set_show_tabs(False)
1947
self._keep_hidden = True
1949
self._keep_hidden = False
1951
for key in self._pages_showing.keys():
1952
if self._pages_showing[key]:
1954
if showing_count > 1:
1955
self.set_show_tabs(True)
1957
def is_showing(self, n):
1959
return self._pages_showing[n]
1963
class ShouldntHappenError(Exception):
1964
def __init__(self,error):