Пример #1
0
class PresenceServiceWatcher(VBox):
    def __init__(self, bus, unique_name, log):
        VBox.__init__(self)

        self.bus = bus
        self.unique_name = unique_name
        self.proxy = bus.get_object(unique_name, PS_PATH)
        self.iface = dbus.Interface(self.proxy, PS_IFACE)
        self.log = log

        self.activities = None
        self.iface.connect_to_signal('ActivityAppeared',
                                     self._on_activity_appeared)
        self.iface.connect_to_signal('ActivityDisappeared',
                                     self._on_activity_disappeared)
        self.iface.GetActivities(reply_handler=self._on_get_activities_success,
                                 error_handler=self._on_get_activities_failure)

        self.buddies = None
        self.iface.connect_to_signal('BuddyAppeared', self._on_buddy_appeared)
        self.iface.connect_to_signal('BuddyDisappeared',
                                     self._on_buddy_disappeared)
        self.iface.GetBuddies(reply_handler=self._on_get_buddies_success,
                              error_handler=self._on_get_buddies_failure)

        # keep this in sync with the ACT_COL_ constants
        self.activities_list_store = ListStore(
            str,  # object path
            int,  # weight (bold if new)
            bool,  # strikethrough (dead)
            str,  # ID
            str,  # color
            str,  # type
            str,  # name
            str,  # conn
            str,  # channels
            str,  # buddies
        )

        self.pack_start(Label('Activities:'), False, False)

        self.activities_list = TreeView(self.activities_list_store)
        c = self.activities_list.insert_column_with_attributes(
            0,
            'Object path',
            CellRendererText(),
            text=ACT_COL_PATH,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_PATH)
        c = self.activities_list.insert_column_with_attributes(
            1,
            'ID',
            CellRendererText(),
            text=ACT_COL_ID,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_ID)
        c = self.activities_list.insert_column_with_attributes(
            2,
            'Color',
            CellRendererText(),
            text=ACT_COL_COLOR,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_COLOR)
        c = self.activities_list.insert_column_with_attributes(
            3,
            'Type',
            CellRendererText(),
            text=ACT_COL_TYPE,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_TYPE)
        c = self.activities_list.insert_column_with_attributes(
            4,
            'Name',
            CellRendererText(),
            text=ACT_COL_NAME,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_NAME)
        c = self.activities_list.insert_column_with_attributes(
            5,
            'Connection',
            CellRendererText(),
            text=ACT_COL_CONN,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(ACT_COL_CONN)
        c = self.activities_list.insert_column_with_attributes(
            6,
            'Channels',
            CellRendererText(),
            text=ACT_COL_CHANNELS,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)
        c = self.activities_list.insert_column_with_attributes(
            7,
            'Buddies',
            CellRendererText(),
            text=ACT_COL_BUDDIES,
            weight=ACT_COL_WEIGHT,
            strikethrough=ACT_COL_STRIKE)
        c.set_resizable(True)

        scroller = ScrolledWindow()
        scroller.add(self.activities_list)
        self.pack_start(scroller)

        # keep this in sync with the BUDDY_COL_ constants
        self.buddies_list_store = ListStore(str, int, bool, str, bool, str,
                                            str, str, str, str, str)

        self.pack_start(Label('Buddies:'), False, False)
        self.buddies_list = TreeView(self.buddies_list_store)
        c = self.buddies_list.insert_column_with_attributes(
            0,
            'Object path',
            CellRendererText(),
            text=BUDDY_COL_PATH,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_PATH)
        c = self.buddies_list.insert_column_with_attributes(
            1,
            'Pubkey',
            CellRendererText(),
            text=BUDDY_COL_KEY_ID,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_KEY_ID)
        c = self.buddies_list.insert_column_with_attributes(
            2,
            'Nick',
            CellRendererText(),
            text=BUDDY_COL_NICK,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_NICK)
        c = self.buddies_list.insert_column_with_attributes(
            3, 'Owner', CellRendererToggle(), active=BUDDY_COL_OWNER)
        c = self.buddies_list.insert_column_with_attributes(
            4,
            'Color',
            CellRendererText(),
            text=BUDDY_COL_COLOR,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_OWNER)
        c = self.buddies_list.insert_column_with_attributes(
            5,
            'IPv4',
            CellRendererText(),
            text=BUDDY_COL_IP4,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_IP4)
        c = self.buddies_list.insert_column_with_attributes(
            6,
            'CurAct',
            CellRendererText(),
            text=BUDDY_COL_CUR_ACT,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_CUR_ACT)
        c = self.buddies_list.insert_column_with_attributes(
            7,
            'Activities',
            CellRendererText(),
            text=BUDDY_COL_ACTIVITIES,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_ACTIVITIES)
        c = self.buddies_list.insert_column_with_attributes(
            8,
            'Handles',
            CellRendererText(),
            text=BUDDY_COL_HANDLES,
            weight=BUDDY_COL_WEIGHT,
            strikethrough=BUDDY_COL_STRIKE)
        c.set_resizable(True)
        c.set_sort_column_id(BUDDY_COL_HANDLES)

        scroller = ScrolledWindow()
        scroller.add(self.buddies_list)
        self.pack_start(scroller)

        self.iface.connect_to_signal('ActivityInvitation',
                                     self._on_activity_invitation)
        self.iface.connect_to_signal('PrivateInvitation',
                                     self._on_private_invitation)

    def _on_get_activities_success(self, paths):
        self.log('INFO: PS GetActivities() returned %r', paths)
        self.activities = {}
        for path in paths:
            self.activities[path] = ActivityWatcher(self, path)

    def _on_get_activities_failure(self, e):
        self.log('ERROR: PS GetActivities() failed with %s', e)

    def add_activity(self, act):
        path = act.object_path
        if path.startswith('/org/laptop/Sugar/Presence/Activities/'):
            path = '.../' + path[38:]
        return self.activities_list_store.append(
            (path, 700, False, act.id, act.color, act.type, act.name, act.conn,
             '?', '?'))

    def remove_activity(self, act):
        self.activities.pop(act.object_path, None)
        self.activities_list_store.remove(act.iter)

    def _on_activity_appeared(self, path):
        if self.activities is None:
            return
        self.log('INFO: PS emitted ActivityAppeared("%s")', path)
        self.activities[path] = ActivityWatcher(self, path)

    def _on_activity_disappeared(self, path):
        if self.activities is None:
            return
        self.log('INFO: PS emitted ActivityDisappeared("%s")', path)
        act = self.activities.get(path)
        if act is None:
            self.log(
                'WARNING: Trying to remove activity "%s" which is '
                'already absent', path)
        else:
            # we don't remove the activity straight away, just cross it out
            act.disappear()

    def _on_activity_invitation(self, path):
        self.log('INFO: PS emitted ActivityInvitation("%s")', path)

    def _on_private_invitation(self, bus_name, conn, channel):
        self.log('INFO: PS emitted PrivateInvitation("%s", "%s", "%s")',
                 bus_name, conn, channel)

    def _on_get_buddies_success(self, paths):
        self.log('INFO: PS GetBuddies() returned %r', paths)
        self.buddies = {}
        for path in paths:
            self.buddies[path] = BuddyWatcher(self, path)

    def _on_get_buddies_failure(self, e):
        self.log('ERROR: PS GetBuddies() failed with %s', e)

    def add_buddy(self, b):
        path = b.object_path
        if path.startswith('/org/laptop/Sugar/Presence/Buddies/'):
            path = '.../' + path[35:]
        return self.buddies_list_store.append(
            (path, 700, False, b.nick, b.owner, b.color, b.ipv4, b.cur_act,
             b.keyid, '?', '?'))

    def remove_buddy(self, b):
        self.buddies.pop(b.object_path, None)
        self.buddies_list_store.remove(b.iter)

    def _on_buddy_appeared(self, path):
        if self.buddies is None:
            return
        self.log('INFO: PS emitted BuddyAppeared("%s")', path)
        self.buddies[path] = BuddyWatcher(self, path)

    def _on_buddy_disappeared(self, path):
        if self.buddies is None:
            return
        self.log('INFO: PS emitted BuddyDisappeared("%s")', path)
        b = self.buddies.get(path)
        if b is None:
            self.log(
                'ERROR: Trying to remove buddy "%s" which is already '
                'absent', path)
        else:
            # we don't remove the activity straight away, just cross it out
            b.disappear()
Пример #2
0
class PeersTab(Tab):
    def __init__(self):
        super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label')

        self.peer_menu = self.main_builder.get_object('menu_peer_tab')
        component.get('MainWindow').connect_signals(self)

        self.listview = self.main_builder.get_object('peers_listview')
        self.listview.props.has_tooltip = True
        self.listview.connect('button-press-event',
                              self._on_button_press_event)
        self.listview.connect('query-tooltip', self._on_query_tooltip)

        # flag, ip, client, downspd, upspd, country code, int_ip, seed/peer icon, progress
        self.liststore = ListStore(Pixbuf, str, str, int, int, str, float,
                                   Pixbuf, float)
        self.cached_flag_pixbufs = {}

        self.seed_pixbuf = icon_seeding
        self.peer_pixbuf = icon_downloading

        # key is ip address, item is row iter
        self.peers = {}

        # Country column
        column = TreeViewColumn()
        render = CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'pixbuf', 0)
        column.set_sort_column_id(5)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(20)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Address column
        column = TreeViewColumn(_('Address'))
        render = CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'pixbuf', 7)
        render = CellRendererText()
        column.pack_start(render, False)
        column.add_attribute(render, 'text', 1)
        column.set_sort_column_id(6)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Client column
        column = TreeViewColumn(_('Client'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.add_attribute(render, 'text', 2)
        column.set_sort_column_id(2)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Progress column
        column = TreeViewColumn(_('Progress'))
        render = CellRendererProgress()
        column.pack_start(render, True)
        column.set_cell_data_func(render, cell_data_peer_progress, 8)
        column.set_sort_column_id(8)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Down Speed column
        column = TreeViewColumn(_('Down Speed'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.set_cell_data_func(render, cell_data_speed_down, 3)
        column.set_sort_column_id(3)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(50)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Up Speed column
        column = TreeViewColumn(_('Up Speed'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.set_cell_data_func(render, cell_data_speed_up, 4)
        column.set_sort_column_id(4)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(50)
        # Bugfix: Last column needs max_width set to stop scrollbar appearing
        column.set_max_width(150)
        column.set_reorderable(True)
        self.listview.append_column(column)

        self.listview.set_model(self.liststore)

        self.load_state()

        self.torrent_id = None

    def save_state(self):
        # Get the current sort order of the view
        column_id, sort_order = self.liststore.get_sort_column_id()

        # Setup state dict
        state = {
            'columns': {},
            'sort_id': column_id,
            'sort_order': int(sort_order) if sort_order else None
        }

        for index, column in enumerate(self.listview.get_columns()):
            state['columns'][column.get_title()] = {
                'position': index,
                'width': column.get_width()
            }
        save_pickled_state_file('peers_tab.state', state)

    def load_state(self):
        state = load_pickled_state_file('peers_tab.state')

        if state is None:
            return

        if len(state['columns']) != len(self.listview.get_columns()):
            log.warning('peers_tab.state is not compatible! rejecting..')
            return

        if state['sort_id'] and state['sort_order'] is not None:
            self.liststore.set_sort_column_id(state['sort_id'],
                                              state['sort_order'])

        for (index, column) in enumerate(self.listview.get_columns()):
            cname = column.get_title()
            if cname in state['columns']:
                cstate = state['columns'][cname]
                column.set_sizing(TREE_VIEW_COLUMN_FIXED)
                column.set_fixed_width(
                    cstate['width'] if cstate['width'] > 0 else 10)
                if state['sort_id'] == index and state[
                        'sort_order'] is not None:
                    column.set_sort_indicator(True)
                    column.set_sort_order(state['sort_order'])
                if cstate['position'] != index:
                    # Column is in wrong position
                    if cstate['position'] == 0:
                        self.listview.move_column_after(column, None)
                    elif self.listview.get_columns()[cstate['position'] -
                                                     1].get_title() != cname:
                        self.listview.move_column_after(
                            column,
                            self.listview.get_columns()[cstate['position'] -
                                                        1])

    def update(self):
        # Get the first selected torrent
        torrent_id = component.get('TorrentView').get_selected_torrents()

        # Only use the first torrent in the list or return if None selected
        if len(torrent_id) != 0:
            torrent_id = torrent_id[0]
        else:
            # No torrent is selected in the torrentview
            self.liststore.clear()
            return

        if torrent_id != self.torrent_id:
            # We only want to do this if the torrent_id has changed
            self.liststore.clear()
            self.peers = {}
            self.torrent_id = torrent_id

        component.get('SessionProxy').get_torrent_status(
            torrent_id, ['peers']).addCallback(self._on_get_torrent_status)

    def get_flag_pixbuf(self, country):
        if not country.strip():
            return None

        if country not in self.cached_flag_pixbufs:
            # We haven't created a pixbuf for this country yet
            try:
                self.cached_flag_pixbufs[country] = pixbuf_new_from_file(
                    deluge.common.resource_filename(
                        'deluge',
                        os.path.join('ui', 'data', 'pixmaps', 'flags',
                                     country.lower() + '.png')))
            except Exception as ex:
                log.debug('Unable to load flag: %s', ex)
                return None

        return self.cached_flag_pixbufs[country]

    def _on_get_torrent_status(self, status):
        new_ips = set()
        for peer in status['peers']:
            new_ips.add(peer['ip'])
            if peer['ip'] in self.peers:
                # We already have this peer in our list, so lets just update it
                row = self.peers[peer['ip']]
                if not self.liststore.iter_is_valid(row):
                    # This iter is invalid, delete it and continue to next iteration
                    del self.peers[peer['ip']]
                    continue
                values = self.liststore.get(row, 3, 4, 5, 7, 8)
                if peer['down_speed'] != values[0]:
                    self.liststore.set_value(row, 3, peer['down_speed'])
                if peer['up_speed'] != values[1]:
                    self.liststore.set_value(row, 4, peer['up_speed'])
                if peer['country'] != values[2]:
                    self.liststore.set_value(row, 5, peer['country'])
                    self.liststore.set_value(
                        row, 0, self.get_flag_pixbuf(peer['country']))
                if peer['seed']:
                    icon = self.seed_pixbuf
                else:
                    icon = self.peer_pixbuf

                if icon != values[3]:
                    self.liststore.set_value(row, 7, icon)

                if peer['progress'] != values[4]:
                    self.liststore.set_value(row, 8, peer['progress'])
            else:
                # Peer is not in list so we need to add it

                # Create an int IP address for sorting purposes
                if peer['ip'].count(':') == 1:
                    # This is an IPv4 address
                    ip_int = sum([
                        int(byte) << shift for byte, shift in zip(
                            peer['ip'].split(':')[0].split('.'), (24, 16, 8,
                                                                  0))
                    ])
                    peer_ip = peer['ip']
                else:
                    # This is an IPv6 address
                    import socket
                    import binascii
                    # Split out the :port
                    ip = ':'.join(peer['ip'].split(':')[:-1])
                    ip_int = int(
                        binascii.hexlify(socket.inet_pton(socket.AF_INET6,
                                                          ip)), 16)
                    peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1])

                if peer['seed']:
                    icon = self.seed_pixbuf
                else:
                    icon = self.peer_pixbuf

                row = self.liststore.append([
                    self.get_flag_pixbuf(peer['country']), peer_ip,
                    peer['client'], peer['down_speed'], peer['up_speed'],
                    peer['country'],
                    float(ip_int), icon, peer['progress']
                ])

                self.peers[peer['ip']] = row

        # Now we need to remove any ips that were not in status["peers"] list
        for ip in set(self.peers).difference(new_ips):
            self.liststore.remove(self.peers[ip])
            del self.peers[ip]

    def clear(self):
        self.liststore.clear()

    def _on_button_press_event(self, widget, event):
        """This is a callback for showing the right-click context menu."""
        log.debug('on_button_press_event')
        # We only care about right-clicks
        if self.torrent_id and event.button == 3:
            self.peer_menu.popup(None, None, None, event.button, event.time)
            return True

    def _on_query_tooltip(self, widget, x, y, keyboard_tip, tooltip):
        if not widget.get_tooltip_context(x, y, keyboard_tip):
            return False
        else:
            model, path, _iter = widget.get_tooltip_context(x, y, keyboard_tip)

            country_code = model.get(_iter, 5)[0]
            if country_code != '  ' and country_code in COUNTRIES:
                tooltip.set_text(COUNTRIES[country_code])
                # widget here is self.listview
                widget.set_tooltip_cell(tooltip, path, widget.get_column(0),
                                        None)
                return True
            else:
                return False

    def on_menuitem_add_peer_activate(self, menuitem):
        """This is a callback for manually adding a peer"""
        log.debug('on_menuitem_add_peer')
        builder = Builder()
        builder.add_from_file(
            deluge.common.resource_filename(
                'deluge.ui.gtkui',
                os.path.join('glade', 'connect_peer_dialog.ui')))
        peer_dialog = builder.get_object('connect_peer_dialog')
        txt_ip = builder.get_object('txt_ip')
        response = peer_dialog.run()
        if response:
            value = txt_ip.get_text()
            if value and ':' in value:
                if ']' in value:
                    # ipv6
                    ip = value.split(']')[0][1:]
                    port = value.split(']')[1][1:]
                else:
                    # ipv4
                    ip = value.split(':')[0]
                    port = value.split(':')[1]
                if deluge.common.is_ip(ip):
                    log.debug('adding peer %s to %s', value, self.torrent_id)
                    client.core.connect_peer(self.torrent_id, ip, port)
        peer_dialog.destroy()
        return True
Пример #3
0
class QueuedTorrents(component.Component):
    def __init__(self):
        component.Component.__init__(self, 'QueuedTorrents', depend=['StatusBar', 'AddTorrentDialog'])
        self.queue = []
        self.status_item = None

        self.config = ConfigManager('gtkui.conf')
        self.builder = Builder()
        self.builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'queuedtorrents.ui')))
        self.builder.get_object('chk_autoadd').set_active(self.config['autoadd_queued'])
        self.dialog = self.builder.get_object('queued_torrents_dialog')
        self.dialog.set_icon(get_logo(32))

        self.builder.connect_signals(self)

        self.treeview = self.builder.get_object('treeview')
        self.treeview.append_column(TreeViewColumn(_('Torrent'), CellRendererText(), text=0))

        self.liststore = ListStore(str, str)
        self.treeview.set_model(self.liststore)
        self.treeview.set_tooltip_column(1)

    def run(self):
        self.dialog.set_transient_for(component.get('MainWindow').window)
        self.dialog.show()

    def start(self):
        if len(self.queue) == 0:
            return

        # Make sure status bar info is showing
        self.update_status_bar()

        # We only want the add button sensitive if we're connected to a host
        self.builder.get_object('button_add').set_sensitive(True)

        if self.config['autoadd_queued'] or self.config['standalone']:
            self.on_button_add_clicked(None)
        else:
            self.run()

    def stop(self):
        # We only want the add button sensitive if we're connected to a host
        self.builder.get_object('button_add').set_sensitive(False)
        self.update_status_bar()

    def add_to_queue(self, torrents):
        """Adds the list of torrents to the queue"""
        # Add to the queue while removing duplicates
        self.queue = list(set(self.queue + torrents))

        # Update the liststore
        self.liststore.clear()
        for torrent in self.queue:
            if deluge.common.is_magnet(torrent):
                magnet = deluge.common.get_magnet_info(torrent)
                self.liststore.append([magnet['name'], torrent])
            else:
                self.liststore.append([os.path.split(torrent)[1], torrent])

        # Update the status bar
        self.update_status_bar()

    def update_status_bar(self):
        """Attempts to update status bar"""
        # If there are no queued torrents.. remove statusbar widgets and return
        if len(self.queue) == 0:
            if self.status_item is not None:
                component.get('StatusBar').remove_item(self.status_item)
                self.status_item = None
            return False

        try:
            component.get('StatusBar')
        except Exception:
            # The statusbar hasn't been loaded yet, so we'll add a timer to
            # update it later.
            timeout_add(100, self.update_status_bar)
            return False

        # Set the label text for statusbar
        if len(self.queue) > 1:
            label = str(len(self.queue)) + _(' Torrents Queued')
        else:
            label = str(len(self.queue)) + _(' Torrent Queued')

        # Add the statusbar items if needed, or just modify the label if they
        # have already been added.
        if self.status_item is None:
            self.status_item = component.get('StatusBar').add_item(
                stock=STOCK_SORT_DESCENDING,
                text=label,
                callback=self.on_statusbar_click)
        else:
            self.status_item.set_text(label)

        # We return False so the timer stops
        return False

    def on_statusbar_click(self, widget, event):
        log.debug('on_statusbar_click')
        self.run()

    def on_button_remove_clicked(self, widget):
        selected = self.treeview.get_selection().get_selected()[1]
        if selected is not None:
            path = self.liststore.get_value(selected, 1)
            self.liststore.remove(selected)
            self.queue.remove(path)
            self.update_status_bar()

    def on_button_clear_clicked(self, widget):
        self.liststore.clear()
        del self.queue[:]
        self.update_status_bar()

    def on_button_close_clicked(self, widget):
        self.dialog.hide()

    def on_button_add_clicked(self, widget):
        # Add all the torrents in the liststore
        def add_torrent(model, path, _iter, data):
            torrent_path = model.get_value(_iter, 1).decode('utf-8')
            process_args([torrent_path])

        self.liststore.foreach(add_torrent, None)
        del self.queue[:]
        self.dialog.hide()
        self.update_status_bar()

    def on_chk_autoadd_toggled(self, widget):
        self.config['autoadd_queued'] = widget.get_active()
Пример #4
0
class PeersTab(Tab):
    def __init__(self):
        super(PeersTab, self).__init__('Peers', 'peers_tab', 'peers_tab_label')

        self.peer_menu = self.main_builder.get_object('menu_peer_tab')
        component.get('MainWindow').connect_signals(self)

        self.listview = self.main_builder.get_object('peers_listview')
        self.listview.props.has_tooltip = True
        self.listview.connect('button-press-event', self._on_button_press_event)
        self.listview.connect('query-tooltip', self._on_query_tooltip)

        # flag, ip, client, downspd, upspd, country code, int_ip, seed/peer icon, progress
        self.liststore = ListStore(Pixbuf, str, str, int, int, str, float, Pixbuf, float)
        self.cached_flag_pixbufs = {}

        self.seed_pixbuf = icon_seeding
        self.peer_pixbuf = icon_downloading

        # key is ip address, item is row iter
        self.peers = {}

        # Country column
        column = TreeViewColumn()
        render = CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'pixbuf', 0)
        column.set_sort_column_id(5)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(20)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Address column
        column = TreeViewColumn(_('Address'))
        render = CellRendererPixbuf()
        column.pack_start(render, False)
        column.add_attribute(render, 'pixbuf', 7)
        render = CellRendererText()
        column.pack_start(render, False)
        column.add_attribute(render, 'text', 1)
        column.set_sort_column_id(6)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Client column
        column = TreeViewColumn(_('Client'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.add_attribute(render, 'text', 2)
        column.set_sort_column_id(2)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Progress column
        column = TreeViewColumn(_('Progress'))
        render = CellRendererProgress()
        column.pack_start(render, True)
        column.set_cell_data_func(render, cell_data_peer_progress, 8)
        column.set_sort_column_id(8)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(100)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Down Speed column
        column = TreeViewColumn(_('Down Speed'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.set_cell_data_func(render, cell_data_speed_down, 3)
        column.set_sort_column_id(3)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(50)
        column.set_reorderable(True)
        self.listview.append_column(column)

        # Up Speed column
        column = TreeViewColumn(_('Up Speed'))
        render = CellRendererText()
        column.pack_start(render, False)
        column.set_cell_data_func(render, cell_data_speed_up, 4)
        column.set_sort_column_id(4)
        column.set_clickable(True)
        column.set_resizable(True)
        column.set_expand(False)
        column.set_min_width(50)
        # Bugfix: Last column needs max_width set to stop scrollbar appearing
        column.set_max_width(150)
        column.set_reorderable(True)
        self.listview.append_column(column)

        self.listview.set_model(self.liststore)

        self.load_state()

        self.torrent_id = None

    def save_state(self):
        # Get the current sort order of the view
        column_id, sort_order = self.liststore.get_sort_column_id()

        # Setup state dict
        state = {
            'columns': {},
            'sort_id': column_id,
            'sort_order': int(sort_order) if sort_order else None
        }

        for index, column in enumerate(self.listview.get_columns()):
            state['columns'][column.get_title()] = {
                'position': index,
                'width': column.get_width()
            }
        save_pickled_state_file('peers_tab.state', state)

    def load_state(self):
        state = load_pickled_state_file('peers_tab.state')

        if state is None:
            return

        if len(state['columns']) != len(self.listview.get_columns()):
            log.warning('peers_tab.state is not compatible! rejecting..')
            return

        if state['sort_id'] and state['sort_order'] is not None:
            self.liststore.set_sort_column_id(state['sort_id'], state['sort_order'])

        for (index, column) in enumerate(self.listview.get_columns()):
            cname = column.get_title()
            if cname in state['columns']:
                cstate = state['columns'][cname]
                column.set_sizing(TREE_VIEW_COLUMN_FIXED)
                column.set_fixed_width(cstate['width'] if cstate['width'] > 0 else 10)
                if state['sort_id'] == index and state['sort_order'] is not None:
                    column.set_sort_indicator(True)
                    column.set_sort_order(state['sort_order'])
                if cstate['position'] != index:
                    # Column is in wrong position
                    if cstate['position'] == 0:
                        self.listview.move_column_after(column, None)
                    elif self.listview.get_columns()[cstate['position'] - 1].get_title() != cname:
                        self.listview.move_column_after(column, self.listview.get_columns()[cstate['position'] - 1])

    def update(self):
        # Get the first selected torrent
        torrent_id = component.get('TorrentView').get_selected_torrents()

        # Only use the first torrent in the list or return if None selected
        if len(torrent_id) != 0:
            torrent_id = torrent_id[0]
        else:
            # No torrent is selected in the torrentview
            self.liststore.clear()
            return

        if torrent_id != self.torrent_id:
            # We only want to do this if the torrent_id has changed
            self.liststore.clear()
            self.peers = {}
            self.torrent_id = torrent_id

        component.get('SessionProxy').get_torrent_status(torrent_id, ['peers']).addCallback(self._on_get_torrent_status)

    def get_flag_pixbuf(self, country):
        if not country.strip():
            return None

        if country not in self.cached_flag_pixbufs:
            # We haven't created a pixbuf for this country yet
            try:
                self.cached_flag_pixbufs[country] = pixbuf_new_from_file(
                    deluge.common.resource_filename(
                        'deluge',
                        os.path.join('ui', 'data', 'pixmaps', 'flags', country.lower() + '.png')))
            except Exception as ex:
                log.debug('Unable to load flag: %s', ex)
                return None

        return self.cached_flag_pixbufs[country]

    def _on_get_torrent_status(self, status):
        new_ips = set()
        for peer in status['peers']:
            new_ips.add(peer['ip'])
            if peer['ip'] in self.peers:
                # We already have this peer in our list, so lets just update it
                row = self.peers[peer['ip']]
                if not self.liststore.iter_is_valid(row):
                    # This iter is invalid, delete it and continue to next iteration
                    del self.peers[peer['ip']]
                    continue
                values = self.liststore.get(row, 3, 4, 5, 7, 8)
                if peer['down_speed'] != values[0]:
                    self.liststore.set_value(row, 3, peer['down_speed'])
                if peer['up_speed'] != values[1]:
                    self.liststore.set_value(row, 4, peer['up_speed'])
                if peer['country'] != values[2]:
                    self.liststore.set_value(row, 5, peer['country'])
                    self.liststore.set_value(row, 0, self.get_flag_pixbuf(peer['country']))
                if peer['seed']:
                    icon = self.seed_pixbuf
                else:
                    icon = self.peer_pixbuf

                if icon != values[3]:
                    self.liststore.set_value(row, 7, icon)

                if peer['progress'] != values[4]:
                    self.liststore.set_value(row, 8, peer['progress'])
            else:
                # Peer is not in list so we need to add it

                # Create an int IP address for sorting purposes
                if peer['ip'].count(':') == 1:
                    # This is an IPv4 address
                    ip_int = sum([int(byte) << shift
                                  for byte, shift in zip(peer['ip'].split(':')[0].split('.'), (24, 16, 8, 0))])
                    peer_ip = peer['ip']
                else:
                    # This is an IPv6 address
                    import socket
                    import binascii
                    # Split out the :port
                    ip = ':'.join(peer['ip'].split(':')[:-1])
                    ip_int = int(binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip)), 16)
                    peer_ip = '[%s]:%s' % (ip, peer['ip'].split(':')[-1])

                if peer['seed']:
                    icon = self.seed_pixbuf
                else:
                    icon = self.peer_pixbuf

                row = self.liststore.append([
                    self.get_flag_pixbuf(peer['country']),
                    peer_ip,
                    peer['client'],
                    peer['down_speed'],
                    peer['up_speed'],
                    peer['country'],
                    float(ip_int),
                    icon,
                    peer['progress']])

                self.peers[peer['ip']] = row

        # Now we need to remove any ips that were not in status["peers"] list
        for ip in set(self.peers).difference(new_ips):
            self.liststore.remove(self.peers[ip])
            del self.peers[ip]

    def clear(self):
        self.liststore.clear()

    def _on_button_press_event(self, widget, event):
        """This is a callback for showing the right-click context menu."""
        log.debug('on_button_press_event')
        # We only care about right-clicks
        if self.torrent_id and event.button == 3:
            self.peer_menu.popup(None, None, None, event.button, event.time)
            return True

    def _on_query_tooltip(self, widget, x, y, keyboard_tip, tooltip):
        if not widget.get_tooltip_context(x, y, keyboard_tip):
            return False
        else:
            model, path, _iter = widget.get_tooltip_context(x, y, keyboard_tip)

            country_code = model.get(_iter, 5)[0]
            if country_code != '  ' and country_code in COUNTRIES:
                tooltip.set_text(COUNTRIES[country_code])
                # widget here is self.listview
                widget.set_tooltip_cell(tooltip, path, widget.get_column(0),
                                        None)
                return True
            else:
                return False

    def on_menuitem_add_peer_activate(self, menuitem):
        """This is a callback for manually adding a peer"""
        log.debug('on_menuitem_add_peer')
        builder = Builder()
        builder.add_from_file(deluge.common.resource_filename(
            'deluge.ui.gtkui', os.path.join('glade', 'connect_peer_dialog.ui')
        ))
        peer_dialog = builder.get_object('connect_peer_dialog')
        txt_ip = builder.get_object('txt_ip')
        response = peer_dialog.run()
        if response:
            value = txt_ip.get_text()
            if value and ':' in value:
                if ']' in value:
                    # ipv6
                    ip = value.split(']')[0][1:]
                    port = value.split(']')[1][1:]
                else:
                    # ipv4
                    ip = value.split(':')[0]
                    port = value.split(':')[1]
                if deluge.common.is_ip(ip):
                    log.debug('adding peer %s to %s', value, self.torrent_id)
                    client.core.connect_peer(self.torrent_id, ip, port)
        peer_dialog.destroy()
        return True