예제 #1
0
 def setup_move_completed_path_chooser(self):
     self.move_completed_hbox = self.builder.get_object(
         'hbox_move_completed_chooser')
     self.move_completed_path_chooser = PathChooser(
         'move_completed_paths_list')
     self.move_completed_hbox.add(self.move_completed_path_chooser)
     self.move_completed_hbox.show_all()
예제 #2
0
 def setup_download_location_path_chooser(self):
     self.download_location_hbox = self.builder.get_object(
         'hbox_download_location_chooser')
     self.download_location_path_chooser = PathChooser(
         'download_location_paths_list')
     self.download_location_hbox.add(self.download_location_path_chooser)
     self.download_location_hbox.show_all()
예제 #3
0
    def __init__(self):
        super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label')

        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

        # Create TabWidget items with widget id, get/set func name, status key.
        self.add_tab_widget('spin_max_download', 'value', ['max_download_speed'])
        self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
        self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections'])
        self.add_tab_widget(
            'spin_max_upload_slots', 'value_as_int', ['max_upload_slots']
        )
        self.add_tab_widget(
            'chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces']
        )
        self.add_tab_widget(
            'chk_sequential_download', 'active', ['sequential_download']
        )
        self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed'])
        self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
        self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio'])
        self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
        self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
        self.add_tab_widget('chk_shared', 'active', ['shared'])
        self.add_tab_widget('summary_owner', 'text', ['owner'])

        # Connect key press event for spin widgets.
        for widget_id in self.tab_widgets:
            if widget_id.startswith('spin_'):
                self.tab_widgets[widget_id].obj.connect(
                    'key-press-event', self.on_key_press_event
                )

        self.button_apply = self.main_builder.get_object('button_apply')

        self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
        self.move_completed_path_chooser.set_sensitive(
            self.tab_widgets['chk_move_completed'].obj.get_active()
        )
        self.move_completed_path_chooser.connect(
            'text-changed', self.on_path_chooser_text_changed_event
        )
        self.status_keys.append('move_completed_path')

        self.move_completed_hbox = self.main_builder.get_object(
            'hbox_move_completed_path_chooser'
        )
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

        component.get('MainWindow').connect_signals(self)
예제 #4
0
    def show_move_storage_dialog(self, status):
        log.debug('show_move_storage_dialog')
        builder = gtk.Builder()
        builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui')
            )
        )
        # Keep it referenced:
        #  https://bugzilla.gnome.org/show_bug.cgi?id=546802
        self.move_storage_dialog = builder.get_object('move_storage_dialog')
        self.move_storage_dialog.set_transient_for(self.mainwindow.window)
        self.move_storage_dialog_hbox = builder.get_object('hbox_entry')
        self.move_storage_path_chooser = PathChooser('move_completed_paths_list')
        self.move_storage_dialog_hbox.add(self.move_storage_path_chooser)
        self.move_storage_dialog_hbox.show_all()
        self.move_storage_path_chooser.set_text(status['download_location'])

        def on_dialog_response_event(widget, response_id):
            def on_core_result(result):
                # Delete references
                self.move_storage_dialog.hide()
                del self.move_storage_dialog
                del self.move_storage_dialog_hbox

            if response_id == gtk.RESPONSE_CANCEL:
                on_core_result(None)

            if response_id == gtk.RESPONSE_OK:
                log.debug(
                    'Moving torrents to %s', self.move_storage_path_chooser.get_text()
                )
                path = self.move_storage_path_chooser.get_text()
                client.core.move_storage(
                    component.get('TorrentView').get_selected_torrents(), path
                ).addCallback(on_core_result)

        self.move_storage_dialog.connect('response', on_dialog_response_event)
        self.move_storage_dialog.show()
예제 #5
0
    def __init__(self):
        super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label')

        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

        # Create TabWidget items with widget id, get/set func name, status key.
        self.add_tab_widget('spin_max_download', 'value', ['max_download_speed'])
        self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
        self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections'])
        self.add_tab_widget('spin_max_upload_slots', 'value_as_int', ['max_upload_slots'])
        self.add_tab_widget('chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces'])
        self.add_tab_widget('chk_sequential_download', 'active', ['sequential_download'])
        self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed'])
        self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
        self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio'])
        self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
        self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
        self.add_tab_widget('chk_shared', 'active', ['shared'])
        self.add_tab_widget('summary_owner', 'text', ['owner'])

        # Connect key press event for spin widgets.
        for widget_id in self.tab_widgets:
            if widget_id.startswith('spin_'):
                self.tab_widgets[widget_id].obj.connect('key-press-event', self.on_key_press_event)

        self.button_apply = self.main_builder.get_object('button_apply')

        self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
        self.move_completed_path_chooser.set_sensitive(
            self.tab_widgets['chk_move_completed'].obj.get_active())
        self.move_completed_path_chooser.connect(
            'text-changed', self.on_path_chooser_text_changed_event)
        self.status_keys.append('move_completed_path')

        self.move_completed_hbox = self.main_builder.get_object('hbox_move_completed_path_chooser')
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

        component.get('MainWindow').connect_signals(self)
예제 #6
0
    def show_move_storage_dialog(self, status):
        log.debug('show_move_storage_dialog')
        builder = gtk.Builder()
        builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui')
        ))
        # Keep it referenced:
        #  https://bugzilla.gnome.org/show_bug.cgi?id=546802
        self.move_storage_dialog = builder.get_object('move_storage_dialog')
        self.move_storage_dialog.set_transient_for(self.mainwindow.window)
        self.move_storage_dialog_hbox = builder.get_object('hbox_entry')
        self.move_storage_path_chooser = PathChooser('move_completed_paths_list')
        self.move_storage_dialog_hbox.add(self.move_storage_path_chooser)
        self.move_storage_dialog_hbox.show_all()
        self.move_storage_path_chooser.set_text(status['download_location'])

        def on_dialog_response_event(widget, response_id):
            def on_core_result(result):
                # Delete references
                self.move_storage_dialog.hide()
                del self.move_storage_dialog
                del self.move_storage_dialog_hbox

            if response_id == gtk.RESPONSE_CANCEL:
                on_core_result(None)

            if response_id == gtk.RESPONSE_OK:
                log.debug('Moving torrents to %s',
                          self.move_storage_path_chooser.get_text())
                path = self.move_storage_path_chooser.get_text()
                client.core.move_storage(
                    component.get('TorrentView').get_selected_torrents(), path
                ).addCallback(on_core_result)

        self.move_storage_dialog.connect('response', on_dialog_response_event)
        self.move_storage_dialog.show()
예제 #7
0
class MenuBar(component.Component):
    def __init__(self):
        log.debug('MenuBar init..')
        component.Component.__init__(self, 'MenuBar')
        self.mainwindow = component.get('MainWindow')
        self.main_builder = self.mainwindow.get_builder()
        self.config = ConfigManager('gtkui.conf')

        self.builder = gtk.Builder()
        # Get the torrent menu from the gtk builder file
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.ui')
            )
        )
        # Get the torrent options menu from the gtk builder file
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.options.ui')
            )
        )
        # Get the torrent queue menu from the gtk builder file
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.queue.ui')
            )
        )

        # Attach queue torrent menu
        torrent_queue_menu = self.builder.get_object('queue_torrent_menu')
        self.builder.get_object('menuitem_queue').set_submenu(torrent_queue_menu)
        # Attach options torrent menu
        torrent_options_menu = self.builder.get_object('options_torrent_menu')
        self.builder.get_object('menuitem_options').set_submenu(torrent_options_menu)

        self.builder.get_object('download-limit-image').set_from_file(
            deluge.common.get_pixmap('downloading16.png')
        )
        self.builder.get_object('upload-limit-image').set_from_file(
            deluge.common.get_pixmap('seeding16.png')
        )

        for menuitem in (
            'menuitem_down_speed',
            'menuitem_up_speed',
            'menuitem_max_connections',
            'menuitem_upload_slots',
        ):
            submenu = gtk.Menu()
            item = gtk.MenuItem(_('Set Unlimited'))
            item.set_name(menuitem)
            item.connect('activate', self.on_menuitem_set_unlimited)
            submenu.append(item)
            item = gtk.MenuItem(_('Other...'))
            item.set_name(menuitem)
            item.connect('activate', self.on_menuitem_set_other)
            submenu.append(item)
            submenu.show_all()
            self.builder.get_object(menuitem).set_submenu(submenu)

        submenu = gtk.Menu()
        item = gtk.MenuItem(_('On'))
        item.connect('activate', self.on_menuitem_set_automanaged_on)
        submenu.append(item)
        item = gtk.MenuItem(_('Off'))
        item.connect('activate', self.on_menuitem_set_automanaged_off)
        submenu.append(item)
        submenu.show_all()
        self.builder.get_object('menuitem_auto_managed').set_submenu(submenu)

        submenu = gtk.Menu()
        item = gtk.MenuItem(_('Disable'))
        item.connect('activate', self.on_menuitem_set_stop_seed_at_ratio_disable)
        submenu.append(item)
        item = gtk.MenuItem(_('Enable...'))
        item.set_name('menuitem_stop_seed_at_ratio')
        item.connect('activate', self.on_menuitem_set_other)
        submenu.append(item)
        submenu.show_all()
        self.builder.get_object('menuitem_stop_seed_at_ratio').set_submenu(submenu)

        self.torrentmenu = self.builder.get_object('torrent_menu')
        self.menu_torrent = self.main_builder.get_object('menu_torrent')

        # Attach the torrent_menu to the Torrent file menu
        self.menu_torrent.set_submenu(self.torrentmenu)

        # Make sure the view menuitems are showing the correct active state
        self.main_builder.get_object('menuitem_toolbar').set_active(
            self.config['show_toolbar']
        )
        self.main_builder.get_object('menuitem_sidebar').set_active(
            self.config['show_sidebar']
        )
        self.main_builder.get_object('menuitem_statusbar').set_active(
            self.config['show_statusbar']
        )
        self.main_builder.get_object('sidebar_show_zero').set_active(
            self.config['sidebar_show_zero']
        )
        self.main_builder.get_object('sidebar_show_trackers').set_active(
            self.config['sidebar_show_trackers']
        )
        self.main_builder.get_object('sidebar_show_owners').set_active(
            self.config['sidebar_show_owners']
        )

        # Connect main window Signals #
        self.mainwindow.connect_signals(self)

        # Connect menubar signals
        self.builder.connect_signals(self)

        self.change_sensitivity = ['menuitem_addtorrent']

    def start(self):
        for widget in self.change_sensitivity:
            self.main_builder.get_object(widget).set_sensitive(True)

        # Only show open_folder menuitem and separator if connected to a localhost daemon.
        localhost_items = ['menuitem_open_folder', 'separator4']
        if client.is_localhost():
            for widget in localhost_items:
                self.builder.get_object(widget).show()
                self.builder.get_object(widget).set_no_show_all(False)
        else:
            for widget in localhost_items:
                self.builder.get_object(widget).hide()
                self.builder.get_object(widget).set_no_show_all(True)

        self.main_builder.get_object('separatormenuitem').set_visible(
            not self.config['standalone']
        )
        self.main_builder.get_object('menuitem_quitdaemon').set_visible(
            not self.config['standalone']
        )
        self.main_builder.get_object('menuitem_connectionmanager').set_visible(
            not self.config['standalone']
        )

        # Show the Torrent menu because we're connected to a host
        self.menu_torrent.show()

        if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
            # Get known accounts to allow changing ownership
            client.core.get_known_accounts().addCallback(
                self._on_known_accounts
            ).addErrback(self._on_known_accounts_fail)

        client.register_event_handler(
            'TorrentStateChangedEvent', self.on_torrentstatechanged_event
        )
        client.register_event_handler(
            'TorrentResumedEvent', self.on_torrentresumed_event
        )
        client.register_event_handler('SessionPausedEvent', self.on_sessionpaused_event)
        client.register_event_handler(
            'SessionResumedEvent', self.on_sessionresumed_event
        )

    def stop(self):
        log.debug('MenuBar stopping')

        client.deregister_event_handler(
            'TorrentStateChangedEvent', self.on_torrentstatechanged_event
        )
        client.deregister_event_handler(
            'TorrentResumedEvent', self.on_torrentresumed_event
        )
        client.deregister_event_handler(
            'SessionPausedEvent', self.on_sessionpaused_event
        )
        client.deregister_event_handler(
            'SessionResumedEvent', self.on_sessionresumed_event
        )

        for widget in self.change_sensitivity:
            self.main_builder.get_object(widget).set_sensitive(False)

        # Hide the Torrent menu
        self.menu_torrent.hide()

        self.main_builder.get_object('separatormenuitem').hide()
        self.main_builder.get_object('menuitem_quitdaemon').hide()

    def update_menu(self):
        selected = component.get('TorrentView').get_selected_torrents()
        if not selected or len(selected) == 0:
            # No torrent is selected. Disable the 'Torrents' menu
            self.menu_torrent.set_sensitive(False)
            return

        self.menu_torrent.set_sensitive(True)
        # XXX: Should also update Pause/Resume/Remove menuitems.
        # Any better way than duplicating toolbar.py:update_buttons in here?

    def add_torrentmenu_separator(self):
        sep = gtk.SeparatorMenuItem()
        self.torrentmenu.append(sep)
        sep.show()
        return sep

    # Callbacks #
    def on_torrentstatechanged_event(self, torrent_id, state):
        if state == 'Paused':
            self.update_menu()

    def on_torrentresumed_event(self, torrent_id):
        self.update_menu()

    def on_sessionpaused_event(self):
        self.update_menu()

    def on_sessionresumed_event(self):
        self.update_menu()

    # File Menu #
    def on_menuitem_addtorrent_activate(self, data=None):
        log.debug('on_menuitem_addtorrent_activate')
        component.get('AddTorrentDialog').show()

    def on_menuitem_createtorrent_activate(self, data=None):
        log.debug('on_menuitem_createtorrent_activate')
        from deluge.ui.gtkui.createtorrentdialog import CreateTorrentDialog

        CreateTorrentDialog().show()

    def on_menuitem_quitdaemon_activate(self, data=None):
        log.debug('on_menuitem_quitdaemon_activate')
        self.mainwindow.quit(shutdown=True)

    def on_menuitem_quit_activate(self, data=None):
        log.debug('on_menuitem_quit_activate')
        self.mainwindow.quit()

    # Edit Menu #
    def on_menuitem_preferences_activate(self, data=None):
        log.debug('on_menuitem_preferences_activate')
        component.get('Preferences').show()

    def on_menuitem_connectionmanager_activate(self, data=None):
        log.debug('on_menuitem_connectionmanager_activate')
        component.get('ConnectionManager').show()

    # Torrent Menu #
    def on_menuitem_pause_activate(self, data=None):
        log.debug('on_menuitem_pause_activate')
        client.core.pause_torrent(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_resume_activate(self, data=None):
        log.debug('on_menuitem_resume_activate')
        client.core.resume_torrent(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_updatetracker_activate(self, data=None):
        log.debug('on_menuitem_updatetracker_activate')
        client.core.force_reannounce(
            component.get('TorrentView').get_selected_torrents()
        )

    def on_menuitem_edittrackers_activate(self, data=None):
        log.debug('on_menuitem_edittrackers_activate')
        from deluge.ui.gtkui.edittrackersdialog import EditTrackersDialog

        dialog = EditTrackersDialog(
            component.get('TorrentView').get_selected_torrent(), self.mainwindow.window
        )
        dialog.run()

    def on_menuitem_remove_activate(self, data=None):
        log.debug('on_menuitem_remove_activate')
        torrent_ids = component.get('TorrentView').get_selected_torrents()
        if torrent_ids:
            from deluge.ui.gtkui.removetorrentdialog import RemoveTorrentDialog

            RemoveTorrentDialog(torrent_ids).run()

    def on_menuitem_recheck_activate(self, data=None):
        log.debug('on_menuitem_recheck_activate')
        client.core.force_recheck(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_open_folder_activate(self, data=None):
        log.debug('on_menuitem_open_folder')

        def _on_torrent_status(status):
            timestamp = gtk.get_current_event_time()
            path = os.path.join(
                status['download_location'], status['files'][0]['path'].split('/')[0]
            )
            deluge.common.show_file(path, timestamp=timestamp)

        for torrent_id in component.get('TorrentView').get_selected_torrents():
            component.get('SessionProxy').get_torrent_status(
                torrent_id, ['download_location', 'files']
            ).addCallback(_on_torrent_status)

    def on_menuitem_move_activate(self, data=None):
        log.debug('on_menuitem_move_activate')
        component.get('SessionProxy').get_torrent_status(
            component.get('TorrentView').get_selected_torrent(), ['download_location']
        ).addCallback(self.show_move_storage_dialog)

    def show_move_storage_dialog(self, status):
        log.debug('show_move_storage_dialog')
        builder = gtk.Builder()
        builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui')
            )
        )
        # Keep it referenced:
        #  https://bugzilla.gnome.org/show_bug.cgi?id=546802
        self.move_storage_dialog = builder.get_object('move_storage_dialog')
        self.move_storage_dialog.set_transient_for(self.mainwindow.window)
        self.move_storage_dialog_hbox = builder.get_object('hbox_entry')
        self.move_storage_path_chooser = PathChooser('move_completed_paths_list')
        self.move_storage_dialog_hbox.add(self.move_storage_path_chooser)
        self.move_storage_dialog_hbox.show_all()
        self.move_storage_path_chooser.set_text(status['download_location'])

        def on_dialog_response_event(widget, response_id):
            def on_core_result(result):
                # Delete references
                self.move_storage_dialog.hide()
                del self.move_storage_dialog
                del self.move_storage_dialog_hbox

            if response_id == gtk.RESPONSE_CANCEL:
                on_core_result(None)

            if response_id == gtk.RESPONSE_OK:
                log.debug(
                    'Moving torrents to %s', self.move_storage_path_chooser.get_text()
                )
                path = self.move_storage_path_chooser.get_text()
                client.core.move_storage(
                    component.get('TorrentView').get_selected_torrents(), path
                ).addCallback(on_core_result)

        self.move_storage_dialog.connect('response', on_dialog_response_event)
        self.move_storage_dialog.show()

    def on_menuitem_queue_top_activate(self, value):
        log.debug('on_menuitem_queue_top_activate')
        client.core.queue_top(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_up_activate(self, value):
        log.debug('on_menuitem_queue_up_activate')
        client.core.queue_up(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_down_activate(self, value):
        log.debug('on_menuitem_queue_down_activate')
        client.core.queue_down(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_bottom_activate(self, value):
        log.debug('on_menuitem_queue_bottom_activate')
        client.core.queue_bottom(component.get('TorrentView').get_selected_torrents())

    # View Menu #
    def on_menuitem_toolbar_toggled(self, value):
        log.debug('on_menuitem_toolbar_toggled')
        component.get('ToolBar').visible(value.get_active())

    def on_menuitem_sidebar_toggled(self, value):
        log.debug('on_menuitem_sidebar_toggled')
        component.get('SideBar').visible(value.get_active())

    def on_menuitem_statusbar_toggled(self, value):
        log.debug('on_menuitem_statusbar_toggled')
        component.get('StatusBar').visible(value.get_active())

    # Help Menu #
    def on_menuitem_homepage_activate(self, data=None):
        log.debug('on_menuitem_homepage_activate')
        deluge.common.open_url_in_browser('http://deluge-torrent.org')

    def on_menuitem_faq_activate(self, data=None):
        log.debug('on_menuitem_faq_activate')
        deluge.common.open_url_in_browser('http://dev.deluge-torrent.org/wiki/Faq')

    def on_menuitem_community_activate(self, data=None):
        log.debug('on_menuitem_community_activate')
        deluge.common.open_url_in_browser('http://forum.deluge-torrent.org/')

    def on_menuitem_about_activate(self, data=None):
        log.debug('on_menuitem_about_activate')
        from deluge.ui.gtkui.aboutdialog import AboutDialog

        AboutDialog().run()

    def on_menuitem_set_unlimited(self, widget):
        log.debug('widget name: %s', widget.get_name())
        funcs = {
            'menuitem_down_speed': 'max_download_speed',
            'menuitem_up_speed': 'max_upload_speed',
            'menuitem_max_connections': 'max_connections',
            'menuitem_upload_slots': 'max_upload_slots',
        }
        if widget.get_name() in funcs:
            torrent_ids = component.get('TorrentView').get_selected_torrents()
            client.core.set_torrent_options(torrent_ids, {funcs[widget.get_name()]: -1})

    def on_menuitem_set_other(self, widget):
        log.debug('widget name: %s', widget.get_name())
        status_map = {
            'menuitem_down_speed': ['max_download_speed', 'max_download_speed'],
            'menuitem_up_speed': ['max_upload_speed', 'max_upload_speed'],
            'menuitem_max_connections': ['max_connections', 'max_connections_global'],
            'menuitem_upload_slots': ['max_upload_slots', 'max_upload_slots_global'],
            'menuitem_stop_seed_at_ratio': ['stop_ratio', 'stop_seed_ratio'],
        }

        other_dialog_info = {
            'menuitem_down_speed': [
                _('Download Speed Limit'),
                _('Set the maximum download speed'),
                _('KiB/s'),
                'downloading.svg',
            ],
            'menuitem_up_speed': [
                _('Upload Speed Limit'),
                _('Set the maximum upload speed'),
                _('KiB/s'),
                'seeding.svg',
            ],
            'menuitem_max_connections': [
                _('Incoming Connections'),
                _('Set the maximum incoming connections'),
                '',
                gtk.STOCK_NETWORK,
            ],
            'menuitem_upload_slots': [
                _('Peer Upload Slots'),
                _('Set the maximum upload slots'),
                '',
                gtk.STOCK_SORT_ASCENDING,
            ],
            'menuitem_stop_seed_at_ratio': [
                _('Stop Seed At Ratio'),
                'Stop torrent seeding at ratio',
                '',
                None,
            ],
        }

        core_key = status_map[widget.get_name()][0]
        core_key_global = status_map[widget.get_name()][1]

        def _on_torrent_status(status):
            other_dialog = other_dialog_info[widget.get_name()]
            # Add the default using status value
            if status:
                other_dialog.append(status[core_key_global])

            def set_value(value):
                if value is not None:
                    if value == 0:
                        value += -1
                    options = {core_key: value}
                    if core_key == 'stop_ratio':
                        options['stop_at_ratio'] = True
                    client.core.set_torrent_options(torrent_ids, options)

            dialog = OtherDialog(*other_dialog)
            dialog.run().addCallback(set_value)

        torrent_ids = component.get('TorrentView').get_selected_torrents()
        if len(torrent_ids) == 1:
            core_key_global = core_key
            d = component.get('SessionProxy').get_torrent_status(
                torrent_ids[0], [core_key]
            )
        else:
            d = client.core.get_config_values([core_key_global])
        d.addCallback(_on_torrent_status)

    def on_menuitem_set_automanaged_on(self, widget):
        client.core.set_torrent_options(
            component.get('TorrentView').get_selected_torrents(), {'auto_managed': True}
        )

    def on_menuitem_set_automanaged_off(self, widget):
        client.core.set_torrent_options(
            component.get('TorrentView').get_selected_torrents(),
            {'auto_managed': False},
        )

    def on_menuitem_set_stop_seed_at_ratio_disable(self, widget):
        client.core.set_torrent_options(
            component.get('TorrentView').get_selected_torrents(),
            {'stop_at_ratio': False},
        )

    def on_menuitem_sidebar_zero_toggled(self, widget):
        self.config['sidebar_show_zero'] = widget.get_active()
        component.get('FilterTreeView').update()

    def on_menuitem_sidebar_trackers_toggled(self, widget):
        self.config['sidebar_show_trackers'] = widget.get_active()
        component.get('FilterTreeView').update()

    def on_menuitem_sidebar_owners_toggled(self, widget):
        self.config['sidebar_show_owners'] = widget.get_active()
        component.get('FilterTreeView').update()

    def _on_known_accounts(self, known_accounts):
        known_accounts_to_log = []
        for account in known_accounts:
            account_to_log = {}
            for key, value in account.copy().items():
                if key == 'password':
                    value = '*' * len(value)
                account_to_log[key] = value
            known_accounts_to_log.append(account_to_log)
        log.debug('_on_known_accounts: %s', known_accounts_to_log)
        if len(known_accounts) <= 1:
            return

        self.builder.get_object('menuitem_change_owner').set_visible(True)

        self.change_owner_submenu = gtk.Menu()
        self.change_owner_submenu_items = {}
        maingroup = gtk.RadioMenuItem(None, None)

        self.change_owner_submenu_items[None] = gtk.RadioMenuItem(group=maingroup)

        for account in known_accounts:
            username = account['username']
            item = gtk.RadioMenuItem(group=maingroup, label=username)
            self.change_owner_submenu_items[username] = item
            self.change_owner_submenu.append(item)
            item.connect('toggled', self._on_change_owner_toggled, username)

        self.change_owner_submenu.show_all()
        self.change_owner_submenu_items[None].set_active(True)
        self.change_owner_submenu_items[None].hide()
        self.builder.get_object('menuitem_change_owner').connect(
            'activate', self._on_change_owner_submenu_active
        )
        self.builder.get_object('menuitem_change_owner').set_submenu(
            self.change_owner_submenu
        )

    def _on_known_accounts_fail(self, reason):
        self.builder.get_object('menuitem_change_owner').set_visible(False)

    def _on_change_owner_submenu_active(self, widget):
        log.debug('_on_change_owner_submenu_active')
        selected = component.get('TorrentView').get_selected_torrents()
        if len(selected) > 1:
            self.change_owner_submenu_items[None].set_active(True)
            return

        torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])[
            'owner'
        ]
        for username, item in self.change_owner_submenu_items.items():
            item.set_active(username == torrent_owner)

    def _on_change_owner_toggled(self, widget, username):
        log.debug('_on_change_owner_toggled')
        update_torrents = []
        selected = component.get('TorrentView').get_selected_torrents()
        for torrent_id in selected:
            torrent_status = component.get('TorrentView').get_torrent_status(torrent_id)
            if torrent_status['owner'] != username:
                update_torrents.append(torrent_id)

        if update_torrents:
            log.debug('Setting torrent owner "%s" on %s', username, update_torrents)

            def failed_change_owner(failure):
                ErrorDialog(
                    _('Ownership Change Error'),
                    _('There was an error while trying changing ownership.'),
                    self.mainwindow.window,
                    details=failure.value.logable(),
                ).run()

            client.core.set_torrent_options(
                update_torrents, {'owner': username}
            ).addErrback(failed_change_owner)
예제 #8
0
class OptionsTab(Tab):
    def __init__(self):
        super(OptionsTab, self).__init__('Options', 'options_tab',
                                         'options_tab_label')

        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

        # Create TabWidget items with widget id, get/set func name, status key.
        self.add_tab_widget('spin_max_download', 'value',
                            ['max_download_speed'])
        self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
        self.add_tab_widget('spin_max_connections', 'value_as_int',
                            ['max_connections'])
        self.add_tab_widget('spin_max_upload_slots', 'value_as_int',
                            ['max_upload_slots'])
        self.add_tab_widget('chk_prioritize_first_last', 'active',
                            ['prioritize_first_last_pieces'])
        self.add_tab_widget('chk_sequential_download', 'active',
                            ['sequential_download'])
        self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed'])
        self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
        self.add_tab_widget('chk_remove_at_ratio', 'active',
                            ['remove_at_ratio'])
        self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
        self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
        self.add_tab_widget('chk_shared', 'active', ['shared'])
        self.add_tab_widget('summary_owner', 'text', ['owner'])

        # Connect key press event for spin widgets.
        for widget_id in self.tab_widgets:
            if widget_id.startswith('spin_'):
                self.tab_widgets[widget_id].obj.connect(
                    'key-press-event', self.on_key_press_event)

        self.button_apply = self.main_builder.get_object('button_apply')

        self.move_completed_path_chooser = PathChooser(
            'move_completed_paths_list')
        self.move_completed_path_chooser.set_sensitive(
            self.tab_widgets['chk_move_completed'].obj.get_active())
        self.move_completed_path_chooser.connect(
            'text-changed', self.on_path_chooser_text_changed_event)
        self.status_keys.append('move_completed_path')

        self.move_completed_hbox = self.main_builder.get_object(
            'hbox_move_completed_path_chooser')
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

        component.get('MainWindow').connect_signals(self)

    def start(self):
        pass

    def stop(self):
        pass

    def clear(self):
        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

    def update(self):
        torrent_ids = component.get('TorrentView').get_selected_torrents()

        # Set True if torrent(s) selected in torrentview, else False.
        self._child_widget.set_sensitive(bool(torrent_ids))

        if torrent_ids:
            if torrent_ids != self.prev_torrent_ids:
                self.clear()

            component.get('SessionProxy').get_torrents_status(
                {
                    'id': torrent_ids
                }, self.status_keys).addCallback(self.parse_torrents_statuses)

            self.prev_torrent_ids = torrent_ids

    def parse_torrents_statuses(self, statuses):
        """Finds common status values to all torrrents in statuses.

        Values which differ are replaced with config values.


        Args:
            statuses (dict): A status dict of {torrent_id: {key: value}}.

        Returns:
            dict: A single status dict.

        """
        status = {}
        if len(statuses) == 1:
            # A single torrent so pop torrent status.
            status = statuses.popitem()[1]
            self.button_apply.set_label('_Apply')
        else:
            for status_key in self.status_keys:
                prev_value = None
                for idx, status in enumerate(statuses.values()):
                    if idx == 0:
                        prev_value = status[status_key]
                        continue
                    elif status[status_key] != prev_value:
                        self.inconsistent_keys.append(status_key)
                        break
                status[status_key] = prev_value
            self.button_apply.set_label(_('_Apply to selected'))

        self.on_get_torrent_status(status)

    def on_get_torrent_status(self, new_status):
        # So we don't overwrite the user's unapplied changes we only
        # want to update values that have been applied in the core.
        if self.prev_status is None:
            self.prev_status = dict.fromkeys(new_status, None)

        if new_status != self.prev_status:
            for widget in self.tab_widgets.values():
                status_key = widget.status_keys[0]
                status_value = new_status[status_key]
                if status_value != self.prev_status[status_key]:
                    set_func = 'set_' + widget.func.replace('_as_int', '')
                    getattr(widget.obj, set_func)(status_value)
                    if set_func == 'set_active':
                        widget.obj.set_inconsistent(
                            status_key in self.inconsistent_keys)

            if new_status['move_completed_path'] != self.prev_status[
                    'move_completed_path']:
                text = new_status['move_completed_path']
                self.move_completed_path_chooser.set_text(text,
                                                          cursor_end=False,
                                                          default_text=True)

            # Update sensitivity of widgets.
            self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(
                new_status['stop_at_ratio'])
            self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(
                new_status['stop_at_ratio'])

            # Ensure apply button sensitivity is set False.
            self.button_apply.set_sensitive(False)
            self.prev_status = new_status

# === Widget signal handlers === #

    def on_button_apply_clicked(self, button):
        options = {}
        for widget in self.tab_widgets.values():
            status_key = widget.status_keys[0]
            if status_key == 'owner':
                continue  # A label so read-only
            widget_value = getattr(widget.obj, 'get_' + widget.func)()
            if widget_value != self.prev_status[status_key] or (
                    status_key in self.inconsistent_keys
                    and not widget.obj.get_inconsistent()):
                options[status_key] = widget_value

        if options.get('move_completed', False):
            options[
                'move_completed_path'] = self.move_completed_path_chooser.get_text(
                )

        client.core.set_torrent_options(self.prev_torrent_ids, options)
        self.button_apply.set_sensitive(False)

    def on_chk_move_completed_toggled(self, widget):
        self.move_completed_path_chooser.set_sensitive(widget.get_active())
        self.on_chk_toggled(widget)

    def on_chk_stop_at_ratio_toggled(self, widget):
        self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(
            widget.get_active())
        self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(
            widget.get_active())
        self.on_chk_toggled(widget)

    def on_chk_toggled(self, widget):
        widget.set_inconsistent(False)
        self.button_apply.set_sensitive(True)

    def on_spin_value_changed(self, widget):
        self.button_apply.set_sensitive(True)

    def on_key_press_event(self, widget, event):
        keyname = keyval_name(event.keyval).lstrip('KP_').lower()
        if keyname.isdigit() or keyname in [
                'period', 'minus', 'delete', 'backspace'
        ]:
            self.button_apply.set_sensitive(True)

    def on_path_chooser_text_changed_event(self, widget, path):
        self.button_apply.set_sensitive(True)
예제 #9
0
class OptionsTab(Tab):
    def __init__(self):
        super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label')

        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

        # Create TabWidget items with widget id, get/set func name, status key.
        self.add_tab_widget('spin_max_download', 'value', ['max_download_speed'])
        self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
        self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections'])
        self.add_tab_widget('spin_max_upload_slots', 'value_as_int', ['max_upload_slots'])
        self.add_tab_widget('chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces'])
        self.add_tab_widget('chk_sequential_download', 'active', ['sequential_download'])
        self.add_tab_widget('chk_auto_managed', 'active', ['is_auto_managed'])
        self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
        self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio'])
        self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
        self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
        self.add_tab_widget('chk_shared', 'active', ['shared'])
        self.add_tab_widget('summary_owner', 'text', ['owner'])

        # Connect key press event for spin widgets.
        for widget_id in self.tab_widgets:
            if widget_id.startswith('spin_'):
                self.tab_widgets[widget_id].obj.connect('key-press-event', self.on_key_press_event)

        self.button_apply = self.main_builder.get_object('button_apply')

        self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
        self.move_completed_path_chooser.set_sensitive(
            self.tab_widgets['chk_move_completed'].obj.get_active())
        self.move_completed_path_chooser.connect(
            'text-changed', self.on_path_chooser_text_changed_event)
        self.status_keys.append('move_completed_path')

        self.move_completed_hbox = self.main_builder.get_object('hbox_move_completed_path_chooser')
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

        component.get('MainWindow').connect_signals(self)

    def start(self):
        pass

    def stop(self):
        pass

    def clear(self):
        self.prev_torrent_ids = None
        self.prev_status = None
        self.inconsistent_keys = []

    def update(self):
        torrent_ids = component.get('TorrentView').get_selected_torrents()

        # Set True if torrent(s) selected in torrentview, else False.
        self._child_widget.set_sensitive(bool(torrent_ids))

        if torrent_ids:
            if torrent_ids != self.prev_torrent_ids:
                self.clear()

            component.get('SessionProxy').get_torrents_status(
                {'id': torrent_ids}, self.status_keys
                ).addCallback(self.parse_torrents_statuses)

            self.prev_torrent_ids = torrent_ids

    def parse_torrents_statuses(self, statuses):
        """Finds common status values to all torrrents in statuses.

        Values which differ are replaced with config values.


        Args:
            statuses (dict): A status dict of {torrent_id: {key: value}}.

        Returns:
            dict: A single status dict.

        """
        status = {}
        if len(statuses) == 1:
            # A single torrent so pop torrent status.
            status = statuses.popitem()[1]
            self.button_apply.set_label('_Apply')
        else:
            for status_key in self.status_keys:
                prev_value = None
                for idx, status in enumerate(statuses.values()):
                    if idx == 0:
                        prev_value = status[status_key]
                        continue
                    elif status[status_key] != prev_value:
                        self.inconsistent_keys.append(status_key)
                        break
                status[status_key] = prev_value
            self.button_apply.set_label(_('_Apply to selected'))

        self.on_get_torrent_status(status)

    def on_get_torrent_status(self, new_status):
        # So we don't overwrite the user's unapplied changes we only
        # want to update values that have been applied in the core.
        if self.prev_status is None:
            self.prev_status = dict.fromkeys(new_status, None)

        if new_status != self.prev_status:
            for widget in self.tab_widgets.values():
                status_key = widget.status_keys[0]
                status_value = new_status[status_key]
                if status_value != self.prev_status[status_key]:
                    set_func = 'set_' + widget.func.replace('_as_int', '')
                    getattr(widget.obj, set_func)(status_value)
                    if set_func == 'set_active':
                        widget.obj.set_inconsistent(status_key in self.inconsistent_keys)

            if new_status['move_completed_path'] != self.prev_status['move_completed_path']:
                text = new_status['move_completed_path']
                self.move_completed_path_chooser.set_text(text, cursor_end=False, default_text=True)

            # Update sensitivity of widgets.
            self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(new_status['stop_at_ratio'])
            self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(new_status['stop_at_ratio'])

            # Ensure apply button sensitivity is set False.
            self.button_apply.set_sensitive(False)
            self.prev_status = new_status

# === Widget signal handlers === #

    def on_button_apply_clicked(self, button):
        options = {}
        for widget in self.tab_widgets.values():
            status_key = widget.status_keys[0]
            if status_key == 'owner':
                continue  # A label so read-only
            widget_value = getattr(widget.obj, 'get_' + widget.func)()
            if widget_value != self.prev_status[status_key] or (
                    status_key in self.inconsistent_keys and not widget.obj.get_inconsistent()):
                options[status_key] = widget_value

        if options.get('move_completed', False):
            options['move_completed_path'] = self.move_completed_path_chooser.get_text()

        client.core.set_torrent_options(self.prev_torrent_ids, options)
        self.button_apply.set_sensitive(False)

    def on_chk_move_completed_toggled(self, widget):
        self.move_completed_path_chooser.set_sensitive(widget.get_active())
        self.on_chk_toggled(widget)

    def on_chk_stop_at_ratio_toggled(self, widget):
        self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(widget.get_active())
        self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(widget.get_active())
        self.on_chk_toggled(widget)

    def on_chk_toggled(self, widget):
        widget.set_inconsistent(False)
        self.button_apply.set_sensitive(True)

    def on_spin_value_changed(self, widget):
        self.button_apply.set_sensitive(True)

    def on_key_press_event(self, widget, event):
        keyname = keyval_name(event.keyval).lstrip('KP_').lower()
        if keyname.isdigit() or keyname in ['period', 'minus', 'delete', 'backspace']:
            self.button_apply.set_sensitive(True)

    def on_path_chooser_text_changed_event(self, widget, path):
        self.button_apply.set_sensitive(True)
예제 #10
0
class AddTorrentDialog(component.Component):
    def __init__(self):
        component.Component.__init__(self, 'AddTorrentDialog')
        self.builder = gtk.Builder()
        # The base dialog
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui',
                os.path.join('glade', 'add_torrent_dialog.ui')))
        # The infohash dialog
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui',
                os.path.join('glade', 'add_torrent_dialog.infohash.ui'),
            ))
        # The url dialog
        self.builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui',
                os.path.join('glade', 'add_torrent_dialog.url.ui')))

        self.dialog = self.builder.get_object('dialog_add_torrent')

        self.dialog.connect('delete-event', self._on_delete_event)

        self.builder.connect_signals(self)

        # download?, path, filesize, sequence number, inconsistent?
        self.files_treestore = gtk.TreeStore(bool, str, TYPE_UINT64,
                                             TYPE_INT64, bool, str)
        self.files_treestore.set_sort_column_id(1, gtk.SORT_ASCENDING)

        # Holds the files info
        self.files = {}
        self.infos = {}
        self.core_config = {}
        self.options = {}

        self.previous_selected_torrent = None

        self.listview_torrents = self.builder.get_object('listview_torrents')
        self.listview_files = self.builder.get_object('listview_files')

        self.prefetching_magnets = []

        render = gtk.CellRendererText()
        render.connect('edited', self._on_torrent_name_edit)
        render.set_property('editable', True)
        column = gtk.TreeViewColumn(_('Torrent'), render, text=1)
        self.listview_torrents.append_column(column)

        render = gtk.CellRendererToggle()
        render.connect('toggled', self._on_file_toggled)
        column = gtk.TreeViewColumn(None, render, active=0, inconsistent=4)
        self.listview_files.append_column(column)

        column = gtk.TreeViewColumn(_('Filename'))
        render = gtk.CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'stock-id', 5)
        render = gtk.CellRendererText()
        render.set_property('editable', True)
        render.connect('edited', self._on_filename_edited)
        column.pack_start(render, True)
        column.add_attribute(render, 'text', 1)
        column.set_expand(True)
        self.listview_files.append_column(column)

        render = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Size'))
        column.pack_start(render, True)
        column.set_cell_data_func(render, cell_data_size, 2)
        self.listview_files.append_column(column)

        self.torrent_liststore = gtk.ListStore(str, str, str)
        self.listview_torrents.set_model(self.torrent_liststore)
        self.listview_torrents.set_tooltip_column(2)
        self.listview_files.set_model(self.files_treestore)

        self.listview_files.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.listview_torrents.get_selection().connect(
            'changed', self._on_torrent_changed)
        self.torrent_liststore.connect('row-inserted',
                                       self.update_dialog_title_count)
        self.torrent_liststore.connect('row-deleted',
                                       self.update_dialog_title_count)

        self.setup_move_completed_path_chooser()
        self.setup_download_location_path_chooser()

        # Get default config values from the core
        self.core_keys = [
            'pre_allocate_storage',
            'max_connections_per_torrent',
            'max_upload_slots_per_torrent',
            'max_upload_speed_per_torrent',
            'max_download_speed_per_torrent',
            'prioritize_first_last_pieces',
            'sequential_download',
            'add_paused',
            'download_location',
            'download_location_paths_list',
            'move_completed',
            'move_completed_path',
            'move_completed_paths_list',
        ]
        # self.core_keys += self.move_completed_path_chooser.get_config_keys()
        self.builder.get_object('notebook1').connect('switch-page',
                                                     self._on_switch_page)

    def start(self):
        self.update_core_config()

    def show(self, focus=False):
        self.update_core_config(True, focus)

    def _show(self, focus=False):
        if component.get('MainWindow').is_on_active_workspace():
            self.dialog.set_transient_for(component.get('MainWindow').window)
        else:
            self.dialog.set_transient_for(None)

        self.dialog.present()
        if focus:
            self.dialog.window.focus()

    def hide(self):
        self.dialog.hide()
        self.files = {}
        self.infos = {}
        self.options = {}
        self.previous_selected_torrent = None
        self.torrent_liststore.clear()
        self.files_treestore.clear()
        self.prefetching_magnets = []
        self.dialog.set_transient_for(component.get('MainWindow').window)

    def _on_config_values(self, config, show=False, focus=False):
        self.core_config = config
        if self.core_config:
            self.set_default_options()
        if show:
            self._show(focus)

    def update_core_config(self, show=False, focus=False):
        # Send requests to the core for these config values
        d = client.core.get_config_values(self.core_keys)
        d.addCallback(self._on_config_values, show, focus)

    def _add_torrent_liststore(self, info_hash, name, filename, files,
                               filedata):
        """Add a torrent to torrent_liststore."""
        if info_hash in self.files:
            return False

        torrent_row = [info_hash, name, xml_escape(filename)]
        row_iter = self.torrent_liststore.append(torrent_row)
        self.files[info_hash] = files
        self.infos[info_hash] = filedata
        self.listview_torrents.get_selection().select_iter(row_iter)

        self.set_default_options()
        self.save_torrent_options(row_iter)

        return row_iter

    def update_dialog_title_count(self, *args):
        """Update the AddTorrent dialog title with current torrent count."""
        self.dialog.set_title(
            _('Add Torrents (%d)') % len(self.torrent_liststore))

    def show_already_added_dialog(self, count):
        """Show a message about trying to add duplicate torrents."""
        log.debug('Tried to add %d duplicate torrents!', count)
        ErrorDialog(
            _('Duplicate torrent(s)'),
            _('You cannot add the same torrent twice.'
              ' %d torrents were already added.' % count),
            self.dialog,
        ).run()

    def add_from_files(self, filenames):
        already_added = 0

        for filename in filenames:
            # Get the torrent data from the torrent file
            try:
                info = TorrentInfo(filename)
            except Exception as ex:
                log.debug('Unable to open torrent file: %s', ex)
                ErrorDialog(_('Invalid File'), ex, self.dialog).run()
                continue

            if not self._add_torrent_liststore(info.info_hash, info.name,
                                               filename, info.files,
                                               info.filedata):
                already_added += 1

        if already_added:
            self.show_already_added_dialog(already_added)

    def _on_uri_metadata(self, result, uri):
        """Process prefetched metadata to allow file priority selection."""
        info_hash, b64_metadata = result
        log.debug('on_uri_metadata for %s (%s)', uri, info_hash)
        if info_hash not in self.prefetching_magnets:
            return

        if b64_metadata:
            metadata = b64decode(b64_metadata)
            info = TorrentInfo(metadata=metadata)
            self.files[info_hash] = info.files
            self.infos[info_hash] = info.filedata
        else:
            log.info('Unable to fetch metadata for magnet: %s', uri)
        self.prefetching_magnets.remove(info_hash)
        self._on_torrent_changed(self.listview_torrents.get_selection())

    def prefetch_waiting_message(self, torrent_id, files):
        """Show magnet files fetching or failed message above files list."""
        if torrent_id in self.prefetching_magnets:
            self.builder.get_object('prefetch_label').set_text(
                _('Please wait for files...'))
            self.builder.get_object('prefetch_spinner').show()
            self.builder.get_object('prefetch_hbox').show()
        elif not files:
            self.builder.get_object('prefetch_label').set_text(
                _('Unable to download files for this magnet'))
            self.builder.get_object('prefetch_spinner').hide()
            self.builder.get_object('prefetch_hbox').show()
        else:
            self.builder.get_object('prefetch_hbox').hide()

    def add_from_magnets(self, uris):
        """Add a list of magnet uris to torrent_liststore."""
        already_added = 0

        for uri in uris:
            magnet = deluge.common.get_magnet_info(uri)
            if not magnet:
                log.error('Invalid magnet: %s', uri)
                continue

            torrent_id = magnet['info_hash']
            files = magnet['files_tree']
            if not self._add_torrent_liststore(torrent_id, magnet['name'],
                                               xml_escape(uri), files, None):
                already_added += 1
                continue

            if files:
                continue

            d = client.core.prefetch_magnet_metadata(uri)
            d.addCallback(self._on_uri_metadata, uri)
            self.prefetching_magnets.append(magnet['info_hash'])
            self.prefetch_waiting_message(torrent_id, None)

        if already_added:
            self.show_already_added_dialog(already_added)

    def _on_torrent_changed(self, treeselection):
        (model, row) = treeselection.get_selected()
        if row is None or not model.iter_is_valid(row):
            self.files_treestore.clear()
            self.previous_selected_torrent = None
            return

        if model[row][0] not in self.files:
            self.files_treestore.clear()
            self.previous_selected_torrent = None
            return

        # Save the previous torrents options
        self.save_torrent_options()

        torrent_id = model.get_value(row, 0)
        # Update files list
        files_list = self.files[torrent_id]
        self.prepare_file_store(files_list)

        if self.core_config == {}:
            self.update_core_config()

        # Update the options frame
        self.update_torrent_options(torrent_id)
        # Update magnet prefetch message
        self.prefetch_waiting_message(torrent_id, files_list)

        self.previous_selected_torrent = row

    def _on_torrent_name_edit(self, w, row, new_name):
        # TODO: Update torrent name
        pass

    def _on_switch_page(self, widget, page, page_num):
        # Save the torrent options when switching notebook pages
        self.save_torrent_options()

    def prepare_file_store(self, files):
        with listview_replace_treestore(self.listview_files):
            split_files = {}
            for idx, _file in enumerate(files):
                self.prepare_file(_file, _file['path'], idx,
                                  _file.get('download', True), split_files)
            self.add_files(None, split_files)
        self.listview_files.expand_row(b'0', False)

    def prepare_file(self, _file, file_name, file_num, download,
                     files_storage):
        first_slash_index = file_name.find(os.path.sep)
        if first_slash_index == -1:
            files_storage[file_name] = (file_num, _file, download)
        else:
            file_name_chunk = file_name[:first_slash_index + 1]
            if file_name_chunk not in files_storage:
                files_storage[file_name_chunk] = {}
            self.prepare_file(
                _file,
                file_name[first_slash_index + 1:],
                file_num,
                download,
                files_storage[file_name_chunk],
            )

    def add_files(self, parent_iter, split_files):
        ret = 0
        for key, value in split_files.items():
            if key.endswith(os.path.sep):
                chunk_iter = self.files_treestore.append(
                    parent_iter,
                    [True, key, 0, -1, False, gtk.STOCK_DIRECTORY])
                chunk_size = self.add_files(chunk_iter, value)
                self.files_treestore.set(chunk_iter, 2, chunk_size)
                ret += chunk_size
            else:
                self.files_treestore.append(
                    parent_iter,
                    [
                        value[2], key, value[1]['size'], value[0], False,
                        gtk.STOCK_FILE
                    ],
                )
                ret += value[1]['size']
        if parent_iter and self.files_treestore.iter_has_child(parent_iter):
            # Iterate through the children and see what we should label the
            # folder, download true, download false or inconsistent.
            itr = self.files_treestore.iter_children(parent_iter)
            download = []
            download_value = False
            inconsistent = False
            while itr:
                download.append(self.files_treestore.get_value(itr, 0))
                itr = self.files_treestore.iter_next(itr)

            if sum(download) == len(download):
                download_value = True
            elif sum(download) == 0:
                download_value = False
            else:
                inconsistent = True

            self.files_treestore.set_value(parent_iter, 0, download_value)
            self.files_treestore.set_value(parent_iter, 4, inconsistent)
        return ret

    def load_path_choosers_data(self):
        self.move_completed_path_chooser.set_text(
            self.core_config['move_completed_path'],
            cursor_end=False,
            default_text=True)
        self.download_location_path_chooser.set_text(
            self.core_config['download_location'],
            cursor_end=False,
            default_text=True)
        self.builder.get_object('chk_move_completed').set_active(
            self.core_config['move_completed'])

    def setup_move_completed_path_chooser(self):
        self.move_completed_hbox = self.builder.get_object(
            'hbox_move_completed_chooser')
        self.move_completed_path_chooser = PathChooser(
            'move_completed_paths_list')
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

    def setup_download_location_path_chooser(self):
        self.download_location_hbox = self.builder.get_object(
            'hbox_download_location_chooser')
        self.download_location_path_chooser = PathChooser(
            'download_location_paths_list')
        self.download_location_hbox.add(self.download_location_path_chooser)
        self.download_location_hbox.show_all()

    def update_torrent_options(self, torrent_id):
        if torrent_id not in self.options:
            self.set_default_options()
            return

        options = self.options[torrent_id]

        self.download_location_path_chooser.set_text(
            options['download_location'], cursor_end=True)
        self.move_completed_path_chooser.set_text(
            options['move_completed_path'], cursor_end=True)

        self.builder.get_object('spin_maxdown').set_value(
            options['max_download_speed'])
        self.builder.get_object('spin_maxup').set_value(
            options['max_upload_speed'])
        self.builder.get_object('spin_maxconnections').set_value(
            options['max_connections'])
        self.builder.get_object('spin_maxupslots').set_value(
            options['max_upload_slots'])
        self.builder.get_object('chk_paused').set_active(options['add_paused'])
        self.builder.get_object('chk_pre_alloc').set_active(
            options['pre_allocate_storage'])
        self.builder.get_object('chk_prioritize').set_active(
            options['prioritize_first_last_pieces'])
        self.builder.get_object('chk_sequential_download').set_active(
            options['sequential_download'])
        self.builder.get_object('chk_move_completed').set_active(
            options['move_completed'])

    def save_torrent_options(self, row=None):
        # Keeps the torrent options dictionary up-to-date with what the user has
        # selected.
        if row is None:
            if self.previous_selected_torrent and self.torrent_liststore.iter_is_valid(
                    self.previous_selected_torrent):
                row = self.previous_selected_torrent
            else:
                return

        torrent_id = self.torrent_liststore.get_value(row, 0)

        if torrent_id in self.options:
            options = self.options[torrent_id]
        else:
            options = {}

        options[
            'download_location'] = self.download_location_path_chooser.get_text(
            )
        options[
            'move_completed_path'] = self.move_completed_path_chooser.get_text(
            )
        options['pre_allocate_storage'] = self.builder.get_object(
            'chk_pre_alloc').get_active()
        options['move_completed'] = self.builder.get_object(
            'chk_move_completed').get_active()
        options['max_download_speed'] = self.builder.get_object(
            'spin_maxdown').get_value()
        options['max_upload_speed'] = self.builder.get_object(
            'spin_maxup').get_value()
        options['max_connections'] = self.builder.get_object(
            'spin_maxconnections').get_value_as_int()
        options['max_upload_slots'] = self.builder.get_object(
            'spin_maxupslots').get_value_as_int()
        options['add_paused'] = self.builder.get_object(
            'chk_paused').get_active()
        options['prioritize_first_last_pieces'] = self.builder.get_object(
            'chk_prioritize').get_active()
        options['sequential_download'] = (
            self.builder.get_object('chk_sequential_download').get_active()
            or False)
        options['move_completed'] = self.builder.get_object(
            'chk_move_completed').get_active()
        options['seed_mode'] = self.builder.get_object(
            'chk_seed_mode').get_active()

        self.options[torrent_id] = options

        # Save the file priorities
        files_priorities = self.build_priorities(
            self.files_treestore.get_iter_first(), {})

        if len(files_priorities) > 0:
            for i, file_dict in enumerate(self.files[torrent_id]):
                file_dict['download'] = files_priorities[i]

    def build_priorities(self, _iter, priorities):
        while _iter is not None:
            if self.files_treestore.iter_has_child(_iter):
                self.build_priorities(
                    self.files_treestore.iter_children(_iter), priorities)
            elif not self.files_treestore.get_value(_iter, 1).endswith(
                    os.path.sep):
                priorities[self.files_treestore.get_value(
                    _iter, 3)] = self.files_treestore.get_value(_iter, 0)
            _iter = self.files_treestore.iter_next(_iter)
        return priorities

    def set_default_options(self):
        if not self.core_config:
            # update_core_config will call this method again.
            self.update_core_config()
            return

        self.load_path_choosers_data()

        self.builder.get_object('chk_pre_alloc').set_active(
            self.core_config['pre_allocate_storage'])
        self.builder.get_object('spin_maxdown').set_value(
            self.core_config['max_download_speed_per_torrent'])
        self.builder.get_object('spin_maxup').set_value(
            self.core_config['max_upload_speed_per_torrent'])
        self.builder.get_object('spin_maxconnections').set_value(
            self.core_config['max_connections_per_torrent'])
        self.builder.get_object('spin_maxupslots').set_value(
            self.core_config['max_upload_slots_per_torrent'])
        self.builder.get_object('chk_paused').set_active(
            self.core_config['add_paused'])
        self.builder.get_object('chk_prioritize').set_active(
            self.core_config['prioritize_first_last_pieces'])
        self.builder.get_object('chk_sequential_download').set_active(
            self.core_config['sequential_download'])
        self.builder.get_object('chk_move_completed').set_active(
            self.core_config['move_completed'])
        self.builder.get_object('chk_seed_mode').set_active(False)

    def get_file_priorities(self, torrent_id):
        # A list of priorities
        files_list = []

        for file_dict in self.files[torrent_id]:
            if not file_dict['download']:
                files_list.append(0)
            else:
                # Default lt file priority is 4
                files_list.append(4)

        return files_list

    def _on_file_toggled(self, render, path):
        (model,
         paths) = self.listview_files.get_selection().get_selected_rows()
        if len(paths) > 1:
            for path in paths:
                row = model.get_iter(path)
                self.toggle_iter(row)
        else:
            row = model.get_iter(path)
            self.toggle_iter(row)
        self.update_treeview_toggles(self.files_treestore.get_iter_first())

    def toggle_iter(self, _iter, toggle_to=None):
        if toggle_to is None:
            toggle_to = not self.files_treestore.get_value(_iter, 0)
        self.files_treestore.set_value(_iter, 0, toggle_to)
        if self.files_treestore.iter_has_child(_iter):
            child = self.files_treestore.iter_children(_iter)
            while child is not None:
                self.toggle_iter(child, toggle_to)
                child = self.files_treestore.iter_next(child)

    def update_treeview_toggles(self, _iter):
        toggle_inconsistent = -1
        this_level_toggle = None
        while _iter is not None:
            if self.files_treestore.iter_has_child(_iter):
                toggle = self.update_treeview_toggles(
                    self.files_treestore.iter_children(_iter))
                if toggle == toggle_inconsistent:
                    self.files_treestore.set_value(_iter, 4, True)
                else:
                    self.files_treestore.set_value(_iter, 0, toggle)
                    # set inconsistent to false
                    self.files_treestore.set_value(_iter, 4, False)
            else:
                toggle = self.files_treestore.get_value(_iter, 0)
            if this_level_toggle is None:
                this_level_toggle = toggle
            elif this_level_toggle != toggle:
                this_level_toggle = toggle_inconsistent
            _iter = self.files_treestore.iter_next(_iter)
        return this_level_toggle

    def on_button_file_clicked(self, widget):
        log.debug('on_button_file_clicked')
        # Setup the filechooserdialog
        chooser = gtk.FileChooserDialog(
            _('Choose a .torrent file'),
            None,
            gtk.FILE_CHOOSER_ACTION_OPEN,
            buttons=(
                gtk.STOCK_CANCEL,
                gtk.RESPONSE_CANCEL,
                gtk.STOCK_OPEN,
                gtk.RESPONSE_OK,
            ),
        )

        chooser.set_transient_for(self.dialog)
        chooser.set_select_multiple(True)
        chooser.set_property('skip-taskbar-hint', True)
        chooser.set_local_only(False)

        # Add .torrent and * file filters
        file_filter = gtk.FileFilter()
        file_filter.set_name(_('Torrent files'))
        file_filter.add_pattern('*.' + 'torrent')
        chooser.add_filter(file_filter)
        file_filter = gtk.FileFilter()
        file_filter.set_name(_('All files'))
        file_filter.add_pattern('*')
        chooser.add_filter(file_filter)

        # Load the 'default_load_path' from the config
        self.config = ConfigManager('gtkui.conf')
        if self.config['default_load_path'] is not None:
            chooser.set_current_folder(self.config['default_load_path'])

        # Run the dialog
        response = chooser.run()

        if response == gtk.RESPONSE_OK:
            result = chooser.get_filenames()
            self.config['default_load_path'] = chooser.get_current_folder()
        else:
            chooser.destroy()
            return

        chooser.destroy()
        self.add_from_files(result)

    def on_button_url_clicked(self, widget):
        log.debug('on_button_url_clicked')
        dialog = self.builder.get_object('url_dialog')
        entry = self.builder.get_object('entry_url')

        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_transient_for(self.dialog)
        entry.grab_focus()

        text = get_clipboard_text()
        if text and deluge.common.is_url(text) or deluge.common.is_magnet(
                text):
            entry.set_text(text)

        dialog.show_all()
        response = dialog.run()

        if response == gtk.RESPONSE_OK:
            url = entry.get_text().decode('utf-8')
        else:
            url = None

        entry.set_text('')
        dialog.hide()

        # This is where we need to fetch the .torrent file from the URL and
        # add it to the list.
        log.debug('url: %s', url)
        if url:
            if deluge.common.is_url(url):
                self.add_from_url(url)
            elif deluge.common.is_magnet(url):
                self.add_from_magnets([url])
            else:
                ErrorDialog(
                    _('Invalid URL'),
                    '%s %s' % (url, _('is not a valid URL.')),
                    self.dialog,
                ).run()

    def add_from_url(self, url):
        dialog = gtk.Dialog(
            _('Downloading...'),
            flags=gtk.DIALOG_MODAL
            | gtk.DIALOG_DESTROY_WITH_PARENT
            | gtk.DIALOG_NO_SEPARATOR,
            parent=self.dialog,
        )
        dialog.set_transient_for(self.dialog)

        pb = gtk.ProgressBar()
        dialog.vbox.pack_start(pb, True, True, 0)
        dialog.show_all()

        # Create a tmp file path
        import tempfile

        tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.',
                                            suffix='.torrent')

        def on_part(data, current_length, total_length):
            if total_length:
                percent = current_length / total_length
                pb.set_fraction(percent)
                pb.set_text('%.2f%% (%s / %s)' % (
                    percent * 100,
                    deluge.common.fsize(current_length),
                    deluge.common.fsize(total_length),
                ))
            else:
                pb.pulse()
                pb.set_text('%s' % deluge.common.fsize(current_length))

        def on_download_success(result):
            self.add_from_files([result])
            dialog.destroy()

        def on_download_fail(result):
            log.debug('Download failed: %s', result)
            dialog.destroy()
            ErrorDialog(
                _('Download Failed'),
                '%s %s' % (_('Failed to download:'), url),
                details=result.getErrorMessage(),
                parent=self.dialog,
            ).run()
            return result

        d = download_file(url, tmp_file, on_part)
        os.close(tmp_fd)
        d.addCallbacks(on_download_success, on_download_fail)

    def on_button_hash_clicked(self, widget):
        log.debug('on_button_hash_clicked')
        dialog = self.builder.get_object('dialog_infohash')
        entry = self.builder.get_object('entry_hash')
        textview = self.builder.get_object('text_trackers')

        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_transient_for(self.dialog)
        entry.grab_focus()

        text = get_clipboard_text()
        if deluge.common.is_infohash(text):
            entry.set_text(text)

        dialog.show_all()
        response = dialog.run()
        infohash = entry.get_text().strip()
        if response == gtk.RESPONSE_OK and deluge.common.is_infohash(infohash):
            # Create a list of trackers from the textview buffer
            tview_buf = textview.get_buffer()
            trackers_text = tview_buf.get_text(*tview_buf.get_bounds())
            log.debug('Create torrent tracker lines: %s', trackers_text)
            trackers = list(trackers_tiers_from_text(trackers_text).keys())

            # Convert the information to a magnet uri, this is just easier to
            # handle this way.
            log.debug('trackers: %s', trackers)
            magnet = deluge.common.create_magnet_uri(infohash, infohash,
                                                     trackers)
            log.debug('magnet uri: %s', magnet)
            self.add_from_magnets([magnet])

        entry.set_text('')
        textview.get_buffer().set_text('')
        dialog.hide()

    def on_button_remove_clicked(self, widget):
        log.debug('on_button_remove_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        torrent_id = model.get_value(row, 0)

        model.remove(row)
        del self.files[torrent_id]
        del self.infos[torrent_id]

    def on_button_trackers_clicked(self, widget):
        log.debug('on_button_trackers_clicked')

    def on_button_cancel_clicked(self, widget):
        log.debug('on_button_cancel_clicked')
        self.hide()

    def on_button_add_clicked(self, widget):
        log.debug('on_button_add_clicked')
        self.add_torrents()
        self.hide()

    def add_torrents(self):
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is not None:
            self.save_torrent_options(row)

        torrents_to_add = []

        row = self.torrent_liststore.get_iter_first()
        while row is not None:
            torrent_id = self.torrent_liststore.get_value(row, 0)
            filename = xml_unescape(self.torrent_liststore.get_value(row, 2))
            try:
                options = self.options[torrent_id]
            except KeyError:
                options = None

            file_priorities = self.get_file_priorities(torrent_id)
            if options is not None:
                options['file_priorities'] = file_priorities

            if self.infos[torrent_id]:
                torrents_to_add.append((
                    os.path.split(filename)[-1],
                    b64encode(self.infos[torrent_id]),
                    options,
                ))
            elif deluge.common.is_magnet(filename):
                client.core.add_torrent_magnet(filename, options)

            row = self.torrent_liststore.iter_next(row)

        def on_torrents_added(errors):
            if errors:
                log.info(
                    'Failed to add %d out of %d torrents.',
                    len(errors),
                    len(torrents_to_add),
                )
                for e in errors:
                    log.info('Torrent add failed: %s', e)
            else:
                log.info('Successfully added %d torrents.',
                         len(torrents_to_add))

        client.core.add_torrent_files(torrents_to_add).addCallback(
            on_torrents_added)

    def on_button_apply_clicked(self, widget):
        log.debug('on_button_apply_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        self.save_torrent_options(row)

        # The options, except file renames, we want all the torrents to have
        options = self.options[model.get_value(row, 0)].copy()
        options.pop('mapped_files', None)

        # Set all the torrent options
        row = model.get_iter_first()
        while row is not None:
            torrent_id = model.get_value(row, 0)
            self.options[torrent_id].update(options)
            row = model.iter_next(row)

    def on_button_revert_clicked(self, widget):
        log.debug('on_button_revert_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        del self.options[model.get_value(row, 0)]
        self.set_default_options()

    def on_chk_move_completed_toggled(self, widget):
        value = widget.get_active()
        self.move_completed_path_chooser.set_sensitive(value)

    def _on_delete_event(self, widget, event):
        self.hide()
        return True

    def get_file_path(self, row, path=''):
        if not row:
            return path

        path = self.files_treestore[row][1] + path
        return self.get_file_path(self.files_treestore.iter_parent(row), path)

    def _on_filename_edited(self, renderer, path, new_text):
        index = self.files_treestore[path][3]

        new_text = new_text.strip(os.path.sep).strip()

        # Return if the text hasn't changed
        if new_text == self.files_treestore[path][1]:
            return

        # Get the tree iter
        itr = self.files_treestore.get_iter(path)

        # Get the torrent_id
        (model, row) = self.listview_torrents.get_selection().get_selected()
        torrent_id = model[row][0]

        if 'mapped_files' not in self.options[torrent_id]:
            self.options[torrent_id]['mapped_files'] = {}

        if index > -1:
            # We're renaming a file! Yay! That's easy!
            if not new_text:
                return
            parent = self.files_treestore.iter_parent(itr)
            file_path = os.path.join(self.get_file_path(parent), new_text)
            # Don't rename if filename exists
            if parent:
                for row in self.files_treestore[parent].iterchildren():
                    if new_text == row[1]:
                        return
            if os.path.sep in new_text:
                # There are folders in this path, so we need to create them
                # and then move the file iter to top
                split_text = new_text.split(os.path.sep)
                for s in split_text[:-1]:
                    parent = self.files_treestore.append(
                        parent, [True, s, 0, -1, False, gtk.STOCK_DIRECTORY])

                self.files_treestore[itr][1] = split_text[-1]
                reparent_iter(self.files_treestore, itr, parent)
            else:
                # Update the row's text
                self.files_treestore[itr][1] = new_text

            # Update the mapped_files dict in the options with the index and new
            # file path.
            # We'll send this to the core when adding the torrent so it knows
            # what to rename before adding.
            self.options[torrent_id]['mapped_files'][index] = file_path
            self.files[torrent_id][index]['path'] = file_path
        else:
            # Folder!
            def walk_tree(row):
                if not row:
                    return

                # Get the file path base once, since it will be the same for
                # all siblings
                file_path_base = self.get_file_path(
                    self.files_treestore.iter_parent(row))

                # Iterate through all the siblings at this level
                while row:
                    # We recurse if there are children
                    if self.files_treestore.iter_has_child(row):
                        walk_tree(self.files_treestore.iter_children(row))

                    index = self.files_treestore[row][3]

                    if index > -1:
                        # Get the new full path for this file
                        file_path = file_path_base + self.files_treestore[row][
                            1]

                        # Update the file path in the mapped_files dict
                        self.options[torrent_id]['mapped_files'][
                            index] = file_path
                        self.files[torrent_id][index]['path'] = file_path

                    # Get the next siblings iter
                    row = self.files_treestore.iter_next(row)

            # Update the treestore row first so that when walking the tree
            # we can construct the new proper paths

            # We need to check if this folder has been split
            if os.path.sep in new_text:
                # It's been split, so we need to add new folders and then re-parent
                # itr.
                parent = self.files_treestore.iter_parent(itr)
                split_text = new_text.split(os.path.sep)
                for s in split_text[:-1]:
                    # We don't iterate over the last item because we'll just use
                    # the existing itr and change the text
                    parent = self.files_treestore.append(
                        parent,
                        [
                            True, s + os.path.sep, 0, -1, False,
                            gtk.STOCK_DIRECTORY
                        ],
                    )

                self.files_treestore[itr][1] = split_text[-1] + os.path.sep

                # Now re-parent itr to parent
                reparent_iter(self.files_treestore, itr, parent)
                itr = parent

                # We need to re-expand the view because it might contracted
                # if we change the root iter
                self.listview_files.expand_row(b'0', False)
            else:
                # This was a simple folder rename without any splits, so just
                # change the path for itr
                self.files_treestore[itr][1] = new_text + os.path.sep

            # Walk through the tree from 'itr' and add all the new file paths
            # to the 'mapped_files' option
            walk_tree(itr)
예제 #11
0
class MenuBar(component.Component):

    def __init__(self):
        log.debug('MenuBar init..')
        component.Component.__init__(self, 'MenuBar')
        self.mainwindow = component.get('MainWindow')
        self.main_builder = self.mainwindow.get_builder()
        self.config = ConfigManager('gtkui.conf')

        self.builder = gtk.Builder()
        # Get the torrent menu from the gtk builder file
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.ui')
        ))
        # Get the torrent options menu from the gtk builder file
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.options.ui')
        ))
        # Get the torrent queue menu from the gtk builder file
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'torrent_menu.queue.ui')
        ))

        # Attach queue torrent menu
        torrent_queue_menu = self.builder.get_object('queue_torrent_menu')
        self.builder.get_object('menuitem_queue').set_submenu(torrent_queue_menu)
        # Attach options torrent menu
        torrent_options_menu = self.builder.get_object('options_torrent_menu')
        self.builder.get_object('menuitem_options').set_submenu(torrent_options_menu)

        self.builder.get_object('download-limit-image').set_from_file(deluge.common.get_pixmap('downloading16.png'))
        self.builder.get_object('upload-limit-image').set_from_file(deluge.common.get_pixmap('seeding16.png'))

        for menuitem in ('menuitem_down_speed', 'menuitem_up_speed',
                         'menuitem_max_connections', 'menuitem_upload_slots'):
            submenu = gtk.Menu()
            item = gtk.MenuItem(_('Set Unlimited'))
            item.set_name(menuitem)
            item.connect('activate', self.on_menuitem_set_unlimited)
            submenu.append(item)
            item = gtk.MenuItem(_('Other...'))
            item.set_name(menuitem)
            item.connect('activate', self.on_menuitem_set_other)
            submenu.append(item)
            submenu.show_all()
            self.builder.get_object(menuitem).set_submenu(submenu)

        submenu = gtk.Menu()
        item = gtk.MenuItem(_('On'))
        item.connect('activate', self.on_menuitem_set_automanaged_on)
        submenu.append(item)
        item = gtk.MenuItem(_('Off'))
        item.connect('activate', self.on_menuitem_set_automanaged_off)
        submenu.append(item)
        submenu.show_all()
        self.builder.get_object('menuitem_auto_managed').set_submenu(submenu)

        submenu = gtk.Menu()
        item = gtk.MenuItem(_('Disable'))
        item.connect('activate', self.on_menuitem_set_stop_seed_at_ratio_disable)
        submenu.append(item)
        item = gtk.MenuItem(_('Enable...'))
        item.set_name('menuitem_stop_seed_at_ratio')
        item.connect('activate', self.on_menuitem_set_other)
        submenu.append(item)
        submenu.show_all()
        self.builder.get_object('menuitem_stop_seed_at_ratio').set_submenu(submenu)

        self.torrentmenu = self.builder.get_object('torrent_menu')
        self.menu_torrent = self.main_builder.get_object('menu_torrent')

        # Attach the torrent_menu to the Torrent file menu
        self.menu_torrent.set_submenu(self.torrentmenu)

        # Make sure the view menuitems are showing the correct active state
        self.main_builder.get_object('menuitem_toolbar').set_active(self.config['show_toolbar'])
        self.main_builder.get_object('menuitem_sidebar').set_active(self.config['show_sidebar'])
        self.main_builder.get_object('menuitem_statusbar').set_active(self.config['show_statusbar'])
        self.main_builder.get_object('sidebar_show_zero').set_active(self.config['sidebar_show_zero'])
        self.main_builder.get_object('sidebar_show_trackers').set_active(self.config['sidebar_show_trackers'])
        self.main_builder.get_object('sidebar_show_owners').set_active(self.config['sidebar_show_owners'])

        # Connect main window Signals #
        self.mainwindow.connect_signals(self)

        # Connect menubar signals
        self.builder.connect_signals(self)

        self.change_sensitivity = [
            'menuitem_addtorrent'
        ]

    def start(self):
        for widget in self.change_sensitivity:
            self.main_builder.get_object(widget).set_sensitive(True)

        # Only show open_folder menuitem and separator if connected to a localhost daemon.
        localhost_items = [
            'menuitem_open_folder',
            'separator4'
        ]
        if client.is_localhost():
            for widget in localhost_items:
                self.builder.get_object(widget).show()
                self.builder.get_object(widget).set_no_show_all(False)
        else:
            for widget in localhost_items:
                self.builder.get_object(widget).hide()
                self.builder.get_object(widget).set_no_show_all(True)

        self.main_builder.get_object('separatormenuitem').set_visible(not self.config['standalone'])
        self.main_builder.get_object('menuitem_quitdaemon').set_visible(not self.config['standalone'])
        self.main_builder.get_object('menuitem_connectionmanager').set_visible(not self.config['standalone'])

        # Show the Torrent menu because we're connected to a host
        self.menu_torrent.show()

        if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
            # Get known accounts to allow changing ownership
            client.core.get_known_accounts().addCallback(
                self._on_known_accounts).addErrback(self._on_known_accounts_fail)

        client.register_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event)
        client.register_event_handler('TorrentResumedEvent', self.on_torrentresumed_event)
        client.register_event_handler('SessionPausedEvent', self.on_sessionpaused_event)
        client.register_event_handler('SessionResumedEvent', self.on_sessionresumed_event)

    def stop(self):
        log.debug('MenuBar stopping')

        client.deregister_event_handler('TorrentStateChangedEvent', self.on_torrentstatechanged_event)
        client.deregister_event_handler('TorrentResumedEvent', self.on_torrentresumed_event)
        client.deregister_event_handler('SessionPausedEvent', self.on_sessionpaused_event)
        client.deregister_event_handler('SessionResumedEvent', self.on_sessionresumed_event)

        for widget in self.change_sensitivity:
            self.main_builder.get_object(widget).set_sensitive(False)

        # Hide the Torrent menu
        self.menu_torrent.hide()

        self.main_builder.get_object('separatormenuitem').hide()
        self.main_builder.get_object('menuitem_quitdaemon').hide()

    def update_menu(self):
        selected = component.get('TorrentView').get_selected_torrents()
        if not selected or len(selected) == 0:
            # No torrent is selected. Disable the 'Torrents' menu
            self.menu_torrent.set_sensitive(False)
            return

        self.menu_torrent.set_sensitive(True)
        # XXX: Should also update Pause/Resume/Remove menuitems.
        # Any better way than duplicating toolbar.py:update_buttons in here?

    def add_torrentmenu_separator(self):
        sep = gtk.SeparatorMenuItem()
        self.torrentmenu.append(sep)
        sep.show()
        return sep

    # Callbacks #
    def on_torrentstatechanged_event(self, torrent_id, state):
        if state == 'Paused':
            self.update_menu()

    def on_torrentresumed_event(self, torrent_id):
        self.update_menu()

    def on_sessionpaused_event(self):
        self.update_menu()

    def on_sessionresumed_event(self):
        self.update_menu()

    # File Menu #
    def on_menuitem_addtorrent_activate(self, data=None):
        log.debug('on_menuitem_addtorrent_activate')
        component.get('AddTorrentDialog').show()

    def on_menuitem_createtorrent_activate(self, data=None):
        log.debug('on_menuitem_createtorrent_activate')
        from deluge.ui.gtkui.createtorrentdialog import CreateTorrentDialog
        CreateTorrentDialog().show()

    def on_menuitem_quitdaemon_activate(self, data=None):
        log.debug('on_menuitem_quitdaemon_activate')
        self.mainwindow.quit(shutdown=True)

    def on_menuitem_quit_activate(self, data=None):
        log.debug('on_menuitem_quit_activate')
        self.mainwindow.quit()

    # Edit Menu #
    def on_menuitem_preferences_activate(self, data=None):
        log.debug('on_menuitem_preferences_activate')
        component.get('Preferences').show()

    def on_menuitem_connectionmanager_activate(self, data=None):
        log.debug('on_menuitem_connectionmanager_activate')
        component.get('ConnectionManager').show()

    # Torrent Menu #
    def on_menuitem_pause_activate(self, data=None):
        log.debug('on_menuitem_pause_activate')
        client.core.pause_torrent(
            component.get('TorrentView').get_selected_torrents())

    def on_menuitem_resume_activate(self, data=None):
        log.debug('on_menuitem_resume_activate')
        client.core.resume_torrent(
            component.get('TorrentView').get_selected_torrents())

    def on_menuitem_updatetracker_activate(self, data=None):
        log.debug('on_menuitem_updatetracker_activate')
        client.core.force_reannounce(
            component.get('TorrentView').get_selected_torrents())

    def on_menuitem_edittrackers_activate(self, data=None):
        log.debug('on_menuitem_edittrackers_activate')
        from deluge.ui.gtkui.edittrackersdialog import EditTrackersDialog
        dialog = EditTrackersDialog(
            component.get('TorrentView').get_selected_torrent(),
            self.mainwindow.window)
        dialog.run()

    def on_menuitem_remove_activate(self, data=None):
        log.debug('on_menuitem_remove_activate')
        torrent_ids = component.get('TorrentView').get_selected_torrents()
        if torrent_ids:
            from deluge.ui.gtkui.removetorrentdialog import RemoveTorrentDialog
            RemoveTorrentDialog(torrent_ids).run()

    def on_menuitem_recheck_activate(self, data=None):
        log.debug('on_menuitem_recheck_activate')
        client.core.force_recheck(
            component.get('TorrentView').get_selected_torrents())

    def on_menuitem_open_folder_activate(self, data=None):
        log.debug('on_menuitem_open_folder')

        def _on_torrent_status(status):
            timestamp = gtk.get_current_event_time()
            path = os.path.join(status['download_location'], status['files'][0]['path'].split('/')[0])
            deluge.common.show_file(path, timestamp=timestamp)
        for torrent_id in component.get('TorrentView').get_selected_torrents():
            component.get('SessionProxy').get_torrent_status(
                torrent_id, ['download_location', 'files']).addCallback(_on_torrent_status)

    def on_menuitem_move_activate(self, data=None):
        log.debug('on_menuitem_move_activate')
        component.get('SessionProxy').get_torrent_status(
            component.get('TorrentView').get_selected_torrent(),
            ['download_location']).addCallback(self.show_move_storage_dialog)

    def show_move_storage_dialog(self, status):
        log.debug('show_move_storage_dialog')
        builder = gtk.Builder()
        builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'move_storage_dialog.ui')
        ))
        # Keep it referenced:
        #  https://bugzilla.gnome.org/show_bug.cgi?id=546802
        self.move_storage_dialog = builder.get_object('move_storage_dialog')
        self.move_storage_dialog.set_transient_for(self.mainwindow.window)
        self.move_storage_dialog_hbox = builder.get_object('hbox_entry')
        self.move_storage_path_chooser = PathChooser('move_completed_paths_list')
        self.move_storage_dialog_hbox.add(self.move_storage_path_chooser)
        self.move_storage_dialog_hbox.show_all()
        self.move_storage_path_chooser.set_text(status['download_location'])

        def on_dialog_response_event(widget, response_id):
            def on_core_result(result):
                # Delete references
                self.move_storage_dialog.hide()
                del self.move_storage_dialog
                del self.move_storage_dialog_hbox

            if response_id == gtk.RESPONSE_CANCEL:
                on_core_result(None)

            if response_id == gtk.RESPONSE_OK:
                log.debug('Moving torrents to %s',
                          self.move_storage_path_chooser.get_text())
                path = self.move_storage_path_chooser.get_text()
                client.core.move_storage(
                    component.get('TorrentView').get_selected_torrents(), path
                ).addCallback(on_core_result)

        self.move_storage_dialog.connect('response', on_dialog_response_event)
        self.move_storage_dialog.show()

    def on_menuitem_queue_top_activate(self, value):
        log.debug('on_menuitem_queue_top_activate')
        client.core.queue_top(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_up_activate(self, value):
        log.debug('on_menuitem_queue_up_activate')
        client.core.queue_up(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_down_activate(self, value):
        log.debug('on_menuitem_queue_down_activate')
        client.core.queue_down(component.get('TorrentView').get_selected_torrents())

    def on_menuitem_queue_bottom_activate(self, value):
        log.debug('on_menuitem_queue_bottom_activate')
        client.core.queue_bottom(component.get('TorrentView').get_selected_torrents())

    # View Menu #
    def on_menuitem_toolbar_toggled(self, value):
        log.debug('on_menuitem_toolbar_toggled')
        component.get('ToolBar').visible(value.get_active())

    def on_menuitem_sidebar_toggled(self, value):
        log.debug('on_menuitem_sidebar_toggled')
        component.get('SideBar').visible(value.get_active())

    def on_menuitem_statusbar_toggled(self, value):
        log.debug('on_menuitem_statusbar_toggled')
        component.get('StatusBar').visible(value.get_active())

    # Help Menu #
    def on_menuitem_homepage_activate(self, data=None):
        log.debug('on_menuitem_homepage_activate')
        deluge.common.open_url_in_browser('http://deluge-torrent.org')

    def on_menuitem_faq_activate(self, data=None):
        log.debug('on_menuitem_faq_activate')
        deluge.common.open_url_in_browser('http://dev.deluge-torrent.org/wiki/Faq')

    def on_menuitem_community_activate(self, data=None):
        log.debug('on_menuitem_community_activate')
        deluge.common.open_url_in_browser('http://forum.deluge-torrent.org/')

    def on_menuitem_about_activate(self, data=None):
        log.debug('on_menuitem_about_activate')
        from deluge.ui.gtkui.aboutdialog import AboutDialog
        AboutDialog().run()

    def on_menuitem_set_unlimited(self, widget):
        log.debug('widget name: %s', widget.get_name())
        funcs = {
            'menuitem_down_speed': 'max_download_speed',
            'menuitem_up_speed': 'max_upload_speed',
            'menuitem_max_connections': 'max_connections',
            'menuitem_upload_slots': 'max_upload_slots'
        }
        if widget.get_name() in funcs:
            torrent_ids = component.get('TorrentView').get_selected_torrents()
            client.core.set_torrent_options(torrent_ids, {funcs[widget.get_name()]: -1})

    def on_menuitem_set_other(self, widget):
        log.debug('widget name: %s', widget.get_name())
        status_map = {
            'menuitem_down_speed': ['max_download_speed', 'max_download_speed'],
            'menuitem_up_speed': ['max_upload_speed', 'max_upload_speed'],
            'menuitem_max_connections': ['max_connections', 'max_connections_global'],
            'menuitem_upload_slots': ['max_upload_slots', 'max_upload_slots_global'],
            'menuitem_stop_seed_at_ratio': ['stop_ratio', 'stop_seed_ratio']
        }

        other_dialog_info = {
            'menuitem_down_speed': [_('Download Speed Limit'), _('Set the maximum download speed'),
                                    _('KiB/s'), 'downloading.svg'],
            'menuitem_up_speed': [_('Upload Speed Limit'), _('Set the maximum upload speed'),
                                  _('KiB/s'), 'seeding.svg'],
            'menuitem_max_connections': [_('Incoming Connections'), _('Set the maximum incoming connections'),
                                         '', gtk.STOCK_NETWORK],
            'menuitem_upload_slots': [_('Peer Upload Slots'), _('Set the maximum upload slots'),
                                      '', gtk.STOCK_SORT_ASCENDING],
            'menuitem_stop_seed_at_ratio': [_('Stop Seed At Ratio'), 'Stop torrent seeding at ratio', '', None]
        }

        core_key = status_map[widget.get_name()][0]
        core_key_global = status_map[widget.get_name()][1]

        def _on_torrent_status(status):
            other_dialog = other_dialog_info[widget.get_name()]
            # Add the default using status value
            if status:
                other_dialog.append(status[core_key_global])

            def set_value(value):
                if value is not None:
                    if value == 0:
                        value += -1
                    options = {core_key: value}
                    if core_key == 'stop_ratio':
                        options['stop_at_ratio'] = True
                    client.core.set_torrent_options(torrent_ids, options)

            dialog = OtherDialog(*other_dialog)
            dialog.run().addCallback(set_value)

        torrent_ids = component.get('TorrentView').get_selected_torrents()
        if len(torrent_ids) == 1:
            core_key_global = core_key
            d = component.get('SessionProxy').get_torrent_status(torrent_ids[0], [core_key])
        else:
            d = client.core.get_config_values([core_key_global])
        d.addCallback(_on_torrent_status)

    def on_menuitem_set_automanaged_on(self, widget):
        client.core.set_torrent_options(component.get('TorrentView').get_selected_torrents(),
                                        {'auto_managed': True})

    def on_menuitem_set_automanaged_off(self, widget):
        client.core.set_torrent_options(component.get('TorrentView').get_selected_torrents(),
                                        {'auto_managed': False})

    def on_menuitem_set_stop_seed_at_ratio_disable(self, widget):
        client.core.set_torrent_options(component.get('TorrentView').get_selected_torrents(),
                                        {'stop_at_ratio': False})

    def on_menuitem_sidebar_zero_toggled(self, widget):
        self.config['sidebar_show_zero'] = widget.get_active()
        component.get('FilterTreeView').update()

    def on_menuitem_sidebar_trackers_toggled(self, widget):
        self.config['sidebar_show_trackers'] = widget.get_active()
        component.get('FilterTreeView').update()

    def on_menuitem_sidebar_owners_toggled(self, widget):
        self.config['sidebar_show_owners'] = widget.get_active()
        component.get('FilterTreeView').update()

    def _on_known_accounts(self, known_accounts):
        known_accounts_to_log = []
        for account in known_accounts:
            account_to_log = {}
            for key, value in account.copy().items():
                if key == 'password':
                    value = '*' * len(value)
                account_to_log[key] = value
            known_accounts_to_log.append(account_to_log)
        log.debug('_on_known_accounts: %s', known_accounts_to_log)
        if len(known_accounts) <= 1:
            return

        self.builder.get_object('menuitem_change_owner').set_visible(True)

        self.change_owner_submenu = gtk.Menu()
        self.change_owner_submenu_items = {}
        maingroup = gtk.RadioMenuItem(None, None)

        self.change_owner_submenu_items[None] = gtk.RadioMenuItem(group=maingroup)

        for account in known_accounts:
            username = account['username']
            item = gtk.RadioMenuItem(group=maingroup, label=username)
            self.change_owner_submenu_items[username] = item
            self.change_owner_submenu.append(item)
            item.connect('toggled', self._on_change_owner_toggled, username)

        self.change_owner_submenu.show_all()
        self.change_owner_submenu_items[None].set_active(True)
        self.change_owner_submenu_items[None].hide()
        self.builder.get_object('menuitem_change_owner').connect(
            'activate', self._on_change_owner_submenu_active
        )
        self.builder.get_object('menuitem_change_owner').set_submenu(self.change_owner_submenu)

    def _on_known_accounts_fail(self, reason):
        self.builder.get_object('menuitem_change_owner').set_visible(False)

    def _on_change_owner_submenu_active(self, widget):
        log.debug('_on_change_owner_submenu_active')
        selected = component.get('TorrentView').get_selected_torrents()
        if len(selected) > 1:
            self.change_owner_submenu_items[None].set_active(True)
            return

        torrent_owner = component.get('TorrentView').get_torrent_status(selected[0])['owner']
        for username, item in self.change_owner_submenu_items.items():
            item.set_active(username == torrent_owner)

    def _on_change_owner_toggled(self, widget, username):
        log.debug('_on_change_owner_toggled')
        update_torrents = []
        selected = component.get('TorrentView').get_selected_torrents()
        for torrent_id in selected:
            torrent_status = component.get('TorrentView').get_torrent_status(torrent_id)
            if torrent_status['owner'] != username:
                update_torrents.append(torrent_id)

        if update_torrents:
            log.debug('Setting torrent owner "%s" on %s', username, update_torrents)

            def failed_change_owner(failure):
                ErrorDialog(
                    _('Ownership Change Error'),
                    _('There was an error while trying changing ownership.'),
                    self.mainwindow.window, details=failure.value.logable()
                ).run()
            client.core.set_torrent_options(
                update_torrents, {'owner': username}).addErrback(failed_change_owner)
예제 #12
0
class AddTorrentDialog(component.Component):
    def __init__(self):
        component.Component.__init__(self, 'AddTorrentDialog')
        self.builder = gtk.Builder()
        # The base dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.ui')
        ))
        # The infohash dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.infohash.ui')
        ))
        # The url dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'add_torrent_dialog.url.ui')
        ))

        self.dialog = self.builder.get_object('dialog_add_torrent')

        self.dialog.connect('delete-event', self._on_delete_event)

        self.builder.connect_signals(self)

        # download?, path, filesize, sequence number, inconsistent?
        self.files_treestore = gtk.TreeStore(
            bool, str, TYPE_UINT64, TYPE_INT64, bool, str)
        self.files_treestore.set_sort_column_id(1, gtk.SORT_ASCENDING)

        # Holds the files info
        self.files = {}
        self.infos = {}
        self.core_config = {}
        self.options = {}

        self.previous_selected_torrent = None

        self.listview_torrents = self.builder.get_object('listview_torrents')
        self.listview_files = self.builder.get_object('listview_files')

        render = gtk.CellRendererText()
        render.connect('edited', self._on_torrent_name_edit)
        render.set_property('editable', True)
        column = gtk.TreeViewColumn(_('Torrent'), render, text=1)
        self.listview_torrents.append_column(column)

        render = gtk.CellRendererToggle()
        render.connect('toggled', self._on_file_toggled)
        column = gtk.TreeViewColumn(None, render, active=0, inconsistent=4)
        self.listview_files.append_column(column)

        column = gtk.TreeViewColumn(_('Filename'))
        render = gtk.CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'stock-id', 5)
        render = gtk.CellRendererText()
        render.set_property('editable', True)
        render.connect('edited', self._on_filename_edited)
        column.pack_start(render, True)
        column.add_attribute(render, 'text', 1)
        column.set_expand(True)
        self.listview_files.append_column(column)

        render = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Size'))
        column.pack_start(render, True)
        column.set_cell_data_func(render, cell_data_size, 2)
        self.listview_files.append_column(column)

        self.torrent_liststore = gtk.ListStore(str, str, str)
        self.listview_torrents.set_model(self.torrent_liststore)
        self.listview_torrents.set_tooltip_column(2)
        self.listview_files.set_model(self.files_treestore)

        self.listview_files.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.listview_torrents.get_selection().connect('changed', self._on_torrent_changed)

        self.setup_move_completed_path_chooser()
        self.setup_download_location_path_chooser()

        # Get default config values from the core
        self.core_keys = [
            'pre_allocate_storage',
            'max_connections_per_torrent',
            'max_upload_slots_per_torrent',
            'max_upload_speed_per_torrent',
            'max_download_speed_per_torrent',
            'prioritize_first_last_pieces',
            'sequential_download',
            'add_paused',
            'download_location',
            'download_location_paths_list',
            'move_completed',
            'move_completed_path',
            'move_completed_paths_list',
        ]
        # self.core_keys += self.move_completed_path_chooser.get_config_keys()
        self.builder.get_object('notebook1').connect('switch-page', self._on_switch_page)

    def start(self):
        self.update_core_config()

    def show(self, focus=False):
        return self.update_core_config(True, focus)

    def _show(self, focus=False):
        if component.get('MainWindow').is_on_active_workspace():
            self.dialog.set_transient_for(component.get('MainWindow').window)
        else:
            self.dialog.set_transient_for(None)

        self.dialog.present()
        if focus:
            self.dialog.window.focus()

        return None

    def hide(self):
        self.dialog.hide()
        self.files = {}
        self.infos = {}
        self.options = {}
        self.previous_selected_torrent = None
        self.torrent_liststore.clear()
        self.files_treestore.clear()
        self.dialog.set_transient_for(component.get('MainWindow').window)
        return None

    def update_core_config(self, show=False, focus=False):
        def _on_config_values(config):
            self.core_config = config
            if self.core_config:
                self.set_default_options()
            if show:
                self._show(focus)

        # Send requests to the core for these config values
        return client.core.get_config_values(self.core_keys).addCallback(_on_config_values)

    def add_from_files(self, filenames):
        new_row = None
        already_added = 0

        for filename in filenames:
            # Get the torrent data from the torrent file
            try:
                info = TorrentInfo(filename)
            except Exception as ex:
                log.debug('Unable to open torrent file: %s', ex)
                ErrorDialog(_('Invalid File'), ex, self.dialog).run()
                continue

            if info.info_hash in self.files:
                already_added += 1
                continue

            new_row = self.torrent_liststore.append([info.info_hash, info.name, xml_escape(filename)])
            self.files[info.info_hash] = info.files
            self.infos[info.info_hash] = info.filedata
            self.listview_torrents.get_selection().select_iter(new_row)

            self.set_default_options()
            self.save_torrent_options(new_row)

        (model, row) = self.listview_torrents.get_selection().get_selected()
        if not row and new_row:
            self.listview_torrents.get_selection().select_iter(new_row)

        self.dialog.set_title(_('Add Torrents (%d)') % len(self.torrent_liststore))

        if already_added:
            log.debug('Tried to add %d duplicate torrents!', already_added)
            ErrorDialog(
                _('Duplicate Torrent(s)'),
                _('You cannot add the same torrent twice. %d torrents were already added.' % already_added),
                self.dialog
            ).run()

    def add_from_magnets(self, uris):
        new_row = None

        for uri in uris:
            magnet = deluge.common.get_magnet_info(uri)
            if not magnet:
                log.error('Invalid magnet: %s', uri)
                continue
            if magnet['info_hash'] in self.infos:
                log.info('Torrent already in Add Dialog list: %s', uri)
                continue
            new_row = self.torrent_liststore.append([magnet['info_hash'], magnet['name'], xml_escape(uri)])
            self.files[magnet['info_hash']] = magnet['files_tree']
            self.infos[magnet['info_hash']] = None
            self.listview_torrents.get_selection().select_iter(new_row)
            self.set_default_options()
            self.save_torrent_options(new_row)

        (model, row) = self.listview_torrents.get_selection().get_selected()
        if not row and new_row:
            self.listview_torrents.get_selection().select_iter(new_row)

    def _on_torrent_changed(self, treeselection):
        (model, row) = treeselection.get_selected()
        if row is None or not model.iter_is_valid(row):
            self.files_treestore.clear()
            self.previous_selected_torrent = None
            return

        if model[row][0] not in self.files:
            self.files_treestore.clear()
            self.previous_selected_torrent = None
            return

        # Save the previous torrents options
        self.save_torrent_options()
        # Update files list
        files_list = self.files[model.get_value(row, 0)]

        self.prepare_file_store(files_list)

        if self.core_config == {}:
            self.update_core_config()

        # Update the options frame
        self.update_torrent_options(model.get_value(row, 0))

        self.previous_selected_torrent = row

    def _on_torrent_name_edit(self, w, row, new_name):
        # TODO: Update torrent name
        pass

    def _on_switch_page(self, widget, page, page_num):
        # Save the torrent options when switching notebook pages
        self.save_torrent_options()

    def prepare_file_store(self, files):
        with listview_replace_treestore(self.listview_files):
            split_files = {}
            for i, _file in enumerate(files):
                self.prepare_file(
                    _file, _file['path'], i, _file['download'], split_files
                )
            self.add_files(None, split_files)
        self.listview_files.expand_row(b'0', False)

    def prepare_file(self, _file, file_name, file_num, download, files_storage):
        first_slash_index = file_name.find(os.path.sep)
        if first_slash_index == -1:
            files_storage[file_name] = (file_num, _file, download)
        else:
            file_name_chunk = file_name[:first_slash_index + 1]
            if file_name_chunk not in files_storage:
                files_storage[file_name_chunk] = {}
            self.prepare_file(_file, file_name[first_slash_index + 1:],
                              file_num, download, files_storage[file_name_chunk])

    def add_files(self, parent_iter, split_files):
        ret = 0
        for key, value in split_files.items():
            if key.endswith(os.path.sep):
                chunk_iter = self.files_treestore.append(
                    parent_iter, [True, key, 0, -1, False, gtk.STOCK_DIRECTORY])
                chunk_size = self.add_files(chunk_iter, value)
                self.files_treestore.set(chunk_iter, 2, chunk_size)
                ret += chunk_size
            else:
                self.files_treestore.append(parent_iter, [
                    value[2], key, value[1]['size'],
                    value[0], False, gtk.STOCK_FILE
                ])
                ret += value[1]['size']
        if parent_iter and self.files_treestore.iter_has_child(parent_iter):
            # Iterate through the children and see what we should label the
            # folder, download true, download false or inconsistent.
            itr = self.files_treestore.iter_children(parent_iter)
            download = []
            download_value = False
            inconsistent = False
            while itr:
                download.append(self.files_treestore.get_value(itr, 0))
                itr = self.files_treestore.iter_next(itr)

            if sum(download) == len(download):
                download_value = True
            elif sum(download) == 0:
                download_value = False
            else:
                inconsistent = True

            self.files_treestore.set_value(parent_iter, 0, download_value)
            self.files_treestore.set_value(parent_iter, 4, inconsistent)
        return ret

    def load_path_choosers_data(self):
        self.move_completed_path_chooser.set_text(self.core_config['move_completed_path'],
                                                  cursor_end=False, default_text=True)
        self.download_location_path_chooser.set_text(self.core_config['download_location'],
                                                     cursor_end=False, default_text=True)
        self.builder.get_object('chk_move_completed').set_active(self.core_config['move_completed'])

    def setup_move_completed_path_chooser(self):
        self.move_completed_hbox = self.builder.get_object('hbox_move_completed_chooser')
        self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
        self.move_completed_hbox.add(self.move_completed_path_chooser)
        self.move_completed_hbox.show_all()

    def setup_download_location_path_chooser(self):
        self.download_location_hbox = self.builder.get_object('hbox_download_location_chooser')
        self.download_location_path_chooser = PathChooser('download_location_paths_list')
        self.download_location_hbox.add(self.download_location_path_chooser)
        self.download_location_hbox.show_all()

    def update_torrent_options(self, torrent_id):
        if torrent_id not in self.options:
            self.set_default_options()
            return

        options = self.options[torrent_id]

        self.download_location_path_chooser.set_text(options['download_location'], cursor_end=True)
        self.move_completed_path_chooser.set_text(options['move_completed_path'], cursor_end=True)

        self.builder.get_object('spin_maxdown').set_value(
            options['max_download_speed'])
        self.builder.get_object('spin_maxup').set_value(
            options['max_upload_speed'])
        self.builder.get_object('spin_maxconnections').set_value(
            options['max_connections'])
        self.builder.get_object('spin_maxupslots').set_value(
            options['max_upload_slots'])
        self.builder.get_object('chk_paused').set_active(
            options['add_paused'])
        self.builder.get_object('chk_pre_alloc').set_active(
            options['pre_allocate_storage'])
        self.builder.get_object('chk_prioritize').set_active(
            options['prioritize_first_last_pieces'])
        self.builder.get_object('chk_sequential_download').set_active(
            options['sequential_download'])
        self.builder.get_object('chk_move_completed').set_active(
            options['move_completed'])

    def save_torrent_options(self, row=None):
        # Keeps the torrent options dictionary up-to-date with what the user has
        # selected.
        if row is None:
            if self.previous_selected_torrent and \
                    self.torrent_liststore.iter_is_valid(self.previous_selected_torrent):
                row = self.previous_selected_torrent
            else:
                return

        torrent_id = self.torrent_liststore.get_value(row, 0)

        if torrent_id in self.options:
            options = self.options[torrent_id]
        else:
            options = {}

        options['download_location'] = self.download_location_path_chooser.get_text()
        options['move_completed_path'] = self.move_completed_path_chooser.get_text()
        options['pre_allocate_storage'] = self.builder.get_object('chk_pre_alloc').get_active()
        options['move_completed'] = self.builder.get_object('chk_move_completed').get_active()
        options['max_download_speed'] = self.builder.get_object('spin_maxdown').get_value()
        options['max_upload_speed'] = self.builder.get_object('spin_maxup').get_value()
        options['max_connections'] = self.builder.get_object('spin_maxconnections').get_value_as_int()
        options['max_upload_slots'] = self.builder.get_object('spin_maxupslots').get_value_as_int()
        options['add_paused'] = self.builder.get_object('chk_paused').get_active()
        options['prioritize_first_last_pieces'] = self.builder.get_object('chk_prioritize').get_active()
        options['sequential_download'] = self.builder.get_object(
            'chk_sequential_download').get_active() or False
        options['move_completed'] = self.builder.get_object('chk_move_completed').get_active()
        options['seed_mode'] = self.builder.get_object('chk_seed_mode').get_active()

        self.options[torrent_id] = options

        # Save the file priorities
        files_priorities = self.build_priorities(
            self.files_treestore.get_iter_first(), {}
        )

        if len(files_priorities) > 0:
            for i, file_dict in enumerate(self.files[torrent_id]):
                file_dict['download'] = files_priorities[i]

    def build_priorities(self, _iter, priorities):
        while _iter is not None:
            if self.files_treestore.iter_has_child(_iter):
                self.build_priorities(self.files_treestore.iter_children(_iter), priorities)
            elif not self.files_treestore.get_value(_iter, 1).endswith(os.path.sep):
                priorities[self.files_treestore.get_value(_iter, 3)] = self.files_treestore.get_value(_iter, 0)
            _iter = self.files_treestore.iter_next(_iter)
        return priorities

    def set_default_options(self):
        if not self.core_config:
            # update_core_config will call this method again.
            self.update_core_config()
            return

        self.load_path_choosers_data()

        self.builder.get_object('chk_pre_alloc').set_active(
            self.core_config['pre_allocate_storage'])
        self.builder.get_object('spin_maxdown').set_value(
            self.core_config['max_download_speed_per_torrent'])
        self.builder.get_object('spin_maxup').set_value(
            self.core_config['max_upload_speed_per_torrent'])
        self.builder.get_object('spin_maxconnections').set_value(
            self.core_config['max_connections_per_torrent'])
        self.builder.get_object('spin_maxupslots').set_value(
            self.core_config['max_upload_slots_per_torrent'])
        self.builder.get_object('chk_paused').set_active(
            self.core_config['add_paused'])
        self.builder.get_object('chk_prioritize').set_active(
            self.core_config['prioritize_first_last_pieces'])
        self.builder.get_object('chk_sequential_download').set_active(
            self.core_config['sequential_download'])
        self.builder.get_object('chk_move_completed').set_active(
            self.core_config['move_completed'])
        self.builder.get_object('chk_seed_mode').set_active(False)

    def get_file_priorities(self, torrent_id):
        # A list of priorities
        files_list = []

        for file_dict in self.files[torrent_id]:
            if not file_dict['download']:
                files_list.append(0)
            else:
                files_list.append(1)

        return files_list

    def _on_file_toggled(self, render, path):
        (model, paths) = self.listview_files.get_selection().get_selected_rows()
        if len(paths) > 1:
            for path in paths:
                row = model.get_iter(path)
                self.toggle_iter(row)
        else:
            row = model.get_iter(path)
            self.toggle_iter(row)
        self.update_treeview_toggles(self.files_treestore.get_iter_first())

    def toggle_iter(self, _iter, toggle_to=None):
        if toggle_to is None:
            toggle_to = not self.files_treestore.get_value(_iter, 0)
        self.files_treestore.set_value(_iter, 0, toggle_to)
        if self.files_treestore.iter_has_child(_iter):
            child = self.files_treestore.iter_children(_iter)
            while child is not None:
                self.toggle_iter(child, toggle_to)
                child = self.files_treestore.iter_next(child)

    def update_treeview_toggles(self, _iter):
        toggle_inconsistent = -1
        this_level_toggle = None
        while _iter is not None:
            if self.files_treestore.iter_has_child(_iter):
                toggle = self.update_treeview_toggles(self.files_treestore.iter_children(_iter))
                if toggle == toggle_inconsistent:
                    self.files_treestore.set_value(_iter, 4, True)
                else:
                    self.files_treestore.set_value(_iter, 0, toggle)
                    # set inconsistent to false
                    self.files_treestore.set_value(_iter, 4, False)
            else:
                toggle = self.files_treestore.get_value(_iter, 0)
            if this_level_toggle is None:
                this_level_toggle = toggle
            elif this_level_toggle != toggle:
                this_level_toggle = toggle_inconsistent
            _iter = self.files_treestore.iter_next(_iter)
        return this_level_toggle

    def on_button_file_clicked(self, widget):
        log.debug('on_button_file_clicked')
        # Setup the filechooserdialog
        chooser = gtk.FileChooserDialog(
            _('Choose a .torrent file'),
            None,
            gtk.FILE_CHOOSER_ACTION_OPEN,
            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
                     gtk.RESPONSE_OK)
        )

        chooser.set_transient_for(self.dialog)
        chooser.set_select_multiple(True)
        chooser.set_property('skip-taskbar-hint', True)
        chooser.set_local_only(False)

        # Add .torrent and * file filters
        file_filter = gtk.FileFilter()
        file_filter.set_name(_('Torrent files'))
        file_filter.add_pattern('*.' + 'torrent')
        chooser.add_filter(file_filter)
        file_filter = gtk.FileFilter()
        file_filter.set_name(_('All files'))
        file_filter.add_pattern('*')
        chooser.add_filter(file_filter)

        # Load the 'default_load_path' from the config
        self.config = ConfigManager('gtkui.conf')
        if self.config['default_load_path'] is not None:
            chooser.set_current_folder(self.config['default_load_path'])

        # Run the dialog
        response = chooser.run()

        if response == gtk.RESPONSE_OK:
            result = chooser.get_filenames()
            self.config['default_load_path'] = chooser.get_current_folder()
        else:
            chooser.destroy()
            return

        chooser.destroy()
        self.add_from_files(result)

    def on_button_url_clicked(self, widget):
        log.debug('on_button_url_clicked')
        dialog = self.builder.get_object('url_dialog')
        entry = self.builder.get_object('entry_url')

        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_transient_for(self.dialog)
        entry.grab_focus()

        text = get_clipboard_text()
        if text and deluge.common.is_url(text) or deluge.common.is_magnet(text):
            entry.set_text(text)

        dialog.show_all()
        response = dialog.run()

        if response == gtk.RESPONSE_OK:
            url = entry.get_text().decode('utf-8')
        else:
            url = None

        entry.set_text('')
        dialog.hide()

        # This is where we need to fetch the .torrent file from the URL and
        # add it to the list.
        log.debug('url: %s', url)
        if url:
            if deluge.common.is_url(url):
                self.add_from_url(url)
            elif deluge.common.is_magnet(url):
                self.add_from_magnets([url])
            else:
                ErrorDialog(
                    _('Invalid URL'),
                    '%s %s' % (url, _('is not a valid URL.')),
                    self.dialog
                ).run()

    def add_from_url(self, url):
        dialog = gtk.Dialog(
            _('Downloading...'),
            flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
            parent=self.dialog)
        dialog.set_transient_for(self.dialog)

        pb = gtk.ProgressBar()
        dialog.vbox.pack_start(pb, True, True, 0)
        dialog.show_all()

        # Create a tmp file path
        import tempfile
        tmp_fd, tmp_file = tempfile.mkstemp(prefix='deluge_url.', suffix='.torrent')

        def on_part(data, current_length, total_length):
            if total_length:
                percent = current_length / total_length
                pb.set_fraction(percent)
                pb.set_text('%.2f%% (%s / %s)' % (
                    percent * 100,
                    deluge.common.fsize(current_length),
                    deluge.common.fsize(total_length)))
            else:
                pb.pulse()
                pb.set_text('%s' % deluge.common.fsize(current_length))

        def on_download_success(result):
            self.add_from_files([result])
            dialog.destroy()

        def on_download_fail(result):
            log.debug('Download failed: %s', result)
            dialog.destroy()
            ErrorDialog(
                _('Download Failed'), '%s %s' % (_('Failed to download:'), url),
                details=result.getErrorMessage(), parent=self.dialog
            ).run()
            return result

        d = download_file(url, tmp_file, on_part)
        os.close(tmp_fd)
        d.addCallbacks(on_download_success, on_download_fail)

    def on_button_hash_clicked(self, widget):
        log.debug('on_button_hash_clicked')
        dialog = self.builder.get_object('dialog_infohash')
        entry = self.builder.get_object('entry_hash')
        textview = self.builder.get_object('text_trackers')

        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_transient_for(self.dialog)
        entry.grab_focus()

        text = get_clipboard_text()
        if deluge.common.is_infohash(text):
            entry.set_text(text)

        dialog.show_all()
        response = dialog.run()
        infohash = entry.get_text().strip()
        if response == gtk.RESPONSE_OK and deluge.common.is_infohash(infohash):
            # Create a list of trackers from the textview buffer
            tview_buf = textview.get_buffer()
            trackers_text = tview_buf.get_text(*tview_buf.get_bounds())
            log.debug('Create torrent tracker lines: %s', trackers_text)
            trackers = list(trackers_tiers_from_text(trackers_text).keys())

            # Convert the information to a magnet uri, this is just easier to
            # handle this way.
            log.debug('trackers: %s', trackers)
            magnet = deluge.common.create_magnet_uri(infohash, infohash, trackers)
            log.debug('magnet uri: %s', magnet)
            self.add_from_magnets([magnet])

        entry.set_text('')
        textview.get_buffer().set_text('')
        dialog.hide()

    def on_button_remove_clicked(self, widget):
        log.debug('on_button_remove_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        torrent_id = model.get_value(row, 0)

        model.remove(row)
        del self.files[torrent_id]
        del self.infos[torrent_id]

    def on_button_trackers_clicked(self, widget):
        log.debug('on_button_trackers_clicked')

    def on_button_cancel_clicked(self, widget):
        log.debug('on_button_cancel_clicked')
        self.hide()

    def on_button_add_clicked(self, widget):
        log.debug('on_button_add_clicked')
        self.add_torrents()
        self.hide()

    def add_torrents(self):
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is not None:
            self.save_torrent_options(row)

        torrents_to_add = []

        row = self.torrent_liststore.get_iter_first()
        while row is not None:
            torrent_id = self.torrent_liststore.get_value(row, 0)
            filename = xml_unescape(self.torrent_liststore.get_value(row, 2))
            try:
                options = self.options[torrent_id]
            except KeyError:
                options = None

            file_priorities = self.get_file_priorities(torrent_id)
            if options is not None:
                options['file_priorities'] = file_priorities

            if deluge.common.is_magnet(filename):
                del options['file_priorities']
                client.core.add_torrent_magnet(filename, options)
            else:
                torrents_to_add.append((os.path.split(filename)[-1],
                                        base64.encodestring(self.infos[torrent_id]),
                                        options))
            row = self.torrent_liststore.iter_next(row)

        def on_torrents_added(errors):
            if errors:
                log.info('Failed to add %d out of %d torrents.', len(errors), len(torrents_to_add))
                for e in errors:
                    log.info('Torrent add failed: %s', e)
            else:
                log.info('Successfully added %d torrents.', len(torrents_to_add))
        client.core.add_torrent_files(torrents_to_add).addCallback(on_torrents_added)

    def on_button_apply_clicked(self, widget):
        log.debug('on_button_apply_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        self.save_torrent_options(row)

        # The options, except file renames, we want all the torrents to have
        options = self.options[model.get_value(row, 0)].copy()
        options.pop('mapped_files', None)

        # Set all the torrent options
        row = model.get_iter_first()
        while row is not None:
            torrent_id = model.get_value(row, 0)
            self.options[torrent_id].update(options)
            row = model.iter_next(row)

    def on_button_revert_clicked(self, widget):
        log.debug('on_button_revert_clicked')
        (model, row) = self.listview_torrents.get_selection().get_selected()
        if row is None:
            return

        del self.options[model.get_value(row, 0)]
        self.set_default_options()

    def on_chk_move_completed_toggled(self, widget):
        value = widget.get_active()
        self.move_completed_path_chooser.set_sensitive(value)

    def _on_delete_event(self, widget, event):
        self.hide()
        return True

    def get_file_path(self, row, path=''):
        if not row:
            return path

        path = self.files_treestore[row][1] + path
        return self.get_file_path(self.files_treestore.iter_parent(row), path)

    def _on_filename_edited(self, renderer, path, new_text):
        index = self.files_treestore[path][3]

        new_text = new_text.strip(os.path.sep).strip()

        # Return if the text hasn't changed
        if new_text == self.files_treestore[path][1]:
            return

        # Get the tree iter
        itr = self.files_treestore.get_iter(path)

        # Get the torrent_id
        (model, row) = self.listview_torrents.get_selection().get_selected()
        torrent_id = model[row][0]

        if 'mapped_files' not in self.options[torrent_id]:
            self.options[torrent_id]['mapped_files'] = {}

        if index > -1:
            # We're renaming a file! Yay! That's easy!
            if not new_text:
                return
            parent = self.files_treestore.iter_parent(itr)
            file_path = os.path.join(self.get_file_path(parent), new_text)
            # Don't rename if filename exists
            if parent:
                for row in self.files_treestore[parent].iterchildren():
                    if new_text == row[1]:
                        return
            if os.path.sep in new_text:
                # There are folders in this path, so we need to create them
                # and then move the file iter to top
                split_text = new_text.split(os.path.sep)
                for s in split_text[:-1]:
                    parent = self.files_treestore.append(parent, [True, s, 0, -1, False, gtk.STOCK_DIRECTORY])

                self.files_treestore[itr][1] = split_text[-1]
                reparent_iter(self.files_treestore, itr, parent)
            else:
                # Update the row's text
                self.files_treestore[itr][1] = new_text

            # Update the mapped_files dict in the options with the index and new
            # file path.
            # We'll send this to the core when adding the torrent so it knows
            # what to rename before adding.
            self.options[torrent_id]['mapped_files'][index] = file_path
            self.files[torrent_id][index]['path'] = file_path
        else:
            # Folder!
            def walk_tree(row):
                if not row:
                    return

                # Get the file path base once, since it will be the same for
                # all siblings
                file_path_base = self.get_file_path(
                    self.files_treestore.iter_parent(row)
                )

                # Iterate through all the siblings at this level
                while row:
                    # We recurse if there are children
                    if self.files_treestore.iter_has_child(row):
                        walk_tree(self.files_treestore.iter_children(row))

                    index = self.files_treestore[row][3]

                    if index > -1:
                        # Get the new full path for this file
                        file_path = file_path_base + self.files_treestore[row][1]

                        # Update the file path in the mapped_files dict
                        self.options[torrent_id]['mapped_files'][index] = file_path
                        self.files[torrent_id][index]['path'] = file_path

                    # Get the next siblings iter
                    row = self.files_treestore.iter_next(row)

            # Update the treestore row first so that when walking the tree
            # we can construct the new proper paths

            # We need to check if this folder has been split
            if os.path.sep in new_text:
                # It's been split, so we need to add new folders and then re-parent
                # itr.
                parent = self.files_treestore.iter_parent(itr)
                split_text = new_text.split(os.path.sep)
                for s in split_text[:-1]:
                    # We don't iterate over the last item because we'll just use
                    # the existing itr and change the text
                    parent = self.files_treestore.append(parent, [
                        True, s + os.path.sep, 0, -1, False, gtk.STOCK_DIRECTORY
                    ])

                self.files_treestore[itr][1] = split_text[-1] + os.path.sep

                # Now re-parent itr to parent
                reparent_iter(self.files_treestore, itr, parent)
                itr = parent

                # We need to re-expand the view because it might contracted
                # if we change the root iter
                self.listview_files.expand_row(b'0', False)
            else:
                # This was a simple folder rename without any splits, so just
                # change the path for itr
                self.files_treestore[itr][1] = new_text + os.path.sep

            # Walk through the tree from 'itr' and add all the new file paths
            # to the 'mapped_files' option
            walk_tree(itr)
예제 #13
0
 def setup_download_location_path_chooser(self):
     self.download_location_hbox = self.builder.get_object('hbox_download_location_chooser')
     self.download_location_path_chooser = PathChooser('download_location_paths_list')
     self.download_location_hbox.add(self.download_location_path_chooser)
     self.download_location_hbox.show_all()
예제 #14
0
 def setup_move_completed_path_chooser(self):
     self.move_completed_hbox = self.builder.get_object('hbox_move_completed_chooser')
     self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
     self.move_completed_hbox.add(self.move_completed_path_chooser)
     self.move_completed_hbox.show_all()