def __init__(self, library, song, lyrics=True, bookmarks=True): super(OneSong, self).__init__() vbox = Gtk.VBox(spacing=12) vbox.set_border_width(12) self._title(song, vbox) self._album(song, vbox) self._people(song, vbox) self._library(song, vbox) self._file(song, vbox) self._additional(song, vbox) sw = SW() sw.title = _("Information") sw.add_with_viewport(vbox) self.append_page(sw) if lyrics: lyrics = LyricsPane(song) lyrics.title = _("Lyrics") self.append_page(lyrics) if bookmarks: bookmarks = EditBookmarksPane(None, song) bookmarks.title = _("Bookmarks") bookmarks.set_border_width(12) self.append_page(bookmarks) connect_destroy(library, 'changed', self.__check_changed, vbox, song)
def __init__(self, library): print_d(f"Creating Soundcloud Browser {self}") super().__init__(spacing=12) self.set_orientation(Gtk.Orientation.VERTICAL) if not self.instances(): self._init(library) self._register_instance() self.connect('destroy', self.__destroy) self.connect('uri-received', self.__handle_incoming_uri) connect_destroy(self.api_client, 'authenticated', self.__on_authenticated) connect_destroy(self.library, 'changed', self.__changed) self.login_state = (State.LOGGED_IN if self.online else State.LOGGED_OUT) self._create_searchbar(self.library) vbox = Gtk.VBox() vbox.pack_start(self._create_footer(), False, False, 6) vbox.pack_start(self._create_category_widget(), True, True, 0) vbox.pack_start(self.create_login_button(), False, False, 6) vbox.show() pane = qltk.ConfigRHPaned("browsers", "soundcloud_pos", 0.4) pane.show() pane.pack1(vbox, resize=False, shrink=False) self._songs_box = songs_box = Gtk.VBox(spacing=6) songs_box.pack_start(self._searchbox, False, True, 0) songs_box.show() pane.pack2(songs_box, resize=True, shrink=False) self.pack_start(pane, True, True, 0) self.show()
def __init__(self, library): print_d("Creating Soundcloud Browser") super(SoundcloudBrowser, self).__init__(spacing=12) self.set_orientation(Gtk.Orientation.VERTICAL) if not self.instances(): self._init(library) self._register_instance() self.connect('destroy', self.__destroy) self.connect('uri-received', self.__handle_incoming_uri) self.__auth_sig = self.api_client.connect('authenticated', self.__on_authenticated) connect_destroy(self.library, 'changed', self.__changed) self.login_state = (State.LOGGED_IN if self.online else State.LOGGED_OUT) self._create_searchbar(self.library) vbox = Gtk.VBox() vbox.pack_start(self._create_footer(), False, False, 6) vbox.pack_start(self._create_category_widget(), True, True, 0) vbox.pack_start(self.create_login_button(), False, False, 6) vbox.show() pane = qltk.ConfigRHPaned("browsers", "soundcloud_pos", 0.4) pane.show() pane.pack1(vbox, resize=False, shrink=False) self._songs_box = songs_box = Gtk.VBox(spacing=6) songs_box.pack_start(self._searchbox, False, True, 0) songs_box.show() pane.pack2(songs_box, resize=True, shrink=False) self.pack_start(pane, True, True, 0) self.show()
def __init__(self, player, library): super(WaveformSeekBar, self).__init__() self._player = player self._rms_vals = [] self._elapsed_label = TimeLabel() self._remaining_label = TimeLabel() self._waveform_scale = WaveformScale() self.pack_start(Align(self._elapsed_label, border=6), False, True, 0) self.pack_start(self._waveform_scale, True, True, 0) self.pack_start(Align(self._remaining_label, border=6), False, True, 0) for child in self.get_children(): child.show_all() self._tracker = TimeTracker(player) self._tracker.connect('tick', self._on_tick, player) connect_destroy(player, 'seek', self._on_player_seek) connect_destroy(player, 'song-started', self._on_song_started) connect_destroy(player, 'song-ended', self._on_song_ended) connect_destroy(player, 'notify::seekable', self._on_seekable_changed) connect_destroy(library, 'changed', self._on_song_changed, player) self.connect('destroy', self._on_destroy) self._update(player) self._tracker.tick() if player.info: self._create_waveform(player.info, CONFIG.data_size)
def __init__(self, library, player, pattern_filename): super(SongInfo, self).__init__() self._pattern_filename = pattern_filename self.set_visible_window(False) align = Gtk.Alignment(xscale=0.0, xalign=0.0, yscale=0.0, yalign=0.0) label = Gtk.Label() label.set_ellipsize(Pango.EllipsizeMode.MIDDLE) label.set_selectable(True) align.add(label) label.set_alignment(0.0, 0.0) self._label = label connect_destroy(library, 'changed', self._on_library_changed, player) connect_destroy(player, 'song-started', self._on_song_started) label.connect('populate-popup', self._on_label_popup, player, library) self.connect('button-press-event', self._on_button_press_event, player, library) try: with open(self._pattern_filename, "rb") as h: self._pattern = h.read().strip().decode("utf-8") except (EnvironmentError, UnicodeDecodeError): pass self._compiled = XMLFromMarkupPattern(self._pattern) align.show_all() self.add(align)
def __init__(self, library, player, pattern_filename): super(SongInfo, self).__init__() self._pattern_filename = pattern_filename self.set_visible_window(False) align = Align(halign=Gtk.Align.START, valign=Gtk.Align.START) label = Gtk.Label() label.set_ellipsize(Pango.EllipsizeMode.MIDDLE) label.set_track_visited_links(False) label.set_selectable(True) align.add(label) label.set_alignment(0.0, 0.0) self._label = label connect_destroy(library, 'changed', self._on_library_changed, player) connect_destroy(player, 'song-started', self._on_song_started) label.connect('populate-popup', self._on_label_popup, player, library) self.connect('button-press-event', self._on_button_press_event, player, library) try: with open(self._pattern_filename, "rb") as h: self._pattern = h.read().strip().decode("utf-8") except (EnvironmentError, UnicodeDecodeError): pass self._compiled = XMLFromMarkupPattern(self._pattern) align.show_all() self.add(align)
def enable_window_tracking(self, config_prefix, size_suffix=""): """Enable tracking/saving of changes and restore size/pos/maximized. Make sure to call set_transient_for() before since position is restored relative to the parent in this case. config_prefix -- prefix for the config key (prefix_size, prefix_position, prefix_maximized) size_suffix -- optional suffix for saving the size. For cases where the window has multiple states with different content sizes. (example: edit tags with one song or multiple) """ self.__state = 0 self.__name = config_prefix self.__size_suffix = size_suffix self.__save_size_pos_deferred = DeferredSignal( self.__do_save_size_pos, timeout=50, owner=self) self.connect('configure-event', self.__configure_event) self.connect('window-state-event', self.__window_state_changed) self.connect('notify::visible', self.__visible_changed) parent = self.get_transient_for() if parent: connect_destroy( parent, 'configure-event', self.__parent_configure_event) self.__restore_window_state()
def enable_window_tracking(self, config_prefix, size_suffix=""): """Enable tracking/saving of changes and restore size/pos/maximized. Make sure to call set_transient_for() before since position is restored relative to the parent in this case. config_prefix -- prefix for the config key (prefix_size, prefix_position, prefix_maximized) size_suffix -- optional suffix for saving the size. For cases where the window has multiple states with different content sizes. (example: edit tags with one song or multiple) """ self.__state = 0 self.__name = config_prefix self.__size_suffix = size_suffix self.__save_size_pos_deferred = DeferredSignal(self.__do_save_size_pos, timeout=50, owner=self) self.connect('configure-event', self.__configure_event) self.connect('window-state-event', self.__window_state_changed) self.connect('notify::visible', self.__visible_changed) parent = self.get_transient_for() if parent: connect_destroy(parent, 'configure-event', self.__parent_configure_event) self.__restore_window_state()
def enable_row_update(self, view, sw, column): connect_obj(view, "draw", self.__update_visibility, view) connect_destroy(sw.get_vadjustment(), "value-changed", self.__stop_update, view) self.__pending_paths = [] self.__update_deferred = DeferredSignal(self.__update_visible_rows, timeout=50, priority=GLib.PRIORITY_LOW) self.__column = column self.__first_expose = True
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume # only restore the volume in case it is managed locally, otherwise # this could affect the system volume if not player.has_external_volume: player.volume = config.getfloat("memory", "volume") self.volume.connect("value-changed", self._on_volume_changed) control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(quodlibet.get_user_dir(), "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Align(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy( app.cover_manager, 'cover-changed', self.__song_art_changed, library) box.pack_start(Align(self.image, border=2), False, True, 0) # On older Gtk+ (3.4, at least) # setting a margin on CoverImage leads to errors and result in the # QL window not beeing visible for some reason. assert self.image.props.margin == 0 for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def enable_row_update(self, view, sw, column): connect_obj(view, 'draw', self.__update_visibility, view) connect_destroy( sw.get_vadjustment(), "value-changed", self.__stop_update, view) self.__pending_paths = [] self.__update_deferred = DeferredSignal( self.__update_visible_rows, timeout=50, priority=GLib.PRIORITY_LOW) self.__column = column self.__first_expose = True
def __init__(self, library, songs, parent=None): super(Information, self).__init__(dialog=False) self.set_default_size(400, 400) self.set_transient_for(qltk.get_top_parent(parent)) self.enable_window_tracking("quodlibet_information") if len(songs) > 1: connect_destroy(library, 'changed', self.__check_changed) if len(songs) > 0: connect_destroy(library, 'removed', self.__check_removed) self.__songs = songs self.__update(library) self.get_child().show_all()
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume # only restore the volume in case it is managed locally, otherwise # this could affect the system volume if not player.has_external_volume: player.volume = config.getfloat("memory", "volume") self.volume.connect("value-changed", self._on_volume_changed) control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(quodlibet.get_user_dir(), "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Align(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy(app.cover_manager, 'cover-changed', self.__song_art_changed, library) box.pack_start(Align(self.image, border=2), False, True, 0) # On older Gtk+ (3.4, at least) # setting a margin on CoverImage leads to errors and result in the # QL window not beeing visible for some reason. assert self.image.props.margin == 0 for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def __init__(self, songs: Iterable[AudioFile], manager: CoverManager, config: Config, headless: bool = False, **kwargs): super().__init__(title=_("Cover Art Download"), use_header_bar=True, **kwargs) self.set_default_size(1400, 720) id_ = DownloadCoverArt.PLUGIN_ID.lower().replace(" ", "_") self.enable_window_tracking(id_) self.config = config self.headless = headless self.flow_box = box = Gtk.FlowBox() self.model = Gio.ListStore() self.songs = songs self._groups = {} self.button = None def selected(fb): children = fb.get_selected_children() if children: cover = self.model.get_item(children[0].get_index()) if cover: self.button.set_sensitive(True) # Only supported on GTK >= 3.18 (not Ubuntu 16.04) # Re-enable some day perhaps... # box.bind_model(self.model, self.create_widget, None) box.set_valign(Gtk.Align.START) box.set_max_children_per_line(4) box.connect("selected-children-changed", selected) paned = Paned(orientation=Gtk.Orientation.VERTICAL) paned.ensure_wide_handle() sw = Gtk.ScrolledWindow() sw.add(self.flow_box) paned.pack1(sw, True, True) paned.pack2(self.create_options(), False, False) self.vbox.pack_start(paned, True, True, 0) connect_destroy(manager, "covers-found", self._covers_found) connect_destroy(manager, "searches-complete", self._finished) cancellable = self.__cancellable = Gio.Cancellable() self.show_all() # Do the search self._groups = manager.search_cover(cancellable, songs)
def __init__(self, player, library): super(SeekBar, self).__init__() self._elapsed_label = TimeLabel() self._remaining_label = TimeLabel() scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) scale.set_adjustment(Gtk.Adjustment.new(0, 0, 0, 3, -15, 0)) scale.set_draw_value(False) self._scale = scale self.pack_start(Align(self._elapsed_label, border=6), False, True, 0) self.pack_start(scale, True, True, 0) self.pack_start(Align(self._remaining_label, border=6), False, True, 0) for child in self.get_children(): child.show_all() self._id = self._scale.connect('value-changed', self._on_user_changed, player) self._scale.connect('value-changed', self._on_scale_value_changed, player) self._tracker = TimeTracker(player) self._tracker.connect('tick', self._on_tick, player) connect_destroy(player, 'seek', self._on_player_seek) connect_destroy(player, 'song-started', self._on_song_started) connect_destroy(player, "notify::seekable", self._on_seekable_changed) connect_destroy(library, "changed", self._on_song_changed, player) self.connect("destroy", self._on_destroy) with self._inhibit(): self._update(player) self._tracker.tick()
def __init__(self, player, library): super(SeekBar, self).__init__() self._elapsed_label = TimeLabel() self._remaining_label = TimeLabel() scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) scale.set_adjustment(Gtk.Adjustment.new(0, 0, 0, 3, 15, 0)) scale.set_draw_value(False) self._scale = scale self.pack_start(Align(self._elapsed_label, border=6), False, True, 0) self.pack_start(scale, True, True, 0) self.pack_start(Align(self._remaining_label, border=6), False, True, 0) for child in self.get_children(): child.show_all() self._id = self._scale.connect("value-changed", self._on_user_changed, player) self._scale.connect("value-changed", self._on_scale_value_changed, player) self._tracker = TimeTracker(player) self._tracker.connect("tick", self._on_tick, player) connect_destroy(player, "seek", self._on_player_seek) connect_destroy(player, "song-started", self._on_song_started) connect_destroy(player, "notify::seekable", self._on_seekable_changed) connect_destroy(library, "changed", self._on_song_changed, player) self.connect("destroy", self._on_destroy) with self._inhibit(): self._update(player) self._tracker.tick()
def test_main(self): from gi.repository import Gtk b = Gtk.Button() class A(Gtk.Button): def foo(self): pass a = A() ref = sys.getrefcount(a) util.connect_destroy(b, "clicked", a.foo) self.assertEqual(sys.getrefcount(a), ref + 1) a.destroy() self.assertEqual(sys.getrefcount(a), ref)
def __init__(self, model): super(DuplicateSongsView, self).__init__(model) connect_obj(self, 'row-activated', self.__select_song, app.player) # Selecting multiple is a nice feature it turns out. self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) # Handle signals propagated from the underlying library self.connected_library_sigs = [] SIGNAL_MAP = { 'removed': self._removed, 'added': self._added, 'changed': self._changed } for (sig, callback) in SIGNAL_MAP.items(): print_d("Listening to library.%s signals" % sig) connect_destroy(app.library, sig, callback)
def _create_category_widget(self): scrolled_window = ScrolledWindow() scrolled_window.show() scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self.view = view = RCMHintedTreeView() view.show() view.set_headers_visible(False) scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled_window.add(view) model = Gtk.ListStore(int, str, str, str, bool) filters = self.filters for (i, (name, data)) in enumerate(filters): filter_type, icon, query, always = data enabled = always model.append(row=[filter_type, icon, name, query, enabled]) def search_func(model, column, key, iter, data): return key.lower() not in model[iter][column].lower() view.set_search_column(self.ModelIndex.NAME) view.set_search_equal_func(search_func, None) column = Gtk.TreeViewColumn("Songs") column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) renderpb = Gtk.CellRendererPixbuf() renderpb.props.xpad = 6 renderpb.props.ypad = 6 column.pack_start(renderpb, False) column.add_attribute(renderpb, "icon-name", self.ModelIndex.ICON_NAME) render = Gtk.CellRendererText() render.set_property('ellipsize', Pango.EllipsizeMode.END) def cdf(column, cell, model, iter_, user_data): on = (self.login_state == State.LOGGED_IN or model[iter_][self.ModelIndex.ALWAYS_ENABLE]) cell.set_sensitive(on) column.set_cell_data_func(render, cdf) column.set_cell_data_func(renderpb, cdf) view.append_column(column) column.pack_start(render, True) column.add_attribute(render, "text", self.ModelIndex.NAME) view.set_model(model) selection = view.get_selection() def select_func(sel, model, path, value): return (self.login_state == State.LOGGED_IN or model[model.get_iter(path)][self.ModelIndex.ALWAYS_ENABLE]) selection.set_select_function(select_func) selection.select_iter(model.get_iter_first()) self._refresh_online_filters() self.__changed_sig = connect_destroy(selection, 'changed', DeferredSignal(self._on_select)) return scrolled_window
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(const.USERDIR, "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Alignment(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy( app.cover_manager, 'cover-changed', self.__song_art_changed, library) # CoverImage doesn't behave in a Alignment, so wrap it coverbox = Gtk.Box() coverbox.pack_start(self.image, True, True, 0) box.pack_start(Alignment(coverbox, border=2), False, True, 0) for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def _create_category_widget(self): scrolled_window = ScrolledWindow() scrolled_window.show() scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self.view = view = RCMHintedTreeView() view.show() view.set_headers_visible(False) scrolled_window.set_policy( Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled_window.add(view) model = Gtk.ListStore(int, str, str, str, bool) filters = self.filters for (i, (name, data)) in enumerate(filters): filter_type, icon, query, always = data enabled = always model.append(row=[filter_type, icon, name, query, enabled]) def search_func(model, column, key, iter, data): return key.lower() not in model[iter][column].lower() view.set_search_column(self.ModelIndex.NAME) view.set_search_equal_func(search_func, None) column = Gtk.TreeViewColumn("Songs") column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) renderpb = Gtk.CellRendererPixbuf() renderpb.props.xpad = 6 renderpb.props.ypad = 6 column.pack_start(renderpb, False) column.add_attribute(renderpb, "icon-name", self.ModelIndex.ICON_NAME) render = Gtk.CellRendererText() render.set_property('ellipsize', Pango.EllipsizeMode.END) def cdf(column, cell, model, iter_, user_data): on = (self.login_state == State.LOGGED_IN or model[iter_][self.ModelIndex.ALWAYS_ENABLE]) cell.set_sensitive(on) column.set_cell_data_func(render, cdf) column.set_cell_data_func(renderpb, cdf) view.append_column(column) column.pack_start(render, True) column.add_attribute(render, "text", self.ModelIndex.NAME) view.set_model(model) selection = view.get_selection() def select_func(sel, model, path, value): return (self.login_state == State.LOGGED_IN or model[model.get_iter(path)][self.ModelIndex.ALWAYS_ENABLE]) selection.set_select_function(select_func) self._refresh_online_filters() self.__changed_sig = connect_destroy(selection, 'changed', DeferredSignal(self._on_select)) return scrolled_window
def __init__(self, player, library): super(WaveformSeekBar, self).__init__() self._player = player self._rms_vals = [] self._hovering = False self._elapsed_label = TimeLabel() self._remaining_label = TimeLabel() self._waveform_scale = WaveformScale(player) self.pack_start(Align(self._elapsed_label, border=6), False, True, 0) self.pack_start(self._waveform_scale, True, True, 0) self.pack_start(Align(self._remaining_label, border=6), False, True, 0) for child in self.get_children(): child.show_all() self.set_time_label_visibility(CONFIG.show_time_labels) self._waveform_scale.connect('size-allocate', self._update_redraw_interval) self._waveform_scale.connect('motion-notify-event', self._on_mouse_hover) self._waveform_scale.connect('leave-notify-event', self._on_mouse_leave) self._label_tracker = TimeTracker(player) self._label_tracker.connect('tick', self._on_tick_label, player) self._redraw_tracker = TimeTracker(player) self._redraw_tracker.connect('tick', self._on_tick_waveform, player) connect_destroy(player, 'seek', self._on_player_seek) connect_destroy(player, 'song-started', self._on_song_started) connect_destroy(player, 'song-ended', self._on_song_ended) connect_destroy(player, 'notify::seekable', self._on_seekable_changed) connect_destroy(library, 'changed', self._on_song_changed, player) self.connect('destroy', self._on_destroy) self._update(player) if player.info: self._create_waveform(player.info, CONFIG.max_data_points)
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(const.USERDIR, "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Align(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy( app.cover_manager, 'cover-changed', self.__song_art_changed, library) self.image.props.margin = 2 box.pack_start(self.image, False, True, 0) for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def __init__(self, player, library): hbox = Gtk.HBox(spacing=3) l = TimeLabel() self._time_label = l hbox.pack_start(l, True, True, 0) arrow = Gtk.Arrow.new(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE) hbox.pack_start(arrow, False, True, 0) super(SeekBar, self).__init__(hbox) self._slider_label = TimeLabel() self.set_slider_widget(self._slider_label) self._on_seekable_changed(player) connect_destroy(player, "notify::seekable", self._on_seekable_changed) self.scale.connect('button-press-event', self.__seek_lock) self.scale.connect('button-release-event', self.__seek_unlock, player) self.scale.connect('key-press-event', self.__seek_lock) self.scale.connect('key-release-event', self.__seek_unlock, player) self.connect('scroll-event', self.__scroll, player) self.scale.connect('value-changed', self.__update_time, l) m = Gtk.Menu() c = ConfigCheckMenuItem( _("Display remaining time"), "player", "time_remaining") c.set_active(config.getboolean("player", "time_remaining")) connect_obj(c, 'toggled', self.scale.emit, 'value-changed') self.__remaining = c m.append(c) m.append(SeparatorMenuItem()) i = qltk.MenuItem(_(u"_Edit Bookmarks…"), Icons.EDIT) def edit_bookmarks_cb(menu_item): window = bookmarks.EditBookmarks(self, library, player) window.show() i.connect('activate', edit_bookmarks_cb) m.append(i) m.show_all() m.attach_to_widget(self, None) connect_obj(self, 'button-press-event', self.__check_menu, m, player, c) connect_obj(self, 'popup-menu', self.__popup_menu, m, player) timer = TimeTracker(player) connect_obj(timer, 'tick', self.__check_time, player) connect_destroy( library, "changed", self.__songs_changed, player, m) connect_destroy(player, 'song-started', self.__song_started, m) connect_destroy(player, 'seek', self.__seeked)
def __init__(self, player, library): hbox = Gtk.HBox(spacing=3) l = TimeLabel() self._time_label = l hbox.pack_start(l, True, True, 0) arrow = Gtk.Arrow.new(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE) hbox.pack_start(arrow, False, True, 0) super(SeekButton, self).__init__(hbox) self._slider_label = TimeLabel() self.set_slider_widget(self._slider_label) self._on_seekable_changed(player) connect_destroy(player, "notify::seekable", self._on_seekable_changed) self.scale.connect('button-press-event', self.__seek_lock) self.scale.connect('button-release-event', self.__seek_unlock, player) self.scale.connect('key-press-event', self.__seek_lock) self.scale.connect('key-release-event', self.__seek_unlock, player) self.connect('scroll-event', self.__scroll, player) self.scale.connect('value-changed', self.__update_time, l) m = Gtk.Menu() c = ConfigCheckMenuItem( _("Display remaining time"), "player", "time_remaining") c.set_active(config.getboolean("player", "time_remaining")) connect_obj(c, 'toggled', self.scale.emit, 'value-changed') self.__remaining = c m.append(c) m.append(SeparatorMenuItem()) i = qltk.MenuItem(_(u"_Edit Bookmarks…"), Icons.EDIT) def edit_bookmarks_cb(menu_item): window = bookmarks.EditBookmarks(self, library, player) window.show() i.connect('activate', edit_bookmarks_cb) m.append(i) m.show_all() connect_obj(self, 'button-press-event', self.__check_menu, m, player, c) connect_obj(self, 'popup-menu', self.__popup_menu, m, player) timer = TimeTracker(player) connect_obj(timer, 'tick', self.__check_time, player) connect_destroy( library, "changed", self.__songs_changed, player, m) connect_destroy(player, 'song-started', self.__song_started, m) connect_destroy(player, 'seek', self.__seeked)
def __init__(self, plugin_handler, filter_types=None): super(FilterPluginBox, self).__init__() # static filters if filter_types is None: filter_types = [] filters = [Kind() for Kind in filter_types] filters.sort() for f in filters: self.pack_start(f, True, True, 0) self.__filters = filters # plugins self.__plugins = [] hb = Gtk.HBox() expander = Gtk.Expander(label=_(u"_More options…")) expander.set_use_underline(True) expander.set_no_show_all(True) hb.pack_start(expander, True, True, 0) self.pack_start(hb, False, True, 0) for filt in filters: filt.connect('preview', lambda *x: self.emit("preview")) vbox = Gtk.VBox() expander.add(vbox) connect_destroy(plugin_handler, "changed", self.__refresh_plugins, vbox, expander) expander.connect("notify::expanded", self.__notify_expanded, vbox) expander.set_expanded(False) for child in self.get_children(): child.show() plugin_handler.changed()
def __init__(self, player, library): super(PlayControls, self).__init__(spacing=3) upper = Gtk.Table(n_rows=1, n_columns=3, homogeneous=True) upper.set_row_spacings(3) upper.set_col_spacings(3) prev = Gtk.Button(relief=Gtk.ReliefStyle.NONE) prev.add( SymbolicIconImage("media-skip-backward", Gtk.IconSize.LARGE_TOOLBAR)) upper.attach(prev, 0, 1, 0, 1) play = PlayPauseButton() upper.attach(play, 1, 2, 0, 1) next_ = Gtk.Button(relief=Gtk.ReliefStyle.NONE) next_.add( SymbolicIconImage("media-skip-forward", Gtk.IconSize.LARGE_TOOLBAR)) upper.attach(next_, 2, 3, 0, 1) lower = Gtk.Table(n_rows=1, n_columns=3, homogeneous=True) lower.set_row_spacings(3) lower.set_col_spacings(3) self.volume = Volume(player) self.volume.set_relief(Gtk.ReliefStyle.NONE) lower.attach(self.volume, 0, 1, 0, 1) # XXX: Adwaita defines a different padding for GtkVolumeButton # We force it to 0 here, which works because the other (normal) buttons # in the grid set the width/height qltk.add_css( self.volume, """ .button { padding: 0px; } """) seekbutton = SeekButton(player, library) seekbutton.set_relief(Gtk.ReliefStyle.NONE) lower.attach(seekbutton, 1, 3, 0, 1) self.pack_start(upper, False, True, 0) self.pack_start(lower, False, True, 0) connect_obj(prev, 'clicked', self.__previous, player) self._toggle_id = play.connect('toggled', self.__playpause, player) play.add_events(Gdk.EventMask.SCROLL_MASK) connect_obj(play, 'scroll-event', self.__scroll, player) connect_obj(next_, 'clicked', self.__next, player) connect_destroy(player, 'song-started', self.__song_started, next_, play) connect_destroy(player, 'paused', self.__on_set_paused_unpaused, play, False) connect_destroy(player, 'unpaused', self.__on_set_paused_unpaused, play, True)
def __init__(self, player, library): super(PlayControls, self).__init__(spacing=3) upper = Gtk.Table(n_rows=1, n_columns=3, homogeneous=True) upper.set_row_spacings(3) upper.set_col_spacings(3) prev = Gtk.Button(relief=Gtk.ReliefStyle.NONE) prev.add(SymbolicIconImage("media-skip-backward", Gtk.IconSize.LARGE_TOOLBAR)) upper.attach(prev, 0, 1, 0, 1) play = Gtk.ToggleButton(relief=Gtk.ReliefStyle.NONE) play.add(SymbolicIconImage("media-playback-start", Gtk.IconSize.LARGE_TOOLBAR)) upper.attach(play, 1, 2, 0, 1) next_ = Gtk.Button(relief=Gtk.ReliefStyle.NONE) next_.add(SymbolicIconImage("media-skip-forward", Gtk.IconSize.LARGE_TOOLBAR)) upper.attach(next_, 2, 3, 0, 1) lower = Gtk.Table(n_rows=1, n_columns=3, homogeneous=True) lower.set_row_spacings(3) lower.set_col_spacings(3) self.volume = Volume(player) self.volume.set_relief(Gtk.ReliefStyle.NONE) lower.attach(self.volume, 0, 1, 0, 1) # XXX: Adwaita defines a different padding for GtkVolumeButton # We force it to 0 here, which works because the other (normal) buttons # in the grid set the width/height qltk.add_css(self.volume, """ .button { padding: 0px; } """) seekbar = SeekBar(player, library) seekbar.set_relief(Gtk.ReliefStyle.NONE) lower.attach(seekbar, 1, 3, 0, 1) self.pack_start(upper, False, True, 0) self.pack_start(lower, False, True, 0) connect_obj(prev, 'clicked', self.__previous, player) self._toggle_id = play.connect('toggled', self.__playpause, player) play.add_events(Gdk.EventMask.SCROLL_MASK) connect_obj(play, 'scroll-event', self.__scroll, player) connect_obj(next_, 'clicked', self.__next, player) connect_destroy( player, 'song-started', self.__song_started, next_, play) connect_destroy( player, 'paused', self.__on_set_paused_unpaused, play, False) connect_destroy( player, 'unpaused', self.__on_set_paused_unpaused, play, True)
def __init__(self, library): super(PanedBrowser, self).__init__() self._register_instance() self._filter = lambda s: False self._library = library self.set_spacing(6) self.set_orientation(Gtk.Orientation.VERTICAL) completion = LibraryTagCompletion(library.librarian) self.accelerators = Gtk.AccelGroup() sbb = SearchBarBox(completion=completion, accel_group=self.accelerators) sbb.connect('query-changed', self.__text_parse) sbb.connect('focus-out', self.__focus) sbb.connect('key-press-event', self.__sb_key_pressed) self._sb_box = sbb align = Align(sbb, left=6, right=6, top=6) self.pack_start(align, False, True, 0) keyval, mod = Gtk.accelerator_parse("<Primary>Home") self.accelerators.connect(keyval, mod, 0, self.__select_all) select = Gtk.Button(label=_("Select _All"), use_underline=True) select.connect('clicked', self.__select_all) sbb.pack_start(select, False, True, 0) prefs = PreferencesButton(self) sbb.pack_start(prefs, False, True, 0) connect_destroy(library, 'changed', self.__changed) connect_destroy(library, 'added', self.__added) connect_destroy(library, 'removed', self.__removed) self.connect('destroy', self.__destroy) # contains the panes and the song list self.main_box = qltk.ConfigRPaned("browsers", "panedbrowser_pos", 0.4) self.pack_start(self.main_box, True, True, 0) self.multi_paned = ConfigMultiRHPaned("browsers", "panedbrowser_pane_widths") self.refresh_panes() for child in self.get_children(): child.show_all()
def __init__(self, library): super(InternetRadio, self).__init__(spacing=12) self.set_orientation(Gtk.Orientation.VERTICAL) if not self.instances(): self._init(library) self._register_instance() self.connect('destroy', self.__destroy) completion = LibraryTagCompletion(self.__stations) self.accelerators = Gtk.AccelGroup() self.__searchbar = search = SearchBarBox(completion=completion, accel_group=self.accelerators) search.connect('query-changed', self.__filter_changed) menu = Gtk.Menu() new_item = MenuItem(_(u"_New Station…"), Icons.LIST_ADD) new_item.connect('activate', self.__add) menu.append(new_item) update_item = MenuItem(_("_Update Stations"), Icons.VIEW_REFRESH) update_item.connect('activate', self.__update) menu.append(update_item) menu.show_all() button = MenuButton(SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.MENU), arrow=True) button.set_menu(menu) def focus(widget, *args): qltk.get_top_parent(widget).songlist.grab_focus() search.connect('focus-out', focus) # treeview scrolled_window = ScrolledWindow() scrolled_window.show() scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self.view = view = AllTreeView() view.show() view.set_headers_visible(False) scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled_window.add(view) model = Gtk.ListStore(int, str, str, str) model.append( row=[self.TYPE_ALL, Icons.FOLDER, "__all", _("All Stations")]) model.append(row=[self.TYPE_SEP, Icons.FOLDER, "", ""]) #Translators: Favorite radio stations model.append( row=[self.TYPE_FAV, Icons.FOLDER, "__fav", _("Favorites")]) model.append(row=[self.TYPE_SEP, Icons.FOLDER, "", ""]) filters = self.filters for text, k in sorted([(filters.text(k), k) for k in filters.keys()]): model.append(row=[self.TYPE_FILTER, Icons.EDIT_FIND, k, text]) model.append( row=[self.TYPE_NOCAT, Icons.FOLDER, "nocat", _("No Category")]) def separator(model, iter, data): return model[iter][self.TYPE] == self.TYPE_SEP view.set_row_separator_func(separator, None) def search_func(model, column, key, iter, data): return key.lower() not in model[iter][column].lower() view.set_search_column(self.NAME) view.set_search_equal_func(search_func, None) column = Gtk.TreeViewColumn("genres") column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) renderpb = Gtk.CellRendererPixbuf() renderpb.props.xpad = 3 column.pack_start(renderpb, False) column.add_attribute(renderpb, "icon-name", self.ICON_NAME) render = Gtk.CellRendererText() render.set_property('ellipsize', Pango.EllipsizeMode.END) view.append_column(column) column.pack_start(render, True) column.add_attribute(render, "text", self.NAME) view.set_model(model) # selection selection = view.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) self.__changed_sig = connect_destroy( selection, 'changed', util.DeferredSignal(lambda x: self.activate())) box = Gtk.HBox(spacing=6) box.pack_start(search, True, True, 0) box.pack_start(button, False, True, 0) self._searchbox = Align(box, left=0, right=6, top=6) self._searchbox.show_all() def qbar_response(infobar, response_id): if response_id == infobar.RESPONSE_LOAD: self.__update() self.qbar = QuestionBar() self.qbar.connect("response", qbar_response) if self._is_library_empty(): self.qbar.show() pane = qltk.ConfigRHPaned("browsers", "internetradio_pos", 0.4) pane.show() pane.pack1(scrolled_window, resize=False, shrink=False) songbox = Gtk.VBox(spacing=6) songbox.pack_start(self._searchbox, False, True, 0) self._songpane_container = Gtk.VBox() self._songpane_container.show() songbox.pack_start(self._songpane_container, True, True, 0) songbox.pack_start(self.qbar, False, True, 0) songbox.show() pane.pack2(songbox, resize=True, shrink=False) self.pack_start(pane, True, True, 0) self.show()
def __init__(self, library): Browser.__init__(self, spacing=6) self.set_orientation(Gtk.Orientation.VERTICAL) self._register_instance() if self.__model is None: self._init_model(library) self._cover_cancel = Gio.Cancellable.new() self.scrollwin = sw = ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.IN) model_sort = AlbumSortModel(model=self.__model) model_filter = AlbumFilterModel(child_model=model_sort) self.view = view = Gtk.IconView(model_filter) #view.set_item_width(get_cover_size() + 12) self.view.set_row_spacing(config.getint("browsers", "row_spacing", 6)) self.view.set_column_spacing(config.getint("browsers", "column_spacing", 6)) self.view.set_item_padding(config.getint("browsers", "item_padding", 6)) self.view.set_has_tooltip(True) self.view.connect("query-tooltip", self._show_tooltip) self.__bg_filter = background_filter() self.__filter = None model_filter.set_visible_func(self.__parse_query) mag = config.getfloat("browsers", "covergrid_magnification", 3.) self.view.set_item_width(get_cover_size() * mag + 8) self.__cover = render = Gtk.CellRendererPixbuf() render.set_property('width', get_cover_size() * mag + 8) render.set_property('height', get_cover_size() * mag + 8) view.pack_start(render, False) def cell_data_pb(view, cell, model, iter_, no_cover): item = model.get_value(iter_) if item.album is None: surface = None elif item.cover: pixbuf = item.cover pixbuf = add_border_widget(pixbuf, self.view) surface = get_surface_for_pixbuf(self, pixbuf) # don't cache, too much state has an effect on the result self.__last_render_surface = None else: surface = no_cover if self.__last_render_surface == surface: return self.__last_render_surface = surface cell.set_property("surface", surface) view.set_cell_data_func(render, cell_data_pb, self._no_cover) self.__text_cells = render = Gtk.CellRendererText() render.set_visible(config.getboolean("browsers", "album_text", True)) render.set_property('alignment', Pango.Alignment.CENTER) render.set_property('xalign', 0.5) render.set_property('ellipsize', Pango.EllipsizeMode.END) view.pack_start(render, False) def cell_data(view, cell, model, iter_, data): album = model.get_album(iter_) if album is None: text = "<b>%s</b>" % _("All Albums") text += "\n" + ngettext("%d album", "%d albums", len(model) - 1) % (len(model) - 1) markup = text else: markup = self.display_pattern % album if self.__last_render == markup: return self.__last_render = markup cell.markup = markup cell.set_property('markup', markup) view.set_cell_data_func(render, cell_data, None) view.set_selection_mode(Gtk.SelectionMode.MULTIPLE) sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw.add(view) view.connect('item-activated', self.__play_selection, None) self.__sig = connect_destroy( view, 'selection-changed', util.DeferredSignal(self.__update_songs, owner=self)) targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP, 1), ("text/uri-list", 0, 2)] targets = [Gtk.TargetEntry.new(*t) for t in targets] view.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) view.connect("drag-data-get", self.__drag_data_get) connect_obj(view, 'popup-menu', self.__popup, view, library) self.accelerators = Gtk.AccelGroup() search = SearchBarBox(completion=AlbumTagCompletion(), accel_group=self.accelerators) search.connect('query-changed', self.__update_filter) connect_obj(search, 'focus-out', lambda w: w.grab_focus(), view) self.__search = search prefs = PreferencesButton(self, model_sort) search.pack_start(prefs, False, True, 0) self.pack_start(Align(search, left=6, top=6), False, True, 0) self.pack_start(sw, True, True, 0) self.connect("destroy", self.__destroy) self.enable_row_update(view, sw, self.view) self.connect('key-press-event', self.__key_pressed, library.librarian) if app.cover_manager: connect_destroy( app.cover_manager, "cover-changed", self._cover_changed) self.show_all()
def __init__(self, library, player, headless=False, restore_cb=None): super(QuodLibetWindow, self).__init__(dialog=False) self.__destroyed = False self.__update_title(player) self.set_default_size(600, 480) main_box = Gtk.VBox() self.add(main_box) self.side_book = qltk.Notebook() # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after( 'drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = ScrolledWindow() self.song_scroller.set_policy( Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.song_scroller.set_shadow_type(Gtk.ShadowType.IN) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(library, player) self.qexpander.set_no_show_all(True) self.qexpander.set_visible(config.getboolean("memory", "queue")) def on_queue_visible(qex, param): config.set("memory", "queue", str(qex.get_visible())) self.qexpander.connect("notify::visible", on_queue_visible) self.playlist = PlaylistMux( player, self.qexpander.model, self.songlist.model) self.__player = player # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, None, True) keyval, mod = Gtk.accelerator_parse("<Primary><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) self.__paned = paned = ConfigRHPaned("memory", "sidebar_pos", 0.25) paned.pack1(self.__browserbox, resize=True) # We'll pack2 when necessary (when the first sidebar plugin is set up) main_box.pack_start(paned, True, True, 0) play_order = PlayOrderWidget(self.songlist.model, player) statusbox = StatusBarBox(play_order, self.qexpander) self.order = play_order self.statusbar = statusbox.statusbar main_box.pack_start( Align(statusbox, border=3, top=-3), False, True, 0) self.songpane = SongListPaned(self.song_scroller, self.qexpander) self.songpane.show_all() try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self._select_browser( self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(browsers.default)) config.save() raise self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_totals) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, 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-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
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): super(AlbumList, self).__init__(spacing=6) self.set_orientation(Gtk.Orientation.VERTICAL) self._register_instance() if self.__model is None: self._init_model(library) self._cover_cancel = Gio.Cancellable() sw = ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.IN) self.view = view = AllTreeView() view.set_headers_visible(False) model_sort = AlbumSortModel(model=self.__model) model_filter = AlbumFilterModel(child_model=model_sort) self.__bg_filter = background_filter() self.__filter = None model_filter.set_visible_func(self.__parse_query) render = Gtk.CellRendererPixbuf() self.__cover_column = column = Gtk.TreeViewColumn("covers", render) column.set_visible(config.getboolean("browsers", "album_covers")) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_fixed_width(get_cover_size() + 12) render.set_property('height', get_cover_size() + 8) render.set_property('width', get_cover_size() + 8) def cell_data_pb(column, cell, model, iter_, no_cover): item = model.get_value(iter_) if item.album is None: surface = None elif item.cover: pixbuf = item.cover pixbuf = add_border_widget(pixbuf, self.view) surface = get_surface_for_pixbuf(self, pixbuf) # don't cache, too much state has an effect on the result self.__last_render_surface = None else: surface = no_cover if self.__last_render_surface == surface: return self.__last_render_surface = surface cell.set_property("surface", surface) column.set_cell_data_func(render, cell_data_pb, self._no_cover) view.append_column(column) render = Gtk.CellRendererText() column = Gtk.TreeViewColumn("albums", render) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) if view.supports_hints(): render.set_property('ellipsize', Pango.EllipsizeMode.END) def cell_data(column, cell, model, iter_, data): album = model.get_album(iter_) if album is None: text = "<b>%s</b>\n" % _("All Albums") text += numeric_phrase("%d album", "%d albums", len(model) - 1) markup = text else: markup = self.display_pattern % album if self.__last_render == markup: return self.__last_render = markup cell.markup = markup cell.set_property('markup', markup) column.set_cell_data_func(render, cell_data) view.append_column(column) view.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) view.set_rules_hint(True) view.set_search_equal_func(self.__search_func, None) view.set_search_column(0) view.set_model(model_filter) sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.add(view) view.connect('row-activated', self.__play_selection) self.__sig = view.connect( 'selection-changed', util.DeferredSignal(self.__update_songs, owner=view)) targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP, 1), ("text/uri-list", 0, 2)] targets = [Gtk.TargetEntry.new(*t) for t in targets] view.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) view.connect("drag-data-get", self.__drag_data_get) connect_obj(view, 'popup-menu', self.__popup, view, library) self.accelerators = Gtk.AccelGroup() search = SearchBarBox(completion=AlbumTagCompletion(), accel_group=self.accelerators) search.connect('query-changed', self.__update_filter) connect_obj(search, 'focus-out', lambda w: w.grab_focus(), view) self.__search = search prefs = PreferencesButton(self, model_sort) search.pack_start(prefs, False, True, 0) self.pack_start(Align(search, left=6, top=6), False, True, 0) self.pack_start(sw, True, True, 0) self.connect("destroy", self.__destroy) self.enable_row_update(view, sw, self.__cover_column) self.connect('key-press-event', self.__key_pressed, library.librarian) if app.cover_manager: connect_destroy(app.cover_manager, "cover-changed", self._cover_changed) self.show_all()
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() pconfig.set("window_visible", menuitem.get_active()) 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("random", shuffle, "active", GObject.BindingFlags.BIDIRECTIONAL) player_options.notify("random") 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.DOCUMENT_PROPERTIES) 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) 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._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): super(InternetRadio, self).__init__(spacing=12) self.set_orientation(Gtk.Orientation.VERTICAL) if not self.instances(): self._init(library) self._register_instance() self.connect("destroy", self.__destroy) completion = LibraryTagCompletion(self.__stations) self.accelerators = Gtk.AccelGroup() self.__searchbar = search = SearchBarBox(completion=completion, accel_group=self.accelerators) search.connect("query-changed", self.__filter_changed) menu = Gtk.Menu() new_item = MenuItem(_(u"_New Station…"), Icons.LIST_ADD) new_item.connect("activate", self.__add) menu.append(new_item) update_item = MenuItem(_("_Update Stations"), Icons.VIEW_REFRESH) update_item.connect("activate", self.__update) menu.append(update_item) menu.show_all() button = MenuButton(SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.MENU), arrow=True) button.set_menu(menu) def focus(widget, *args): qltk.get_top_parent(widget).songlist.grab_focus() search.connect("focus-out", focus) # treeview scrolled_window = ScrolledWindow() scrolled_window.show() scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self.view = view = AllTreeView() view.show() view.set_headers_visible(False) scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled_window.add(view) model = Gtk.ListStore(int, str, str, str) model.append(row=[self.TYPE_ALL, Icons.FOLDER, "__all", _("All Stations")]) model.append(row=[self.TYPE_SEP, Icons.FOLDER, "", ""]) # Translators: Favorite radio stations model.append(row=[self.TYPE_FAV, Icons.FOLDER, "__fav", _("Favorites")]) model.append(row=[self.TYPE_SEP, Icons.FOLDER, "", ""]) filters = self.filters for text, k in sorted([(filters.text(k), k) for k in filters.keys()]): model.append(row=[self.TYPE_FILTER, Icons.EDIT_FIND, k, text]) model.append(row=[self.TYPE_NOCAT, Icons.FOLDER, "nocat", _("No Category")]) def separator(model, iter, data): return model[iter][self.TYPE] == self.TYPE_SEP view.set_row_separator_func(separator, None) def search_func(model, column, key, iter, data): return key.lower() not in model[iter][column].lower() view.set_search_column(self.NAME) view.set_search_equal_func(search_func, None) column = Gtk.TreeViewColumn("genres") column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) renderpb = Gtk.CellRendererPixbuf() renderpb.props.xpad = 3 column.pack_start(renderpb, False) column.add_attribute(renderpb, "icon-name", self.ICON_NAME) render = Gtk.CellRendererText() render.set_property("ellipsize", Pango.EllipsizeMode.END) view.append_column(column) column.pack_start(render, True) column.add_attribute(render, "text", self.NAME) view.set_model(model) # selection selection = view.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) self.__changed_sig = connect_destroy(selection, "changed", util.DeferredSignal(lambda x: self.activate())) box = Gtk.HBox(spacing=6) box.pack_start(search, True, True, 0) box.pack_start(button, False, True, 0) self._searchbox = Align(box, left=0, right=6, top=6) self._searchbox.show_all() def qbar_response(infobar, response_id): if response_id == infobar.RESPONSE_LOAD: self.__update() self.qbar = QuestionBar() self.qbar.connect("response", qbar_response) if self._is_library_empty(): self.qbar.show() pane = qltk.ConfigRHPaned("browsers", "internetradio_pos", 0.4) pane.show() pane.pack1(scrolled_window, resize=False, shrink=False) songbox = Gtk.VBox(spacing=6) songbox.pack_start(self._searchbox, False, True, 0) self._songpane_container = Gtk.VBox() self._songpane_container.show() songbox.pack_start(self._songpane_container, True, True, 0) songbox.pack_start(self.qbar, False, True, 0) songbox.show() pane.pack2(songbox, resize=True, shrink=False) self.pack_start(pane, True, True, 0) self.show()
def __init__(self, library, songs, parent=None): super(SongProperties, self).__init__(dialog=False) self.set_transient_for(qltk.get_top_parent(parent)) default_width = 600 config_suffix = "" if len(songs) <= 1: default_width -= 200 config_suffix += "single" self.set_default_size(default_width, 400) self.enable_window_tracking("quodlibet_properties", size_suffix=config_suffix) self.auto_save_on_change = config.getboolean( 'editing', 'auto_save_changes', False) paned = ConfigRPaned("memory", "quodlibet_properties_pos", 0.4) notebook = qltk.Notebook() notebook.props.scrollable = True pages = [] pages.extend([Ctr(self, library) for Ctr in [EditTags, TagsFromPath, RenameFiles]]) if len(songs) > 1: pages.append(TrackNumbers(self, library)) for page in pages: page.show() notebook.append_page(page) fbasemodel = ObjectStore() fmodel = ObjectModelSort(model=fbasemodel) fview = HintedTreeView(model=fmodel) fview.connect('button-press-event', self.__pre_selection_changed) fview.set_rules_hint(True) selection = fview.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) self.__save = None render = Gtk.CellRendererText() c1 = Gtk.TreeViewColumn(_('File'), render) if fview.supports_hints(): render.set_property('ellipsize', Pango.EllipsizeMode.END) render.set_property('xpad', 3) def cell_data(column, cell, model, iter_, data): entry = model.get_value(iter_) cell.set_property('text', entry.name) c1.set_cell_data_func(render, cell_data) def sort_func(model, a, b, data): a = model.get_value(a) b = model.get_value(b) return cmp(a.name, b.name) fmodel.set_sort_func(100, sort_func) c1.set_sort_column_id(100) fview.append_column(c1) sw = ScrolledWindow() sw.add(fview) sw.set_shadow_type(Gtk.ShadowType.IN) sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) # only show the list if there are is more than one song if len(songs) > 1: sw.show_all() paned.pack1(sw, shrink=False, resize=True) for song in songs: fbasemodel.append(row=[_ListEntry(song)]) self.connect("changed", self.__on_changed) selection.select_all() paned.pack2(notebook, shrink=False, resize=True) csig = selection.connect('changed', self.__selection_changed) connect_destroy(library, 'changed', self.__on_library_changed, fbasemodel, fview) connect_destroy(library, 'removed', self.__on_library_removed, fbasemodel, selection, csig) self.emit('changed', songs) self.add(paned) paned.set_position(175) notebook.show() paned.show()
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 __init__(self, library, dir=None): super(ExFalsoWindow, self).__init__(dialog=False) self.set_title("Ex Falso") self.set_default_size(750, 475) self.enable_window_tracking("exfalso") self.__library = library hp = ConfigRHPaned("memory", "exfalso_paned_position", 1.0) hp.set_border_width(0) hp.set_position(250) hp.show() self.add(hp) vb = Gtk.VBox() bbox = Gtk.HBox(spacing=6) about = Gtk.Button() about.add(Gtk.Image.new_from_icon_name( Icons.HELP_ABOUT, Gtk.IconSize.BUTTON)) connect_obj(about, 'clicked', self.__show_about, self) bbox.pack_start(about, False, True, 0) def prefs_cb(*args): window = PreferencesWindow(self) window.show() def plugin_window_cb(*args): window = PluginWindow(self) window.show() menu = Gtk.Menu() plugin_item = MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) plugin_item.connect("activate", plugin_window_cb) menu.append(plugin_item) pref_item = MenuItem(_("_Preferences"), Icons.PREFERENCES_SYSTEM) pref_item.connect("activate", prefs_cb) menu.append(pref_item) menu.show_all() menu_button = MenuButton( SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.BUTTON), arrow=True, down=False) menu_button.set_menu(menu) bbox.pack_start(menu_button, False, True, 0) l = Gtk.Label() l.set_alignment(1.0, 0.5) l.set_ellipsize(Pango.EllipsizeMode.END) bbox.pack_start(l, True, True, 0) fs = MainFileSelector() vb.pack_start(fs, True, True, 0) vb.pack_start(Align(bbox, border=6), False, True, 0) vb.show_all() hp.pack1(vb, resize=True, shrink=False) nb = qltk.Notebook() nb.props.scrollable = True nb.show() for Page in [EditTags, TagsFromPath, RenameFiles, TrackNumbers]: page = Page(self, self.__library) page.show() nb.append_page(page) hp.pack2(nb, resize=True, shrink=False) fs.connect('changed', self.__changed, l) if dir: fs.go_to(dir) connect_destroy(self.__library, 'changed', self.__library_changed, fs) self.__save = None connect_obj(self, 'changed', self.set_pending, None) for c in fs.get_children(): c.get_child().connect('button-press-event', self.__pre_selection_changed, fs, nb) c.get_child().connect('focus', self.__pre_selection_changed, fs, nb) fs.get_children()[1].get_child().connect('popup-menu', self.__popup_menu, fs) self.emit('changed', []) self.get_child().show() self.__ag = Gtk.AccelGroup() key, mod = Gtk.accelerator_parse("<Primary>Q") self.__ag.connect(key, mod, 0, lambda *x: self.destroy()) self.add_accel_group(self.__ag) # GtkosxApplication assumes the menu bar is mapped, so add # it but don't show it. self._dummy_osx_menu_bar = Gtk.MenuBar() vb.pack_start(self._dummy_osx_menu_bar, False, False, 0)
def __init__(self, menu, library, player): super(QueueExpander, self).__init__(spacing=3) sw = ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) self.queue = PlayQueue(library, player) sw.add(self.queue) outer = Gtk.HBox(spacing=12) 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) hb2.pack_start(name_label, True, True, 0) left.pack_start(hb2, False, True, 0) b = SmallImageButton( image=Gtk.Image.new_from_stock(Gtk.STOCK_CLEAR, Gtk.IconSize.MENU)) b.set_tooltip_text(_("Remove all songs from the queue")) b.connect('clicked', self.__clear_queue) b.hide() b.set_relief(Gtk.ReliefStyle.NONE) left.pack_start(b, False, False, 0) count_label = Gtk.Label() left.pack_start(count_label, False, True, 0) outer.pack_start(left, True, True, 0) 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_fill(True) cb = ConfigCheckButton( _("_Random"), "memory", "shufflequeue") cb.connect('toggled', self.__queue_shuffle, self.queue.model) cb.set_active(config.getboolean("memory", "shufflequeue")) left.pack_start(cb, False, True, 0) self.set_label_widget(outer) self.add(sw) connect_obj(self, 'notify::expanded', self.__expand, cb, b) 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.show_all() self.queue.model.connect_after('row-inserted', util.DeferredSignal(self.__check_expand), count_label) self.queue.model.connect_after('row-deleted', util.DeferredSignal(self.__update_count), count_label) cb.hide() connect_obj(self, 'notify::visible', self.__visible, cb, menu, b) 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) # 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)
def __init__(self, library): Browser.__init__(self, spacing=6) self.set_orientation(Gtk.Orientation.VERTICAL) self.songcontainer = qltk.paned.ConfigRVPaned( "browsers", "covergrid_pos", 0.4) if config.getboolean("browsers", "covergrid_wide", False): self.songcontainer.set_orientation(Gtk.Orientation.HORIZONTAL) self._register_instance() if self.__model is None: self._init_model(library) self._cover_cancel = Gio.Cancellable() self.scrollwin = sw = ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.IN) model_sort = AlbumSortModel(model=self.__model) model_filter = AlbumFilterModel(child_model=model_sort) self.view = view = IconView(model_filter) #view.set_item_width(get_cover_size() + 12) self.view.set_row_spacing(config.getint("browsers", "row_spacing", 6)) self.view.set_column_spacing(config.getint("browsers", "column_spacing", 6)) self.view.set_item_padding(config.getint("browsers", "item_padding", 6)) self.view.set_has_tooltip(True) self.view.connect("query-tooltip", self._show_tooltip) self.__bg_filter = background_filter() self.__filter = None model_filter.set_visible_func(self.__parse_query) mag = config.getfloat("browsers", "covergrid_magnification", 3.) self.view.set_item_width(get_cover_size() * mag + 8) self.__cover = render = Gtk.CellRendererPixbuf() render.set_property('width', get_cover_size() * mag + 8) render.set_property('height', get_cover_size() * mag + 8) view.pack_start(render, False) def cell_data_pb(view, cell, model, iter_, no_cover): item = model.get_value(iter_) if item.album is None: surface = None elif item.cover: pixbuf = item.cover pixbuf = add_border_widget(pixbuf, self.view) surface = get_surface_for_pixbuf(self, pixbuf) # don't cache, too much state has an effect on the result self.__last_render_surface = None else: surface = no_cover if self.__last_render_surface == surface: return self.__last_render_surface = surface cell.set_property("surface", surface) view.set_cell_data_func(render, cell_data_pb, self._no_cover) self.__text_cells = render = Gtk.CellRendererText() render.set_visible(config.getboolean("browsers", "album_text", True)) render.set_property('alignment', Pango.Alignment.CENTER) render.set_property('xalign', 0.5) render.set_property('ellipsize', Pango.EllipsizeMode.END) view.pack_start(render, False) def cell_data(view, cell, model, iter_, data): album = model.get_album(iter_) if album is None: text = "<b>%s</b>" % _("All Albums") text += "\n" + ngettext("%d album", "%d albums", len(model) - 1) % (len(model) - 1) markup = text else: markup = self.display_pattern % album if self.__last_render == markup: return self.__last_render = markup cell.markup = markup cell.set_property('markup', markup) view.set_cell_data_func(render, cell_data, None) view.set_selection_mode(Gtk.SelectionMode.MULTIPLE) sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw.add(view) view.connect('item-activated', self.__play_selection, None) self.__sig = connect_destroy( view, 'selection-changed', util.DeferredSignal(self.__update_songs, owner=self)) targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP, 1), ("text/uri-list", 0, 2)] targets = [Gtk.TargetEntry.new(*t) for t in targets] view.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) view.connect("drag-data-get", self.__drag_data_get) # NOT WORKING connect_obj(view, 'button-press-event', self.__rightclick, view, library) connect_obj(view, 'popup-menu', self.__popup, view, library) self.accelerators = Gtk.AccelGroup() search = SearchBarBox(completion=AlbumTagCompletion(), accel_group=self.accelerators) search.connect('query-changed', self.__update_filter) connect_obj(search, 'focus-out', lambda w: w.grab_focus(), view) self.__search = search prefs = PreferencesButton(self, model_sort) search.pack_start(prefs, False, True, 0) self.pack_start(Align(search, left=6, top=6), False, True, 0) self.pack_start(sw, True, True, 0) self.connect("destroy", self.__destroy) self.enable_row_update(view, sw, self.view) self.__update_filter() self.connect('key-press-event', self.__key_pressed, library.librarian) if app.cover_manager: connect_destroy( app.cover_manager, "cover-changed", self._cover_changed) self.show_all()
def __init__(self, library, player, headless=False, restore_cb=None): super().__init__(dialog=False) self.__destroyed = False self.__update_title(player) self.set_default_size(600, 480) main_box = Gtk.VBox() self.add(main_box) self.side_book = qltk.Notebook() # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after('drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = ScrolledWindow() self.song_scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.song_scroller.set_shadow_type(Gtk.ShadowType.IN) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(library, player) self.qexpander.set_no_show_all(True) self.qexpander.set_visible(config.getboolean("memory", "queue")) def on_queue_visible(qex, param): config.set("memory", "queue", str(qex.get_visible())) self.qexpander.connect("notify::visible", on_queue_visible) self.playlist = PlaylistMux(player, self.qexpander.model, self.songlist.model) self.__player = player # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, None, True) keyval, mod = Gtk.accelerator_parse("<Primary><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) self.__paned = paned = ConfigRHPaned("memory", "sidebar_pos", 0.25) paned.pack1(self.__browserbox, resize=True) # We'll pack2 when necessary (when the first sidebar plugin is set up) main_box.pack_start(paned, True, True, 0) play_order = PlayOrderWidget(self.songlist.model, player) statusbox = StatusBarBox(play_order, self.qexpander) self.order = play_order self.statusbar = statusbox.statusbar main_box.pack_start(Align(statusbox, border=3, top=-3), False, True, 0) self.songpane = SongListPaned(self.song_scroller, self.qexpander) self.songpane.show_all() try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self._select_browser(self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(browsers.default)) config.save() raise self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_totals) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, 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-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def __init__(self, library, dir=None): super(ExFalsoWindow, self).__init__(dialog=False) self.set_title("Ex Falso") self.set_default_size(750, 475) self.enable_window_tracking("exfalso") self.__library = library hp = ConfigRHPaned("memory", "exfalso_paned_position", 1.0) hp.set_border_width(0) hp.set_position(250) hp.show() self.add(hp) vb = Gtk.VBox() bbox = Gtk.HBox(spacing=6) about = Gtk.Button() about.add( Gtk.Image.new_from_icon_name(Icons.HELP_ABOUT, Gtk.IconSize.BUTTON)) connect_obj(about, 'clicked', self.__show_about, self) bbox.pack_start(about, False, True, 0) def prefs_cb(*args): window = PreferencesWindow(self) window.show() def plugin_window_cb(*args): window = PluginWindow(self) window.show() menu = Gtk.Menu() plugin_item = MenuItem(_("_Plugins"), Icons.SYSTEM_RUN) plugin_item.connect("activate", plugin_window_cb) menu.append(plugin_item) pref_item = MenuItem(_("_Preferences"), Icons.PREFERENCES_SYSTEM) pref_item.connect("activate", prefs_cb) menu.append(pref_item) menu.show_all() menu_button = MenuButton(SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.BUTTON), arrow=True, down=False) menu_button.set_menu(menu) bbox.pack_start(menu_button, False, True, 0) l = Gtk.Label() l.set_alignment(1.0, 0.5) l.set_ellipsize(Pango.EllipsizeMode.END) bbox.pack_start(l, True, True, 0) fs = MainFileSelector() vb.pack_start(fs, True, True, 0) vb.pack_start(Align(bbox, border=6), False, True, 0) vb.show_all() hp.pack1(vb, resize=True, shrink=False) nb = qltk.Notebook() nb.props.scrollable = True nb.show() for Page in [EditTags, TagsFromPath, RenameFiles, TrackNumbers]: page = Page(self, self.__library) page.show() nb.append_page(page) hp.pack2(nb, resize=True, shrink=False) fs.connect('changed', self.__changed, l) if dir: fs.go_to(dir) connect_destroy(self.__library, 'changed', self.__library_changed, fs) self.__save = None connect_obj(self, 'changed', self.set_pending, None) for c in fs.get_children(): c.get_child().connect('button-press-event', self.__pre_selection_changed, fs, nb) c.get_child().connect('focus', self.__pre_selection_changed, fs, nb) fs.get_children()[1].get_child().connect('popup-menu', self.__popup_menu, fs) self.emit('changed', []) self.get_child().show() self.__ag = Gtk.AccelGroup() key, mod = Gtk.accelerator_parse("<Primary>Q") self.__ag.connect(key, mod, 0, lambda *x: self.destroy()) self.add_accel_group(self.__ag) # GtkosxApplication assumes the menu bar is mapped, so add # it but don't show it. self._dummy_osx_menu_bar = Gtk.MenuBar() vb.pack_start(self._dummy_osx_menu_bar, False, False, 0)
def __init__(self, library): super(AlbumList, self).__init__(spacing=6) self.set_orientation(Gtk.Orientation.VERTICAL) self._register_instance() if self.__model is None: self._init_model(library) self._cover_cancel = Gio.Cancellable.new() sw = ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.IN) self.view = view = AllTreeView() view.set_headers_visible(False) model_sort = AlbumSortModel(model=self.__model) model_filter = AlbumFilterModel(child_model=model_sort) self.__bg_filter = background_filter() self.__filter = None model_filter.set_visible_func(self.__parse_query) render = Gtk.CellRendererPixbuf() self.__cover_column = column = Gtk.TreeViewColumn("covers", render) column.set_visible(config.getboolean("browsers", "album_covers")) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_fixed_width(Album.COVER_SIZE + 12) render.set_property('height', Album.COVER_SIZE + 8) render.set_property('width', Album.COVER_SIZE + 8) def cell_data_pb(column, cell, model, iter_, no_cover): album = model.get_album(iter_) if album is None: pixbuf = None elif album.cover: pixbuf = album.cover round_ = config.getboolean("albumart", "round") pixbuf = add_border_widget( pixbuf, self.view, cell, round_) pixbuf = get_pbosf_for_pixbuf(self, pixbuf) # don't cache, too much state has an effect on the result self.__last_render_pb = None else: pixbuf = no_cover if self.__last_render_pb == pixbuf: return self.__last_render_pb = pixbuf set_renderer_from_pbosf(cell, pixbuf) column.set_cell_data_func(render, cell_data_pb, self._no_cover) view.append_column(column) render = Gtk.CellRendererText() column = Gtk.TreeViewColumn("albums", render) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) if view.supports_hints(): render.set_property('ellipsize', Pango.EllipsizeMode.END) def cell_data(column, cell, model, iter_, data): album = model.get_album(iter_) if album is None: text = "<b>%s</b>" % _("All Albums") text += "\n" + ngettext("%d album", "%d albums", len(model) - 1) % (len(model) - 1) markup = text else: markup = AlbumList._pattern % album if self.__last_render == markup: return self.__last_render = markup cell.markup = markup cell.set_property('markup', markup) column.set_cell_data_func(render, cell_data) view.append_column(column) view.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) view.set_rules_hint(True) view.set_search_equal_func(self.__search_func, None) view.set_search_column(0) view.set_model(model_filter) sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.add(view) view.connect('row-activated', self.__play_selection) self.__sig = connect_destroy( view.get_selection(), 'changed', util.DeferredSignal(self.__update_songs, owner=self)) targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP, 1), ("text/uri-list", 0, 2)] targets = [Gtk.TargetEntry.new(*t) for t in targets] view.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) view.connect("drag-data-get", self.__drag_data_get) connect_obj(view, 'popup-menu', self.__popup, view, library) self.accelerators = Gtk.AccelGroup() search = SearchBarBox(completion=AlbumTagCompletion(), accel_group=self.accelerators) search.connect('query-changed', self.__update_filter) connect_obj(search, 'focus-out', lambda w: w.grab_focus(), view) self.__search = search prefs = PreferencesButton(self, model_sort) search.pack_start(prefs, False, True, 0) self.pack_start(Align(search, left=6, top=6), False, True, 0) self.pack_start(sw, True, True, 0) self.connect("destroy", self.__destroy) self.enable_row_update(view, sw, self.__cover_column) self.connect('key-press-event', self.__key_pressed, library.librarian) self.show_all()
def __init__(self, library, player=None, update=False, model_cls=PlaylistModel): super().__init__() self._register_instance(SongList) self.set_model(model_cls()) self.info = SongSelectionInfo(self) self.set_size_request(200, 150) self.set_rules_hint(True) self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) self.set_fixed_height_mode(True) self.__csig = self.connect('columns-changed', self.__columns_changed) self._first_column = None # A priority list of how to apply the sort keys. # might contain column header names not present... self._sort_sequence = [] self.set_column_headers(self.headers) librarian = library.librarian or library connect_destroy(librarian, 'changed', self.__song_updated) connect_destroy(librarian, 'removed', self.__song_removed, player) if update: connect_destroy(librarian, 'added', self.__song_added) if player: connect_destroy( player, 'paused', lambda *x: self.__redraw_current()) connect_destroy( player, 'unpaused', lambda *x: self.__redraw_current()) connect_destroy( player, 'error', lambda *x: self.__redraw_current()) self.connect('button-press-event', self.__button_press, librarian) self.connect('key-press-event', self.__key_press, librarian, player) self.setup_drop(library) self.disable_drop() self.set_search_equal_func(self.__search_func, None) self.connect('destroy', self.__destroy)
def __init__(self, library, player): super(QueueExpander, self).__init__(spacing=3) sw = ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) self.queue = PlayQueue(library, player) self.queue.props.expand = True sw.add(self.queue) add_css(self, ".ql-expanded title { margin-bottom: 5px; }") outer = ExpandBoxHack(spacing=12) 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) hb2.pack_start(name_label, True, True, 0) left.pack_start(hb2, False, True, 0) b = SmallImageButton( image=SymbolicIconImage(Icons.EDIT_CLEAR, Gtk.IconSize.MENU)) b.set_tooltip_text(_("Remove all songs from the queue")) b.connect('clicked', self.__clear_queue) b.set_no_show_all(True) b.set_relief(Gtk.ReliefStyle.NONE) left.pack_start(b, False, False, 0) self.count_label = count_label = Gtk.Label() left.pack_start(count_label, False, True, 0) outer.pack_start(left, True, True, 0) 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_fill(True) cb = ConfigCheckButton( _("_Random"), "memory", "shufflequeue") cb.connect('toggled', self.__queue_shuffle, self.queue.model) cb.set_active(config.getboolean("memory", "shufflequeue")) cb.set_no_show_all(True) left.pack_start(cb, False, True, 0) self.set_label_widget(outer) self.add(sw) connect_obj(self, 'notify::expanded', self.__expand, cb, b) 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', util.DeferredSignal(self.__check_expand), count_label) self.queue.model.connect_after('row-deleted', util.DeferredSignal(self.__update_count), count_label) connect_obj(self, 'notify::visible', self.__visible, cb, b) 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) # 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 __init__(self, library, player=None, update=False, model_cls=PlaylistModel): super(SongList, self).__init__() self._register_instance(SongList) self.set_model(model_cls()) self.info = SongInfoSelection(self) self.set_size_request(200, 150) self.set_rules_hint(True) self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) self.set_fixed_height_mode(True) self.__csig = self.connect('columns-changed', self.__columns_changed) self._first_column = None # A priority list of how to apply the sort keys. # might contain column header names not present... self._sort_sequence = [] self.set_column_headers(self.headers) librarian = library.librarian or library connect_destroy(librarian, 'changed', self.__song_updated) connect_destroy(librarian, 'removed', self.__song_removed, player) if update: connect_destroy(librarian, 'added', self.__song_added) if player: connect_destroy( player, 'paused', lambda *x: self.__redraw_current()) connect_destroy( player, 'unpaused', lambda *x: self.__redraw_current()) connect_destroy( player, 'error', lambda *x: self.__redraw_current()) self.connect('button-press-event', self.__button_press, librarian) self.connect('key-press-event', self.__key_press, librarian) self.setup_drop(library) self.disable_drop() self.set_search_equal_func(self.__search_func, None) self.connect('destroy', self.__destroy)
def __init__(self, library, dir=None): super(ExFalsoWindow, self).__init__(dialog=False) self.set_title("Ex Falso") self.set_default_size(750, 475) self.enable_window_tracking("exfalso") self.__library = library hp = ConfigRHPaned("memory", "exfalso_paned_position", 1.0) hp.set_border_width(0) hp.set_position(250) hp.show() self.add(hp) vb = Gtk.VBox() bbox = Gtk.HBox(spacing=6) about = Gtk.Button() about.add(Gtk.Image.new_from_stock( Gtk.STOCK_ABOUT, Gtk.IconSize.BUTTON)) connect_obj(about, 'clicked', self.__show_about, self) bbox.pack_start(about, False, True, 0) prefs = Gtk.Button() prefs.add(Gtk.Image.new_from_stock( Gtk.STOCK_PREFERENCES, Gtk.IconSize.BUTTON)) def prefs_cb(button): window = PreferencesWindow(self) window.show() prefs.connect('clicked', prefs_cb) bbox.pack_start(prefs, False, True, 0) plugins = qltk.Button(_("_Plugins"), Gtk.STOCK_EXECUTE) def plugin_window_cb(button): window = PluginWindow(self) window.show() plugins.connect('clicked', plugin_window_cb) bbox.pack_start(plugins, False, True, 0) l = Gtk.Label() l.set_alignment(1.0, 0.5) l.set_ellipsize(Pango.EllipsizeMode.END) bbox.pack_start(l, True, True, 0) fs = MainFileSelector() vb.pack_start(fs, True, True, 0) vb.pack_start(Alignment(bbox, border=6), False, True, 0) vb.show_all() hp.pack1(vb, resize=True, shrink=False) nb = qltk.Notebook() nb.props.scrollable = True nb.show() for Page in [EditTags, TagsFromPath, RenameFiles, TrackNumbers]: page = Page(self, self.__library) page.show() nb.append_page(page) align = Alignment(nb, top=3) align.show() hp.pack2(align, resize=True, shrink=False) fs.connect('changed', self.__changed, l) if dir: fs.go_to(dir) connect_destroy(self.__library, 'changed', self.__library_changed, fs) self.__save = None connect_obj(self, 'changed', self.set_pending, None) for c in fs.get_children(): c.get_child().connect('button-press-event', self.__pre_selection_changed, fs, nb) c.get_child().connect('focus', self.__pre_selection_changed, fs, nb) fs.get_children()[1].get_child().connect('popup-menu', self.__popup_menu, fs) self.emit('changed', []) self.get_child().show() self.__ag = Gtk.AccelGroup() key, mod = Gtk.accelerator_parse("<control>Q") self.__ag.connect(key, mod, 0, lambda *x: self.destroy()) self.add_accel_group(self.__ag)
def __init__(self, library, player, headless=False): super(QuodLibetWindow, self).__init__(dialog=False) self.last_dir = const.HOME self.__destroyed = False self.__update_title(player) self.set_default_size(550, 450) main_box = Gtk.VBox() self.add(main_box) # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, True) keyval, mod = Gtk.accelerator_parse("<control><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # dbus app menu AppMenu(self, ui.get_action_groups()[0]) # custom accel map accel_fn = os.path.join(const.USERDIR, "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.show_all() self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after( 'drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = SongListScroller( ui.get_widget("/Menu/View/SongList")) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander( ui.get_widget("/Menu/View/Queue"), library, player) self.playlist = PlaylistMux( player, self.qexpander.model, self.songlist.model) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Alignment(bottom=3) main_box.pack_start(self.__browserbox, True, True, 0) statusbox = StatusBarBox(self.songlist.model, player) self.order = statusbox.order self.repeat = statusbox.repeat self.statusbar = statusbox.statusbar main_box.pack_start( Alignment(statusbox, border=3, top=-3, right=3), False, True, 0) self.songpane = ConfigRVPaned("memory", "queue_position", 0.75) self.songpane.pack1(self.song_scroller, resize=True, shrink=False) self.songpane.pack2(self.qexpander, resize=True, shrink=False) self.__handle_position = self.songpane.get_property("position") def songpane_button_press_cb(pane, event): """If we start to drag the pane handle while the queue expander is unexpanded, expand it and move the handle to the bottom, so we can 'drag' the queue out """ if event.window != pane.get_handle_window(): return False if not self.qexpander.get_expanded(): self.qexpander.set_expanded(True) pane.set_relative(1.0) return False self.songpane.connect("button-press-event", songpane_button_press_cb) self.song_scroller.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::expanded', self.__expand_or) self.qexpander.connect('draw', self.__qex_size_allocate) self.songpane.connect('notify', self.__moved_pane_handle) try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__first_browser_set = True restore_browser = not headless try: self.select_browser( self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(0)) config.save(const.CONFIG) raise self.showhide_playlist(ui.get_widget("/Menu/View/SongList")) self.showhide_playqueue(ui.get_widget("/Menu/View/Queue")) self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_time) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) self._playback_error_dialog = None player_sigs = [ ('song-started', self.__song_started), ('paused', self.__update_paused, True), ('unpaused', self.__update_paused, False), ] for sig in player_sigs: connect_destroy(player, *sig) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, 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-data-received', self.__drag_data_received) if not headless: GLib.idle_add(self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def __init__(self, library, player, headless=False, restore_cb=None): super(QuodLibetWindow, self).__init__(dialog=False) self.last_dir = get_home_dir() self.__destroyed = False self.__update_title(player) self.set_default_size(550, 450) main_box = Gtk.VBox() self.add(main_box) # create main menubar, load/restore accelerator groups self.__library = library ui = self.__create_menu(player, library) accel_group = ui.get_accel_group() self.add_accel_group(accel_group) def scroll_and_jump(*args): self.__jump_to_current(True, True) keyval, mod = Gtk.accelerator_parse("<control><shift>J") accel_group.connect(keyval, mod, 0, scroll_and_jump) # dbus app menu # Unity puts the app menu next to our menu bar. Since it only contains # menu items also available in the menu bar itself, don't add it. if not util.is_unity(): AppMenu(self, ui.get_action_groups()[0]) # custom accel map accel_fn = os.path.join(quodlibet.get_user_dir(), "accels") Gtk.AccelMap.load(accel_fn) # save right away so we fill the file with example comments of all # accels Gtk.AccelMap.save(accel_fn) menubar = ui.get_widget("/Menu") # Since https://git.gnome.org/browse/gtk+/commit/?id=b44df22895c79 # toplevel menu items show an empty 16x16 image. While we don't # need image items there UIManager creates them by default. # Work around by removing the empty GtkImages for child in menubar.get_children(): if isinstance(child, Gtk.ImageMenuItem): child.set_image(None) main_box.pack_start(menubar, False, True, 0) # get the playlist up before other stuff self.songlist = MainSongList(library, player) self.songlist.show_all() self.songlist.connect("key-press-event", self.__songlist_key_press) self.songlist.connect_after('drag-data-received', self.__songlist_drag_data_recv) self.song_scroller = SongListScroller( ui.get_widget("/Menu/View/SongList")) self.song_scroller.add(self.songlist) self.qexpander = QueueExpander(ui.get_widget("/Menu/View/Queue"), library, player) self.playlist = PlaylistMux(player, self.qexpander.model, self.songlist.model) top_bar = TopBar(self, player, library) main_box.pack_start(top_bar, False, True, 0) self.top_bar = top_bar self.__browserbox = Align(bottom=3) main_box.pack_start(self.__browserbox, True, True, 0) statusbox = StatusBarBox(self.songlist.model, player) self.order = statusbox.order self.repeat = statusbox.repeat self.statusbar = statusbox.statusbar main_box.pack_start(Align(statusbox, border=3, top=-3, right=3), False, True, 0) self.songpane = ConfigRVPaned("memory", "queue_position", 0.75) self.songpane.pack1(self.song_scroller, resize=True, shrink=False) self.songpane.pack2(self.qexpander, resize=True, shrink=False) self.__handle_position = self.songpane.get_property("position") def songpane_button_press_cb(pane, event): """If we start to drag the pane handle while the queue expander is unexpanded, expand it and move the handle to the bottom, so we can 'drag' the queue out """ if event.window != pane.get_handle_window(): return False if not self.qexpander.get_expanded(): self.qexpander.set_expanded(True) pane.set_relative(1.0) return False self.songpane.connect("button-press-event", songpane_button_press_cb) self.song_scroller.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::visible', self.__show_or) self.qexpander.connect('notify::expanded', self.__expand_or) self.qexpander.connect('draw', self.__qex_size_allocate) self.songpane.connect('notify', self.__moved_pane_handle) try: orders = [] for e in config.getstringlist('memory', 'sortby', []): orders.append((e[1:], int(e[0]))) except ValueError: pass else: self.songlist.set_sort_orders(orders) self.browser = None self.ui = ui main_box.show_all() self._playback_error_dialog = None connect_destroy(player, 'song-started', self.__song_started) connect_destroy(player, 'paused', self.__update_paused, True) connect_destroy(player, 'unpaused', self.__update_paused, False) # make sure we redraw all error indicators before opening # a dialog (blocking the main loop), so connect after default handlers connect_after_destroy(player, 'error', self.__player_error) # connect after to let SongTracker update stats connect_after_destroy(player, "song-ended", self.__song_ended) # set at least the playlist. the song should be restored # after the browser emits the song list player.setup(self.playlist, None, 0) self.__restore_cb = restore_cb self.__first_browser_set = True restore_browser = not headless try: self.select_browser(self, config.get("memory", "browser"), library, player, restore_browser) except: config.set("memory", "browser", browsers.name(0)) config.save() raise self.showhide_playlist(ui.get_widget("/Menu/View/SongList")) self.showhide_playqueue(ui.get_widget("/Menu/View/Queue")) self.songlist.connect('popup-menu', self.__songs_popup_menu) self.songlist.connect('columns-changed', self.__cols_changed) self.songlist.connect('columns-changed', self.__hide_headers) self.songlist.info.connect("changed", self.__set_time) lib = library.librarian connect_destroy(lib, 'changed', self.__song_changed, player) targets = [("text/uri-list", Gtk.TargetFlags.OTHER_APP, 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-data-received', self.__drag_data_received) if not headless: on_first_map(self, self.__configure_scan_dirs, library) if config.getboolean('library', 'refresh_on_start'): self.__rebuild(None, False) self.connect("key-press-event", self.__key_pressed, player) self.connect("destroy", self.__destroy) self.enable_window_tracking("quodlibet")
def __init__(self, library, player): super(QueueExpander, self).__init__(spacing=3) sw = ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) self.queue = PlayQueue(library, player) 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) 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 Once Empty"), "memory", "queue_stop_once_empty", populate=True) menu.append(stop_checkbox) clear_item = MenuItem(_("_Clear Queue"), Icons.EDIT_CLEAR) menu.append(clear_item) clear_item.connect("activate", self.__clear_queue) button = SmallMenuButton( SymbolicIconImage(Icons.EMBLEM_SYSTEM, Gtk.IconSize.MENU), arrow=True) button.set_relief(Gtk.ReliefStyle.NONE) button.show_all() button.hide() button.set_no_show_all(True) menu.show_all() button.set_menu(menu) outer.pack_start(button, False, False, 0) 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', util.DeferredSignal(self.__check_expand), count_label) self.queue.model.connect_after('row-deleted', util.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.__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()