diff --git a/deluge/ui/gtkui/details_tab.py b/deluge/ui/gtkui/details_tab.py new file mode 100644 index 000000000..ef57de42e --- /dev/null +++ b/deluge/ui/gtkui/details_tab.py @@ -0,0 +1,98 @@ +# +# details_tab.py +# +# Copyright (C) 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gtk, gtk.glade + +from deluge.ui.client import aclient as client +import deluge.component as component +import deluge.common + +class DetailsTab: + def __init__(self): + # Get the labels we need to update. + # widgetname, modifier function, status keys + glade = component.get("MainWindow").main_glade + + self.label_widgets = [ + (glade.get_widget("summary_name"), None, ("name",)), + (glade.get_widget("summary_total_size"), deluge.common.fsize, ("total_size",)), + (glade.get_widget("summary_num_files"), str, ("num_files",)), + (glade.get_widget("summary_tracker"), None, ("tracker",)), + (glade.get_widget("summary_torrent_path"), None, ("save_path",)) + ] + + def update(self): + # Get the first selected torrent + selected = component.get("TorrentView").get_selected_torrents() + + # Only use the first torrent in the list or return if None selected + if len(selected) != 0: + selected = selected[0] + else: + # No torrent is selected in the torrentview + return + + # Get the torrent status + status_keys = ["name", "total_size", "num_files", + "tracker", "save_path", "private"] + + client.get_torrent_status( + self._on_get_torrent_status, selected, status_keys) + + def _on_get_torrent_status(self, status): + # Check to see if we got valid data from the core + if status is None: + return + + # Update all the label widgets + for widget in self.label_widgets: + if widget[1] != None: + args = [] + try: + for key in widget[2]: + args.append(status[key]) + except Exception, e: + log.debug("Unable to get status value: %s", e) + continue + + txt = widget[1](*args) + else: + txt = status[widget[2][0]] + + if widget[0].get_text() != txt: + widget[0].set_text(txt) + + def clear(self): + for widget in self.label_widgets: + widget[0].set_text("") + diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py new file mode 100644 index 000000000..c4d90bc75 --- /dev/null +++ b/deluge/ui/gtkui/files_tab.py @@ -0,0 +1,144 @@ +# +# files_tab.py +# +# Copyright (C) 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gtk, gtk.glade +import gobject +import gettext +import os.path + +from deluge.ui.client import aclient as client +import deluge.component as component +import deluge.common +import deluge.ui.gtkui.listview + +from deluge.log import LOG as log + +class FilesTab: + def __init__(self): + glade = component.get("MainWindow").get_glade() + self.listview = glade.get_widget("files_listview") + # country, filename, size, priority + self.liststore = gtk.ListStore(str, gobject.TYPE_UINT64, str, int, str) + + # Filename column + column = gtk.TreeViewColumn(_("Filename")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.add_attribute(render, "text", 0) + self.listview.append_column(column) + + # Size column + column = gtk.TreeViewColumn(_("Size")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.set_cell_data_func(render, deluge.ui.gtkui.listview.cell_data_size, 1) + self.listview.append_column(column) + + # Progress column + column = gtk.TreeViewColumn(_("Progress")) + render = gtk.CellRendererProgress() + column.pack_start(render, False) + column.add_attribute(render, "text", 2) + column.add_attribute(render, "value", 3) + self.listview.append_column(column) + + # Priority column + column = gtk.TreeViewColumn(_("Priority")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.add_attribute(render, "text", 4) + self.listview.append_column(column) + + self.listview.set_model(self.liststore) + + # torrent_id: (filepath, size) + self.files_list = {} + + self.torrent_id = None + + 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.torrent_id = torrent_id + + if self.torrent_id not in self.files_list.keys(): + # We need to get the files list + log.debug("Getting file list from core..") + client.get_torrent_status( + self._on_get_torrent_files, + self.torrent_id, + ["files", "file_progress", "file_priorities"]) + client.force_call(block=True) + else: + self.update_files() + client.get_torrent_status(self._on_get_torrent_status, self.torrent_id, ["file_progress", "file_priorities"]) + client.force_call(True) + else: + client.get_torrent_status(self._on_get_torrent_status, self.torrent_id, ["file_progress", "file_priorities"]) + client.force_call(True) + + def update_files(self): + # Updates the filename and size columns based on info in self.files_list + # This assumes the list is currently empty. + for file in self.files_list[self.torrent_id]: + row = self.liststore.append() + # Store the torrent id + self.liststore.set_value(row, 0, + os.path.split(file["path"])[1]) + self.liststore.set_value(row, 1, file["size"]) + + def _on_get_torrent_files(self, status): + self.files_list[self.torrent_id] = status["files"] + self.update_files() + self._on_get_torrent_status(status) + + def _on_get_torrent_status(self, status): + for index, row in enumerate(self.liststore): + row[2] = "%.2f%%" % (status["file_progress"][index] * 100) + row[3] = status["file_progress"][index] * 100 + row[4] = status["file_priorities"][index] + + def clear(self): + self.liststore.clear() diff --git a/deluge/ui/gtkui/glade/main_window.glade b/deluge/ui/gtkui/glade/main_window.glade index 23d549acd..eefb1199d 100644 --- a/deluge/ui/gtkui/glade/main_window.glade +++ b/deluge/ui/gtkui/glade/main_window.glade @@ -267,6 +267,7 @@ True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Remove Torrent gtk-remove @@ -328,6 +329,7 @@ True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Queue Torrent Up Queue Up @@ -342,6 +344,7 @@ True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Queue Torrent Down Queue Down @@ -467,7 +470,6 @@ True - False False @@ -483,677 +485,433 @@ GTK_RESIZE_QUEUE GTK_SHADOW_NONE - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - 5 + 10 + 10 + 15 + 15 - + True - 1 - 2 - 10 + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 - + True - 0 - - - True - 10 - 10 - 15 - 15 - - - True - 5 - - - True - 0.10000000149 - - - False - False - - - - - True - 5 - 4 - 5 - - - True - 0 - - - 1 - 2 - - - - - True - 0 - - - 3 - 4 - - - - - True - 0 - - - 1 - 2 - 1 - 2 - - - - - True - 0 - - - 3 - 4 - 1 - 2 - - - - - True - 0 - - - 1 - 2 - 2 - 3 - - - - - True - 0 - - - 3 - 4 - 2 - 3 - - - - - True - 0 - - - 1 - 2 - 3 - 4 - - - - - True - 0 - - - 3 - 4 - 3 - 4 - - - - - True - 5 - - - True - 0 - <b>Downloaded:</b> - True - - - - - - - True - 5 - - - True - 0 - <b>Uploaded:</b> - True - - - - - 1 - 2 - - - - - True - 5 - - - True - 0 - <b>Seeders:</b> - True - - - - - 2 - 3 - - - - - True - 5 - - - True - 0 - <b>Share Ratio:</b> - True - - - - - 3 - 4 - - - - - True - 15 - 5 - - - True - 0 - <b>Speed:</b> - True - - - - - 2 - 3 - - - - - True - 15 - 5 - - - True - 0 - <b>Speed:</b> - True - - - - - 2 - 3 - 1 - 2 - - - - - True - 15 - 5 - - - True - 0 - <b>Peers:</b> - True - - - - - 2 - 3 - 2 - 3 - - - - - True - 15 - 5 - - - True - 0 - <b>ETA:</b> - True - - - - - 2 - 3 - 3 - 4 - - - - - True - 0 - 1 - <b>Pieces:</b> - True - - - 4 - 5 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - - - - - 1 - 2 - 4 - 5 - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 15 - 5 - - - True - 0 - 1 - <b>Availability:</b> - True - - - - - 2 - 3 - 4 - 5 - - - - - True - 0 - True - PANGO_WRAP_WORD_CHAR - - - 3 - 4 - 4 - 5 - - - - - - False - 1 - - - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - <b>Statistics</b> - True - - - label_item - - + True + 0.10000000149 - - GTK_FILL + False + False - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 + 5 + 6 + 15 + 5 - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 10 - 10 - 15 - 15 - - - True - 7 - 2 - 2 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 1 - <b># of files:</b> - True - - - - - 3 - 4 - GTK_FILL - - - - - True - 0 - True - - - 1 - 2 - 3 - 4 - - - - - - True - 0 - True - - - 1 - 2 - 2 - 3 - - - - - - True - 0 - True - PANGO_WRAP_CHAR - True - - - 1 - 2 - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 1 - <b>Total Size:</b> - True - - - - - 2 - 3 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 1 - <b>Tracker:</b> - True - - - - - 4 - 5 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 1 - <b>Tracker Status:</b> - True - - - - - 5 - 6 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 1 - <b>Next Announce:</b> - True - - - - - 6 - 7 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - 0 - 0 - 1 - <b>Name:</b> - True - - - - - GTK_FILL - - - - - True - 0 - True - PANGO_WRAP_CHAR - True - - - 1 - 2 - 4 - 5 - - - - - - True - 0 - True - PANGO_WRAP_CHAR - True - - - 1 - 2 - 5 - 6 - - - - - - True - 0 - True - True - - - 1 - 2 - 6 - 7 - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - <b>Path:</b> - True - - - - - 1 - 2 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - True - PANGO_WRAP_CHAR - True - - - 1 - 2 - 1 - 2 - - - - - + - + + + + + + + + + + + + + + + + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - <b>Torrent Info</b> + 0 + True + True + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + True + 0 + + + 3 + 4 + 3 + 4 + GTK_FILL + + + + + True + 0 + True + PANGO_WRAP_CHAR + True + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + True + 0 + <b>Tracker Status:</b> True - label_item + 4 + 5 + GTK_FILL + + + + + + True + 0 + True + PANGO_WRAP_WORD_CHAR + + + 5 + 6 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 1 + <b>Availability:</b> + True + + + 4 + 5 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 3 + 4 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + True + 0 + + + 5 + 6 + 1 + 2 + GTK_FILL + + + + + True + 0 + <b>Peers:</b> + True + + + 4 + 5 + 1 + 2 + GTK_FILL + + + + + True + 0 + + + 5 + 6 + GTK_FILL + + + + + True + 0 + <b>Seeders:</b> + True + + + 4 + 5 + GTK_FILL + + + + + True + 15 + 5 + + + True + 0 + <b>Pieces:</b> + True + + + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + True + 15 + 5 + + + True + 0 + <b>ETA:</b> + True + + + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + True + 15 + 5 + + + True + 0 + <b>Speed:</b> + True + + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + True + 15 + 5 + + + True + 0 + <b>Speed:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Next Announce:</b> + True + + + + + 3 + 4 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Share Ratio:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Uploaded:</b> + True + + + + + 1 + 2 + GTK_FILL + + + + + True + 5 + + + True + 0 + <b>Downloaded:</b> + True + + + + + GTK_FILL + + + + + True + 0 + + + 3 + 4 + 1 + 2 + GTK_FILL + + + + + True + 0 + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + 0 + + + 3 + 4 + GTK_FILL + + + + + True + 0 + + + 1 + 2 + GTK_FILL - 1 - 2 - GTK_FILL + False + 1 @@ -1165,15 +923,396 @@ - + True - Details + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-dialog-info + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Statistics + True + + + 1 + + tab False + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + 10 + 15 + 15 + + + True + 5 + 2 + 2 + + + True + 0 + True + PANGO_WRAP_CHAR + True + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + 0 + 1 + <b>Tracker:</b> + True + + + 4 + 5 + GTK_FILL + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + 0 + 1 + <b># of files:</b> + True + + + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + True + + + 1 + 2 + 3 + 4 + + + + + + True + 0 + True + + + 1 + 2 + 2 + 3 + + + + + + True + 0 + True + PANGO_WRAP_CHAR + True + + + 1 + 2 + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + 0 + 1 + <b>Total Size:</b> + True + + + + + 2 + 3 + GTK_FILL + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + 0 + 0 + 1 + <b>Name:</b> + True + + + + + GTK_FILL + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <b>Path:</b> + True + + + + + 1 + 2 + GTK_FILL + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + True + PANGO_WRAP_CHAR + True + + + 1 + 2 + 1 + 2 + + + + + + + + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-properties + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Details + True + + + 1 + + + + + tab + 1 + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-copy + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Files + True + + + 1 + + + + + tab + 2 + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + + + + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-network + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Peers + True + + + 1 + + + + + tab + 3 + False + + False diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index c92c5ff22..1836a1b56 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -102,7 +102,11 @@ class MainWindow(component.Component): def visible(self): """Returns True if window is visible, False if not.""" return self.window.get_property("visible") - + + def get_glade(self): + """Returns a reference to the main window glade object.""" + return self.main_glade + def quit(self): del self.config gtk.main_quit() diff --git a/deluge/ui/gtkui/peers_tab.py b/deluge/ui/gtkui/peers_tab.py new file mode 100644 index 000000000..b683cc930 --- /dev/null +++ b/deluge/ui/gtkui/peers_tab.py @@ -0,0 +1,99 @@ +# +# peers_tab.py +# +# Copyright (C) 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gtk, gtk.glade + +from deluge.ui.client import aclient as client +import deluge.component as component +import deluge.common + +def cell_data_country(column, cell, model, row, data): + pass + +class PeersTab: + def __init__(self): + glade = component.get("MainWindow").get_glade() + self.listview = glade.get_widget("peers_listview") + # country, filename, size, priority + self.liststore = gtk.ListStore(str, str, str, str, int, int, int) + + # Country column + column = gtk.TreeViewColumn() + render = gtk.CellRendererPixbuf() + column.pack_start(render, False) + column.set_cell_data_func(render, cell_data_country, 0) + self.listview.append_column(column) + + # Address column + column = gtk.TreeViewColumn(_("Address")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.add_attribute(render, "text", 1) + self.listview.append_column(column) + + # Client column + column = gtk.TreeViewColumn(_("Client")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.add_attribute(render, "text", 2) + self.listview.append_column(column) + + # Progress column + column = gtk.TreeViewColumn(_("Progress")) + render = gtk.CellRendererProgress() + column.pack_start(render, False) + column.add_attribute(render, "text", 3) + column.add_attribute(render, "value", 4) + self.listview.append_column(column) + + # Down Speed column + column = gtk.TreeViewColumn(_("Down Speed")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.set_cell_data_func(render, deluge.common.fspeed, 5) + self.listview.append_column(column) + + # Up Speed column + column = gtk.TreeViewColumn(_("Up Speed")) + render = gtk.CellRendererText() + column.pack_start(render, False) + column.set_cell_data_func(render, deluge.common.fspeed, 6) + self.listview.append_column(column) + + self.listview.set_model(self.liststore) + + def update(self): + pass + + def clear(self): + pass diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index e97ec0a53..489fdb26b 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -40,7 +40,7 @@ from deluge.log import LOG as log class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Component): def __init__(self): - component.Component.__init__(self, "PluginManager") + component.Component.__init__(self, "PluginManager", depend=["Signals"]) self.config = ConfigManager("gtkui.conf") deluge.pluginmanagerbase.PluginManagerBase.__init__( self, "gtkui.conf", "deluge.plugin.gtkui") diff --git a/deluge/ui/gtkui/statistics_tab.py b/deluge/ui/gtkui/statistics_tab.py new file mode 100644 index 000000000..81e4cc246 --- /dev/null +++ b/deluge/ui/gtkui/statistics_tab.py @@ -0,0 +1,136 @@ +# +# statistics_tab.py +# +# Copyright (C) 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gtk, gtk.glade + +from deluge.ui.client import aclient as client +import deluge.component as component +import deluge.common + +def fpeer_sized(first, second): + return "%s (%s)" % (deluge.common.fsize(first), deluge.common.fsize(second)) + +def fpeer_size_second(first, second): + return "%s (%s)" % (first, deluge.common.fsize(second)) + +def fratio(value): + return "%.3f" % value + +def fpcnt(value): + return "%.2f%%" % value + +def fspeed(value, max_value=-1): + if max_value > -1: + return "%s [%s KiB/s]" % (deluge.common.fspeed(value), max_value) + else: + return deluge.common.fspeed(value) + +class StatisticsTab: + def __init__(self): + # Get the labels we need to update. + # widgetname, modifier function, status keys + glade = component.get("MainWindow").main_glade + + self.label_widgets = [ + (glade.get_widget("summary_pieces"), fpeer_size_second, ("num_pieces", "piece_length")), + (glade.get_widget("summary_availability"), fratio, ("distributed_copies",)), + (glade.get_widget("summary_total_downloaded"), fpeer_sized, ("total_done", "total_payload_download")), + (glade.get_widget("summary_total_uploaded"), fpeer_sized, ("total_uploaded", "total_payload_upload")), + (glade.get_widget("summary_download_speed"), fspeed, ("download_payload_rate", "max_download_speed")), + (glade.get_widget("summary_upload_speed"), fspeed, ("upload_payload_rate", "max_upload_speed")), + (glade.get_widget("summary_seeders"), deluge.common.fpeer, ("num_seeds", "total_seeds")), + (glade.get_widget("summary_peers"), deluge.common.fpeer, ("num_peers", "total_peers")), + (glade.get_widget("summary_eta"), deluge.common.ftime, ("eta",)), + (glade.get_widget("summary_share_ratio"), fratio, ("ratio",)), + (glade.get_widget("summary_tracker_status"), None, ("tracker_status",)), + (glade.get_widget("summary_next_announce"), deluge.common.ftime, ("next_announce",)), + (glade.get_widget("progressbar"), fpcnt, ("progress",)) + ] + + def update(self): + # Get the first selected torrent + selected = component.get("TorrentView").get_selected_torrents() + + # Only use the first torrent in the list or return if None selected + if len(selected) != 0: + selected = selected[0] + else: + # No torrent is selected in the torrentview + return + + # Get the torrent status + status_keys = ["progress", "num_pieces", "piece_length", + "distributed_copies", "total_done", "total_payload_download", + "total_uploaded", "total_payload_upload", "download_payload_rate", + "upload_payload_rate", "num_peers", "num_seeds", "total_peers", + "total_seeds", "eta", "ratio", "next_announce", + "tracker_status", "max_connections", "max_upload_slots", + "max_upload_speed", "max_download_speed"] + + client.get_torrent_status( + self._on_get_torrent_status, selected, status_keys) + + def _on_get_torrent_status(self, status): + # Check to see if we got valid data from the core + if status is None: + return + + # Update all the label widgets + for widget in self.label_widgets: + if widget[1] != None: + args = [] + try: + for key in widget[2]: + args.append(status[key]) + except Exception, e: + log.debug("Unable to get status value: %s", e) + continue + + txt = widget[1](*args) + else: + txt = status[widget[2][0]] + + if widget[0].get_text() != txt: + widget[0].set_text(txt) + + # Do the progress bar because it's a special case (not a label) + w = component.get("MainWindow").main_glade.get_widget("progressbar") + fraction = status["progress"] / 100 + if w.get_fraction() != fraction: + w.set_fraction(fraction) + + def clear(self): + for widget in self.label_widgets: + widget[0].set_text("") + + component.get("MainWindow").main_glade.get_widget("progressbar").set_fraction(0.0) diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index 09d835c6d..4681f381c 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -64,7 +64,9 @@ class ToolBar(component.Component): "toolbutton_add", "toolbutton_remove", "toolbutton_pause", - "toolbutton_resume" + "toolbutton_resume", + "toolbutton_queue_up", + "toolbutton_queue_down" ] # Set the Remove Torrent toolbuttons drop-down menu diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index aef516478..3c1e5ed23 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -33,34 +33,17 @@ """The torrent details component shows info about the selected torrent.""" -import pygtk -pygtk.require('2.0') import gtk, gtk.glade -import gettext import deluge.component as component from deluge.ui.client import aclient as client -import deluge.common +from statistics_tab import StatisticsTab +from details_tab import DetailsTab +from files_tab import FilesTab +from peers_tab import PeersTab + from deluge.log import LOG as log -def fpeer_sized(first, second): - return "%s (%s)" % (deluge.common.fsize(first), deluge.common.fsize(second)) - -def fpeer_size_second(first, second): - return "%s (%s)" % (first, deluge.common.fsize(second)) - -def fratio(value): - return "%.3f" % value - -def fpcnt(value): - return "%.2f%%" % value - -def fspeed(value, max_value=-1): - if max_value > -1: - return "%s [%s KiB/s]" % (deluge.common.fspeed(value), max_value) - else: - return deluge.common.fspeed(value) - class TorrentDetails(component.Component): def __init__(self): component.Component.__init__(self, "TorrentDetails", interval=2000) @@ -69,37 +52,19 @@ class TorrentDetails(component.Component): self.notebook = glade.get_widget("torrent_info") self.details_tab = glade.get_widget("torrentdetails_tab") - - # Don't show tabs if there is only 1 - if self.notebook.get_n_pages() < 2: - self.notebook.set_show_tabs(False) - else: - self.notebook.set_show_tabs(True) - - self.is_visible = True - # Get the labels we need to update. - # widgetname, modifier function, status keys - self.label_widgets = [ - (glade.get_widget("summary_name"), None, ("name",)), - (glade.get_widget("summary_total_size"), deluge.common.fsize, ("total_size",)), - (glade.get_widget("summary_num_files"), str, ("num_files",)), - (glade.get_widget("summary_pieces"), fpeer_size_second, ("num_pieces", "piece_length")), - (glade.get_widget("summary_availability"), fratio, ("distributed_copies",)), - (glade.get_widget("summary_total_downloaded"), fpeer_sized, ("total_done", "total_payload_download")), - (glade.get_widget("summary_total_uploaded"), fpeer_sized, ("total_uploaded", "total_payload_upload")), - (glade.get_widget("summary_download_speed"), fspeed, ("download_payload_rate", "max_download_speed")), - (glade.get_widget("summary_upload_speed"), fspeed, ("upload_payload_rate", "max_upload_speed")), - (glade.get_widget("summary_seeders"), deluge.common.fpeer, ("num_seeds", "total_seeds")), - (glade.get_widget("summary_peers"), deluge.common.fpeer, ("num_peers", "total_peers")), - (glade.get_widget("summary_eta"), deluge.common.ftime, ("eta",)), - (glade.get_widget("summary_share_ratio"), fratio, ("ratio",)), - (glade.get_widget("summary_tracker"), None, ("tracker",)), - (glade.get_widget("summary_tracker_status"), None, ("tracker_status",)), - (glade.get_widget("summary_next_announce"), deluge.common.ftime, ("next_announce",)), - (glade.get_widget("summary_torrent_path"), None, ("save_path",)), - (glade.get_widget("progressbar"), fpcnt, ("progress",)) - ] + self.notebook.connect("switch-page", self._on_switch_page) + + statistics_tab = StatisticsTab() + details_tab = DetailsTab() + files_tab = FilesTab() + peers_tab = PeersTab() + + self.tabs = [] + self.tabs.insert(0, statistics_tab) + self.tabs.insert(1, details_tab) + self.tabs.insert(2, files_tab) + self.tabs.insert(3, peers_tab) def visible(self, visible): if visible: @@ -108,78 +73,18 @@ class TorrentDetails(component.Component): self.notebook.hide() self.window.vpaned.set_position(-1) - self.is_visible = visible - def stop(self): self.clear() def update(self): - # Show tabs if more than 1 page - if self.notebook.get_n_pages() > 1: - self.notebook.set_show_tabs(True) - - # Only update if this page is showing - if self.notebook.page_num(self.details_tab) is \ - self.notebook.get_current_page() and \ - self.notebook.get_property("visible"): - # Get the first selected torrent - selected = component.get("TorrentView").get_selected_torrents() - - # Only use the first torrent in the list or return if None selected - if len(selected) != 0: - selected = selected[0] - else: - # No torrent is selected in the torrentview - return - - # Get the torrent status - status_keys = ["progress", "name", "total_size", "num_files", - "num_pieces", "piece_length", "distributed_copies", - "total_done", "total_payload_download", "total_uploaded", - "total_payload_upload", "download_payload_rate", - "upload_payload_rate", "num_peers", "num_seeds", "total_peers", - "total_seeds", "eta", "ratio", "tracker", "next_announce", - "tracker_status", "save_path", "max_connections", - "max_upload_slots", "max_upload_speed", "max_download_speed", - "private"] - client.get_torrent_status( - self._on_get_torrent_status, selected, status_keys) - - def _on_get_torrent_status(self, status): - # Check to see if we got valid data from the core - if status is None: - return - - # Update all the label widgets - for widget in self.label_widgets: - if widget[1] != None: - args = [] - try: - for key in widget[2]: - args.append(status[key]) - except Exception, e: - log.debug("Unable to get status value: %s", e) - continue - - txt = widget[1](*args) - else: - txt = status[widget[2][0]] - - if widget[0].get_text() != txt: - widget[0].set_text(txt) - - # Do the progress bar because it's a special case (not a label) - w = self.window.main_glade.get_widget("progressbar") - fraction = status["progress"] / 100 - if w.get_fraction() != fraction: - w.set_fraction(fraction) + if self.notebook.get_property("visible"): + # Update the tab that is in view + self.tabs[self.notebook.get_current_page()].update() def clear(self): - # Only update if this page is showing - if self.notebook.page_num(self.details_tab) is \ - self.notebook.get_current_page(): + self.tabs[self.notebook.get_current_page()].clear() + + def _on_switch_page(self, notebook, page, page_num): + self.tabs[page_num].update() + client.force_call(False) - for widget in self.label_widgets: - widget[0].set_text("") - - self.window.main_glade.get_widget("progressbar").set_fraction(0.0)