def __popup_menu(self, view, library): model, itr = view.get_selection().get_selected() if itr is None: return songs = list(model[itr][0]) songs = [s for s in songs if isinstance(s, AudioFile)] menu = SongsMenu(library, songs, playlists=False, remove=False, ratings=False) menu.preseparate() def _remove(model, itr): playlist = model[itr][0] dialog = ConfirmRemovePlaylistDialog(self, playlist) if dialog.run() == Gtk.ResponseType.YES: playlist.delete() model.get_model().remove( model.convert_iter_to_child_iter(itr)) rem = MenuItem(_("_Delete"), Icons.EDIT_DELETE) connect_obj(rem, 'activate', _remove, model, itr) menu.prepend(rem) def _rename(path): self._start_rename(path) ren = qltk.MenuItem(_("_Rename"), Icons.EDIT) qltk.add_fake_accel(ren, "F2") connect_obj(ren, 'activate', _rename, model.get_path(itr)) menu.prepend(ren) playlist = model[itr][0] PLAYLIST_HANDLER.populate_menu(menu, library, self, [playlist]) menu.show_all() return view.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup_menu(self, view, parent): menu = Gtk.Menu() view.ensure_popup_selection() model, rows = view.get_selection().get_selected_rows() can_change = min([model[path][0].canedit for path in rows]) items = [SplitDisc, SplitTitle, SplitPerformer, SplitArranger, SplitValues, SplitPerformerFromTitle, SplitOriginalArtistFromTitle] items.extend(self.handler.plugins) items.sort(key=lambda item: (item._order, item.__name__)) if len(rows) == 1: row = model[rows[0]] entry = row[0] comment = entry.value text = comment.text for Item in items: if Item.tags and entry.tag not in Item.tags: continue try: b = Item(entry.tag, text) except: util.print_exc() else: b.connect('activate', self.__menu_activate, view) if (not min(listmap(self.__songinfo.can_change, b.needs) + [1]) or comment.is_special()): b.set_sensitive(False) menu.append(b) if menu.get_children(): menu.append(SeparatorMenuItem()) b = MenuItem(_("_Remove"), Icons.LIST_REMOVE) b.connect('activate', self.__remove_tag, view) qltk.add_fake_accel(b, "Delete") menu.append(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')) 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 Menu(self, songs, library, items): model, iters = self.__get_selected_songs() remove = qltk.MenuItem(_("_Remove from Playlist"), Icons.LIST_REMOVE) qltk.add_fake_accel(remove, "Delete") connect_obj(remove, 'activate', self.__remove, iters, model) playlist_iter = self.__selected_playlists()[1] remove.set_sensitive(bool(playlist_iter)) items.append([remove]) menu = super(PlaylistsBrowser, self).Menu(songs, library, items) return menu
def Menu(self, songs, library, items): songlist = qltk.get_top_parent(self).songlist model, iters = self.__get_selected_songs(songlist) item = qltk.MenuItem(_("_Remove from Playlist"), Gtk.STOCK_REMOVE) qltk.add_fake_accel(item, "Delete") connect_obj(item, 'activate', self.__remove, iters, model) item.set_sensitive(bool(self.__view.get_selection().get_selected()[1])) items.append([item]) menu = super(PlaylistsBrowser, self).Menu(songs, library, items) return menu
def __popup(self, library): songs = self.get_selected_songs() if not songs: return menu = SongsMenu(library, songs, queue=False, remove=False, delete=False, ratings=False) menu.preseparate() remove = MenuItem(_("_Remove"), Icons.LIST_REMOVE) qltk.add_fake_accel(remove, "Delete") remove.connect("activate", self.__remove) menu.prepend(remove) menu.show_all() return self.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup(self, library): songs = self.get_selected_songs() if not songs: return menu = SongsMenu( library, songs, queue=False, remove=False, delete=False, ratings=False) menu.preseparate() remove = Gtk.ImageMenuItem(Gtk.STOCK_REMOVE, use_stock=True) qltk.add_fake_accel(remove, "Delete") remove.connect('activate', self.__remove) menu.prepend(remove) menu.show_all() return self.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup(self, entry, menu): undo = MenuItem(_("_Undo"), Icons.EDIT_UNDO) add_fake_accel(undo, "<Primary>z") redo = MenuItem(_("_Redo"), Icons.EDIT_REDO) add_fake_accel(redo, "<Primary><shift>z") sep = SeparatorMenuItem() for widget in [sep, redo, undo]: widget.show() undo.connect("activate", lambda *x: self.undo()) redo.connect("activate", lambda *x: self.redo()) undo.set_sensitive(self.can_undo()) redo.set_sensitive(self.can_redo()) for item in [sep, redo, undo]: menu.prepend(item)
def __popup(self, library): songs = self.get_selected_songs() if not songs: return menu = SongsMenu(library, songs, queue=False, remove=False, delete=False, ratings=False) menu.preseparate() remove = MenuItem(_("_Remove"), Icons.LIST_REMOVE) qltk.add_fake_accel(remove, "Delete") remove.connect('activate', self.__remove) menu.prepend(remove) menu.show_all() return self.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup(self, entry, menu): undo = MenuItem(_("_Undo"), Icons.EDIT_UNDO) add_fake_accel(undo, "<Primary>z") redo = MenuItem(_("_Redo"), Icons.EDIT_REDO) add_fake_accel(redo, "<Primary><shift>z") sep = SeparatorMenuItem() for widget in [sep, redo, undo]: widget.show() undo.connect('activate', lambda *x: self.undo()) redo.connect('activate', lambda *x: self.redo()) undo.set_sensitive(self.can_undo()) redo.set_sensitive(self.can_redo()) for item in [sep, redo, undo]: menu.prepend(item)
def __popup(self, entry, menu): undo = Gtk.ImageMenuItem(Gtk.STOCK_UNDO, use_stock=True) add_fake_accel(undo, "<ctrl>z") redo = Gtk.ImageMenuItem(Gtk.STOCK_REDO, use_stock=True) add_fake_accel(redo, "<ctrl><shift>z") sep = SeparatorMenuItem() for widget in [sep, redo, undo]: widget.show() undo.connect('activate', lambda *x: self.undo()) redo.connect('activate', lambda *x: self.redo()) undo.set_sensitive(self.can_undo()) redo.set_sensitive(self.can_redo()) for item in [sep, redo, undo]: menu.prepend(item)
def __popup_menu(self, view, library): model, itr = view.get_selection().get_selected() if itr is None: return songs = list(model[itr][0]) songs = [s for s in songs if isinstance(s, AudioFile)] menu = SongsMenu(library, songs, playlists=False, remove=False, ratings=False) menu.preseparate() def _remove(model, itr): playlist = model[itr][0] response = confirm_remove_playlist_dialog_invoke( self, playlist, self.Confirmer) if response: playlist.delete() model.get_model().remove(model.convert_iter_to_child_iter(itr)) else: print_d("Playlist removal cancelled through prompt") rem = MenuItem(_("_Delete"), Icons.EDIT_DELETE) connect_obj(rem, 'activate', _remove, model, itr) menu.prepend(rem) def _rename(path): self._start_rename(path) ren = qltk.MenuItem(_("_Rename"), Icons.EDIT) qltk.add_fake_accel(ren, "F2") connect_obj(ren, 'activate', _rename, model.get_path(itr)) menu.prepend(ren) playlist = model[itr][0] PLAYLIST_HANDLER.populate_menu(menu, library, self, [playlist]) menu.show_all() return view.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup_menu(self, view, library): model, itr = view.get_selection().get_selected() if itr is None: return songs = list(model[itr][0]) songs = filter(lambda s: isinstance(s, AudioFile), songs) menu = SongsMenu(library, songs, playlists=False, remove=False, ratings=False) menu.preseparate() def _remove(model, itr): playlist = model[itr][0] dialog = ConfirmRemovePlaylistDialog(self, playlist) if dialog.run() == Gtk.ResponseType.YES: playlist.delete() model.get_model().remove(model.convert_iter_to_child_iter(itr)) rem = Gtk.ImageMenuItem(Gtk.STOCK_DELETE, use_stock=True) connect_obj(rem, 'activate', _remove, model, itr) menu.prepend(rem) def _rename(path): self.__render.set_property('editable', True) view.set_cursor(path, view.get_columns()[0], start_editing=True) ren = qltk.MenuItem(_("_Rename"), Gtk.STOCK_EDIT) qltk.add_fake_accel(ren, "F2") connect_obj(ren, 'activate', _rename, model.get_path(itr)) menu.prepend(ren) playlist = model[itr][0] PLAYLIST_HANDLER.populate_menu(menu, library, self, [playlist]) menu.show_all() return view.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup_menu(self, view, library): model, itr = view.get_selection().get_selected() if itr is None: return songs = list(model[itr][0]) songs = filter(lambda s: isinstance(s, AudioFile), songs) menu = SongsMenu(library, songs, playlists=False, remove=False, ratings=False) menu.preseparate() def _remove(model, itr): playlist = model[itr][0] dialog = ConfirmRemovePlaylistDialog(self, playlist) if dialog.run() == Gtk.ResponseType.YES: playlist.delete() model.get_model().remove( model.convert_iter_to_child_iter(itr)) rem = Gtk.ImageMenuItem(Gtk.STOCK_DELETE, use_stock=True) connect_obj(rem, 'activate', _remove, model, itr) menu.prepend(rem) def _rename(path): self.__render.set_property('editable', True) view.set_cursor(path, view.get_columns()[0], start_editing=True) ren = qltk.MenuItem(_("_Rename"), Gtk.STOCK_EDIT) qltk.add_fake_accel(ren, "F2") connect_obj(ren, 'activate', _rename, model.get_path(itr)) menu.prepend(ren) playlist = model[itr][0] PLAYLIST_HANDLER.populate_menu(menu, library, self, [playlist]) menu.show_all() return view.popup_menu(menu, 0, Gtk.get_current_event_time())
def __popup_menu(self, view, parent): menu = Gtk.Menu() view.ensure_popup_selection() model, rows = view.get_selection().get_selected_rows() can_change = min([model[path][0].canedit for path in rows]) items = [ SplitDisc, SplitTitle, SplitPerformer, SplitArranger, SplitValues, SplitPerformerFromTitle, SplitOriginalArtistFromTitle ] items.extend(self.handler.plugins) items.sort(key=lambda item: (item._order, item.__name__)) if len(rows) == 1: row = model[rows[0]] entry = row[0] comment = entry.value text = comment.text split_menu = Gtk.Menu() for Item in items: if Item.tags and entry.tag not in Item.tags: continue try: b = Item(entry.tag, text) except: util.print_exc() else: b.connect('activate', self.__menu_activate, view) if (not min( list(map(self.__songinfo.can_change, b.needs)) + [1]) or comment.is_special()): b.set_sensitive(False) vals = b.activated(entry.tag, text) if len(vals) > 1 and vals[1][1]: split_menu.append(b) if split_menu.get_children(): split_menu.append(SeparatorMenuItem()) 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, library, songs, plugins=True, playlists=True, queue=True, remove=True, delete=False, edit=True, ratings=True, show_files=True, items=None, accels=True, removal_confirmer=None): super().__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if ratings: ratings_item = RatingsMenuItem(songs, librarian) ratings_item.set_sensitive(bool(songs)) self.append(ratings_item) self.separate() # external item groups for subitems in reversed(items or []): self.separate() for item in subitems: self.append(item) self.separate() if plugins: submenu = self.plugins.Menu(librarian, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) b.set_sensitive(bool(songs)) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False if playlists: # Needed here to avoid a circular import; most browsers use # a SongsMenu, but SongsMenu needs access to the playlist # browser for this item. # FIXME: Two things are now importing browsers, so we need # some kind of inversion of control here. from quodlibet.browsers.playlists.menu import PlaylistMenu from quodlibet.browsers.playlists import PlaylistsBrowser try: submenu = PlaylistMenu(songs, PlaylistsBrowser.playlists()) def on_new(widget, playlist): PlaylistsBrowser.changed(playlist) submenu.connect('new', on_new) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Icons.FOLDER_DRAG_ACCEPT) b.set_sensitive(can_add and bool(songs)) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Icons.LIST_ADD) def enqueue_cb(item, songs): songs = [s for s in songs if s.can_add] if songs: from quodlibet import app app.window.playlist.enqueue(songs) b.connect('activate', enqueue_cb, songs) if accels: qltk.add_fake_accel(b, "<Primary>Return") self.append(b) b.set_sensitive(can_add and bool(songs)) if remove or delete: self.separate() if remove: self._confirm_song_removal = (removal_confirmer or confirm_song_removal_invoke) b = qltk.MenuItem(_("_Remove from Library…"), Icons.LIST_REMOVE) if callable(remove): b.connect('activate', lambda item: remove(songs)) else: def remove_cb(item, songs, library): parent = get_menu_item_top_parent(item) if self._confirm_song_removal(parent, songs): library.remove(songs) b.connect('activate', remove_cb, songs, library) b.set_sensitive(in_lib and bool(songs)) self.append(b) if delete: if callable(delete): b = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) b.connect('activate', lambda item: delete(songs)) if accels: qltk.add_fake_accel(b, "<Primary>Delete") else: b = TrashMenuItem() if accels: qltk.add_fake_accel(b, "<Primary>Delete") def trash_cb(item): parent = get_menu_item_top_parent(item) trash_songs(parent, songs, librarian) b.connect('activate', trash_cb) b.set_sensitive(is_file and bool(songs)) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Icons.EDIT) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = qltk.MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<Primary>I") def information_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) if show_files and any(is_a_file(s) for s in songs): def show_files_cb(menu_item): print_d("Trying to show files...") if not show_songs(songs): parent = get_menu_item_top_parent(menu_item) msg = ErrorMessage( parent, _("Unable to show files"), _("Error showing files, " "or no program available to show them.")) msg.run() self.separate() total = len([s for s in songs if is_a_file(s)]) text = ngettext("_Show in File Manager", "_Show %(total)d Files in File Manager", total) % { "total": total } b = qltk.MenuItem(text, Icons.DOCUMENT_OPEN) b.set_sensitive( bool(songs) and len(songs) < MenuItemPlugin.MAX_INVOCATIONS) b.connect('activate', show_files_cb) self.append(b) def selection_done_cb(menu): menu.destroy() self.connect('selection-done', selection_done_cb)
def __init__(self, library, songs, plugins=True, playlists=True, queue=True, devices=True, remove=True, delete=False, edit=True, parent=None): super(SongsMenu, self).__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if plugins: submenu = self.plugins.Menu(librarian, parent, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Gtk.STOCK_EXECUTE) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False self.separate() if playlists: # Needed here to avoid a circular import; most browsers use # a SongsMenu, but SongsMenu needs access to the playlist # browser for this item. # FIXME: Two things are now importing browsers, so we need # some kind of inversion of control here. from quodlibet.browsers.playlists.menu import PlaylistMenu try: submenu = PlaylistMenu(songs, parent) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Gtk.STOCK_ADD) b.set_sensitive(can_add) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Gtk.STOCK_ADD) b.connect('activate', self.__enqueue, songs) qltk.add_fake_accel(b, "<ctrl>Return") self.append(b) b.set_sensitive(can_add) if devices: from quodlibet import browsers try: browsers.media except AttributeError: pass else: if browsers.media.MediaDevices in browsers.browsers: submenu = browsers.media.Menu(songs, library) b = qltk.MenuItem(_("_Copy to Device"), Gtk.STOCK_COPY) b.set_sensitive(can_add and len(submenu) > 0) b.set_submenu(submenu) self.append(b) if remove or delete: self.separate() if remove: b = qltk.MenuItem(_("_Remove from library"), Gtk.STOCK_REMOVE) if callable(remove): b.connect_object('activate', remove, songs) else: b.connect('activate', self.__remove, songs, library) b.set_sensitive(in_lib) self.append(b) if delete: if callable(delete): b = Gtk.ImageMenuItem(Gtk.STOCK_DELETE, use_stock=True) b.connect_object('activate', delete, songs) else: b = TrashMenuItem() b.connect_object('activate', trash_songs, parent, songs, librarian) b.set_sensitive(is_file) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Gtk.STOCK_PROPERTIES) qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = Gtk.ImageMenuItem(label=Gtk.STOCK_INFO, use_stock=True) qltk.add_fake_accel(b, "<ctrl>I") def information_cb(menu_item): window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) self.connect_object('selection-done', Gtk.Menu.destroy, self)
def __init__(self, title, validator=None): super(_KeyValueEditor, self).__init__() self.set_border_width(12) self.set_title(title) self.set_default_size(self._WIDTH, self._HEIGHT) self.add(Gtk.VBox(spacing=6)) t = Gtk.Table(n_rows=2, n_columns=3) t.set_row_spacings(3) t.set_col_spacing(0, 3) t.set_col_spacing(1, 12) l = Gtk.Label(label=_("_Name:")) name = entry.UndoEntry() l.set_mnemonic_widget(name) l.set_use_underline(True) l.set_alignment(0.0, 0.5) t.attach(l, 0, 1, 0, 1, xoptions=Gtk.AttachOptions.FILL) t.attach(name, 1, 2, 0, 1) l = Gtk.Label(label=_("_Value:")) self.value = entry.ValidatingEntry(validator) l.set_mnemonic_widget(self.value) l.set_use_underline(True) l.set_alignment(0.0, 0.5) t.attach(l, 0, 1, 1, 2, xoptions=Gtk.AttachOptions.FILL) t.attach(self.value, 1, 2, 1, 2) add = qltk.Button(_("_Add"), Icons.LIST_ADD) add.set_sensitive(False) t.attach(add, 2, 3, 1, 2, xoptions=Gtk.AttachOptions.FILL) self.get_child().pack_start(t, False, True, 0) # Set up the model for this widget self.model = Gtk.ListStore(str, str) self.fill_values() view = RCMHintedTreeView(model=self.model) view.set_headers_visible(False) view.set_reorderable(True) view.set_rules_hint(True) render = Gtk.CellRendererText() render.props.ellipsize = Pango.EllipsizeMode.END column = Gtk.TreeViewColumn("", render) column.set_cell_data_func(render, self.__cdf, None) view.append_column(column) sw = Gtk.ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.IN) sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw.add(view) self.get_child().pack_start(sw, True, True, 0) menu = Gtk.Menu() remove = qltk.MenuItem(_("_Remove"), Icons.LIST_REMOVE) connect_obj(remove, 'activate', self.__remove, view) qltk.add_fake_accel(remove, "Delete") menu.append(remove) menu.show_all() bbox = Gtk.HButtonBox() rem_b = qltk.Button(_("_Remove"), Icons.LIST_REMOVE) rem_b.set_sensitive(False) bbox.pack_start(rem_b, True, True, 0) self.use_header_bar() close = qltk.Button(_("_Close"), Icons.WINDOW_CLOSE) if not self.has_close_button(): bbox.pack_start(close, True, True, 0) else: bbox.set_layout(Gtk.ButtonBoxStyle.START) self.get_child().pack_start(bbox, False, True, 0) selection = view.get_selection() connect_obj(name, 'activate', Gtk.Entry.grab_focus, self.value) connect_obj(self.value, 'activate', Gtk.Button.clicked, add) self.value.connect('changed', self.__changed, [add]) connect_obj(add, 'clicked', self.__add, selection, name, self.value, self.model) selection.connect('changed', self.__set_text, name, self.value, rem_b) view.connect('popup-menu', self.__popup, menu) connect_obj(rem_b, 'clicked', self.__remove, view) connect_obj(close, 'clicked', qltk.Window.destroy, self) view.connect('key-press-event', self.__view_key_press) connect_obj(self, 'destroy', Gtk.Menu.destroy, menu) name.grab_focus() self.get_child().show_all()
def __init__(self, library, songs, plugins=True, playlists=True, queue=True, remove=True, delete=False, edit=True, ratings=True, show_files=True, items=None, accels=True, removal_confirmer=None): super(SongsMenu, self).__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if ratings: ratings_item = RatingsMenuItem(songs, librarian) ratings_item.set_sensitive(bool(songs)) self.append(ratings_item) self.separate() # external item groups for subitems in reversed(items or []): self.separate() for item in subitems: self.append(item) self.separate() if plugins: submenu = self.plugins.Menu(librarian, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) b.set_sensitive(bool(songs)) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False if playlists: # Needed here to avoid a circular import; most browsers use # a SongsMenu, but SongsMenu needs access to the playlist # browser for this item. # FIXME: Two things are now importing browsers, so we need # some kind of inversion of control here. from quodlibet.browsers.playlists.menu import PlaylistMenu from quodlibet.browsers.playlists import PlaylistsBrowser try: submenu = PlaylistMenu(songs, PlaylistsBrowser.playlists()) def on_new(widget, playlist): PlaylistsBrowser.changed(playlist) submenu.connect('new', on_new) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Icons.FOLDER_DRAG_ACCEPT) b.set_sensitive(can_add and bool(songs)) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Icons.LIST_ADD) def enqueue_cb(item, songs): songs = [s for s in songs if s.can_add] if songs: from quodlibet import app app.window.playlist.enqueue(songs) b.connect('activate', enqueue_cb, songs) if accels: qltk.add_fake_accel(b, "<Primary>Return") self.append(b) b.set_sensitive(can_add and bool(songs)) if remove or delete: self.separate() if remove: self._confirm_song_removal = (removal_confirmer or confirm_song_removal_invoke) b = qltk.MenuItem(_("_Remove from Library…"), Icons.LIST_REMOVE) if callable(remove): b.connect('activate', lambda item: remove(songs)) else: def remove_cb(item, songs, library): parent = get_menu_item_top_parent(item) if self._confirm_song_removal(parent, songs): library.remove(songs) b.connect('activate', remove_cb, songs, library) b.set_sensitive(in_lib and bool(songs)) self.append(b) if delete: if callable(delete): b = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) b.connect('activate', lambda item: delete(songs)) if accels: qltk.add_fake_accel(b, "<Primary>Delete") else: b = TrashMenuItem() if accels: qltk.add_fake_accel(b, "<Primary>Delete") def trash_cb(item): parent = get_menu_item_top_parent(item) trash_songs(parent, songs, librarian) b.connect('activate', trash_cb) b.set_sensitive(is_file and bool(songs)) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Icons.EDIT) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = qltk.MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<Primary>I") def information_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) if show_files and any(is_a_file(s) for s in songs): def show_files_cb(menu_item): print_d("Trying to show files...") if not show_songs(songs): msg = ErrorMessage(self.plugin_window, _("Unable to show files"), _("Error showing files, " "or no program available to show them.")) msg.run() self.separate() total = len([s for s in songs if is_a_file(s)]) text = ngettext( "_Show in File Manager", "_Show %(total)d Files in File Manager", total) % { "total": total} b = qltk.MenuItem(text, Icons.DOCUMENT_OPEN) b.set_sensitive(bool(songs) and len(songs) < MenuItemPlugin.MAX_INVOCATIONS) b.connect('activate', show_files_cb) self.append(b) def selection_done_cb(menu): menu.destroy() self.connect('selection-done', selection_done_cb)
def __init__(self, library, songs, plugins=True, playlists=True, queue=True, remove=True, delete=False, edit=True, ratings=True, show_files=True, download=False, items=None, accels=True, removal_confirmer=None, folder_chooser=None): super().__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if ratings: ratings_item = RatingsMenuItem(songs, librarian) ratings_item.set_sensitive(bool(songs)) self.append(ratings_item) self.separate() # external item groups for subitems in reversed(items or []): self.separate() for item in subitems: self.append(item) self.separate() if plugins: submenu = self.plugins.Menu(librarian, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) b.set_sensitive(bool(songs)) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False if playlists: try: from quodlibet.browsers.playlists.menu import PlaylistMenu submenu = PlaylistMenu(songs, library.playlists) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Icons.FOLDER_DRAG_ACCEPT) b.set_sensitive(can_add and bool(songs)) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Icons.LIST_ADD) def enqueue_cb(item, songs): songs = [s for s in songs if s.can_add] if songs: from quodlibet import app app.window.playlist.enqueue(songs) b.connect('activate', enqueue_cb, songs) if accels: qltk.add_fake_accel(b, "<Primary>Return") self.append(b) b.set_sensitive(can_add and bool(songs)) if remove or delete: self.separate() if remove: self._confirm_song_removal = (removal_confirmer or confirm_song_removal_invoke) b = qltk.MenuItem(_("_Remove from Library…"), Icons.LIST_REMOVE) if callable(remove): b.connect('activate', lambda item: remove(songs)) else: def remove_cb(item, songs, library): parent = get_menu_item_top_parent(item) if self._confirm_song_removal(parent, songs): library.remove(songs) b.connect('activate', remove_cb, songs, library) b.set_sensitive(in_lib and bool(songs)) self.append(b) if delete: if callable(delete): b = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) b.connect('activate', lambda item: delete(songs)) if accels: qltk.add_fake_accel(b, "<Primary>Delete") else: b = TrashMenuItem() if accels: qltk.add_fake_accel(b, "<Primary>Delete") def trash_cb(item): parent = get_menu_item_top_parent(item) trash_songs(parent, songs, librarian) b.connect('activate', trash_cb) b.set_sensitive(is_file and bool(songs)) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Icons.EDIT) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = qltk.MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<Primary>I") def information_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) if show_files and any(is_a_file(s) for s in songs): def show_files_cb(menu_item): print_d("Trying to show files...") if not show_songs(songs): parent = get_menu_item_top_parent(menu_item) msg = ErrorMessage( parent, _("Unable to show files"), _("Error showing files, " "or no program available to show them.")) msg.run() self.separate() total = len([s for s in songs if is_a_file(s)]) text = ngettext("_Show in File Manager", "_Show %(total)d Files in File Manager", total) % { "total": total } b = qltk.MenuItem(text, Icons.DOCUMENT_OPEN) b.set_sensitive( bool(songs) and len(songs) < MenuItemPlugin.MAX_INVOCATIONS) b.connect('activate', show_files_cb) self.append(b) if download: def is_downloadable(song: AudioFile): return bool(not song.is_file and song.get("~uri", False)) self.separate() relevant = [s for s in songs if is_downloadable(s)] total = len(relevant) text = ngettext("_Download file…", "_Download %(total)d files…", total) % { "total": total } b = qltk.MenuItem(text, Icons.EMBLEM_DOWNLOADS) b.set_sensitive(relevant and len(relevant) < MenuItemPlugin.MAX_INVOCATIONS) def _finished(p, successes, failures): msg = (f"<b>{successes}</b> " + _("successful") + f"\n<b>{failures}</b> " + _("failed")) print_d(msg.replace("\n", "; ")) warning = Message(Gtk.MessageType.INFO, app.window, _("Downloads complete"), msg, escape_desc=False) warning.run() def download_cb(menu_item): songs = relevant total = len(songs) msg = ngettext("Download {name!r} to", "Download {total} files to", total) msg = msg.format( name=next(iter(songs))("title")[:99] if total else "?", total=total) chooser = folder_chooser or choose_folders paths = chooser(None, msg, _("Download here"), allow_multiple=False) if not paths: print_d("Cancelling download") return path = paths[0] progress = DownloadProgress(songs) progress.connect('finished', _finished) copool.add(progress.download_songs, path) b.connect('activate', download_cb) self.append(b) def selection_done_cb(menu): menu.destroy() self.connect('selection-done', selection_done_cb)
def __init__(self, library, songs, plugins=True, playlists=True, queue=True, devices=True, remove=True, delete=False, edit=True, ratings=True, items=None, accels=True): super(SongsMenu, self).__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if ratings: ratings_item = RatingsMenuItem(songs, librarian) ratings_item.set_sensitive(bool(songs)) self.append(ratings_item) self.separate() # external item groups for subitems in reversed(items or []): self.separate() for item in subitems: self.append(item) self.separate() if plugins: submenu = self.plugins.Menu(librarian, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) b.set_sensitive(bool(songs)) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False if playlists: # Needed here to avoid a circular import; most browsers use # a SongsMenu, but SongsMenu needs access to the playlist # browser for this item. # FIXME: Two things are now importing browsers, so we need # some kind of inversion of control here. from quodlibet.browsers.playlists.menu import PlaylistMenu try: submenu = PlaylistMenu(songs) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Icons.LIST_ADD) b.set_sensitive(can_add and bool(songs)) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Icons.LIST_ADD) def enqueue_cb(item, songs): songs = filter(lambda s: s.can_add, songs) if songs: from quodlibet import app app.window.playlist.enqueue(songs) b.connect('activate', enqueue_cb, songs) if accels: qltk.add_fake_accel(b, "<Primary>Return") self.append(b) b.set_sensitive(can_add and bool(songs)) if devices: from quodlibet import browsers try: browsers.media except AttributeError: pass else: if browsers.media.MediaDevices in browsers.browsers: submenu = browsers.media.Menu(songs, library) b = qltk.MenuItem(_("_Copy to Device"), Icons.EDIT_COPY) b.set_sensitive(can_add and len(submenu) > 0 and bool(songs)) b.set_submenu(submenu) self.append(b) if remove or delete: self.separate() if remove: b = qltk.MenuItem(_("_Remove from Library"), Icons.LIST_REMOVE) if callable(remove): b.connect('activate', lambda item: remove(songs)) else: def remove_cb(item, songs, library): library.remove(set(songs)) b.connect('activate', remove_cb, songs, library) b.set_sensitive(in_lib and bool(songs)) self.append(b) if delete: if callable(delete): b = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) b.connect('activate', lambda item: delete(songs)) if accels: qltk.add_fake_accel(b, "<Primary>Delete") else: b = TrashMenuItem() if accels: qltk.add_fake_accel(b, "<Primary>Delete") def trash_cb(item): parent = get_menu_item_top_parent(item) trash_songs(parent, songs, librarian) b.connect('activate', trash_cb) b.set_sensitive(is_file and bool(songs)) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Icons.DOCUMENT_PROPERTIES) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = qltk.MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<Primary>I") def information_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) def selection_done_cb(menu): menu.destroy() self.connect('selection-done', selection_done_cb)
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, library, songs, plugins=True, playlists=True, queue=True, devices=True, remove=True, delete=False, edit=True, ratings=True, items=None, accels=True): super(SongsMenu, self).__init__() # The library may actually be a librarian; if it is, use it, # otherwise find the real librarian. librarian = getattr(library, 'librarian', library) if ratings: ratings_item = RatingsMenuItem(songs, librarian) ratings_item.set_sensitive(bool(songs)) self.append(ratings_item) self.separate() # external item groups for subitems in reversed(items or []): self.separate() for item in subitems: self.append(item) self.separate() if plugins: submenu = self.plugins.Menu(librarian, songs) if submenu is not None: b = qltk.MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) b.set_sensitive(bool(songs)) self.append(b) b.set_submenu(submenu) self.append(SeparatorMenuItem()) in_lib = True can_add = True is_file = True for song in songs: if song not in library: in_lib = False if not song.can_add: can_add = False if not song.is_file: is_file = False if playlists: # Needed here to avoid a circular import; most browsers use # a SongsMenu, but SongsMenu needs access to the playlist # browser for this item. # FIXME: Two things are now importing browsers, so we need # some kind of inversion of control here. from quodlibet.browsers.playlists.menu import PlaylistMenu try: submenu = PlaylistMenu(songs) except AttributeError as e: print_w("Couldn't get Playlists menu: %s" % e) else: b = qltk.MenuItem(_("Play_lists"), Icons.LIST_ADD) b.set_sensitive(can_add and bool(songs)) b.set_submenu(submenu) self.append(b) if queue: b = qltk.MenuItem(_("Add to _Queue"), Icons.LIST_ADD) def enqueue_cb(item, songs): songs = filter(lambda s: s.can_add, songs) if songs: from quodlibet import app app.window.playlist.enqueue(songs) b.connect('activate', enqueue_cb, songs) if accels: qltk.add_fake_accel(b, "<Primary>Return") self.append(b) b.set_sensitive(can_add and bool(songs)) if devices: from quodlibet import browsers try: browsers.media except AttributeError: pass else: if browsers.media.MediaDevices in browsers.browsers: submenu = browsers.media.Menu(songs, library) b = qltk.MenuItem(_("_Copy to Device"), Icons.EDIT_COPY) b.set_sensitive( can_add and len(submenu) > 0 and bool(songs)) b.set_submenu(submenu) self.append(b) if remove or delete: self.separate() if remove: b = qltk.MenuItem(_("_Remove from Library"), Icons.LIST_REMOVE) if callable(remove): b.connect('activate', lambda item: remove(songs)) else: def remove_cb(item, songs, library): library.remove(set(songs)) b.connect('activate', remove_cb, songs, library) b.set_sensitive(in_lib and bool(songs)) self.append(b) if delete: if callable(delete): b = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) b.connect('activate', lambda item: delete(songs)) if accels: qltk.add_fake_accel(b, "<Primary>Delete") else: b = TrashMenuItem() if accels: qltk.add_fake_accel(b, "<Primary>Delete") def trash_cb(item): parent = get_menu_item_top_parent(item) trash_songs(parent, songs, librarian) b.connect('activate', trash_cb) b.set_sensitive(is_file and bool(songs)) self.append(b) if edit: self.separate() b = qltk.MenuItem(_("Edit _Tags"), Icons.DOCUMENT_PROPERTIES) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<alt>Return") def song_properties_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = SongProperties(librarian, songs, parent) window.show() b.connect('activate', song_properties_cb) self.append(b) b = qltk.MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) b.set_sensitive(bool(songs)) if accels: qltk.add_fake_accel(b, "<Primary>I") def information_cb(menu_item): parent = get_menu_item_top_parent(menu_item) window = Information(librarian, songs, parent) window.show() b.connect('activate', information_cb) self.append(b) def selection_done_cb(menu): menu.destroy() self.connect('selection-done', selection_done_cb)
def __popup_menu(self, view, parent): menu = Gtk.Menu() view.ensure_popup_selection() model, rows = view.get_selection().get_selected_rows() can_change = min([model[path][0].canedit for path in rows]) items = [ SplitDisc, SplitTitle, SplitPerformer, SplitArranger, SplitValues, SplitPerformerFromTitle, SplitOriginalArtistFromTitle ] items.extend(self.handler.plugins) items.sort(key=lambda item: (item._order, item.__name__)) if len(rows) == 1: row = model[rows[0]] entry = row[0] comment = entry.value text = comment.text for Item in items: if Item.tags and entry.tag not in Item.tags: continue try: b = Item(entry.tag, text) except: util.print_exc() else: b.connect('activate', self.__menu_activate, view) if (not min( list(map(self.__songinfo.can_change, b.needs)) + [1]) or comment.is_special()): b.set_sensitive(False) menu.append(b) if menu.get_children(): menu.append(SeparatorMenuItem()) 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())