~ubuntu-branches/ubuntu/vivid/gpodder/vivid

« back to all changes in this revision

Viewing changes to src/gpodder/gtkui/main.py

  • Committer: Package Import Robot
  • Author(s): tony mancill
  • Date: 2013-04-12 22:07:02 UTC
  • mfrom: (5.2.27 sid)
  • Revision ID: package-import@ubuntu.com-20130412220702-mux8v9r8zt2jin0x
Tags: 3.5.1-1
* New upstream release.
* d/control: declare versioned dependency on python-gtk2 (>= 2.16)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
2
2
#
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
5
5
#
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
29
29
import subprocess
30
30
import glob
31
31
import time
 
32
import threading
32
33
import tempfile
33
34
import collections
34
35
import urllib
93
94
macapp = None
94
95
if gpodder.ui.osx and getattr(gtk.gdk, 'WINDOWING', 'x11') == 'quartz':
95
96
    try:
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
101
102
        """
102
103
 
103
104
 
119
120
        self.model = self.core.model
120
121
        self.flattr = self.core.flattr
121
122
        BuilderWidget.__init__(self, None)
122
 
    
 
123
 
123
124
    def new(self):
124
125
        gpodder.user_extensions.on_ui_object_available('gpodder-gtk', self)
125
126
        self.toolbar.set_property('visible', self.config.show_toolbar)
128
129
 
129
130
        self.config.connect_gtk_window(self.main_window, 'main_window')
130
131
 
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)
132
133
 
133
134
        self.main_window.show()
134
135
 
141
142
 
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)
 
146
 
 
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)
 
154
        self.vpaned.show()
 
155
 
 
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)
 
159
 
 
160
        self.config.connect_gtk_paned('ui.gtk.state.main_window.episode_list_size',
 
161
                self.vpaned)
 
162
        paned.add2(self.vpaned)
146
163
 
147
164
        if self.config.enable_html_shownotes and shownotes.have_webkit:
148
165
            self.shownotes_object = shownotes.gPodderShownotesHTML(self.sw_shownotes)
220
237
        # Subscribed channels
221
238
        self.active_channel = None
222
239
        self.channels = self.model.get_podcasts()
223
 
        
 
240
 
224
241
        # Set up the first instance of MygPoClient
225
242
        self.mygpo_client = my.MygPoClient(self.config)
226
243
 
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)
1372
1389
 
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)
1593
1610
 
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
1661
1678
 
 
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.
 
1686
 
 
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)
 
1692
 
 
1693
        def handle_event(item, event=None):
 
1694
            if only_once.acquire(False):
 
1695
                util.idle_add(callback, *args)
 
1696
 
 
1697
        item.connect('button-press-event', handle_event)
 
1698
        item.connect('activate', handle_event)
 
1699
 
1662
1700
    def treeview_available_show_context_menu(self, treeview, event=None):
1663
1701
        model, paths = self.treeview_handle_context_menu_click(treeview, event)
1664
1702
        if not paths:
1715
1753
                menu.append(gtk.SeparatorMenuItem())
1716
1754
                submenus = {}
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)
1722
1759
                    if key:
1723
1760
                        if key not in submenus:
1724
1761
                            sub_menu = self._add_sub_menu(menu, key)
1736
1773
 
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)
1746
1783
 
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)
1762
 
                
 
1799
 
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()
1956
 
         
 
1993
 
1957
1994
            for path in paths:
1958
1995
                try:
1959
1996
                    episode = model.get_value(model.get_iter(path), EpisodeListModel.C_EPISODE)
2536
2573
        if macapp is None:
2537
2574
            sys.exit(0)
2538
2575
 
2539
 
    def delete_episode_list(self, episodes, confirm=True, skip_locked=True):
 
2576
    def delete_episode_list(self, episodes, confirm=True, skip_locked=True,
 
2577
            callback=None):
2540
2578
        if not episodes:
2541
2579
            return False
2542
2580
 
2589
2627
            self.mygpo_client.on_delete(episodes_status_update)
2590
2628
            self.mygpo_client.flush()
2591
2629
 
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)
 
2632
            else:
 
2633
                util.idle_add(callback, episode_urls, channel_urls, progress)
2593
2634
 
2594
2635
        return True
2595
2636
 
2622
2663
 
2623
2664
        episodes = []
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()
2656
 
        
 
2697
 
2657
2698
    def flattr_selected_episodes(self, w=None):
2658
2699
        if not self.config.flattr.token:
2659
2700
            return
3329
3370
                self.enable_download_list_update()
3330
3371
            elif task.status == task.DONE:
3331
3372
                model.remove(model.get_iter(tree_row_reference.get_path()))
3332
 
                
 
3373
 
3333
3374
        self.play_or_download()
3334
3375
 
3335
3376
        # Update the tab title and downloads list
3388
3429
            self.gPodder.hide()
3389
3430
            self.gPodder.show()
3390
3431
            self.gPodder.present()
3391
 
 
 
3432
 
3392
3433
    def iconify_main_window(self):
3393
3434
        if not self.is_iconified():
3394
 
            self.gPodder.iconify()          
 
3435
            self.gPodder.iconify()
3395
3436
 
3396
3437
    @dbus.service.method(gpodder.dbus_interface)
3397
3438
    def show_gui_window(self):
3437
3478
                self.update_episode_list_icons,
3438
3479
                self.update_podcast_list_model,
3439
3480
                self.toolPreferences,
3440
 
                gPodderEpisodeSelector,
 
3481
                self.channels,
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)
3445
3487
 
3446
3488
        self.sync_ui.on_synchronize_episodes(self.channels, episodes, force_played)
3447
3489