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())
class IndicatorMenu(Gtk.Menu): __gsignals__: GSignals = { 'action-item-changed': (GObject.SignalFlags.RUN_LAST, None, tuple()), } 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 get_action_item(self): """Returns the 'Play' or 'Pause' action menu item (used for unity). 'action-item-changed' gets emitted if this changes. """ return self._action_item def set_paused(self, paused): """Update the menu based on the player paused state""" self._play_item.set_visible(paused) self._pause_item.set_visible(not paused) self._action_item = self._play_item if paused else self._pause_item self.emit("action-item-changed") def set_song(self, song): """Update the menu based on the passed song. Can be None. This should be the persistent song and not a stream/info one. """ self._rating_item.set_sensitive(song is not None) self._info.set_sensitive(song is not None) self._props.set_sensitive(song is not None) self._rating_item.set_songs([song]) self._new_playlist_submenu_for(song) def _new_playlist_submenu_for(self, song): submenu = self._playlists_item.get_submenu() if submenu: submenu.destroy() playlist_menu = PlaylistMenu([song], PlaylistsBrowser.playlists()) def on_new(widget, playlist): PlaylistsBrowser.changed(playlist) playlist_menu.connect('new', on_new) self._playlists_item.set_submenu(playlist_menu) self._playlists_item.set_sensitive(bool(song) and song.can_add) self._playlists_item.show_all() def _on_play_pause(self, menuitem, player): player.playpause()
class IndicatorMenu(Gtk.Menu): __gsignals__ = { 'action-item-changed': (GObject.SignalFlags.RUN_LAST, None, tuple()), } def __init__(self, app, add_show_item=False): super(IndicatorMenu, self).__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 get_action_item(self): """Returns the 'Play' or 'Pause' action menu item (used for unity). 'action-item-changed' gets emitted if this changes. """ return self._action_item def set_paused(self, paused): """Update the menu based on the player paused state""" self._play_item.set_visible(paused) self._pause_item.set_visible(not paused) self._action_item = self._play_item if paused else self._pause_item self.emit("action-item-changed") def set_song(self, song): """Update the menu based on the passed song. Can be None. This should be the persistent song and not a stream/info one. """ self._rating_item.set_sensitive(song is not None) self._info.set_sensitive(song is not None) self._props.set_sensitive(song is not None) self._rating_item.set_songs([song]) self._new_playlist_submenu_for(song) def _new_playlist_submenu_for(self, song): submenu = self._playlists_item.get_submenu() if submenu: submenu.destroy() playlist_menu = PlaylistMenu([song], PlaylistsBrowser.playlists()) def on_new(widget, playlist): PlaylistsBrowser.changed(playlist) playlist_menu.connect('new', on_new) self._playlists_item.set_submenu(playlist_menu) self._playlists_item.set_sensitive(bool(song) and song.can_add) self._playlists_item.show_all() def _on_play_pause(self, menuitem, player): if player.song: player.paused ^= True else: player.reset()
def __init__(self, library, player): super().__init__(spacing=3) sw = ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) save_interval_secs = config.getint("autosave", "queue_interval") self.queue = PlayQueue(library, player, save_interval_secs) self.queue.props.expand = True sw.add(self.queue) add_css(self, ".ql-expanded title { margin-bottom: 5px; }") outer = ExpandBoxHack() left = Gtk.HBox(spacing=12) hb2 = Gtk.HBox(spacing=3) state_icon = PlaybackStatusIcon() state_icon.stop() state_icon.show() hb2.pack_start(state_icon, True, True, 0) name_label = Gtk.Label(label=_("_Queue"), use_underline=True) name_label.set_size_request(-1, 24) hb2.pack_start(name_label, True, True, 0) left.pack_start(hb2, False, True, 0) menu = Gtk.Menu() self.count_label = count_label = Gtk.Label() self.count_label.set_property("ellipsize", Pango.EllipsizeMode.END) self.count_label.set_width_chars(10) self.count_label.get_style_context().add_class("dim-label") left.pack_start(count_label, False, True, 0) outer.pack_start(left, True, True, 0) self.set_label_fill(True) clear_item = SmallImageButton(image=SymbolicIconImage( Icons.USER_TRASH, Gtk.IconSize.MENU), relief=Gtk.ReliefStyle.NONE, tooltip_text=_("Clear Queue")) clear_item.connect("clicked", self.__clear_queue) outer.pack_start(clear_item, False, False, 3) toggle = SmallImageToggleButton( image=SymbolicIconImage(Icons.SYSTEM_LOCK_SCREEN, Gtk.IconSize.MENU), relief=Gtk.ReliefStyle.NONE, tooltip_text=_( "Disable queue - the queue will be ignored when playing")) disabled = config.getboolean("memory", "queue_disable", False) toggle.props.active = disabled self.__queue_disable(disabled) toggle.connect('toggled', lambda b: self.__queue_disable(b.props.active)) outer.pack_start(toggle, False, False, 3) mode_menu = Gtk.Menu() norm_mode_item = RadioMenuItem( label=_("Ephemeral"), tooltip_text=_("Remove songs from the queue after playing them"), group=None) mode_menu.append(norm_mode_item) norm_mode_item.set_active(True) norm_mode_item.connect("toggled", lambda _: self.__keep_songs_enable(False)) keep_mode_item = RadioMenuItem( label=_("Persistent"), tooltip_text=_("Keep songs in the queue after playing them"), group=norm_mode_item) mode_menu.append(keep_mode_item) keep_mode_item.connect("toggled", lambda b: self.__keep_songs_enable(True)) keep_mode_item.set_active( config.getboolean("memory", "queue_keep_songs", False)) mode_item = MenuItem(_("Mode"), Icons.SYSTEM_RUN) mode_item.set_submenu(mode_menu) menu.append(mode_item) rand_checkbox = ConfigCheckMenuItem(_("_Random"), "memory", "shufflequeue", populate=True) rand_checkbox.connect('toggled', self.__queue_shuffle) self.set_shuffled(rand_checkbox.get_active()) menu.append(rand_checkbox) stop_checkbox = ConfigCheckMenuItem(_("Stop at End"), "memory", "queue_stop_at_end", populate=True) menu.append(stop_checkbox) button = SmallMenuButton(SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.MENU), arrow=True) button.set_relief(Gtk.ReliefStyle.NORMAL) button.show_all() button.hide() button.set_no_show_all(True) menu.show_all() button.set_menu(menu) outer.pack_start(button, False, False, 3) close_button = SmallImageButton(image=SymbolicIconImage( "window-close", Gtk.IconSize.MENU), relief=Gtk.ReliefStyle.NONE) close_button.connect("clicked", lambda *x: self.hide()) outer.pack_start(close_button, False, False, 6) self.set_label_widget(outer) self.add(sw) self.connect('notify::expanded', self.__expand, button) self.connect('notify::expanded', self.__expand, button) targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP, DND_QL), ("text/uri-list", 0, DND_URI_LIST)] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set(Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-motion', self.__motion) self.connect('drag-data-received', self.__drag_data_received) self.queue.model.connect_after('row-inserted', DeferredSignal(self.__check_expand), count_label) self.queue.model.connect_after('row-deleted', DeferredSignal(self.__update_count), count_label) self.__update_count(self.model, None, count_label) connect_destroy(player, 'song-started', self.__update_state_icon, state_icon) connect_destroy(player, 'paused', self.__update_state_icon_pause, state_icon, True) connect_destroy(player, 'unpaused', self.__update_state_icon_pause, state_icon, False) connect_destroy(player, 'song-started', self.__song_started, self.queue.model) connect_destroy(player, 'song-ended', self.__update_queue_stop, self.queue.model) # to make the children clickable if mapped # ....no idea why, but works def hack(expander): label = expander.get_label_widget() if label: label.unmap() label.map() self.connect("map", hack) self.set_expanded(config.getboolean("memory", "queue_expanded")) self.notify("expanded") for child in self.get_children(): child.show_all()
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())