def _create_menu(self): def on_show_menu(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) if parent_id in self._store: items[0].show() items[1].show() else: items[0].hide() items[1].hide() def on_activate(widget, label_id): self._request_options(label_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) self._request_options(parent_id) self._menu = LabelSelectionMenu(self._store.model, on_activate) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items( self._menu, 0, ( ((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), ((gtk.SeparatorMenuItem, ), ), )) if __debug__: for item in items: RT.register(item, __name__) self._menu.connect("show", on_show_menu) self._menu.show_all()
def _create_menu(self): def on_activate(widget, label_id): id = self._get_selected_torrent() if id: self._set_torrent_label(id, label_id) self._display_torrent_label(id) items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NONE), ) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=items) if __debug__: RT.register(self._menu, __name__)
def _create_set_label_menu(self): def on_activate(widget, label_id): torrent_ids = self._view.get_selected_torrents() if torrent_ids and label_id in self._store: log.info("Setting label %r on %r", self._store[label_id]["fullname"], torrent_ids) client.labelplus.set_torrent_labels(torrent_ids, label_id) def on_activate_parent(widget): ids = self.get_selected_torrent_labels() parent_id = labelplus.common.label.get_common_parent(ids) on_activate(widget, parent_id) def on_show_menu(widget): items[0].hide() ids = self.get_selected_torrent_labels() parent_id = labelplus.common.label.get_common_parent(ids) if self._store.is_user_label(parent_id): items[0].show() root_items = (((Gtk.MenuItem, { 'label': _(STR_NONE) }), on_activate, ID_NONE), ) menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) menu.connect("show", on_show_menu) items = labelplus.gtkui.common.gtklib.menu_add_items( menu, 1, (((Gtk.MenuItem, { 'label': _(STR_PARENT) }), on_activate_parent), )) root = Gtk.MenuItem(label=_(TITLE_SET_LABEL)) root.set_submenu(menu) if __debug__: RT.register(menu, __name__) if __debug__: RT.register(root, __name__) return root
def _create_set_label_menu(self): def on_activate(widget, label_id): torrent_ids = self._view.get_selected_torrents() if torrent_ids and label_id in self._store: log.info("Setting label %r on %r", self._store[label_id]["fullname"], torrent_ids) client.labelplus.set_torrent_labels(torrent_ids, label_id) def on_activate_parent(widget): ids = self.get_selected_torrent_labels() parent_id = labelplus.common.label.get_common_parent(ids) on_activate(widget, parent_id) def on_show_menu(widget): items[0].hide() ids = self.get_selected_torrent_labels() parent_id = labelplus.common.label.get_common_parent(ids) if self._store.is_user_label(parent_id): items[0].show() root_items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NONE),) menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) menu.connect("show", on_show_menu) items = labelplus.gtkui.common.gtklib.menu_add_items(menu, 1, (((gtk.MenuItem, _(STR_PARENT)), on_activate_parent),)) root = gtk.MenuItem(_(TITLE_SET_LABEL)) root.set_submenu(menu) if __debug__: RT.register(menu, __name__) if __debug__: RT.register(root, __name__) return root
def _create_menu(self): def on_show_menu(menu): parent_id = labelplus.common.label.get_parent_id(self._parent_id) if parent_id in self._store: items[0].show() else: items[0].hide() def on_activate(widget, parent_id): self._select_parent_label(parent_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._parent_id) self._select_parent_label(parent_id) root_items = (((Gtk.MenuItem, {'label': _(STR_NONE)}), on_activate, ID_NULL),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items(self._menu, 1, (((Gtk.MenuItem, {'label': _(STR_PARENT)}), on_activate_parent),)) if __debug__: RT.register(items[0], __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() if self._type == self.TYPE_RENAME: item = self._menu.get_label_item(self._label_id) if item: item.set_sensitive(False)
def _create_menu(self): def on_activate(widget, label_id): id = self._get_selected_torrent() if id: self._set_torrent_label(id, label_id) self._display_torrent_label(id) items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NONE),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=items) if __debug__: RT.register(self._menu, __name__)
def _create_menu(self): def on_show_menu(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) if parent_id in self._store: items[0].show() items[1].show() else: items[0].hide() items[1].hide() def on_activate(widget, label_id): self._request_options(label_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) self._request_options(parent_id) self._menu = LabelSelectionMenu(self._store.model, on_activate) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items(self._menu, 0, ( ((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), ((gtk.SeparatorMenuItem,),), ) ) if __debug__: for item in items: RT.register(item, __name__) self._menu.connect("show", on_show_menu) self._menu.show_all()
def _create_menu(self): def on_show_menu(menu): parent_id = labelplus.common.label.get_parent_id(self._parent_id) if parent_id in self._store: items[0].show() else: items[0].hide() def on_activate(widget, parent_id): self._select_parent_label(parent_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._parent_id) self._select_parent_label(parent_id) root_items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NULL),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items(self._menu, 1, (((gtk.MenuItem, _(STR_PARENT)), on_activate_parent),)) if __debug__: RT.register(items[0], __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() if self._type == self.TYPE_RENAME: item = self._menu.get_label_item(self._label_id) if item: item.set_sensitive(False)
class LabelOptionsDialog(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("wnd_label_options.glade") ROOT_WIDGET = "wnd_label_options" REQUEST_TIMEOUT = 10.0 CLEAR_TEST_DELAY = 2.0 OP_MAP = { gtk.CheckButton: ("set_active", "get_active"), gtk.Label: ("set_text", "get_text"), gtk.RadioButton: ("set_active", "get_active"), gtk.SpinButton: ("set_value", "get_value"), AutolabelBox: ("set_all_row_values", "get_all_row_values"), RadioButtonGroup: ("set_active_value", "get_active_value"), } SETTER = 0 GETTER = 1 # Section: Initialization def __init__(self, plugin, label_id, page=0): self._plugin = plugin if label_id in RESERVED_IDS or label_id not in plugin.store: raise LabelPlusError(ERR_INVALID_LABEL) self._store = None self._menu = None self._label_defaults = {} self._path_options = {} self._label_options = {} for path_type in PATH_TYPES: self._path_options[path_type] = {} super(LabelOptionsDialog, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() self._set_label(ID_NULL) # Keep window alive with cyclic reference self._root_widget.set_data("owner", self) self._setup_widgets() self._index_widgets() self._load_state() self._nb_tabs.set_current_page(page) self._create_menu() self._request_options(label_id) self._plugin.register_update_func(self.update_store) self._plugin.register_cleanup_func(self.destroy) except: self.destroy() raise # Section: Deinitialization def destroy(self): self._plugin.deregister_update_func(self.update_store) self._plugin.deregister_cleanup_func(self.destroy) self._destroy_menu() self._destroy_store() if self.valid: self._root_widget.set_data("owner", None) super(LabelOptionsDialog, self).destroy() def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: Public def show(self): self._wnd_label_options.show() def update_store(self, store): if self._label_id not in store: self.destroy() return self._destroy_store() self._store = store.copy() self._destroy_menu() self._create_menu() self._request_options(self._label_id) # Section: General def _set_label(self, label_id): if label_id in self._store: self._label_id = label_id self._label_name = self._store[label_id]["name"] self._label_fullname = self._store[label_id]["fullname"] else: self._label_id = ID_NULL self._label_name = _(STR_NONE) self._label_fullname = _(STR_NONE) def _report_error(self, context, error): log.error("%s: %s", context, error) self._set_error(error.tr()) def _get_path_type(self, widget): path_type = widget.get_name()[widget.get_name().index("_") + 1:widget.get_name().rindex("_")] if path_type in PATH_TYPES: return path_type return None # Section: Options def _request_options(self, label_id): def on_timeout(): if self.valid: self._wnd_label_options.set_sensitive(True) self._report_error(STR_LOAD_OPTIONS, LabelPlusError(ERR_TIMED_OUT)) def process_result(result): if self.valid: self._wnd_label_options.set_sensitive(True) for success, data in result: if not success: error = labelplus.common.extract_error(data) if error: self._report_error(STR_LOAD_OPTIONS, error) else: self.destroy() return self._label_defaults = result[0][1] self._path_options = result[1][1] self._label_options = result[2][1] self._load_options(self._label_options) self._enable_options() self._clear_options() self._disable_options() self._set_label(label_id) self._refresh() if not label_id in self._store: self._report_error(STR_LOAD_OPTIONS, LabelPlusError(ERR_INVALID_LABEL)) return self._set_error(None) log.info("Loading options for %r", self._label_fullname) deferreds = [] deferreds.append(client.labelplus.get_label_defaults()) deferreds.append(client.labelplus.get_path_options(label_id)) deferreds.append(client.labelplus.get_label_options(label_id)) deferred = twisted.internet.defer.DeferredList(deferreds, consumeErrors=True) self._wnd_label_options.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, None) def _save_options(self): def on_timeout(options): if self.valid: self._wnd_label_options.set_sensitive(True) self._report_error(STR_SAVE_OPTIONS, LabelPlusError(ERR_TIMED_OUT)) def process_result(result, options): if self.valid: self._wnd_label_options.set_sensitive(True) if isinstance(result, Failure): error = labelplus.common.extract_error(result) if error: self._report_error(STR_SAVE_OPTIONS, error) else: self.destroy() return result else: self._label_options = options if not self._label_id in self._store: self._report_error(STR_SAVE_OPTIONS, LabelPlusError(ERR_INVALID_LABEL)) return self._set_error(None) log.info("Saving options for %r", self._label_fullname) options = self._get_options() same = labelplus.common.dict_equals(options, self._label_options) if self._chk_autolabel_retroactive.get_active(): apply_to_all = not self._chk_autolabel_unlabeled_only.get_active() else: apply_to_all = None if not same or apply_to_all is not None: deferred = client.labelplus.set_label_options( self._label_id, options, apply_to_all) self._wnd_label_options.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, process_result, options) else: log.info("No options were changed") # Section: Dialog: Setup def _setup_widgets(self): self._wnd_label_options.set_transient_for( deluge.component.get("MainWindow").window) self._wnd_label_options.set_title(_(TITLE_LABEL_OPTIONS)) icon = self._wnd_label_options.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self._wnd_label_options.set_icon(icon) self._lbl_header.set_markup("<b>%s:</b>" % _(STR_LABEL)) self._img_error.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR) self._btn_close.grab_focus() self._setup_radio_button_groups() self._setup_autolabel_box() self._setup_test_combo_box() self._setup_criteria_area() for path_type in PATH_TYPES: self.__dict__["_rgrp_%s_mode" % path_type].connect( "changed", self._do_select_mode) self.connect_signals({ "do_close": self._do_close, "do_submit": self._do_submit, "do_open_select_menu": self._do_open_select_menu, "do_revert_to_defaults": self._do_revert_to_defaults, "do_toggle_fullname": self._do_toggle_fullname, "do_toggle_dependents": self._do_toggle_dependents, "on_txt_changed": self._on_txt_changed, "do_open_file_dialog": self._do_open_file_dialog, "do_test_criteria": self._do_test_criteria, }) def _setup_radio_button_groups(self): for path_type in PATH_TYPES: rgrp = RadioButtonGroup(( (getattr(self, "_rb_%s_to_parent" % path_type), MOVE_PARENT), (getattr(self, "_rb_%s_to_subfolder" % path_type), MOVE_SUBFOLDER), (getattr(self, "_rb_%s_to_folder" % path_type), MOVE_FOLDER), )) rgrp.set_name("rgrp_%s_mode" % path_type) self.__dict__["_rgrp_%s_mode" % path_type] = rgrp if __debug__: RT.register(rgrp, __name__) def _setup_autolabel_box(self): crbox = AutolabelBox(row_spacing=6, column_spacing=3) if __debug__: RT.register(crbox, __name__) crbox.set_name("crbox_autolabel_rules") self._crbox_autolabel_rules = crbox self._blk_criteria_box.add(crbox) self._widgets.append(crbox) crbox.show_all() def _setup_test_combo_box(self): prop_store = labelplus.gtkui.common.gtklib.liststore_create( str, [_(x) for x in PROPS]) if __debug__: RT.register(prop_store, __name__) cell = gtk.CellRendererText() if __debug__: RT.register(cell, __name__) self._cmb_test_criteria.pack_start(cell) self._cmb_test_criteria.add_attribute(cell, "text", 0) self._cmb_test_criteria.set_model(prop_store) self._cmb_test_criteria.set_active(0) def _setup_criteria_area(self): def on_mapped(widget, event): # Sometimes a widget is mapped but does not immediately have allocation if self._vp_criteria_area.allocation.x < 0: twisted.internet.reactor.callLater(0.1, widget.emit, "map-event", event) return if widget.handler_is_connected(handle): widget.disconnect(handle) self._vp_criteria_area.set_data("was_mapped", True) if self._plugin.config["common"]["label_options_pane_pos"] > -1: self._vp_criteria_area.set_position( self._vp_criteria_area.allocation.height - self._plugin.config["common"]["label_options_pane_pos"]) clamp_position(self._vp_criteria_area) def clamp_position(widget, *args): handle_size = widget.allocation.height - \ widget.get_property("max-position") max_dist = self._hb_test_criteria.allocation.height + handle_size * 2 threshold = max_dist / 2 if widget.allocation.height - widget.get_position() > threshold: twisted.internet.reactor.callLater( 0.1, widget.set_position, widget.allocation.height - max_dist) else: twisted.internet.reactor.callLater(0.1, widget.set_position, widget.allocation.height) handle = self._eb_criteria_area.connect("map-event", on_mapped) self._vp_criteria_area.connect("button-release-event", clamp_position) self._vp_criteria_area.connect("accept-position", clamp_position) def _index_widgets(self): self._exp_group = ( self._exp_download_location, self._exp_move_completed, ) self._option_groups = ( ( self._chk_download_settings, self._chk_download_location, self._rgrp_download_location_mode, self._lbl_download_location_path, self._chk_move_completed, self._rgrp_move_completed_mode, self._lbl_move_completed_path, self._chk_prioritize_first_last, ), ( self._chk_bandwidth_settings, self._rb_shared_limit, self._spn_max_download_speed, self._spn_max_upload_speed, self._spn_max_connections, self._spn_max_upload_slots, ), ( self._chk_queue_settings, self._chk_auto_managed, self._chk_stop_at_ratio, self._spn_stop_ratio, self._chk_remove_at_ratio, ), ( self._chk_autolabel_settings, self._rb_autolabel_match_all, self._crbox_autolabel_rules, ), ) self._dependency_widgets = { self._chk_download_settings: (self._blk_download_settings_group, ), self._chk_bandwidth_settings: (self._blk_bandwidth_settings_group, ), self._chk_queue_settings: (self._blk_queue_settings_group, ), self._chk_autolabel_settings: (self._blk_autolabel_settings_group, ), self._chk_download_location: (self._blk_download_location_group, ), self._rb_download_location_to_folder: (self._txt_download_location_path, ), self._chk_move_completed: (self._blk_move_completed_group, ), self._rb_move_completed_to_folder: (self._txt_move_completed_path, ), self._chk_stop_at_ratio: (self._spn_stop_ratio, self._chk_remove_at_ratio), self._chk_autolabel_retroactive: (self._chk_autolabel_unlabeled_only, ), } # Section: Dialog: State def _load_state(self): if not client.is_localhost(): for path_type in PATH_TYPES: self.__dict__["_btn_%s_browse" % path_type].hide() if self._plugin.initialized: pos = self._plugin.config["common"]["label_options_pos"] if pos: self._wnd_label_options.move(*pos) size = self._plugin.config["common"]["label_options_size"] if size: self._wnd_label_options.resize(*size) self._tgb_fullname.set_active( self._plugin.config["common"]["label_options_fullname"]) expanded = self._plugin.config["common"]["label_options_exp_state"] for exp in expanded: widget = getattr(self, "_" + exp, None) if widget: widget.set_expanded(True) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["label_options_pos"] = \ list(self._wnd_label_options.get_position()) self._plugin.config["common"]["label_options_size"] = \ list(self._wnd_label_options.get_size()) self._plugin.config["common"]["label_options_fullname"] = \ self._tgb_fullname.get_active() expanded = [] for exp in self._exp_group: if exp.get_expanded(): expanded.append(exp.get_name()) self._plugin.config["common"]["label_options_exp_state"] = expanded if self._vp_criteria_area.get_data("was_mapped"): self._plugin.config["common"]["label_options_pane_pos"] = \ self._vp_criteria_area.allocation.height - \ self._vp_criteria_area.get_position() self._plugin.config.save() # Section: Dialog: Options def _get_widget_values(self, widgets, options_out): for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in options_out: ops = self.OP_MAP.get(type(widget)) if ops: getter = getattr(widget, ops[self.GETTER]) options_out[name] = getter() def _set_widget_values(self, widgets, options_in): for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in options_in: ops = self.OP_MAP.get(type(widget)) if ops: setter = getattr(widget, ops[self.SETTER]) setter(options_in[name]) def _load_options(self, options): for group in self._option_groups: self._set_widget_values(group, options) for path_type in PATH_TYPES: mode = options["%s_mode" % path_type] path = options["%s_path" % path_type] self.__dict__["_txt_%s_path" % path_type].set_text(path) if mode != MOVE_FOLDER: path = self._path_options[path_type].get(mode, "") self._set_path_label(path, path_type) def _get_options(self): options = copy.deepcopy(self._label_defaults) for group in self._option_groups: self._get_widget_values(group, options) return options def _revert_options_by_page(self, options_out, page): widgets = self._option_groups[page] for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in self._label_defaults: options_out[name] = copy.deepcopy(self._label_defaults[name]) def _clear_options(self): if not self._label_defaults: self._label_defaults = LABEL_DEFAULTS for path_type in PATH_TYPES: self._path_options[path_type] = {} self._label_options = {} self._load_options(self._label_defaults) def _enable_options(self): self._nb_tabs.set_sensitive(True) self._btn_defaults.set_sensitive(True) self._btn_defaults_all.set_sensitive(True) self._btn_apply.set_sensitive(True) def _disable_options(self): self._nb_tabs.set_sensitive(False) self._btn_defaults.set_sensitive(False) self._btn_defaults_all.set_sensitive(False) self._btn_apply.set_sensitive(False) # Section: Dialog: Modifiers def _refresh(self): self._do_toggle_fullname() if self._label_id == ID_NULL: self._lbl_selected_label.set_tooltip_text(None) else: self._lbl_selected_label.set_tooltip_text(self._label_fullname) for widget in self._dependency_widgets: self._do_toggle_dependents(widget) self._set_test_result(None) def _set_path_label(self, path, path_type): lbl_widget = self.__dict__["_lbl_%s_path" % path_type] lbl_widget.set_text(path) lbl_widget.set_tooltip_text(path) def _set_test_result(self, result): if result is not None: icon = gtk.STOCK_YES if result else gtk.STOCK_NO self._txt_test_criteria.set_icon_from_stock( gtk.ENTRY_ICON_SECONDARY, icon) else: self._txt_test_criteria.set_icon_from_stock( gtk.ENTRY_ICON_SECONDARY, None) def _set_error(self, message): if message: self._img_error.set_tooltip_text(message) self._img_error.show() else: self._img_error.hide() # Section: Dialog: Handlers: General def _do_close(self, *args): self._save_state() self.destroy() def _do_submit(self, *args): self._save_options() def _do_open_select_menu(self, *args): if self._menu: self._menu.popup(None, None, None, 1, gtk.gdk.CURRENT_TIME) def _do_revert_to_defaults(self, widget): if widget is self._btn_defaults: options = self._get_options() self._revert_options_by_page(options, self._nb_tabs.get_current_page()) else: options = self._label_defaults self._load_options(options) self._refresh() def _do_toggle_fullname(self, *args): if self._tgb_fullname.get_active(): self._lbl_selected_label.set_text(self._label_fullname) else: self._lbl_selected_label.set_text(self._label_name) def _do_toggle_dependents(self, widget): if widget in self._dependency_widgets: toggled = widget.get_active() for dependent in self._dependency_widgets[widget]: dependent.set_sensitive(toggled) # Section: Dialog: Handlers: Paths def _do_select_mode(self, group, button, mode): path_type = self._get_path_type(group) self._do_toggle_dependents(self.__dict__["_rb_%s_to_folder" % path_type]) if mode == MOVE_FOLDER: self._on_txt_changed(self.__dict__["_txt_%s_path" % path_type]) else: path = self._path_options[path_type].get(mode, "") self._set_path_label(path, path_type) def _on_txt_changed(self, widget): path_type = self._get_path_type(widget) self._set_path_label(widget.get_text(), path_type) def _do_open_file_dialog(self, widget): def on_response(widget, response): if self.valid and response == gtk.RESPONSE_OK: txt_widget.set_text(widget.get_filename()) widget.destroy() path_type = self._get_path_type(widget) txt_widget = self.__dict__["_txt_%s_path" % path_type] dialog = gtk.FileChooserDialog(_(TITLE_SELECT_FOLDER), self._wnd_label_options, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, ( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK, )) if __debug__: RT.register(dialog, __name__) dialog.set_destroy_with_parent(True) dialog.connect("response", on_response) path = txt_widget.get_text() if not os.path.exists(path): path = "" dialog.set_filename(path) dialog.show_all() widgets = labelplus.gtkui.common.gtklib.widget_get_descendents( dialog, (gtk.ToggleButton, ), 1) if widgets: location_toggle = widgets[0] location_toggle.set_active(False) # Section: Dialog: Handlers: Criteria Test def _do_test_criteria(self, *args): def clear_result(): if self.valid: self._set_test_result(None) index = self._cmb_test_criteria.get_active() prop_name = PROPS[index] props = { prop_name: [unicode(self._txt_test_criteria.get_text(), "utf8")] } rules = self._crbox_autolabel_rules.get_all_row_values() match_all = self._rb_autolabel_match_all.get_active() log.debug("Properties: %r, Rules: %r, Match all: %s", props, rules, match_all) result = labelplus.common.config.autolabel.find_match( props, rules, match_all) log.debug("Test result: %s", result) self._set_test_result(result) twisted.internet.reactor.callLater(self.CLEAR_TEST_DELAY, clear_result) # Section: Dialog: Menu def _create_menu(self): def on_show_menu(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) if parent_id in self._store: items[0].show() items[1].show() else: items[0].hide() items[1].hide() def on_activate(widget, label_id): self._request_options(label_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) self._request_options(parent_id) self._menu = LabelSelectionMenu(self._store.model, on_activate) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items( self._menu, 0, ( ((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), ((gtk.SeparatorMenuItem, ), ), )) if __debug__: for item in items: RT.register(item, __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None
class LabelOptionsDialog(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("wnd_label_options.glade") ROOT_WIDGET = "wnd_label_options" REQUEST_TIMEOUT = 10.0 CLEAR_TEST_DELAY = 2.0 OP_MAP = { gtk.CheckButton: ("set_active", "get_active"), gtk.Label: ("set_text", "get_text"), gtk.RadioButton: ("set_active", "get_active"), gtk.SpinButton: ("set_value", "get_value"), AutolabelBox: ("set_all_row_values", "get_all_row_values"), RadioButtonGroup: ("set_active_value", "get_active_value"), } SETTER = 0 GETTER = 1 # Section: Initialization def __init__(self, plugin, label_id, page=0): self._plugin = plugin if label_id in RESERVED_IDS or label_id not in plugin.store: raise LabelPlusError(ERR_INVALID_LABEL) self._store = None self._menu = None self._label_defaults = {} self._path_options = {} self._label_options = {} for path_type in PATH_TYPES: self._path_options[path_type] = {} super(LabelOptionsDialog, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() self._set_label(ID_NULL) # Keep window alive with cyclic reference self._root_widget.set_data("owner", self) self._setup_widgets() self._index_widgets() self._load_state() self._nb_tabs.set_current_page(page) self._create_menu() self._request_options(label_id) self._plugin.register_update_func(self.update_store) self._plugin.register_cleanup_func(self.destroy) except: self.destroy() raise # Section: Deinitialization def destroy(self): self._plugin.deregister_update_func(self.update_store) self._plugin.deregister_cleanup_func(self.destroy) self._destroy_menu() self._destroy_store() if self.valid: self._root_widget.set_data("owner", None) super(LabelOptionsDialog, self).destroy() def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: Public def show(self): self._wnd_label_options.show() def update_store(self, store): if self._label_id not in store: self.destroy() return self._destroy_store() self._store = store.copy() self._destroy_menu() self._create_menu() self._request_options(self._label_id) # Section: General def _set_label(self, label_id): if label_id in self._store: self._label_id = label_id self._label_name = self._store[label_id]["name"] self._label_fullname = self._store[label_id]["fullname"] else: self._label_id = ID_NULL self._label_name = _(STR_NONE) self._label_fullname = _(STR_NONE) def _report_error(self, context, error): log.error("%s: %s", context, error) self._set_error(error.tr()) def _get_path_type(self, widget): path_type = widget.get_name()[ widget.get_name().index("_")+1:widget.get_name().rindex("_")] if path_type in PATH_TYPES: return path_type return None # Section: Options def _request_options(self, label_id): def on_timeout(): if self.valid: self._wnd_label_options.set_sensitive(True) self._report_error(STR_LOAD_OPTIONS, LabelPlusError(ERR_TIMED_OUT)) def process_result(result): if self.valid: self._wnd_label_options.set_sensitive(True) for success, data in result: if not success: error = labelplus.common.extract_error(data) if error: self._report_error(STR_LOAD_OPTIONS, error) else: self.destroy() return self._label_defaults = result[0][1] self._path_options = result[1][1] self._label_options = result[2][1] self._load_options(self._label_options) self._enable_options() self._clear_options() self._disable_options() self._set_label(label_id) self._refresh() if not label_id in self._store: self._report_error(STR_LOAD_OPTIONS, LabelPlusError(ERR_INVALID_LABEL)) return self._set_error(None) log.info("Loading options for %r", self._label_fullname) deferreds = [] deferreds.append(client.labelplus.get_label_defaults()) deferreds.append(client.labelplus.get_path_options(label_id)) deferreds.append(client.labelplus.get_label_options(label_id)) deferred = twisted.internet.defer.DeferredList(deferreds, consumeErrors=True) self._wnd_label_options.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, None) def _save_options(self): def on_timeout(options): if self.valid: self._wnd_label_options.set_sensitive(True) self._report_error(STR_SAVE_OPTIONS, LabelPlusError(ERR_TIMED_OUT)) def process_result(result, options): if self.valid: self._wnd_label_options.set_sensitive(True) if isinstance(result, Failure): error = labelplus.common.extract_error(result) if error: self._report_error(STR_SAVE_OPTIONS, error) else: self.destroy() return result else: self._label_options = options if not self._label_id in self._store: self._report_error(STR_SAVE_OPTIONS, LabelPlusError(ERR_INVALID_LABEL)) return self._set_error(None) log.info("Saving options for %r", self._label_fullname) options = self._get_options() same = labelplus.common.dict_equals(options, self._label_options) if self._chk_autolabel_retroactive.get_active(): apply_to_all = not self._chk_autolabel_unlabeled_only.get_active() else: apply_to_all = None if not same or apply_to_all is not None: deferred = client.labelplus.set_label_options(self._label_id, options, apply_to_all) self._wnd_label_options.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, process_result, options) else: log.info("No options were changed") # Section: Dialog: Setup def _setup_widgets(self): self._wnd_label_options.set_transient_for( deluge.component.get("MainWindow").window) self._wnd_label_options.set_title(_(TITLE_LABEL_OPTIONS)) icon = self._wnd_label_options.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self._wnd_label_options.set_icon(icon) self._lbl_header.set_markup("<b>%s:</b>" % _(STR_LABEL)) self._img_error.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR) self._btn_close.grab_focus() self._setup_radio_button_groups() self._setup_autolabel_box() self._setup_test_combo_box() self._setup_criteria_area() for path_type in PATH_TYPES: self.__dict__["_rgrp_%s_mode" % path_type].connect( "changed", self._do_select_mode) self.connect_signals({ "do_close": self._do_close, "do_submit": self._do_submit, "do_open_select_menu": self._do_open_select_menu, "do_revert_to_defaults": self._do_revert_to_defaults, "do_toggle_fullname": self._do_toggle_fullname, "do_toggle_dependents": self._do_toggle_dependents, "on_txt_changed": self._on_txt_changed, "do_open_file_dialog": self._do_open_file_dialog, "do_test_criteria": self._do_test_criteria, }) def _setup_radio_button_groups(self): for path_type in PATH_TYPES: rgrp = RadioButtonGroup(( (getattr(self, "_rb_%s_to_parent" % path_type), MOVE_PARENT), (getattr(self, "_rb_%s_to_subfolder" % path_type), MOVE_SUBFOLDER), (getattr(self, "_rb_%s_to_folder" % path_type), MOVE_FOLDER), )) rgrp.set_name("rgrp_%s_mode" % path_type) self.__dict__["_rgrp_%s_mode" % path_type] = rgrp if __debug__: RT.register(rgrp, __name__) def _setup_autolabel_box(self): crbox = AutolabelBox(row_spacing=6, column_spacing=3) if __debug__: RT.register(crbox, __name__) crbox.set_name("crbox_autolabel_rules") self._crbox_autolabel_rules = crbox self._blk_criteria_box.add(crbox) self._widgets.append(crbox) crbox.show_all() def _setup_test_combo_box(self): prop_store = labelplus.gtkui.common.gtklib.liststore_create(str, [_(x) for x in PROPS]) if __debug__: RT.register(prop_store, __name__) cell = gtk.CellRendererText() if __debug__: RT.register(cell, __name__) self._cmb_test_criteria.pack_start(cell) self._cmb_test_criteria.add_attribute(cell, "text", 0) self._cmb_test_criteria.set_model(prop_store) self._cmb_test_criteria.set_active(0) def _setup_criteria_area(self): def on_mapped(widget, event): # Sometimes a widget is mapped but does not immediately have allocation if self._vp_criteria_area.allocation.x < 0: twisted.internet.reactor.callLater(0.1, widget.emit, "map-event", event) return if widget.handler_is_connected(handle): widget.disconnect(handle) self._vp_criteria_area.set_data("was_mapped", True) if self._plugin.config["common"]["label_options_pane_pos"] > -1: self._vp_criteria_area.set_position( self._vp_criteria_area.allocation.height - self._plugin.config["common"]["label_options_pane_pos"]) clamp_position(self._vp_criteria_area) def clamp_position(widget, *args): handle_size = widget.allocation.height - \ widget.get_property("max-position") max_dist = self._hb_test_criteria.allocation.height + handle_size*2 threshold = max_dist/2 if widget.allocation.height - widget.get_position() > threshold: twisted.internet.reactor.callLater(0.1, widget.set_position, widget.allocation.height - max_dist) else: twisted.internet.reactor.callLater(0.1, widget.set_position, widget.allocation.height) handle = self._eb_criteria_area.connect("map-event", on_mapped) self._vp_criteria_area.connect("button-release-event", clamp_position) self._vp_criteria_area.connect("accept-position", clamp_position) def _index_widgets(self): self._exp_group = ( self._exp_download_location, self._exp_move_completed, ) self._option_groups = ( ( self._chk_download_settings, self._chk_download_location, self._rgrp_download_location_mode, self._lbl_download_location_path, self._chk_move_completed, self._rgrp_move_completed_mode, self._lbl_move_completed_path, self._chk_prioritize_first_last, ), ( self._chk_bandwidth_settings, self._rb_shared_limit, self._spn_max_download_speed, self._spn_max_upload_speed, self._spn_max_connections, self._spn_max_upload_slots, ), ( self._chk_queue_settings, self._chk_auto_managed, self._chk_stop_at_ratio, self._spn_stop_ratio, self._chk_remove_at_ratio, ), ( self._chk_autolabel_settings, self._rb_autolabel_match_all, self._crbox_autolabel_rules, ), ) self._dependency_widgets = { self._chk_download_settings: (self._blk_download_settings_group,), self._chk_bandwidth_settings: (self._blk_bandwidth_settings_group,), self._chk_queue_settings: (self._blk_queue_settings_group,), self._chk_autolabel_settings: (self._blk_autolabel_settings_group,), self._chk_download_location: (self._blk_download_location_group,), self._rb_download_location_to_folder: (self._txt_download_location_path,), self._chk_move_completed: (self._blk_move_completed_group,), self._rb_move_completed_to_folder: (self._txt_move_completed_path,), self._chk_stop_at_ratio: (self._spn_stop_ratio, self._chk_remove_at_ratio), self._chk_autolabel_retroactive: (self._chk_autolabel_unlabeled_only,), } # Section: Dialog: State def _load_state(self): if not client.is_localhost(): for path_type in PATH_TYPES: self.__dict__["_btn_%s_browse" % path_type].hide() if self._plugin.initialized: pos = self._plugin.config["common"]["label_options_pos"] if pos: self._wnd_label_options.move(*pos) size = self._plugin.config["common"]["label_options_size"] if size: self._wnd_label_options.resize(*size) self._tgb_fullname.set_active( self._plugin.config["common"]["label_options_fullname"]) expanded = self._plugin.config["common"]["label_options_exp_state"] for exp in expanded: widget = getattr(self, "_" + exp, None) if widget: widget.set_expanded(True) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["label_options_pos"] = \ list(self._wnd_label_options.get_position()) self._plugin.config["common"]["label_options_size"] = \ list(self._wnd_label_options.get_size()) self._plugin.config["common"]["label_options_fullname"] = \ self._tgb_fullname.get_active() expanded = [] for exp in self._exp_group: if exp.get_expanded(): expanded.append(exp.get_name()) self._plugin.config["common"]["label_options_exp_state"] = expanded if self._vp_criteria_area.get_data("was_mapped"): self._plugin.config["common"]["label_options_pane_pos"] = \ self._vp_criteria_area.allocation.height - \ self._vp_criteria_area.get_position() self._plugin.config.save() # Section: Dialog: Options def _get_widget_values(self, widgets, options_out): for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in options_out: ops = self.OP_MAP.get(type(widget)) if ops: getter = getattr(widget, ops[self.GETTER]) options_out[name] = getter() def _set_widget_values(self, widgets, options_in): for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in options_in: ops = self.OP_MAP.get(type(widget)) if ops: setter = getattr(widget, ops[self.SETTER]) setter(options_in[name]) def _load_options(self, options): for group in self._option_groups: self._set_widget_values(group, options) for path_type in PATH_TYPES: mode = options["%s_mode" % path_type] path = options["%s_path" % path_type] self.__dict__["_txt_%s_path" % path_type].set_text(path) if mode != MOVE_FOLDER: path = self._path_options[path_type].get(mode, "") self._set_path_label(path, path_type) def _get_options(self): options = copy.deepcopy(self._label_defaults) for group in self._option_groups: self._get_widget_values(group, options) return options def _revert_options_by_page(self, options_out, page): widgets = self._option_groups[page] for widget in widgets: prefix, sep, name = widget.get_name().partition("_") if sep and name in self._label_defaults: options_out[name] = copy.deepcopy(self._label_defaults[name]) def _clear_options(self): if not self._label_defaults: self._label_defaults = LABEL_DEFAULTS for path_type in PATH_TYPES: self._path_options[path_type] = {} self._label_options = {} self._load_options(self._label_defaults) def _enable_options(self): self._nb_tabs.set_sensitive(True) self._btn_defaults.set_sensitive(True) self._btn_defaults_all.set_sensitive(True) self._btn_apply.set_sensitive(True) def _disable_options(self): self._nb_tabs.set_sensitive(False) self._btn_defaults.set_sensitive(False) self._btn_defaults_all.set_sensitive(False) self._btn_apply.set_sensitive(False) # Section: Dialog: Modifiers def _refresh(self): self._do_toggle_fullname() if self._label_id == ID_NULL: self._lbl_selected_label.set_tooltip_text(None) else: self._lbl_selected_label.set_tooltip_text(self._label_fullname) for widget in self._dependency_widgets: self._do_toggle_dependents(widget) self._set_test_result(None) def _set_path_label(self, path, path_type): lbl_widget = self.__dict__["_lbl_%s_path" % path_type] lbl_widget.set_text(path) lbl_widget.set_tooltip_text(path) def _set_test_result(self, result): if result is not None: icon = gtk.STOCK_YES if result else gtk.STOCK_NO self._txt_test_criteria.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, icon) else: self._txt_test_criteria.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, None) def _set_error(self, message): if message: self._img_error.set_tooltip_text(message) self._img_error.show() else: self._img_error.hide() # Section: Dialog: Handlers: General def _do_close(self, *args): self._save_state() self.destroy() def _do_submit(self, *args): self._save_options() def _do_open_select_menu(self, *args): if self._menu: self._menu.popup(None, None, None, 1, gtk.gdk.CURRENT_TIME) def _do_revert_to_defaults(self, widget): if widget is self._btn_defaults: options = self._get_options() self._revert_options_by_page(options, self._nb_tabs.get_current_page()) else: options = self._label_defaults self._load_options(options) self._refresh() def _do_toggle_fullname(self, *args): if self._tgb_fullname.get_active(): self._lbl_selected_label.set_text(self._label_fullname) else: self._lbl_selected_label.set_text(self._label_name) def _do_toggle_dependents(self, widget): if widget in self._dependency_widgets: toggled = widget.get_active() for dependent in self._dependency_widgets[widget]: dependent.set_sensitive(toggled) # Section: Dialog: Handlers: Paths def _do_select_mode(self, group, button, mode): path_type = self._get_path_type(group) self._do_toggle_dependents(self.__dict__["_rb_%s_to_folder" % path_type]) if mode == MOVE_FOLDER: self._on_txt_changed(self.__dict__["_txt_%s_path" % path_type]) else: path = self._path_options[path_type].get(mode, "") self._set_path_label(path, path_type) def _on_txt_changed(self, widget): path_type = self._get_path_type(widget) self._set_path_label(widget.get_text(), path_type) def _do_open_file_dialog(self, widget): def on_response(widget, response): if self.valid and response == gtk.RESPONSE_OK: txt_widget.set_text(widget.get_filename()) widget.destroy() path_type = self._get_path_type(widget) txt_widget = self.__dict__["_txt_%s_path" % path_type] dialog = gtk.FileChooserDialog(_(TITLE_SELECT_FOLDER), self._wnd_label_options, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, ( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK, ) ) if __debug__: RT.register(dialog, __name__) dialog.set_destroy_with_parent(True) dialog.connect("response", on_response) path = txt_widget.get_text() if not os.path.exists(path): path = "" dialog.set_filename(path) dialog.show_all() widgets = labelplus.gtkui.common.gtklib.widget_get_descendents(dialog, (gtk.ToggleButton,), 1) if widgets: location_toggle = widgets[0] location_toggle.set_active(False) # Section: Dialog: Handlers: Criteria Test def _do_test_criteria(self, *args): def clear_result(): if self.valid: self._set_test_result(None) index = self._cmb_test_criteria.get_active() prop_name = PROPS[index] props = { prop_name: [unicode(self._txt_test_criteria.get_text(), "utf8")] } rules = self._crbox_autolabel_rules.get_all_row_values() match_all = self._rb_autolabel_match_all.get_active() log.debug("Properties: %r, Rules: %r, Match all: %s", props, rules, match_all) result = labelplus.common.config.autolabel.find_match(props, rules, match_all) log.debug("Test result: %s", result) self._set_test_result(result) twisted.internet.reactor.callLater(self.CLEAR_TEST_DELAY, clear_result) # Section: Dialog: Menu def _create_menu(self): def on_show_menu(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) if parent_id in self._store: items[0].show() items[1].show() else: items[0].hide() items[1].hide() def on_activate(widget, label_id): self._request_options(label_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._label_id) self._request_options(parent_id) self._menu = LabelSelectionMenu(self._store.model, on_activate) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items(self._menu, 0, ( ((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), ((gtk.SeparatorMenuItem,),), ) ) if __debug__: for item in items: RT.register(item, __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None
class NameInputDialog(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("wnd_name_input.glade") ROOT_WIDGET = "wnd_name_input" REQUEST_TIMEOUT = 10.0 TYPE_ADD = "add" TYPE_RENAME = "rename" DIALOG_SPECS = { TYPE_ADD: (_(TITLE_ADD_LABEL), gtk.STOCK_ADD, STR_ADD_LABEL), TYPE_RENAME: (_(TITLE_RENAME_LABEL), gtk.STOCK_EDIT, STR_RENAME_LABEL), } DIALOG_NAME = 0 DIALOG_ICON = 1 DIALOG_CONTEXT = 2 # Section: Initialization def __init__(self, plugin, dialog_type, label_id): self._plugin = plugin self._type = dialog_type if self._type == self.TYPE_ADD: self._parent_id = label_id elif self._type == self.TYPE_RENAME: if label_id in plugin.store: self._parent_id = labelplus.common.label.get_parent_id(label_id) self._label_id = label_id self._label_name = plugin.store[label_id]["name"] self._label_fullname = plugin.store[label_id]["fullname"] else: raise LabelPlusError(ERR_INVALID_LABEL) else: raise LabelPlusError(ERR_INVALID_TYPE) self._store = None self._menu = None super(NameInputDialog, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() self._set_parent_label(self._parent_id) # Keep window alive with cyclic reference self._root_widget.set_data("owner", self) self._setup_widgets() self._load_state() self._create_menu() self._refresh() self._plugin.register_update_func(self.update_store) self._plugin.register_cleanup_func(self.destroy) except: self.destroy() raise # Section: Deinitialization def destroy(self): self._plugin.deregister_update_func(self.update_store) self._plugin.deregister_cleanup_func(self.destroy) self._destroy_menu() self._destroy_store() if self.valid: self._root_widget.set_data("owner", None) super(NameInputDialog, self).destroy() def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: Public def show(self): self._wnd_name_input.show() def update_store(self, store): if self._type == self.TYPE_RENAME: if self._label_id not in store: self.destroy() return self._destroy_store() self._store = store.copy() self._destroy_menu() self._create_menu() self._select_parent_label(self._parent_id) # Section: General def _set_parent_label(self, parent_id): if parent_id in self._store: self._parent_id = parent_id self._parent_name = self._store[parent_id]["name"] self._parent_fullname = self._store[parent_id]["fullname"] else: self._parent_id = ID_NULL self._parent_name = _(STR_NONE) self._parent_fullname = _(STR_NONE) def _validate(self): if self._parent_id != ID_NULL and self._parent_id not in self._store: raise LabelPlusError(ERR_INVALID_PARENT) if self._type == self.TYPE_RENAME: if self._label_id not in self._store: raise LabelPlusError(ERR_INVALID_LABEL) if (self._label_id == self._parent_id or labelplus.common.label.is_ancestor(self._label_id, self._parent_id)): raise LabelPlusError(ERR_INVALID_PARENT) name = unicode(self._txt_name.get_text(), "utf8") labelplus.common.label.validate_name(name) for id in self._store.get_descendent_ids(self._parent_id, max_depth=1): if name == self._store[id]["name"]: raise LabelPlusError(ERR_LABEL_EXISTS) def _report_error(self, error): log.error("%s: %s", self.DIALOG_SPECS[self._type][self.DIALOG_CONTEXT], error) self._set_error(error.tr()) # Section: Dialog: Setup def _setup_widgets(self): self._wnd_name_input.set_transient_for( deluge.component.get("MainWindow").window) spec = self.DIALOG_SPECS[self._type] self._wnd_name_input.set_title(spec[self.DIALOG_NAME]) icon = self._wnd_name_input.render_icon(spec[self.DIALOG_ICON], gtk.ICON_SIZE_SMALL_TOOLBAR) self._wnd_name_input.set_icon(icon) self._lbl_header.set_markup("<b>%s:</b>" % _(STR_PARENT)) self._img_error.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR) if self._type == self.TYPE_RENAME: self._btn_revert.show() self._txt_name.set_text(self._label_name) self._txt_name.grab_focus() self.connect_signals({ "do_close" : self._do_close, "do_submit" : self._do_submit, "do_open_select_menu": self._do_open_select_menu, "do_toggle_fullname": self._do_toggle_fullname, "do_check_input": self._do_check_input, "do_revert": self._do_revert, }) # Section: Dialog: State def _load_state(self): if self._plugin.initialized: pos = self._plugin.config["common"]["name_input_pos"] if pos: self._wnd_name_input.move(*pos) size = self._plugin.config["common"]["name_input_size"] if size: self._wnd_name_input.resize(*size) self._tgb_fullname.set_active( self._plugin.config["common"]["name_input_fullname"]) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["name_input_pos"] = \ list(self._wnd_name_input.get_position()) self._plugin.config["common"]["name_input_size"] = \ list(self._wnd_name_input.get_size()) self._plugin.config["common"]["name_input_fullname"] = \ self._tgb_fullname.get_active() self._plugin.config.save() # Section: Dialog: Modifiers def _refresh(self): self._do_toggle_fullname() if self._parent_id == ID_NULL: self._lbl_selected_label.set_tooltip_text(None) else: self._lbl_selected_label.set_tooltip_text(self._parent_fullname) self._do_check_input() def _set_error(self, message): if message: self._img_error.set_tooltip_text(message) self._img_error.show() else: self._img_error.hide() def _select_parent_label(self, parent_id): self._set_parent_label(parent_id) self._refresh() self._txt_name.grab_focus() # Section: Dialog: Handlers def _do_close(self, *args): self._save_state() self.destroy() def _do_submit(self, *args): def on_timeout(): if self.valid: self._wnd_name_input.set_sensitive(True) self._report_error(LabelPlusError(ERR_TIMED_OUT)) def process_result(result): if self.valid: self._wnd_name_input.set_sensitive(True) if isinstance(result, Failure): error = labelplus.common.extract_error(result) if error: self._report_error(error) else: self.destroy() return result else: self._do_close() self._do_check_input() if not self._btn_ok.get_property("sensitive"): return name = unicode(self._txt_name.get_text(), "utf8") if self._parent_id != ID_NULL: dest_name = "%s/%s" % (self._parent_fullname, name) else: dest_name = name if self._type == self.TYPE_ADD: log.info("Adding label: %r", dest_name) deferred = client.labelplus.add_label(self._parent_id, name) elif self._type == self.TYPE_RENAME: log.info("Renaming label: %r -> %r", self._label_fullname, dest_name) deferred = client.labelplus.move_label(self._label_id, self._parent_id, name) self._wnd_name_input.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, process_result) def _do_open_select_menu(self, *args): if self._menu: self._menu.popup(None, None, None, 1, gtk.gdk.CURRENT_TIME) def _do_toggle_fullname(self, *args): if self._tgb_fullname.get_active(): self._lbl_selected_label.set_text(self._parent_fullname) else: self._lbl_selected_label.set_text(self._parent_name) def _do_check_input(self, *args): try: self._validate() self._btn_ok.set_sensitive(True) self._set_error(None) except LabelPlusError as e: self._btn_ok.set_sensitive(False) self._set_error(e.tr()) def _do_revert(self, *args): if self._type != self.TYPE_RENAME: return self._txt_name.set_text(self._label_name) parent_id = labelplus.common.label.get_parent_id(self._label_id) self._select_parent_label(parent_id) # Section: Dialog: Menu def _create_menu(self): def on_show_menu(menu): parent_id = labelplus.common.label.get_parent_id(self._parent_id) if parent_id in self._store: items[0].show() else: items[0].hide() def on_activate(widget, parent_id): self._select_parent_label(parent_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._parent_id) self._select_parent_label(parent_id) root_items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NULL),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items(self._menu, 1, (((gtk.MenuItem, _(STR_PARENT)), on_activate_parent),)) if __debug__: RT.register(items[0], __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() if self._type == self.TYPE_RENAME: item = self._menu.get_label_item(self._label_id) if item: item.set_sensitive(False) def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None
class AddTorrentExt(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("blk_add_torrent_ext.ui") ROOT_WIDGET = "blk_add_torrent_ext" TORRENT_ID = 0 # Section: Initialization def __init__(self, plugin): self._plugin = plugin self._dialog = deluge.component.get("AddTorrentDialog") self._view = self._dialog.listview_torrents self._store = None self._menu = None self._mappings = {} self._handlers = [] super(AddTorrentExt, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() if __debug__: RT.register(self._store, __name__) log.debug("Setting up widgets...") self._setup_widgets() log.debug("Installing widgets...") self._install_widgets() self._register_handlers() log.debug("Loading state...") self._display_torrent_label(None) self._update_sensitivity() log.debug("Creating menu...") self._create_menu() self._plugin.register_update_func(self.update_store) except: self.unload() raise def _setup_widgets(self): def on_click(widget): if self._menu: self._menu.popup(None, None, None, None, 1, Gdk.CURRENT_TIME) def on_toggle(widget): self._refresh_torrent_label() self._blk_add_torrent_ext.get_label_widget().set_markup("<b>%s</b>" % labelplus.common.DISPLAY_NAME) self._btn_select.connect("clicked", on_click) self._tgb_fullname.connect("toggled", on_toggle) self._load_state() def _install_widgets(self): widget = self._dialog.builder.get_object("button_revert") box = widget.get_ancestor(Gtk.Box).get_parent().get_ancestor(Gtk.Box) box.pack_start(self._blk_add_torrent_ext, False, True, 0) box.child_set_property(self._blk_add_torrent_ext, "position", box.child_get_property(self._blk_add_torrent_ext, "position")-1) def _register_handlers(self): self._register_handler(self._view.get_selection(), "changed", self._on_selection_changed) self._register_handler(self._dialog.builder.get_object("button_revert"), "clicked", self._do_revert) self._register_handler(self._dialog.builder.get_object("button_apply"), "clicked", self._do_apply_to_all) self._register_handler(self._dialog.builder.get_object("button_remove"), "clicked", self._on_remove_torrent) self._register_handler(self._dialog.builder.get_object("button_cancel"), "clicked", self._on_close) self._register_handler(self._dialog.builder.get_object("button_add"), "clicked", self._on_add_torrent) def _create_menu(self): def on_activate(widget, label_id): id = self._get_selected_torrent() if id: self._set_torrent_label(id, label_id) self._display_torrent_label(id) items = (((Gtk.MenuItem, {'label': _(STR_NONE)}), on_activate, ID_NONE),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=items) if __debug__: RT.register(self._menu, __name__) # Section: Deinitialization def unload(self): self._plugin.deregister_update_func(self.update_store) self._deregister_handlers() self._uninstall_widgets() self._destroy_menu() self._destroy_store() super(AddTorrentExt, self).destroy() def _deregister_handlers(self): for widget, handle in self._handlers: widget.disconnect(handle) def _uninstall_widgets(self): box = self._blk_add_torrent_ext.get_parent() if box: box.remove(self._blk_add_torrent_ext) def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: General def _register_handler(self, obj, signal, func, *args, **kwargs): handle = obj.connect(signal, func, *args, **kwargs) self._handlers.append((obj, handle)) def _get_selected_torrent(self): model, row = self._view.get_selection().get_selected() if row: return model[row][self.TORRENT_ID] return None # Section: Update def update_store(self, store): self._store.destroy() self._store = store.copy() if __debug__: RT.register(self._store, __name__) self._destroy_menu() self._create_menu() self._refresh_torrent_label() # Section: Widget State def _load_state(self): if self._plugin.initialized: self._tgb_fullname.set_active( self._plugin.config["common"]["add_torrent_ext_fullname"]) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["add_torrent_ext_fullname"] = \ self._tgb_fullname.get_active() self._plugin.config.save() # Section: Widget Modifiers def _update_sensitivity(self): id = self._get_selected_torrent() self._blk_add_torrent_ext.set_sensitive(id is not None) def _display_torrent_label(self, id): if id in self._mappings: label_id = self._mappings[id] else: label_id = None if label_id in self._store: name = self._store[label_id]["name"] fullname = self._store[label_id]["fullname"] else: name = _(STR_NONE) fullname = _(STR_NONE) if self._tgb_fullname.get_active(): self._lbl_name.set_text(fullname) else: self._lbl_name.set_text(name) if label_id: self._lbl_name.set_tooltip_text(fullname) else: self._lbl_name.set_tooltip_text(None) def _refresh_torrent_label(self): id = self._get_selected_torrent() self._display_torrent_label(id) def _set_torrent_label(self, torrent_id, label_id): if label_id not in RESERVED_IDS and label_id in self._store: log.info("Setting label for %s to %r", torrent_id, self._store[label_id]["fullname"]) self._mappings[torrent_id] = label_id else: log.info("Removing label from %s", torrent_id) if label_id not in self._store: log.error("%s", LabelPlusError(ERR_INVALID_LABEL)) if torrent_id in self._mappings: del self._mappings[torrent_id] def _clear(self): self._mappings.clear() self._display_torrent_label(None) # Section: Deluge Handlers def _on_selection_changed(self, selection): self._refresh_torrent_label() self._update_sensitivity() def _do_revert(self, widget): id = self._get_selected_torrent() if id in self._mappings: log.info("Resetting label setting on %s", id) del self._mappings[id] self._display_torrent_label(None) def _do_apply_to_all(self, widget): def set_mapping(model, path, row): id = model[row][self.TORRENT_ID] self._mappings[id] = label_id id = self._get_selected_torrent() if id in self._mappings: label_id = self._mappings[id] log.info("Applying label %r to all torrents", self._store[label_id]["fullname"]) self._view.get_model().foreach(set_mapping) else: self._mappings.clear() def _on_remove_torrent(self, widget): ids = list(self._dialog.files.keys()) for key in list(self._mappings.keys()): if key not in ids: del self._mappings[key] self._refresh_torrent_label() def _on_add_torrent(self, widget): log.info("Applying labels to the torrents added") reverse_map = {} for torrent_id, label_id in list(self._mappings.items()): if label_id in RESERVED_IDS or label_id not in self._store: continue if label_id not in reverse_map: reverse_map[label_id] = [] reverse_map[label_id].append(torrent_id) for label_id, torrent_ids in list(reverse_map.items()): client.labelplus.set_torrent_labels(torrent_ids, label_id) self._on_close(widget) def _on_close(self, widget): self._save_state() self._clear()
def _create_filter_menu(self): def on_activate(widget, ids): if not isinstance(ids, list): ids = [ids] self._set_filter_sync_sidebar(ids) def on_activate_parent(widget): ids = self.get_any_selected_labels() parent_id = labelplus.common.label.get_common_parent(ids) on_activate(widget, parent_id) def on_activate_selected(widget): ids = self.get_selected_torrent_labels() on_activate(widget, ids) def on_show_menu(widget): items[0].hide() items[1].hide() ids = self.get_any_selected_labels() parent_id = labelplus.common.label.get_common_parent(ids) if self._store.is_user_label(parent_id): items[0].show() ids = self.get_selected_torrent_labels() if self._store.user_labels(ids): items[1].show() root_items = ( ((gtk.MenuItem, _(STR_ALL)), on_activate, ID_ALL), ((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NONE), ) menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) menu.connect("show", on_show_menu) items = labelplus.gtkui.common.gtklib.menu_add_items(menu, 2, ( ((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), ((gtk.MenuItem, _(STR_SELECTED)), on_activate_selected), ) ) root = gtk.MenuItem(_(TITLE_SET_FILTER)) root.set_submenu(menu) if __debug__: RT.register(menu, __name__) if __debug__: RT.register(root, __name__) return root
class AddTorrentExt(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("blk_add_torrent_ext.glade") ROOT_WIDGET = "blk_add_torrent_ext" TORRENT_ID = 0 # Section: Initialization def __init__(self, plugin): self._plugin = plugin self._dialog = deluge.component.get("AddTorrentDialog") self._view = self._dialog.listview_torrents self._store = None self._menu = None self._mappings = {} self._handlers = [] super(AddTorrentExt, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() if __debug__: RT.register(self._store, __name__) log.debug("Setting up widgets...") self._setup_widgets() log.debug("Installing widgets...") self._install_widgets() self._register_handlers() log.debug("Loading state...") self._display_torrent_label(None) self._update_sensitivity() log.debug("Creating menu...") self._create_menu() self._plugin.register_update_func(self.update_store) except: self.unload() raise def _setup_widgets(self): def on_click(widget): if self._menu: self._menu.popup(None, None, None, 1, gtk.gdk.CURRENT_TIME) def on_toggle(widget): self._refresh_torrent_label() self._blk_add_torrent_ext.get_label_widget().set_markup("<b>%s</b>" % labelplus.common.DISPLAY_NAME) self._btn_select.connect("clicked", on_click) self._tgb_fullname.connect("toggled", on_toggle) self._load_state() def _install_widgets(self): widget = self._dialog.glade.get_widget("button_revert") box = widget.get_ancestor(gtk.VBox) box.pack_start(self._blk_add_torrent_ext, expand=False) box.child_set_property(self._blk_add_torrent_ext, "position", box.child_get_property(self._blk_add_torrent_ext, "position")-1) def _register_handlers(self): self._register_handler(self._view.get_selection(), "changed", self._on_selection_changed) self._register_handler(self._dialog.glade.get_widget("button_revert"), "clicked", self._do_revert) self._register_handler(self._dialog.glade.get_widget("button_apply"), "clicked", self._do_apply_to_all) self._register_handler(self._dialog.glade.get_widget("button_remove"), "clicked", self._on_remove_torrent) self._register_handler(self._dialog.glade.get_widget("button_cancel"), "clicked", self._on_close) self._register_handler(self._dialog.glade.get_widget("button_add"), "clicked", self._on_add_torrent) def _create_menu(self): def on_activate(widget, label_id): id = self._get_selected_torrent() if id: self._set_torrent_label(id, label_id) self._display_torrent_label(id) items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NONE),) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=items) if __debug__: RT.register(self._menu, __name__) # Section: Deinitialization def unload(self): self._plugin.deregister_update_func(self.update_store) self._deregister_handlers() self._uninstall_widgets() self._destroy_menu() self._destroy_store() super(AddTorrentExt, self).destroy() def _deregister_handlers(self): for widget, handle in self._handlers: widget.disconnect(handle) def _uninstall_widgets(self): box = self._blk_add_torrent_ext.get_parent() if box: box.remove(self._blk_add_torrent_ext) def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: General def _register_handler(self, obj, signal, func, *args, **kwargs): handle = obj.connect(signal, func, *args, **kwargs) self._handlers.append((obj, handle)) def _get_selected_torrent(self): model, row = self._view.get_selection().get_selected() if row: return model[row][self.TORRENT_ID] return None # Section: Update def update_store(self, store): self._store.destroy() self._store = store.copy() if __debug__: RT.register(self._store, __name__) self._destroy_menu() self._create_menu() self._refresh_torrent_label() # Section: Widget State def _load_state(self): if self._plugin.initialized: self._tgb_fullname.set_active( self._plugin.config["common"]["add_torrent_ext_fullname"]) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["add_torrent_ext_fullname"] = \ self._tgb_fullname.get_active() self._plugin.config.save() # Section: Widget Modifiers def _update_sensitivity(self): id = self._get_selected_torrent() self._blk_add_torrent_ext.set_sensitive(id is not None) def _display_torrent_label(self, id): if id in self._mappings: label_id = self._mappings[id] else: label_id = None if label_id in self._store: name = self._store[label_id]["name"] fullname = self._store[label_id]["fullname"] else: name = _(STR_NONE) fullname = _(STR_NONE) if self._tgb_fullname.get_active(): self._lbl_name.set_text(fullname) else: self._lbl_name.set_text(name) if label_id: self._lbl_name.set_tooltip_text(fullname) else: self._lbl_name.set_tooltip_text(None) def _refresh_torrent_label(self): id = self._get_selected_torrent() self._display_torrent_label(id) def _set_torrent_label(self, torrent_id, label_id): if label_id not in RESERVED_IDS and label_id in self._store: log.info("Setting label for %s to %r", torrent_id, self._store[label_id]["fullname"]) self._mappings[torrent_id] = label_id else: log.info("Removing label from %s", torrent_id) if label_id not in self._store: log.error("%s", LabelPlusError(ERR_INVALID_LABEL)) if torrent_id in self._mappings: del self._mappings[torrent_id] def _clear(self): self._mappings.clear() self._display_torrent_label(None) # Section: Deluge Handlers def _on_selection_changed(self, selection): self._refresh_torrent_label() self._update_sensitivity() def _do_revert(self, widget): id = self._get_selected_torrent() if id in self._mappings: log.info("Resetting label setting on %s", id) del self._mappings[id] self._display_torrent_label(None) def _do_apply_to_all(self, widget): def set_mapping(model, path, row): id = model[row][self.TORRENT_ID] self._mappings[id] = label_id id = self._get_selected_torrent() if id in self._mappings: label_id = self._mappings[id] log.info("Applying label %r to all torrents", self._store[label_id]["fullname"]) self._view.get_model().foreach(set_mapping) else: self._mappings.clear() def _on_remove_torrent(self, widget): ids = self._dialog.files.keys() for key in self._mappings.keys(): if key not in ids: del self._mappings[key] self._refresh_torrent_label() def _on_add_torrent(self, widget): log.info("Applying labels to the torrents added") reverse_map = {} for torrent_id, label_id in self._mappings.iteritems(): if label_id in RESERVED_IDS or label_id not in self._store: continue if label_id not in reverse_map: reverse_map[label_id] = [] reverse_map[label_id].append(torrent_id) for label_id, torrent_ids in reverse_map.iteritems(): client.labelplus.set_torrent_labels(torrent_ids, label_id) self._on_close(widget) def _on_close(self, widget): self._save_state() self._clear()
class NameInputDialog(WidgetEncapsulator): # Section: Constants GLADE_FILE = labelplus.common.get_resource("wnd_name_input.glade") ROOT_WIDGET = "wnd_name_input" REQUEST_TIMEOUT = 10.0 TYPE_ADD = "add" TYPE_RENAME = "rename" DIALOG_SPECS = { TYPE_ADD: (_(TITLE_ADD_LABEL), gtk.STOCK_ADD, STR_ADD_LABEL), TYPE_RENAME: (_(TITLE_RENAME_LABEL), gtk.STOCK_EDIT, STR_RENAME_LABEL), } DIALOG_NAME = 0 DIALOG_ICON = 1 DIALOG_CONTEXT = 2 # Section: Initialization def __init__(self, plugin, dialog_type, label_id): self._plugin = plugin self._type = dialog_type if self._type == self.TYPE_ADD: self._parent_id = label_id elif self._type == self.TYPE_RENAME: if label_id in plugin.store: self._parent_id = labelplus.common.label.get_parent_id( label_id) self._label_id = label_id self._label_name = plugin.store[label_id]["name"] self._label_fullname = plugin.store[label_id]["fullname"] else: raise LabelPlusError(ERR_INVALID_LABEL) else: raise LabelPlusError(ERR_INVALID_TYPE) self._store = None self._menu = None super(NameInputDialog, self).__init__(self.GLADE_FILE, self.ROOT_WIDGET, "_") try: self._store = plugin.store.copy() self._set_parent_label(self._parent_id) # Keep window alive with cyclic reference self._root_widget.set_data("owner", self) self._setup_widgets() self._load_state() self._create_menu() self._refresh() self._plugin.register_update_func(self.update_store) self._plugin.register_cleanup_func(self.destroy) except: self.destroy() raise # Section: Deinitialization def destroy(self): self._plugin.deregister_update_func(self.update_store) self._plugin.deregister_cleanup_func(self.destroy) self._destroy_menu() self._destroy_store() if self.valid: self._root_widget.set_data("owner", None) super(NameInputDialog, self).destroy() def _destroy_store(self): if self._store: self._store.destroy() self._store = None # Section: Public def show(self): self._wnd_name_input.show() def update_store(self, store): if self._type == self.TYPE_RENAME: if self._label_id not in store: self.destroy() return self._destroy_store() self._store = store.copy() self._destroy_menu() self._create_menu() self._select_parent_label(self._parent_id) # Section: General def _set_parent_label(self, parent_id): if parent_id in self._store: self._parent_id = parent_id self._parent_name = self._store[parent_id]["name"] self._parent_fullname = self._store[parent_id]["fullname"] else: self._parent_id = ID_NULL self._parent_name = _(STR_NONE) self._parent_fullname = _(STR_NONE) def _validate(self): if self._parent_id != ID_NULL and self._parent_id not in self._store: raise LabelPlusError(ERR_INVALID_PARENT) if self._type == self.TYPE_RENAME: if self._label_id not in self._store: raise LabelPlusError(ERR_INVALID_LABEL) if (self._label_id == self._parent_id or labelplus.common.label.is_ancestor( self._label_id, self._parent_id)): raise LabelPlusError(ERR_INVALID_PARENT) name = unicode(self._txt_name.get_text(), "utf8") labelplus.common.label.validate_name(name) for id in self._store.get_descendent_ids(self._parent_id, max_depth=1): if name == self._store[id]["name"]: raise LabelPlusError(ERR_LABEL_EXISTS) def _report_error(self, error): log.error("%s: %s", self.DIALOG_SPECS[self._type][self.DIALOG_CONTEXT], error) self._set_error(error.tr()) # Section: Dialog: Setup def _setup_widgets(self): self._wnd_name_input.set_transient_for( deluge.component.get("MainWindow").window) spec = self.DIALOG_SPECS[self._type] self._wnd_name_input.set_title(spec[self.DIALOG_NAME]) icon = self._wnd_name_input.render_icon(spec[self.DIALOG_ICON], gtk.ICON_SIZE_SMALL_TOOLBAR) self._wnd_name_input.set_icon(icon) self._lbl_header.set_markup("<b>%s:</b>" % _(STR_PARENT)) self._img_error.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_SMALL_TOOLBAR) if self._type == self.TYPE_RENAME: self._btn_revert.show() self._txt_name.set_text(self._label_name) self._txt_name.grab_focus() self.connect_signals({ "do_close": self._do_close, "do_submit": self._do_submit, "do_open_select_menu": self._do_open_select_menu, "do_toggle_fullname": self._do_toggle_fullname, "do_check_input": self._do_check_input, "do_revert": self._do_revert, }) # Section: Dialog: State def _load_state(self): if self._plugin.initialized: pos = self._plugin.config["common"]["name_input_pos"] if pos: self._wnd_name_input.move(*pos) size = self._plugin.config["common"]["name_input_size"] if size: self._wnd_name_input.resize(*size) self._tgb_fullname.set_active( self._plugin.config["common"]["name_input_fullname"]) def _save_state(self): if self._plugin.initialized: self._plugin.config["common"]["name_input_pos"] = \ list(self._wnd_name_input.get_position()) self._plugin.config["common"]["name_input_size"] = \ list(self._wnd_name_input.get_size()) self._plugin.config["common"]["name_input_fullname"] = \ self._tgb_fullname.get_active() self._plugin.config.save() # Section: Dialog: Modifiers def _refresh(self): self._do_toggle_fullname() if self._parent_id == ID_NULL: self._lbl_selected_label.set_tooltip_text(None) else: self._lbl_selected_label.set_tooltip_text(self._parent_fullname) self._do_check_input() def _set_error(self, message): if message: self._img_error.set_tooltip_text(message) self._img_error.show() else: self._img_error.hide() def _select_parent_label(self, parent_id): self._set_parent_label(parent_id) self._refresh() self._txt_name.grab_focus() # Section: Dialog: Handlers def _do_close(self, *args): self._save_state() self.destroy() def _do_submit(self, *args): def on_timeout(): if self.valid: self._wnd_name_input.set_sensitive(True) self._report_error(LabelPlusError(ERR_TIMED_OUT)) def process_result(result): if self.valid: self._wnd_name_input.set_sensitive(True) if isinstance(result, Failure): error = labelplus.common.extract_error(result) if error: self._report_error(error) else: self.destroy() return result else: self._do_close() self._do_check_input() if not self._btn_ok.get_property("sensitive"): return name = unicode(self._txt_name.get_text(), "utf8") if self._parent_id != ID_NULL: dest_name = "%s/%s" % (self._parent_fullname, name) else: dest_name = name if self._type == self.TYPE_ADD: log.info("Adding label: %r", dest_name) deferred = client.labelplus.add_label(self._parent_id, name) elif self._type == self.TYPE_RENAME: log.info("Renaming label: %r -> %r", self._label_fullname, dest_name) deferred = client.labelplus.move_label(self._label_id, self._parent_id, name) self._wnd_name_input.set_sensitive(False) labelplus.common.deferred_timeout(deferred, self.REQUEST_TIMEOUT, on_timeout, process_result, process_result) def _do_open_select_menu(self, *args): if self._menu: self._menu.popup(None, None, None, 1, gtk.gdk.CURRENT_TIME) def _do_toggle_fullname(self, *args): if self._tgb_fullname.get_active(): self._lbl_selected_label.set_text(self._parent_fullname) else: self._lbl_selected_label.set_text(self._parent_name) def _do_check_input(self, *args): try: self._validate() self._btn_ok.set_sensitive(True) self._set_error(None) except LabelPlusError as e: self._btn_ok.set_sensitive(False) self._set_error(e.tr()) def _do_revert(self, *args): if self._type != self.TYPE_RENAME: return self._txt_name.set_text(self._label_name) parent_id = labelplus.common.label.get_parent_id(self._label_id) self._select_parent_label(parent_id) # Section: Dialog: Menu def _create_menu(self): def on_show_menu(menu): parent_id = labelplus.common.label.get_parent_id(self._parent_id) if parent_id in self._store: items[0].show() else: items[0].hide() def on_activate(widget, parent_id): self._select_parent_label(parent_id) def on_activate_parent(widget): parent_id = labelplus.common.label.get_parent_id(self._parent_id) self._select_parent_label(parent_id) root_items = (((gtk.MenuItem, _(STR_NONE)), on_activate, ID_NULL), ) self._menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) if __debug__: RT.register(self._menu, __name__) items = labelplus.gtkui.common.gtklib.menu_add_items( self._menu, 1, (((gtk.MenuItem, _(STR_PARENT)), on_activate_parent), )) if __debug__: RT.register(items[0], __name__) self._menu.connect("show", on_show_menu) self._menu.show_all() if self._type == self.TYPE_RENAME: item = self._menu.get_label_item(self._label_id) if item: item.set_sensitive(False) def _destroy_menu(self): if self._menu: self._menu.destroy() self._menu = None
def _create_filter_menu(self): def on_activate(widget, ids): if not isinstance(ids, list): ids = [ids] self._set_filter_sync_sidebar(ids) def on_activate_parent(widget): ids = self.get_any_selected_labels() parent_id = labelplus.common.label.get_common_parent(ids) on_activate(widget, parent_id) def on_activate_selected(widget): ids = self.get_selected_torrent_labels() on_activate(widget, ids) def on_show_menu(widget): items[0].hide() items[1].hide() ids = self.get_any_selected_labels() parent_id = labelplus.common.label.get_common_parent(ids) if self._store.is_user_label(parent_id): items[0].show() ids = self.get_selected_torrent_labels() if self._store.user_labels(ids): items[1].show() root_items = ( ((Gtk.MenuItem, { 'label': _(STR_ALL) }), on_activate, ID_ALL), ((Gtk.MenuItem, { 'label': _(STR_NONE) }), on_activate, ID_NONE), ) menu = LabelSelectionMenu(self._store.model, on_activate, root_items=root_items) menu.connect("show", on_show_menu) items = labelplus.gtkui.common.gtklib.menu_add_items( menu, 2, ( ((Gtk.MenuItem, { 'label': _(STR_PARENT) }), on_activate_parent), ((Gtk.MenuItem, { 'label': _(STR_SELECTED) }), on_activate_selected), )) root = Gtk.MenuItem(label=_(TITLE_SET_FILTER)) root.set_submenu(menu) if __debug__: RT.register(menu, __name__) if __debug__: RT.register(root, __name__) return root