1
1
# -*- coding: utf-8 -*-
3
3
# gPodder - A media aggregator and podcast client
4
# Copyright (c) 2005-2012 Thomas Perl and the gPodder Team
4
# Copyright (c) 2005-2013 Thomas Perl and the gPodder Team
6
6
# gPodder is free software; you can redistribute it and/or modify
7
7
# it under the terms of the GNU General Public License as published by
94
95
if gpodder.ui.osx and getattr(gtk.gdk, 'WINDOWING', 'x11') == 'quartz':
96
from gtk_osxapplication import *
97
macapp = OSXApplication()
97
from gtkosx_application import *
98
macapp = Application()
98
99
except ImportError:
99
100
print >> sys.stderr, """
100
Warning: gtk-mac-integration not found, disabling native menus
101
Warning: gtk-mac-integration not found, disabling native menus
119
120
self.model = self.core.model
120
121
self.flattr = self.core.flattr
121
122
BuilderWidget.__init__(self, None)
124
125
gpodder.user_extensions.on_ui_object_available('gpodder-gtk', self)
125
126
self.toolbar.set_property('visible', self.config.show_toolbar)
129
130
self.config.connect_gtk_window(self.main_window, 'main_window')
131
self.config.connect_gtk_paned('paned_position', self.channelPaned)
132
self.config.connect_gtk_paned('ui.gtk.state.main_window.paned_position', self.channelPaned)
133
134
self.main_window.show()
142
143
self.sw_shownotes = gtk.ScrolledWindow()
143
144
self.sw_shownotes.set_shadow_type(gtk.SHADOW_IN)
144
self.sw_shownotes.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
145
self.vbox_episode_list.add(self.sw_shownotes)
145
self.sw_shownotes.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
147
# Vertical paned for the episode list and shownotes
148
self.vpaned = gtk.VPaned()
149
paned = self.vbox_episode_list.get_parent()
150
self.vbox_episode_list.reparent(self.vpaned)
151
self.vpaned.child_set_property(self.vbox_episode_list, 'resize', True)
152
self.vpaned.child_set_property(self.vbox_episode_list, 'shrink', False)
153
self.vpaned.pack2(self.sw_shownotes, resize=False, shrink=False)
156
# Minimum height for both episode list and shownotes
157
self.vbox_episode_list.set_size_request(-1, 100)
158
self.sw_shownotes.set_size_request(-1, 100)
160
self.config.connect_gtk_paned('ui.gtk.state.main_window.episode_list_size',
162
paned.add2(self.vpaned)
147
164
if self.config.enable_html_shownotes and shownotes.have_webkit:
148
165
self.shownotes_object = shownotes.gPodderShownotesHTML(self.sw_shownotes)
1362
1379
message = self.format_episode_list(finished_downloads, 5)
1363
1380
message += '\n\n<i>%s</i>\n' % _('Could not download some episodes:')
1364
1381
message += self.format_episode_list(failed_downloads, 5)
1365
self.show_message(message, _('Downloads finished'), True, widget=self.labelDownloads)
1382
self.show_message(message, _('Downloads finished'), widget=self.labelDownloads)
1366
1383
elif finished_downloads:
1367
1384
message = self.format_episode_list(finished_downloads)
1368
1385
self.show_message(message, _('Downloads finished'), widget=self.labelDownloads)
1369
1386
elif failed_downloads:
1370
1387
message = self.format_episode_list(failed_downloads)
1371
self.show_message(message, _('Downloads failed'), True, widget=self.labelDownloads)
1388
self.show_message(message, _('Downloads failed'), widget=self.labelDownloads)
1373
1390
if finished_syncs and failed_syncs:
1374
1391
message = self.format_episode_list(map((lambda task: str(task)),finished_syncs), 5)
1592
1609
menu.append(item)
1594
1611
menu.show_all()
1595
# Disable tooltips while we are showing the menu, so
1612
# Disable tooltips while we are showing the menu, so
1596
1613
# the tooltip will not appear over the menu
1597
1614
self.treeview_allow_tooltips(self.treeChannels, False)
1598
1615
menu.connect('deactivate', lambda menushell: self.treeview_allow_tooltips(self.treeChannels, True))
1655
1672
def _add_sub_menu(self, menu, label):
1656
1673
root_item = gtk.MenuItem(label)
1657
1674
menu.append(root_item)
1658
sub_menu = gtk.Menu()
1675
sub_menu = gtk.Menu()
1659
1676
root_item.set_submenu(sub_menu)
1660
1677
return sub_menu
1679
def _submenu_item_activate_hack(self, item, callback, *args):
1680
# See http://stackoverflow.com/questions/5221326/submenu-item-does-not-call-function-with-working-solution
1681
# Note that we can't just call the callback on button-press-event, as
1682
# it might be blocking (see http://gpodder.org/bug/1778), so we run
1683
# this in the GUI thread at a later point in time (util.idle_add).
1684
# Also, we also have to connect to the activate signal, as this is the
1685
# only signal that is fired when keyboard navigation is used.
1687
# It can happen that both (button-release-event and activate) signals
1688
# are fired, and we must avoid calling the callback twice. We do this
1689
# using a semaphore and only acquiring (but never releasing) it, making
1690
# sure that the util.idle_add() call below is only ever called once.
1691
only_once = threading.Semaphore(1)
1693
def handle_event(item, event=None):
1694
if only_once.acquire(False):
1695
util.idle_add(callback, *args)
1697
item.connect('button-press-event', handle_event)
1698
item.connect('activate', handle_event)
1662
1700
def treeview_available_show_context_menu(self, treeview, event=None):
1663
1701
model, paths = self.treeview_handle_context_menu_click(treeview, event)
1715
1753
menu.append(gtk.SeparatorMenuItem())
1717
1755
for label, callback in result:
1718
key, sep, titel = label.rpartition('/')
1719
item = gtk.ImageMenuItem(titel)
1720
item.connect('button-press-event',
1721
lambda w, ee, callback: callback(episodes), callback)
1756
key, sep, title = label.rpartition('/')
1757
item = gtk.ImageMenuItem(title)
1758
self._submenu_item_activate_hack(item, callback, episodes)
1723
1760
if key not in submenus:
1724
1761
sub_menu = self._add_sub_menu(menu, key)
1737
1774
item = gtk.ImageMenuItem(_('Local folder'))
1738
1775
item.set_image(gtk.image_new_from_stock(gtk.STOCK_DIRECTORY, gtk.ICON_SIZE_MENU))
1739
item.connect('button-press-event', lambda w, ee: self.save_episodes_as_file(episodes))
1776
self._submenu_item_activate_hack(item, self.save_episodes_as_file, episodes)
1740
1777
share_menu.append(item)
1741
1778
if self.bluetooth_available:
1742
1779
item = gtk.ImageMenuItem(_('Bluetooth device'))
1743
1780
item.set_image(gtk.image_new_from_icon_name('bluetooth', gtk.ICON_SIZE_MENU))
1744
item.connect('button-press-event', lambda w, ee: self.copy_episodes_bluetooth(episodes))
1781
self._submenu_item_activate_hack(item, self.copy_episodes_bluetooth, episodes)
1745
1782
share_menu.append(item)
1747
1784
menu.append(gtk.SeparatorMenuItem())
1759
1796
item.set_active(any_locked)
1760
1797
item.connect('activate', lambda w: self.on_item_toggle_lock_activate( w, False, not any_locked))
1761
1798
menu.append(item)
1763
1800
if any_flattrable and self.config.flattr.token:
1764
1801
menu.append(gtk.SeparatorMenuItem())
1765
1802
item = gtk.MenuItem(_('Flattr this'))
1953
1990
selection = self.treeAvailable.get_selection()
1954
1991
if selection.count_selected_rows() > 0:
1955
1992
(model, paths) = selection.get_selected_rows()
1957
1994
for path in paths:
1959
1996
episode = model.get_value(model.get_iter(path), EpisodeListModel.C_EPISODE)
2536
2573
if macapp is None:
2539
def delete_episode_list(self, episodes, confirm=True, skip_locked=True):
2576
def delete_episode_list(self, episodes, confirm=True, skip_locked=True,
2540
2578
if not episodes:
2589
2627
self.mygpo_client.on_delete(episodes_status_update)
2590
2628
self.mygpo_client.flush()
2592
util.idle_add(finish_deletion, episode_urls, channel_urls)
2630
if callback is None:
2631
util.idle_add(finish_deletion, episode_urls, channel_urls)
2633
util.idle_add(callback, episode_urls, channel_urls, progress)
2624
2665
for channel in channels:
2625
for episode in channel.get_downloaded_episodes():
2666
for episode in channel.get_episodes(gpodder.STATE_DOWNLOADED):
2626
2667
# Disallow deletion of locked episodes that still exist
2627
2668
if not episode.archive or not episode.file_exists():
2628
2669
episodes.append(episode)
2653
2694
for episode in self.get_selected_episodes():
2654
2695
episode.mark_old()
2655
2696
self.on_selected_episodes_status_changed()
2657
2698
def flattr_selected_episodes(self, w=None):
2658
2699
if not self.config.flattr.token:
3437
3478
self.update_episode_list_icons,
3438
3479
self.update_podcast_list_model,
3439
3480
self.toolPreferences,
3440
gPodderEpisodeSelector,
3441
3482
self.download_status_model,
3442
3483
self.download_queue_manager,
3443
3484
self.enable_download_list_update,
3444
self.commit_changes_to_database)
3485
self.commit_changes_to_database,
3486
self.delete_episode_list)
3446
3488
self.sync_ui.on_synchronize_episodes(self.channels, episodes, force_played)