class TBaseView(TestCase): def setUp(self): self.m = Gtk.ListStore(str) self.c = BaseView(model=self.m) def test_selection_changed(self): events = [] def on_selection_changed(*args): events.append(args) self.c.connect("selection-changed", on_selection_changed) self.m.append(row=["foo"]) self.c.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) self.c.get_selection().select_all() self.assertTrue(events) def test_remove(self): self.m.append(row=["foo"]) self.c.remove_iters([self.m[0].iter]) self.failIf(len(self.m)) self.m.append(row=["foo"]) self.c.remove_iters([]) self.failUnless(len(self.m)) self.c.remove_paths([self.m[0].path]) self.failIf(len(self.m)) def test_key_events(self): with visible(self.c): send_key_click(self.c, "<Primary>Right") send_key_click(self.c, "<Primary>Left") def test_select_func(self): self.m.append(row=["foo"]) self.m.append(row=["bar"]) self.failUnless(self.c.select_by_func(lambda r: True)) self.failIf(self.c.select_by_func(lambda r: False)) self.c.select_by_func(lambda r: False, scroll=False, one=True) def test_iter_select_func(self): # empty self.assertFalse(self.c.iter_select_by_func(lambda r: False)) self.assertFalse(self.c.iter_select_by_func(lambda r: True)) self.m.append(row=["foo"]) self.m.append(row=["bar"]) self.m.append(row=["foo"]) self.c.remove_selection() self.assertTrue(self.c.iter_select_by_func(lambda r: r[0] == "foo")) selection = self.c.get_selection() model, iter_ = selection.get_selected() self.assertEqual(model.get_path(iter_)[:], [0]) self.assertTrue(self.c.iter_select_by_func(lambda r: r[0] == "foo")) model, iter_ = selection.get_selected() self.assertEqual(model.get_path(iter_)[:], [2]) self.assertTrue(self.c.iter_select_by_func(lambda r: r[0] == "foo")) model, iter_ = selection.get_selected() self.assertEqual(model.get_path(iter_)[:], [0]) self.assertTrue(self.c.iter_select_by_func(lambda r: r[0] == "bar")) model, iter_ = selection.get_selected() self.assertEqual(model.get_path(iter_)[:], [1]) self.assertFalse(self.c.iter_select_by_func(lambda r: r[0] == "bar")) def test_remove_select_single(self): # empty self.c.remove_selection() model = _fill_view(self.c) # select first and remove selection = self.c.get_selection() length = len(model) selection.select_path(model[0].path) self.c.remove_selection() self.assertEqual(len(model), length - 1) model, iter_ = selection.get_selected() # afterwards the first is selected self.assertEqual(model[iter_].path, model[0].path) def test_remove_select_multiple(self): selection = self.c.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) # empty self.c.remove_selection() model = _fill_view(self.c) # select first two and remove selection = self.c.get_selection() length = len(model) selection.select_range(model[0].path, model[1].path) self.c.remove_selection() self.assertEqual(len(model), length - 2) # afterwards the first is selected model, paths = selection.get_selected_rows() self.assertEqual(paths, [model[0].path]) def test_without_model(self): column = TreeViewColumn() self.c.append_column(column) column = self.c.get_columns()[0] column.set_sort_indicator(True) self.c.set_search_column(0) with self.c.without_model() as model: self.assertEqual(model, self.m) self.assertTrue(self.c.get_model() is None) self.assertEqual(self.c.get_search_column(), 0) column = self.c.get_columns()[0] self.assertTrue(column.get_sort_indicator()) def test_set_drag_dest(self): x, y = self.c.convert_bin_window_to_widget_coords(0, 0) # empty model self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y) path, pos = self.c.get_drag_dest_row() self.assertTrue(path is None) # filled model but not realized, fall back to last path model = _fill_view(self.c) self.c.set_drag_dest(x, y) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[-1].path, path) # realized now, so the first path with realized(self.c): x, y = self.c.convert_bin_window_to_widget_coords(0, 0) self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y, into_only=False) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[0].path, path) self.assertEqual(pos, Gtk.TreeViewDropPosition.BEFORE) self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y, into_only=True) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[0].path, path) self.assertEqual(pos, Gtk.TreeViewDropPosition.INTO_OR_BEFORE) def tearDown(self): self.c.destroy()
def __init__(self): super(PatternEditor, self).__init__(spacing=6) self.__headers = headers = {} buttons = [] group = None for tags in self.PRESETS: tied = "~" + "~".join(tags) group = Gtk.RadioButton(group=group, label="_" + util.tag(tied), use_underline=True) headers[group] = tags buttons.append(group) group = Gtk.RadioButton(group=group, label=_("_Custom"), use_underline=True) self.__custom = group headers[group] = [] buttons.append(group) button_box = Gtk.HBox(spacing=6) self.__model = model = Gtk.ListStore(str) radio_box = Gtk.VBox(spacing=6) for button in buttons: radio_box.pack_start(button, False, True, 0) button.connect('toggled', self.__toggled, button_box, model) self.pack_start(radio_box, False, True, 0) tooltip = _("Tag pattern with optional markup " "e.g. <tt>composer</tt> or\n<tt>%s</tt>" % escape(self._COMPLEX_PATTERN_EXAMPLE)) cb = TagsComboBoxEntry(self.COMPLETION, tooltip_markup=tooltip) view = BaseView(model=model) view.set_reorderable(True) view.set_headers_visible(False) ctrl_box = Gtk.VBox(spacing=6) add = Button(_("_Add"), Icons.LIST_ADD) ctrl_box.pack_start(add, False, True, 0) add.connect('clicked', self.__add, model, cb) remove = Button(_("_Remove"), Icons.LIST_REMOVE) ctrl_box.pack_start(remove, False, True, 0) remove.connect('clicked', self.__remove, view) selection = view.get_selection() selection.connect('changed', self.__selection_changed, remove) selection.emit('changed') sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) sw.add(view) edit_box = Gtk.VBox(spacing=6) edit_box.pack_start(cb, False, True, 0) edit_box.pack_start(sw, True, True, 0) button_box.pack_start(edit_box, True, True, 0) button_box.pack_start(ctrl_box, False, True, 0) self.pack_start(button_box, True, True, 0) render = Gtk.CellRendererText() render.set_property("editable", True) def edited_cb(render, path, text, model): model[path][0] = text render.connect("edited", edited_cb, model) column = Gtk.TreeViewColumn(None, render, text=0) view.append_column(column)
def __init__(self): super(PatternEditor, self).__init__(spacing=12) self.__headers = headers = {} buttons = [] group = None for tags in self.PRESETS: tied = "~" + "~".join([t[0] for t in tags]) group = Gtk.RadioButton(group=group, label="_" + util.tag(tied), use_underline=True) headers[group] = tags buttons.append(group) group = Gtk.RadioButton(group=group, label=_("_Custom"), use_underline=True) self.__custom = group headers[group] = [] buttons.append(group) button_box = Gtk.HBox(spacing=6) self.__model = model = Gtk.ListStore(str, bool) radio_box = Gtk.VBox(spacing=6) for button in buttons: radio_box.pack_start(button, False, True, 0) button.connect('toggled', self.__toggled, button_box, model) self.pack_start(radio_box, False, True, 0) cb = TagsComboBoxEntry(self.COMPLETION) view = BaseView(model=model) view.set_reorderable(True) view.set_headers_visible(True) ctrl_box = Gtk.VBox(spacing=6) add = Gtk.Button(stock=Gtk.STOCK_ADD) ctrl_box.pack_start(add, False, True, 0) add.connect('clicked', self.__add, model, cb) remove = Gtk.Button(stock=Gtk.STOCK_REMOVE) ctrl_box.pack_start(remove, False, True, 0) remove.connect('clicked', self.__remove, view) selection = view.get_selection() selection.connect('changed', self.__selection_changed, remove) selection.emit('changed') sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) sw.add(view) edit_box = Gtk.VBox(spacing=6) edit_box.pack_start(cb, False, True, 0) edit_box.pack_start(sw, True, True, 0) button_box.pack_start(edit_box, True, True, 0) button_box.pack_start(ctrl_box, False, True, 0) self.pack_start(button_box, True, True, 0) render = Gtk.CellRendererText() render.set_property("editable", True) def edited_cb(render, path, text, model): model[path][0] = text render.connect("edited", edited_cb, model) column = Gtk.TreeViewColumn(_("Tag"), render, text=0) column.set_expand(True) view.append_column(column) toggle = Gtk.CellRendererToggle() toggle.connect("toggled", self.__toggeled, model) toggle_column = Gtk.TreeViewColumn(_("Merge"), toggle, active=1) view.append_column(toggle_column)
class TBaseView(TestCase): def setUp(self): self.m = Gtk.ListStore(str) self.c = BaseView(model=self.m) def test_remove(self): self.m.append(row=["foo"]) self.c.remove_iters([self.m[0].iter]) self.failIf(len(self.m)) self.m.append(row=["foo"]) self.c.remove_iters([]) self.failUnless(len(self.m)) self.c.remove_paths([self.m[0].path]) self.failIf(len(self.m)) def test_key_events(self): with visible(self.c): send_key_click(self.c, "<Primary>Right") send_key_click(self.c, "<Primary>Left") def test_select_func(self): self.m.append(row=["foo"]) self.m.append(row=["bar"]) self.failUnless(self.c.select_by_func(lambda r: True)) self.failIf(self.c.select_by_func(lambda r: False)) self.c.select_by_func(lambda r: False, scroll=False, one=True) def test_remove_select_single(self): # empty self.c.remove_selection() model = _fill_view(self.c) # select first and remove selection = self.c.get_selection() length = len(model) selection.select_path(model[0].path) self.c.remove_selection() self.assertEqual(len(model), length - 1) model, iter_ = selection.get_selected() # afterwards the first is selected self.assertEqual(model[iter_].path, model[0].path) def test_remove_select_multiple(self): selection = self.c.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) # empty self.c.remove_selection() model = _fill_view(self.c) # select first two and remove selection = self.c.get_selection() length = len(model) selection.select_range(model[0].path, model[1].path) self.c.remove_selection() self.assertEqual(len(model), length - 2) # afterwards the first is selected model, paths = selection.get_selected_rows() self.assertEqual(paths, [model[0].path]) def test_without_model(self): column = TreeViewColumn() self.c.append_column(column) column = self.c.get_columns()[0] column.set_sort_indicator(True) self.c.set_search_column(0) with self.c.without_model() as model: self.assertEqual(model, self.m) self.assertTrue(self.c.get_model() is None) self.assertEqual(self.c.get_search_column(), 0) column = self.c.get_columns()[0] self.assertTrue(column.get_sort_indicator()) def test_set_drag_dest(self): x, y = self.c.convert_bin_window_to_widget_coords(0, 0) # empty model self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y) path, pos = self.c.get_drag_dest_row() self.assertTrue(path is None) # filled model but not realized, fall back to last path model = _fill_view(self.c) self.c.set_drag_dest(x, y) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[-1].path, path) # realized now, so the first path with realized(self.c): x, y = self.c.convert_bin_window_to_widget_coords(0, 0) self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y, into_only=False) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[0].path, path) self.assertEqual(pos, Gtk.TreeViewDropPosition.BEFORE) self.c.unset_rows_drag_dest() self.c.set_drag_dest(x, y, into_only=True) path, pos = self.c.get_drag_dest_row() self.assertEqual(model[0].path, path) self.assertEqual(pos, Gtk.TreeViewDropPosition.INTO_OR_BEFORE) def tearDown(self): self.c.destroy()
def __init__(self): super().__init__(spacing=12) self.__headers = headers = {} buttons = [] group = None for tags in self.PRESETS: tied = "~" + "~".join([t[0] for t in tags]) group = Gtk.RadioButton(group=group, label="_" + util.tag(tied), use_underline=True) headers[group] = tags buttons.append(group) group = Gtk.RadioButton(group=group, label=_("_Custom"), use_underline=True) self.__custom = group headers[group] = [] buttons.append(group) button_box = Gtk.HBox(spacing=6) self.__model = model = Gtk.ListStore(str, bool) radio_box = Gtk.VBox(spacing=6) for button in buttons: radio_box.pack_start(button, False, True, 0) button.connect('toggled', self.__toggled, button_box, model) self.pack_start(radio_box, False, True, 0) cb = TagsComboBoxEntry(self.COMPLETION) view = BaseView(model=model) view.set_reorderable(True) view.set_headers_visible(True) ctrl_box = Gtk.VBox(spacing=6) add = Button(_("_Add"), Icons.LIST_ADD) ctrl_box.pack_start(add, False, True, 0) add.connect('clicked', self.__add, model, cb) remove = Button(_("_Remove"), Icons.LIST_REMOVE) ctrl_box.pack_start(remove, False, True, 0) remove.connect('clicked', self.__remove, view) selection = view.get_selection() selection.connect('changed', self.__selection_changed, remove) selection.emit('changed') sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) sw.add(view) edit_box = Gtk.VBox(spacing=6) edit_box.pack_start(cb, False, True, 0) edit_box.pack_start(sw, True, True, 0) button_box.pack_start(edit_box, True, True, 0) button_box.pack_start(ctrl_box, False, True, 0) self.pack_start(button_box, True, True, 0) render = Gtk.CellRendererText() render.set_property("editable", True) def edited_cb(render, path, text, model): model[path][0] = text render.connect("edited", edited_cb, model) column = Gtk.TreeViewColumn(_("Tag"), render, text=0) column.set_expand(True) view.append_column(column) toggle = Gtk.CellRendererToggle() toggle.connect("toggled", self.__toggeled, model) toggle_column = Gtk.TreeViewColumn(_("Merge"), toggle, active=1) view.append_column(toggle_column)
def _popup_menu(self, view: BaseView, _parent): menu = Gtk.Menu() view.ensure_popup_selection() model, rows = view.get_selection().get_selected_rows() can_change = all(model[path][0].canedit for path in rows) if len(rows) == 1: row = model[rows[0]] entry = row[0] comment = entry.value text = comment.text split_menu = Gtk.Menu() for Item in self._SPLITTERS: if Item.tags and entry.tag not in Item.tags: continue item = self.__item_for(view, Item, entry.tag, text) if not item: continue vals = item.activated(entry.tag, text) changeable = any(not self._group_info.can_change(k) for k in item.needs) fixed = changeable or comment.is_special() if fixed: item.set_sensitive(False) if len(vals) > 1 and vals[1][1]: split_menu.append(item) if split_menu.get_children(): split_menu.append(SeparatorMenuItem()) plugins = self.handler.plugins print_d(f"Adding {len(plugins)} plugin(s) to menu: " f"{', '.join(p.__name__ for p in plugins)}") for p_cls in plugins: item = self.__item_for(view, p_cls, entry.tag, text) if not item: continue results = item.activated(entry.tag, text) # Only enable for the user if the plugin would do something item.set_sensitive(results != [(entry.tag, text)]) menu.append(item) pref_item = MenuItem(_("_Configure"), Icons.PREFERENCES_SYSTEM) split_menu.append(pref_item) def show_prefs(parent): from quodlibet.qltk.exfalsowindow import ExFalsoWindow if isinstance(app.window, ExFalsoWindow): from quodlibet.qltk.exfalsowindow import PreferencesWindow window = PreferencesWindow(parent) else: from quodlibet.qltk.prefs import PreferencesWindow window = PreferencesWindow(parent, open_page="tagging") window.show() connect_obj(pref_item, "activate", show_prefs, self) split_item = MenuItem(_("_Split Tag"), Icons.EDIT_FIND_REPLACE) if split_menu.get_children(): split_item.set_submenu(split_menu) else: split_item.set_sensitive(False) menu.append(split_item) copy_b = MenuItem(_("_Copy Value(s)"), Icons.EDIT_COPY) copy_b.connect('activate', self.__copy_tag_value, view) qltk.add_fake_accel(copy_b, "<Primary>c") menu.append(copy_b) remove_b = MenuItem(_("_Remove"), Icons.LIST_REMOVE) remove_b.connect('activate', self.__remove_tag, view) qltk.add_fake_accel(remove_b, "Delete") menu.append(remove_b) menu.show_all() # Setting the menu itself to be insensitive causes it to not # be dismissed; see #473. for c in menu.get_children(): c.set_sensitive(can_change and c.get_property('sensitive')) copy_b.set_sensitive(True) remove_b.set_sensitive(True) menu.connect('selection-done', lambda m: m.destroy()) # XXX: Keep reference self.__menu = menu return view.popup_menu(menu, 3, Gtk.get_current_event_time())
def __init__(self): super(PatternEditor, self).__init__(spacing=6) self.__headers = headers = {} buttons = [] group = None for tags in self.PRESETS: tied = "~" + "~".join(tags) group = gtk.RadioButton(group, "_" + tag(tied)) headers[group] = tags buttons.append(group) group = gtk.RadioButton(group, _("_Custom")) self.__custom = group headers[group] = [] buttons.append(group) button_box = gtk.HBox(spacing=6) self.__model = model = gtk.ListStore(str) radio_box = gtk.VBox(spacing=6) for button in buttons: radio_box.pack_start(button, expand=False) button.connect('toggled', self.__toggled, button_box, model) self.pack_start(radio_box, expand=False) cb = TagsComboBoxEntry(self.COMPLETION) view = BaseView(model) view.set_reorderable(True) view.set_headers_visible(False) ctrl_box = gtk.VBox(spacing=6) add = gtk.Button(stock=gtk.STOCK_ADD) ctrl_box.pack_start(add, expand=False) add.connect('clicked', self.__add, model, cb) remove = gtk.Button(stock=gtk.STOCK_REMOVE) ctrl_box.pack_start(remove, expand=False) remove.connect('clicked', self.__remove, view) selection = view.get_selection() selection.connect('changed', self.__selection_changed, remove) selection.emit('changed') sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.set_shadow_type(gtk.SHADOW_IN) sw.add(view) edit_box = gtk.VBox(spacing=6) edit_box.pack_start(cb, expand=False) edit_box.pack_start(sw) button_box.pack_start(edit_box) button_box.pack_start(ctrl_box, expand=False) self.pack_start(button_box) render = gtk.CellRendererText() render.set_property("editable", True) def edited_cb(render, path, text, model): model[path][0] = text render.connect("edited", edited_cb, model) column = gtk.TreeViewColumn(None, render, text=0) view.append_column(column)