def setUp(self): config.RATINGS = config.HardCodedRatingsPrefs() self.failUnlessEqual(config.RATINGS.number, NUM_RATINGS) self.library = SongLibrary() self.library.librarian = SongLibrarian() self.af = AudioFile({"~filename": fsnative(u"/foo"), "~#rating": 1.0}) self.af.sanitize() self.rmi = RatingsMenuItem([self.af], self.library)
def test_no_rating(self): af = AudioFile({"~filename": fsnative(u"/foobar"), 'artist': 'foo'}) rmi = RatingsMenuItem([af], self.library) children = [ mi for mi in rmi.get_submenu().get_children() if isinstance(mi, Gtk.CheckMenuItem) ] self.failIf(any([c.get_active() for c in children]))
def test_menuitem(self): library = SongLibrary() library.librarian = SongLibrarian() a = AudioFile({"~filename": fsnative(u"/foo")}) a.sanitize() x = RatingsMenuItem([a], library) x.set_rating(0, [a], library) x.destroy() library.destroy() library.librarian.destroy()
def Menu(self, header, browser, library): songs = self.get_selected_songs() if not songs: return can_filter = browser.can_filter menu = browser.Menu(songs, self, library) def Filter(t): # Translators: The substituted string is the name of the # selected column (a translated tag name). b = qltk.MenuItem( _("_Filter on %s") % util.tag(t, True), Gtk.STOCK_INDEX) b.connect_object('activate', self.__filter_on, t, songs, browser) return b header = util.tagsplit(header)[0] if can_filter("artist") or can_filter("album") or can_filter(header): menu.preseparate() if can_filter("artist"): menu.prepend(Filter("artist")) if can_filter("album"): menu.prepend(Filter("album")) if (header not in ["artist", "album"] and can_filter(header)): menu.prepend(Filter(header)) ratings = RatingsMenuItem(songs, library) menu.preseparate() menu.prepend(ratings) menu.show_all() return menu
class TRatingsMenuItem(TestCase): def setUp(self): config.RATINGS = config.HardCodedRatingsPrefs() self.failUnlessEqual(config.RATINGS.number, NUM_RATINGS) self.library = SongLibrary() self.library.librarian = SongLibrarian() self.af = AudioFile({"~filename": fsnative(u"/foo"), "~#rating": 1.0}) self.af.sanitize() self.rmi = RatingsMenuItem([self.af], self.library) def tearDown(self): self.rmi.destroy() self.library.destroy() self.library.librarian.destroy() def test_menuitem_children(self): children = [mi for mi in self.rmi.get_submenu().get_children() if isinstance(mi, Gtk.CheckMenuItem)] self.failUnlessEqual(len(children), NUM_RATINGS + 1) highest = children[-1] self.failUnlessEqual(highest.get_active(), True) self.failUnlessEqual(children[1].get_active(), False) def test_set_remove_rating(self): self.rmi.set_rating(0.5, [self.af], self.library) self.failUnless(self.af.has_rating) self.failUnlessEqual(self.af('~#rating'), 0.5) self.rmi.remove_rating([self.af], self.library) self.failIf(self.af.has_rating)
def test_menuitem(self): library = SongLibrary() library.librarian = SongLibrarian() a = AudioFile({"~filename": "/foo"}) a.sanitize() x = RatingsMenuItem([a], library) x.set_rating(0, [a], library) x.destroy() library.destroy() library.librarian.destroy()
class TRatingsMenuItem(TestCase): def setUp(self): config.RATINGS = config.HardCodedRatingsPrefs() self.failUnlessEqual(config.RATINGS.number, NUM_RATINGS) self.library = SongLibrary() self.library.librarian = SongLibrarian() self.af = AudioFile({"~filename": fsnative(u"/foo"), "~#rating": 1.0}) self.af.sanitize() self.rmi = RatingsMenuItem([self.af], self.library) def tearDown(self): self.rmi.destroy() self.library.destroy() self.library.librarian.destroy() def test_menuitem_children(self): children = [ mi for mi in self.rmi.get_submenu().get_children() if isinstance(mi, Gtk.CheckMenuItem) ] self.failUnlessEqual(len(children), NUM_RATINGS + 1) highest = children[-1] self.failUnlessEqual(highest.get_active(), True) self.failUnlessEqual(children[1].get_active(), False) def test_no_rating(self): af = AudioFile({"~filename": fsnative(u"/foobar"), 'artist': 'foo'}) rmi = RatingsMenuItem([af], self.library) children = [ mi for mi in rmi.get_submenu().get_children() if isinstance(mi, Gtk.CheckMenuItem) ] self.failIf(any([c.get_active() for c in children])) def test_set_remove_rating(self): self.rmi.set_rating(0.5, [self.af], self.library) self.failUnless(self.af.has_rating) self.failUnlessEqual(self.af('~#rating'), 0.5) self.rmi.remove_rating([self.af], self.library) self.failIf(self.af.has_rating)
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, app, add_show_item=False): super().__init__() self._app = app player = app.player show_item_bottom = is_plasma() if add_show_item: show_item = Gtk.CheckMenuItem.new_with_mnemonic( _("_Show %(application-name)s") % {"application-name": app.name}) def on_toggled(menuitem): if menuitem.get_active(): app.present() else: app.hide() self._toggle_id = show_item.connect("toggled", on_toggled) def on_visible_changed(*args): with show_item.handler_block(self._toggle_id): show_item.set_active(app.window.get_visible()) connect_destroy(app.window, "notify::visible", on_visible_changed) else: show_item = None self._play_item = MenuItem(_("_Play"), Icons.MEDIA_PLAYBACK_START) self._play_item.connect("activate", self._on_play_pause, player) self._play_item.set_no_show_all(True) self._pause_item = MenuItem(_("P_ause"), Icons.MEDIA_PLAYBACK_PAUSE) self._pause_item.connect("activate", self._on_play_pause, player) self._pause_item.set_no_show_all(True) self._action_item = None previous = MenuItem(_("Pre_vious"), Icons.MEDIA_SKIP_BACKWARD) previous.connect('activate', lambda *args: player.previous(force=True)) next = MenuItem(_("_Next"), Icons.MEDIA_SKIP_FORWARD) next.connect('activate', lambda *args: player.next()) player_options = app.player_options shuffle = Gtk.CheckMenuItem(label=_("_Shuffle"), use_underline=True) player_options.bind_property("shuffle", shuffle, "active", GObject.BindingFlags.BIDIRECTIONAL) player_options.notify("shuffle") repeat = Gtk.CheckMenuItem(label=_("_Repeat"), use_underline=True) player_options.bind_property("repeat", repeat, "active", GObject.BindingFlags.BIDIRECTIONAL) player_options.notify("repeat") safter = Gtk.CheckMenuItem(label=_("Stop _After This Song"), use_underline=True) player_options.bind_property("stop-after", safter, "active", GObject.BindingFlags.BIDIRECTIONAL) player_options.notify("stop-after") browse = qltk.MenuItem(_("Open _Browser"), Icons.EDIT_FIND) browse_sub = Gtk.Menu() for Kind in browsers.browsers: i = Gtk.MenuItem(label=Kind.accelerated_name, use_underline=True) connect_obj(i, 'activate', LibraryBrowser.open, Kind, app.library, app.player) browse_sub.append(i) browse.set_submenu(browse_sub) self._props = qltk.MenuItem(_("Edit _Tags"), Icons.EDIT) def on_properties(*args): song = player.song window = SongProperties(app.librarian, [song]) window.show() self._props.connect('activate', on_properties) self._info = MenuItem(_("_Information"), Icons.DIALOG_INFORMATION) self._playlists_item = MenuItem(_("Play_lists"), Icons.FOLDER_DRAG_ACCEPT) self._new_playlist_submenu_for(player.song) def on_information(*args): song = player.song window = Information(app.librarian, [song]) window.show() self._info.connect('activate', on_information) def set_rating(value): song = player.song song["~#rating"] = value app.librarian.changed([song]) self._rating_item = rating = RatingsMenuItem([], app.library) quit = MenuItem(_("_Quit"), Icons.APPLICATION_EXIT) quit.connect('activate', lambda *x: app.quit()) if not show_item_bottom and show_item: self.append(show_item) self.append(SeparatorMenuItem()) self.append(self._play_item) self.append(self._pause_item) self.append(previous) self.append(next) self.append(SeparatorMenuItem()) self.append(shuffle) self.append(repeat) self.append(safter) self.append(SeparatorMenuItem()) self.append(rating) self.append(self._playlists_item) self.append(self._props) self.append(self._info) self.append(SeparatorMenuItem()) self.append(browse) self.append(SeparatorMenuItem()) self.append(quit) if show_item_bottom and show_item: self.append(SeparatorMenuItem()) self.append(show_item) self.show_all() self.set_paused(True) self.set_song(None)
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 __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 __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 test_no_rating(self): af = AudioFile({"~filename": fsnative(u"/foobar"), 'artist': 'foo'}) rmi = RatingsMenuItem([af], self.library) children = [mi for mi in rmi.get_submenu().get_children() if isinstance(mi, Gtk.CheckMenuItem)] self.failIf(any([c.get_active() for c in children]))
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)