def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) # Add the 'background' styleclass so it properly hides the # bottom line of the searchbar self._stack.get_style_context().add_class('background') self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) count = 0 cursor = None Query() if Query.music_folder: try: cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) except Exception as e: logger.error("Tracker query crashed: %s", e) count = 0 if count > 0: self._switch_to_player_view() # To revert to the No Music View when no songs are found else: if self.toolbar._selectionMode is False: self._switch_to_empty_view() else: # Revert to No Music view if XDG dirs are not set self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show()
def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Stack( transition_type=StackTransitionType.CROSSFADE, transition_duration=100, visible=True) if Gtk.get_minor_version() > 8: self.set_titlebar(self.toolbar.header_bar) else: self._box.pack_start(self.toolbar.header_bar, False, False, 0) self.set_hide_titlebar_when_maximized(True) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) #self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self.connect('key_press_event', self._on_key_press) self.views[0].populate() #To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show()
def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) # Add the 'background' styleclass so it properly hides the # bottom line of the searchbar self._stack.get_style_context().add_class('background') self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) def songs_available_cb(available): if available: self._switch_to_player_view() else: self._switch_to_empty_view() Query() if Query.music_folder: grilo.songs_available(songs_available_cb) else: self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show()
def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Stack( transition_type=StackTransitionType.CROSSFADE, transition_duration=100, visible=True) if Gtk.get_minor_version() > 8: self.set_titlebar(self.toolbar.header_bar) else: self._box.pack_start(self.toolbar.header_bar, False, False, 0) self.set_hide_titlebar_when_maximized(True) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self.connect('key_press_event', self._on_key_press) self.views[0].populate() #To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show()
def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.set_titlebar(self.toolbar.header_bar) self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True ) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) # self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self._on_notify_model_id = self._stack.connect("notify::visible-child", self._on_notify_mode) self.connect("destroy", self._notify_mode_disconnect) self.views[0].populate() # To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show()
def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self._switch_to_player_view() # To revert to the No Music View when no songs are found else: if self.toolbar._selectionMode is False: self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show()
class Window(Gtk.ApplicationWindow): def __repr__(self): return '<Window>' @log def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) selectAll = Gio.SimpleAction.new('selectAll', None) app.add_accelerator('<Primary>a', 'win.selectAll', None) selectAll.connect('activate', self._on_select_all) self.add_action(selectAll) selectNone = Gio.SimpleAction.new('selectNone', None) selectNone.connect('activate', self._on_select_none) self.add_action(selectNone) self.set_size_request(200, 100) self.set_icon_name('gnome-music') self.notification_handler = None self._loading_counter = 0 self.prev_view = None self.curr_view = None size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self._setup_view() self._setup_loading_notification() self._setup_playlist_notification() self.window_size_update_timeout = None self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) grilo.connect('changes-pending', self._on_changes_pending) @log def _setup_loading_notification(self): self._loading_notification = Gtk.Revealer( halign=Gtk.Align.CENTER, valign=Gtk.Align.START) self._loading_notification.set_transition_type( Gtk.RevealerTransitionType.SLIDE_DOWN) self._overlay.add_overlay(self._loading_notification) def hide_notification_cb(button, self): self._loading_notification.set_reveal_child(False) grid = Gtk.Grid(margin_bottom=18, margin_start=18, margin_end=18) grid.set_column_spacing(18) grid.get_style_context().add_class('app-notification') spinner = Gtk.Spinner() spinner.start() grid.add(spinner) label = Gtk.Label.new(_("Loading")) grid.add(label) button = Gtk.Button.new_from_icon_name('window-close-symbolic', Gtk.IconSize.BUTTON) button.get_style_context().add_class('flat') button.connect('clicked', hide_notification_cb, self) grid.add(button) self._loading_notification.add(grid) self._loading_notification.show_all() @log def _setup_playlist_notification(self): self._playlist_notification_timeout_id = 0 self._playlist_notification = Gtk.Revealer(halign=Gtk.Align.CENTER, valign=Gtk.Align.START) self._playlist_notification.set_transition_type( Gtk.RevealerTransitionType.SLIDE_DOWN) self._overlay.add_overlay(self._playlist_notification) grid = Gtk.Grid(margin_bottom=18, margin_start=18, margin_end=18) grid.set_column_spacing(12) grid.get_style_context().add_class('app-notification') def remove_notification_timeout(self): # Remove the timeout if any if self._playlist_notification_timeout_id > 0: GLib.source_remove(self._playlist_notification_timeout_id) self._playlist_notification_timeout_id = 0 # Hide the notification and delete the playlist def hide_notification_cb(button, self): self._playlist_notification.set_reveal_child(False) if self.views[3].really_delete: playlist.delete_playlist(self.views[3].pl_todelete) else: self.views[3].really_delete = True remove_notification_timeout(self) # Undo playlist removal def undo_remove_cb(button, self): self.views[3].really_delete = False self._playlist_notification.set_reveal_child(False) self.views[3].undo_playlist_deletion() remove_notification_timeout(self) # Playlist name label self._playlist_notification.label = Gtk.Label('') grid.add(self._playlist_notification.label) # Undo button undo_button = Gtk.Button.new_with_mnemonic(_("_Undo")) undo_button.connect("clicked", undo_remove_cb, self) grid.add(undo_button) # Close button button = Gtk.Button.new_from_icon_name('window-close-symbolic', Gtk.IconSize.BUTTON) button.get_style_context().add_class('flat') button.connect('clicked', hide_notification_cb, self) grid.add(button) self._playlist_notification.add(grid) self._playlist_notification.show_all() @log def _on_changes_pending(self, data=None): def songs_available_cb(available): if available: if self.views[0] == self.views[-1]: view = self.views.pop() view.destroy() self._switch_to_player_view() self.toolbar._search_button.set_sensitive(True) self.toolbar._select_button.set_sensitive(True) self.toolbar.show_stack() elif (self.toolbar._selectionMode is False and len(self.views) != 1): self._stack.disconnect(self._on_notify_model_id) self.disconnect(self._key_press_event_id) view_count = len(self.views) for i in range(0, view_count): view = self.views.pop() view.destroy() self.toolbar.hide_stack() self._switch_to_empty_view() grilo.songs_available(songs_available_cb) @log def _on_configure_event(self, widget, event): if self.window_size_update_timeout is None: self.window_size_update_timeout = GLib.timeout_add(500, self.store_window_size_and_position, widget) @log def store_window_size_and_position(self, widget): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) GLib.source_remove(self.window_size_update_timeout) self.window_size_update_timeout = None return False @log def _on_window_state_event(self, widget, event): self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) @log def _grab_media_player_keys(self): self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) self.proxy.connect('g-signal', self._handle_media_keys) @log def _windows_focus_cb(self, window, event): try: self._grab_media_player_keys() except GLib.GError: # We cannot grab media keys if no settings daemon is running pass @log def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() @log def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) # Add the 'background' styleclass so it properly hides the # bottom line of the searchbar self._stack.get_style_context().add_class('background') self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) def songs_available_cb(available): if available: self._switch_to_player_view() else: self._switch_to_empty_view() Query() if Query.music_folder: grilo.songs_available(songs_available_cb) else: self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show() @log def _switch_to_empty_view(self): did_initial_state = self.settings.get_boolean('did-initial-state') view_class = None if did_initial_state: view_class = EmptyView else: view_class = InitialStateView self.views.append(view_class(self, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) @log def _switch_to_player_view(self): self.settings.set_boolean('did-initial-state', True) self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self._key_press_event_id = self.connect('key_press_event', self._on_key_press) self.views.append(AlbumsView(self, self.player)) self.views.append(ArtistsView(self, self.player)) self.views.append(SongsView(self, self.player)) self.views.append(PlaylistView(self, self.player)) self.views.append(SearchView(self, self.player)) self.views.append(EmptySearchView(self, self.player)) for i in self.views: if i.title: self._stack.add_titled(i, i.name, i.title) else: self._stack.add_named(i, i.name) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self.toolbar.dropdown.show() for i in self.views: GLib.idle_add(i.populate) @log def _on_select_all(self, action, param): if self.toolbar._selectionMode is False: return if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() else: view = self._stack.get_visible_child().get_visible_child() view.select_all() @log def _on_select_none(self, action, param): if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() view.unselect_all() else: view = self._stack.get_visible_child().get_visible_child() view.select_none() @log def show_playlist_notification(self): """Show a notification on playlist removal and provide an option to undo for 5 seconds. """ # Callback to remove playlists def remove_playlist_timeout_cb(self): # Remove the playlist self.views[3].really_delete = False playlist.delete_playlist(self.views[3].pl_todelete) # Hide the notification self._playlist_notification.set_reveal_child(False) # Stop the timeout self._playlist_notification_timeout_id = 0 return GLib.SOURCE_REMOVE # If a notification is already visible, remove that playlist if self._playlist_notification_timeout_id > 0: GLib.source_remove(self._playlist_notification_timeout_id) remove_playlist_timeout_cb(self) playlist_title = self.views[3].current_playlist.get_title() label = _("Playlist {} removed".format(playlist_title)) self._playlist_notification.label.set_label(label) self._playlist_notification.set_reveal_child(True) timeout_id = GLib.timeout_add_seconds(5, remove_playlist_timeout_cb, self) self._playlist_notification_timeout_id = timeout_id @log def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() event_and_modifiers = (event.state & modifiers) if event_and_modifiers != 0: # Open search bar on Ctrl + F if (event.keyval == Gdk.KEY_f and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.toolbar.searchbar.toggle_bar() # Play / Pause on Ctrl + SPACE if (event.keyval == Gdk.KEY_space and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_pause() # Play previous on Ctrl + B if (event.keyval == Gdk.KEY_b and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_previous() # Play next on Ctrl + N if (event.keyval == Gdk.KEY_n and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_next() # Toggle repeat on Ctrl + R if (event.keyval == Gdk.KEY_r and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): if self.player.get_repeat_mode() == RepeatType.SONG: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SONG) # Toggle shuffle on Ctrl + S if (event.keyval == Gdk.KEY_s and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): if self.player.get_repeat_mode() == RepeatType.SHUFFLE: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SHUFFLE) # Go back from Album view on Alt + Left if (event.keyval == Gdk.KEY_Left and event_and_modifiers == Gdk.ModifierType.MOD1_MASK): if (self.toolbar._state != ToolbarState.MAIN): self.curr_view.set_visible_child(self.curr_view._grid) self.toolbar.set_state(ToolbarState.MAIN) # Go to Albums view on Ctrl + 1 if (event.keyval == Gdk.KEY_1 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 0) # Go to Artists view on Ctrl + 2 if (event.keyval == Gdk.KEY_2 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 1) # Go to Songs view on Ctrl + 3 if (event.keyval == Gdk.KEY_3 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 2) # Go to Playlists view on Ctrl + 4 if (event.keyval == Gdk.KEY_4 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 3) else: if (event.keyval == Gdk.KEY_Delete): if self._stack.get_visible_child() == self.views[3]: self.views[3].remove_playlist() # Close search bar after Esc is pressed if event.keyval == Gdk.KEY_Escape: self.toolbar.searchbar.show_bar(False) # Also disable selection if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) # Open the search bar when typing printable chars. key_unic = Gdk.keyval_to_unicode(event.keyval) if ((not self.toolbar.searchbar.get_search_mode() and not event.keyval == Gdk.KEY_space) and GLib.unichar_isprint(chr(key_unic)) and (event_and_modifiers == Gdk.ModifierType.SHIFT_MASK or event_and_modifiers == 0)): self.toolbar.searchbar.show_bar(True) @log def _notify_mode_disconnect(self, data=None): self.player.Stop() self._stack.disconnect(self._on_notify_model_id) @log def _on_notify_mode(self, stack, param): self.prev_view = self.curr_view self.curr_view = stack.get_visible_child() # Switch to all albums view when we're clicking Albums if self.curr_view == self.views[0] and not (self.prev_view == self.views[4] or self.prev_view == self.views[5]): self.curr_view.set_visible_child(self.curr_view._grid) # Slide out sidebar on switching to Artists or Playlists view if self.curr_view == self.views[1] or \ self.curr_view == self.views[3]: self.curr_view.stack.set_visible_child_name('dummy') self.curr_view.stack.set_visible_child_name('sidebar') if self.curr_view != self.views[4] and self.curr_view != self.views[5]: self.toolbar.searchbar.show_bar(False) # Toggle the selection button for the EmptySearch view if self.curr_view == self.views[5] or \ self.prev_view == self.views[5]: self.toolbar._select_button.set_sensitive( not self.toolbar._select_button.get_sensitive()) @log def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) @log def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.show_bar(button.get_active(), self.curr_view != self.views[4]) if (not button.get_active() and (self.curr_view == self.views[4] or self.curr_view == self.views[5])): if self.toolbar._state == ToolbarState.MAIN: # We should get back to the view before the search self._stack.set_visible_child(self.views[4].previous_view) elif (self.views[4].previous_view == self.views[0] and self.curr_view.get_visible_child() != self.curr_view._albumWidget and self.curr_view.get_visible_child() != self.curr_view._artistAlbumsWidget): self._stack.set_visible_child(self.views[0]) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) @log def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode is False: self._on_changes_pending() else: in_playlist = self._stack.get_visible_child() == self.views[3] self.selection_toolbar._add_to_playlist_button.set_visible(not in_playlist) self.selection_toolbar._remove_from_playlist_button.set_visible(in_playlist) @log def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return add_to_playlist = PlaylistDialog(self) if add_to_playlist.dialog_box.run() == Gtk.ResponseType.ACCEPT: playlist.add_to_playlist( add_to_playlist.get_selected(), selected_tracks) self.toolbar.set_selection_mode(False) add_to_playlist.dialog_box.destroy() self._stack.get_visible_child().get_selected_tracks(callback) @log def _on_remove_from_playlist_button_clicked(self, widget): if self._stack.get_visible_child() != self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return playlist.remove_from_playlist( self.views[3].current_playlist, selected_tracks) self.toolbar.set_selection_mode(False) self._stack.get_visible_child().get_selected_tracks(callback) @log def push_loading_notification(self): """ Increases the counter of loading notification triggers running. If there is no notification is visible, the loading notification is started. """ def show_notification_cb(self): self._loading_notification.set_reveal_child(True) self._show_notification_timeout_id = 0 return GLib.SOURCE_REMOVE if self._loading_counter == 0: # Only show the notification after a small delay, thus # add a timeout. 500ms feels good enough. self._show_notification_timeout_id = GLib.timeout_add( 500, show_notification_cb, self) self._loading_counter = self._loading_counter + 1 @log def pop_loading_notification(self): """ Decreases the counter of loading notification triggers running. If it reaches zero, the notification is withdrawn. """ self._loading_counter = self._loading_counter - 1 if self._loading_counter == 0: # Remove the previously set timeout, if any if self._show_notification_timeout_id > 0: GLib.source_remove(self._show_notification_timeout_id) self._show_notification_timeout_id = 0 self._loading_notification.set_reveal_child(False)
class Window(Gtk.ApplicationWindow): @log def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) selectAll = Gio.SimpleAction.new('selectAll', None) app.add_accelerator('<Primary>a', 'win.selectAll', None) selectAll.connect('activate', self._on_select_all) self.add_action(selectAll) selectNone = Gio.SimpleAction.new('selectNone', None) selectNone.connect('activate', self._on_select_none) self.add_action(selectNone) self.set_size_request(200, 100) self.set_icon_name('gnome-music') self.prev_view = None self.curr_view = None size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self._setup_view() self.connect("window-state-event", self._on_window_state_event) self.configure_event_handler = self.connect("configure-event", self._on_configure_event) self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() try: self.proxy.connect('g-signal', self._handle_media_keys) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass grilo.connect('changes-pending', self._on_changes_pending) @log def _on_changes_pending(self, data=None): count = 1 cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if not count > 0: if self.toolbar._selectionMode is False and len(self.views) != 1: self._stack.disconnect(self._on_notify_model_id) self.disconnect(self._key_press_event_id) view_count = len(self.views) for i in range(0, view_count): view = self.views.pop() view.destroy() self.toolbar.hide_stack() self._switch_to_empty_view() else: if (self.views[0] == self.views[-1]): view = self.views.pop() view.destroy() self._switch_to_player_view() self.toolbar._search_button.set_sensitive(True) self.toolbar._select_button.set_sensitive(True) self.toolbar.show_stack() def _on_configure_event(self, widget, event): with self.handler_block(self.configure_event_handler): GLib.idle_add(self.store_window_size_and_position, widget, priority=GLib.PRIORITY_LOW) @log def store_window_size_and_position(self, widget): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) @log def _on_window_state_event(self, widget, event): self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) @log def _grab_media_player_keys(self): try: self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass @log def _windows_focus_cb(self, window, event): self._grab_media_player_keys() @log def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() @log def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) count = 0 cursor = None if Query.music_folder and Query.download_folder: try: cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) except Exception as e: logger.error("Tracker query crashed: %s" % e) count = 0 if count > 0: self._switch_to_player_view() # To revert to the No Music View when no songs are found else: if self.toolbar._selectionMode is False: self._switch_to_empty_view() else: # Revert to No Music view if XDG dirs are not set self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show() @log def _switch_to_empty_view(self): self.views.append(Views.Empty(self, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) @log def _switch_to_player_view(self): self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self._key_press_event_id = self.connect('key_press_event', self._on_key_press) self.views.append(Views.Albums(self, self.player)) self.views.append(Views.Artists(self, self.player)) self.views.append(Views.Songs(self, self.player)) self.views.append(Views.Playlist(self, self.player)) self.views.append(Views.Search(self, self.player)) for i in self.views: if i.title: self._stack.add_titled(i, i.name, i.title) else: self._stack.add_named(i, i.name) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self.toolbar.dropdown.show() self.views[0].populate() @log def _set_selection(self, model, value, parent=None): count = 0 _iter = model.iter_children(parent) while _iter is not None: if model.iter_has_child(_iter): count += self._set_selection(model, value, _iter) if model[_iter][5]: model.set(_iter, [6], [value]) count += 1 _iter = model.iter_next(_iter) return count @log def _on_select_all(self, action, param): if self.toolbar._selectionMode is False: return if self.toolbar._state == ToolbarState.MAIN: model = self._stack.get_visible_child().model else: model = self._stack.get_visible_child().get_visible_child().model count = self._set_selection(model, True) if count > 0: self.toolbar._selection_menu_label.set_text( ngettext("Selected %d item", "Selected %d items", count) % count) self.selection_toolbar._add_to_playlist_button.set_sensitive(True) self.selection_toolbar._remove_from_playlist_button.set_sensitive(True) elif count == 0: self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) self._stack.get_visible_child().queue_draw() @log def _on_select_none(self, action, param): if self.toolbar._state == ToolbarState.MAIN: model = self._stack.get_visible_child().model else: model = self._stack.get_visible_child().get_visible_child().model self._set_selection(model, False) self.selection_toolbar._add_to_playlist_button.set_sensitive(False) self.selection_toolbar._remove_from_playlist_button.set_sensitive(False) self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) self._stack.get_visible_child().queue_draw() @log def _init_loading_notification(self): self.notification = Gd.Notification() self.notification.set_timeout(5) grid = Gtk.Grid(valign=Gtk.Align.CENTER, margin_right=8) grid.set_column_spacing(8) spinner = Gtk.Spinner() spinner.start() grid.add(spinner) label = Gtk.Label.new(_("Loading")) grid.add(label) self.notification.add(grid) self.notification.show_all() GLib.timeout_add(1000, self._overlay.add_overlay, self.notification) @log def _init_playlist_removal_notification(self): self.notification = Gd.Notification() self.notification.set_timeout(20) grid = Gtk.Grid(valign=Gtk.Align.CENTER, margin_right=8) grid.set_column_spacing(8) self.notification.add(grid) undo_button = Gtk.Button.new_with_mnemonic(_("_Undo")) label = _("Playlist %s removed" % ( self.views[3].current_playlist.get_title())) grid.add(Gtk.Label.new(label)) grid.add(undo_button) self.notification.show_all() self._overlay.add_overlay(self.notification) self.notification.deletion_index = self.views[3].current_playlist_index self.notification.connect("dismissed", self._playlist_removal_notification_dismissed) undo_button.connect("clicked", self._undo_deletion) @log def _playlist_removal_notification_dismissed(self, widget): if self.views[3].really_delete: Views.playlists.delete_playlist(self.views[3].pl_todelete) else: self.views[3].really_delete = True @log def _undo_deletion(self, widget): self.views[3].really_delete = False self.notification.dismiss() self.views[3].undo_playlist_deletion(self.notification.deletion_index) @log def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() event_and_modifiers = (event.state & modifiers) if event_and_modifiers != 0: # Open search bar on Ctrl + F if (event.keyval == Gdk.KEY_f and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.toolbar.searchbar.toggle_bar() # Go back from Album view on Alt + Left if (event.keyval == Gdk.KEY_Left and event_and_modifiers == Gdk.ModifierType.MOD1_MASK): if (self.toolbar._state != ToolbarState.MAIN): self.curr_view.set_visible_child(self.curr_view._grid) self.toolbar.set_state(ToolbarState.MAIN) else: if (event.keyval == Gdk.KEY_Delete): if self._stack.get_visible_child() == self.views[3]: self.views[3]._on_delete_activate(None) # Close search bar after Esc is pressed if event.keyval == Gdk.KEY_Escape: self.toolbar.searchbar.show_bar(False) # Also disable selection if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) # Open search bar when typing printable chars if it not opened # Make sure we skip unprintable chars and don't grab space press # (this is used for play/pause) if not self.toolbar.searchbar.get_reveal_child() and not event.keyval == Gdk.KEY_space: if (event_and_modifiers == Gdk.ModifierType.SHIFT_MASK or event_and_modifiers == 0) and \ GLib.unichar_isprint(chr(Gdk.keyval_to_unicode(event.keyval))): self.toolbar.searchbar.show_bar(True) else: if not self.toolbar.searchbar.get_reveal_child(): if event.keyval == Gdk.KEY_space and self.player.actionbar.get_visible(): if self.get_focus() != self.player.playBtn: self.player.play_pause() @log def _notify_mode_disconnect(self, data=None): self._stack.disconnect(self._on_notify_model_id) @log def _on_notify_mode(self, stack, param): self.prev_view = self.curr_view self.curr_view = stack.get_visible_child() # Switch to all albums view when we're clicking Albums if self.curr_view == self.views[0]: self.curr_view.set_visible_child(self.curr_view._grid) # Slide out sidebar on switching to Artists or Playlists view if self.curr_view == self.views[1] or \ self.curr_view == self.views[3]: self.curr_view.stack.set_visible_child_name('dummy') self.curr_view.stack.set_visible_child_name('sidebar') if self.curr_view != self.views[4]: self.toolbar.searchbar.show_bar(False) @log def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) @log def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.show_bar(button.get_active(), self.curr_view != self.views[4]) if not button.get_active() and self.curr_view == self.views[4] and \ self.toolbar._state == ToolbarState.MAIN: self._stack.set_visible_child(self.prev_view) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) @log def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode is False: self._on_changes_pending() else: in_playlist = self._stack.get_visible_child() == self.views[3] self.selection_toolbar._add_to_playlist_button.set_visible(not in_playlist) self.selection_toolbar._remove_from_playlist_button.set_visible(in_playlist) @log def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return add_to_playlist = Widgets.PlaylistDialog(self) if add_to_playlist.dialog_box.run() == Gtk.ResponseType.ACCEPT: playlist.add_to_playlist( add_to_playlist.get_selected(), selected_tracks) self.toolbar.set_selection_mode(False) add_to_playlist.dialog_box.destroy() self._stack.get_visible_child().get_selected_tracks(callback) @log def _on_remove_from_playlist_button_clicked(self, widget): if self._stack.get_visible_child() != self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return playlist.remove_from_playlist( self.views[3].current_playlist, selected_tracks) self.toolbar.set_selection_mode(False) self._stack.get_visible_child().get_selected_tracks(callback)
class Window(Gtk.ApplicationWindow): def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) settings = Gio.Settings('org.gnome.Music') self.add_action(settings.create_action('repeat')) self.set_size_request(887, 640) self._setup_view() self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() self.proxy.connect('g-signal', self._handle_media_keys) def _grab_media_player_keys(self): self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) def _windows_focus_cb(self, window, event): self._grab_media_player_keys() def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Stack( transition_type=StackTransitionType.CROSSFADE, transition_duration=100, visible=True) if Gtk.get_minor_version() > 8: self.set_titlebar(self.toolbar.header_bar) else: self._box.pack_start(self.toolbar.header_bar, False, False, 0) self.set_hide_titlebar_when_maximized(True) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) #self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self.connect('key_press_event', self._on_key_press) self.views[0].populate() #To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show() def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() if (event.keyval == Gdk.KEY_f and (event.state & modifiers) == Gdk.ModifierType.CONTROL_MASK): self._show_searchbar(not self.toolbar.searchbar.get_child_revealed()) elif (event.keyval == Gdk.KEY_Escape and (event.state & modifiers) == 0): self._show_searchbar(False) def _notify_mode_disconnect(self, data=None): self._stack.disconnect(self._on_notify_model_id) def _on_notify_mode(self, stack, param): #Slide out artist list on switching to artists view if stack.get_visible_child() == self.views[1]: stack.get_visible_child().stack.set_visible_child_name('dummy') stack.get_visible_child().stack.set_visible_child_name('artists') self._show_searchbar(False) def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) def _on_search_toggled(self, button, data=None): self._show_searchbar(button.get_active()) def _show_searchbar(self, show): self.toolbar.searchbar.set_reveal_child(show) self.toolbar._search_button.set_active(show) if show: self.toolbar.searchbar._search_entry.grab_focus() else: self.toolbar.searchbar._search_entry.set_text('')
class Window(Gtk.ApplicationWindow): def __repr__(self): return '<Window>' @log def __init__(self, app): super().__init__(application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) self.set_size_request(200, 100) self.set_default_icon_name('gnome-music') self.prev_view = None self.curr_view = None size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance( size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self._setup_view() self.notifications_popup = NotificationsPopup() self._overlay.add_overlay(self.notifications_popup) self.window_size_update_timeout = None self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) grilo.connect('changes-pending', self._on_changes_pending) @log def _on_changes_pending(self, data=None): # FIXME: This is not working right. def songs_available_cb(available): if available: if self.views[View.ALBUM] == self.views[-1]: view = self.views.pop() view.destroy() self._switch_to_player_view() self.toolbar._search_button.set_sensitive(True) self.toolbar._select_button.set_sensitive(True) self.toolbar.show_stack() elif (self.toolbar._selectionMode is False and len(self.views) != 1): self._stack.disconnect(self._on_notify_model_id) self.disconnect(self._key_press_event_id) view_count = len(self.views) for i in range(0, view_count): view = self.views.pop() view.destroy() self.toolbar.hide_stack() self._switch_to_empty_view() grilo.songs_available(songs_available_cb) @log def _on_configure_event(self, widget, event): if self.window_size_update_timeout is None: self.window_size_update_timeout = GLib.timeout_add( 500, self.store_window_size_and_position, widget) @log def store_window_size_and_position(self, widget): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) GLib.source_remove(self.window_size_update_timeout) self.window_size_update_timeout = None return False @log def _on_window_state_event(self, widget, event): self.settings.set_boolean( 'window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) @log def _grab_media_player_keys(self): self.proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon.MediaKeys', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) self.proxy.connect('g-signal', self._handle_media_keys) @log def _windows_focus_cb(self, window, event): try: self._grab_media_player_keys() except GLib.GError: # We cannot grab media keys if no settings daemon is running pass @log def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'. format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() @log def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [None] * len(View) self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) # Add the 'background' styleclass so it properly hides the # bottom line of the searchbar self._stack.get_style_context().add_class('background') self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) def songs_available_cb(available): if available: self._switch_to_player_view() else: self._switch_to_empty_view() Query() if Query.music_folder: grilo.songs_available(songs_available_cb) else: self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show() @log def _switch_to_empty_view(self): did_initial_state = self.settings.get_boolean('did-initial-state') view_class = None if did_initial_state: view_class = EmptyView else: view_class = InitialStateView self.views[View.ALBUM] = view_class(self, self.player) self._stack.add_titled(self.views[View.ALBUM], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) @log def _switch_to_player_view(self): self.settings.set_boolean('did-initial-state', True) self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self._key_press_event_id = self.connect('key_press_event', self._on_key_press) self.views[View.ALBUM] = AlbumsView(self, self.player) self.views[View.ARTIST] = ArtistsView(self, self.player) self.views[View.SONG] = SongsView(self, self.player) self.views[View.PLAYLIST] = PlaylistView(self, self.player) self.views[View.SEARCH] = SearchView(self, self.player) self.views[View.EMPTY_SEARCH] = EmptySearchView(self, self.player) for i in self.views: if i.title: self._stack.add_titled(i, i.name, i.title) else: self._stack.add_named(i, i.name) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self.toolbar.dropdown.show() for i in self.views: GLib.idle_add(i.populate) @log def _select_all(self): if not self.toolbar._selectionMode: return if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() else: view = self._stack.get_visible_child().get_visible_child() view.select_all() @log def _select_none(self): if not self.toolbar._selectionMode: return if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() view.unselect_all() else: view = self._stack.get_visible_child().get_visible_child() view.select_none() @log def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() modifiers = (event.state & modifiers) if modifiers != 0: control_mask = Gdk.ModifierType.CONTROL_MASK shift_mask = Gdk.ModifierType.SHIFT_MASK mod1_mask = Gdk.ModifierType.MOD1_MASK if (event.keyval == Gdk.KEY_a and modifiers == control_mask): self._select_all() if (event.keyval == Gdk.KEY_A and modifiers == (shift_mask | control_mask)): self._select_none() # Open search bar on Ctrl + F if (event.keyval == Gdk.KEY_f and modifiers == control_mask): self.toolbar.searchbar.toggle() # Play / Pause on Ctrl + SPACE if (event.keyval == Gdk.KEY_space and modifiers == control_mask): self.player.play_pause() # Play previous on Ctrl + B if (event.keyval == Gdk.KEY_b and modifiers == control_mask): self.player.play_previous() # Play next on Ctrl + N if (event.keyval == Gdk.KEY_n and modifiers == control_mask): self.player.play_next() # Toggle repeat on Ctrl + R if (event.keyval == Gdk.KEY_r and modifiers == control_mask): if self.player.get_repeat_mode() == RepeatType.SONG: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SONG) # Toggle shuffle on Ctrl + S if (event.keyval == Gdk.KEY_s and modifiers == control_mask): if self.player.get_repeat_mode() == RepeatType.SHUFFLE: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SHUFFLE) # Go back from Album view on Alt + Left if (event.keyval == Gdk.KEY_Left and modifiers == mod1_mask): self.toolbar.on_back_button_clicked() if ((event.keyval in [Gdk.KEY_1, Gdk.KEY_KP_1]) and modifiers == control_mask): self._toggle_view(View.ALBUM) if ((event.keyval in [Gdk.KEY_2, Gdk.KEY_KP_2]) and modifiers == control_mask): self._toggle_view(View.ARTIST) if ((event.keyval in [Gdk.KEY_3, Gdk.KEY_KP_3]) and modifiers == control_mask): self._toggle_view(View.SONG) if ((event.keyval in [Gdk.KEY_4, Gdk.KEY_KP_4]) and modifiers == control_mask): self._toggle_view(View.PLAYLIST) else: child = self._stack.get_visible_child() if (event.keyval == Gdk.KEY_Delete and child == self.views[View.PLAYLIST]): self.views[View.PLAYLIST].remove_playlist() # Close search bar after Esc is pressed if event.keyval == Gdk.KEY_Escape: self.toolbar.searchbar.reveal(False) # Also disable selection if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) # Open the search bar when typing printable chars. key_unic = Gdk.keyval_to_unicode(event.keyval) if ((not self.toolbar.searchbar.get_search_mode() and not event.keyval == Gdk.KEY_space) and GLib.unichar_isprint(chr(key_unic)) and (modifiers == Gdk.ModifierType.SHIFT_MASK or modifiers == 0) and not self.views[View.PLAYLIST].rename_active): self.toolbar.searchbar.reveal(True) @log def do_button_release_event(self, event): """Override default button release event :param Gdk.EventButton event: Button event """ __, code = event.get_button() # Mouse button 8 is the navigation button if code == 8: self.toolbar.on_back_button_clicked() @log def _notify_mode_disconnect(self, data=None): self.player.Stop() self.notifications_popup.terminate_pending() self._stack.disconnect(self._on_notify_model_id) @log def _on_notify_mode(self, stack, param): self.prev_view = self.curr_view self.curr_view = stack.get_visible_child() # Switch to all albums view when we're clicking Albums if (self.curr_view == self.views[View.ALBUM] and not (self.prev_view == self.views[View.SEARCH] or self.prev_view == self.views[View.EMPTY_SEARCH])): self.curr_view.set_visible_child(self.curr_view._grid) # Slide out sidebar on switching to Artists or Playlists view if self.curr_view == self.views[View.ARTIST] or \ self.curr_view == self.views[View.PLAYLIST]: self.curr_view.stack.set_visible_child_name('dummy') self.curr_view.stack.set_visible_child_name('sidebar') if self.curr_view != self.views[ View.SEARCH] and self.curr_view != self.views[ View.EMPTY_SEARCH]: self.toolbar.searchbar.reveal(False) # Disable the selection button for the EmptySearch and Playlist # view no_selection_mode = [ self.views[View.EMPTY_SEARCH], self.views[View.PLAYLIST] ] self.toolbar._select_button.set_sensitive( self.curr_view not in no_selection_mode) # Disable renaming playlist if it was active when leaving # Playlist view if (self.prev_view == self.views[View.PLAYLIST] and self.views[View.PLAYLIST].rename_active): self.views[View.PLAYLIST].disable_rename_playlist() @log def _toggle_view(self, view_enum): self._stack.set_visible_child(self.views[view_enum]) @log def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.reveal( button.get_active(), self.curr_view != self.views[View.SEARCH]) if (not button.get_active() and (self.curr_view == self.views[View.SEARCH] or self.curr_view == self.views[View.EMPTY_SEARCH])): child = self.curr_view.get_visible_child() if self.toolbar._state == ToolbarState.MAIN: # We should get back to the view before the search self._stack.set_visible_child( self.views[View.SEARCH].previous_view) elif (self.views[View.SEARCH].previous_view == self.views[View.ALBUM] and child != self.curr_view._album_widget and child != self.curr_view._artist_albums_widget): self._stack.set_visible_child(self.views[View.ALBUM]) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) @log def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode is False: self._on_changes_pending() else: child = self._stack.get_visible_child() in_playlist = (child == self.views[View.PLAYLIST]) self.selection_toolbar._add_to_playlist_button.set_visible( not in_playlist) @log def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[View.PLAYLIST]: return def callback(selected_songs): if len(selected_songs) < 1: return playlist_dialog = PlaylistDialog( self, self.views[View.PLAYLIST].pls_todelete) if playlist_dialog.run() == Gtk.ResponseType.ACCEPT: playlists.add_to_playlist(playlist_dialog.get_selected(), selected_songs) self.toolbar.set_selection_mode(False) playlist_dialog.destroy() self._stack.get_visible_child().get_selected_songs(callback)
class Window(Gtk.ApplicationWindow): def __repr__(self): return '<Window>' @log def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) selectAll = Gio.SimpleAction.new('selectAll', None) app.add_accelerator('<Primary>a', 'win.selectAll', None) selectAll.connect('activate', self._on_select_all) self.add_action(selectAll) selectNone = Gio.SimpleAction.new('selectNone', None) selectNone.connect('activate', self._on_select_none) self.add_action(selectNone) self.set_size_request(200, 100) self.set_icon_name('gnome-music') self.notification_handler = None self._loading_counter = 0 self.prev_view = None self.curr_view = None size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance( size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self._setup_view() self._setup_loading_notification() self._setup_playlist_notification() self.window_size_update_timeout = None self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) grilo.connect('changes-pending', self._on_changes_pending) @log def _setup_loading_notification(self): self._loading_notification = Gtk.Revealer(halign=Gtk.Align.CENTER, valign=Gtk.Align.START) self._loading_notification.set_transition_type( Gtk.RevealerTransitionType.SLIDE_DOWN) self._overlay.add_overlay(self._loading_notification) grid = Gtk.Grid(margin_bottom=18, margin_start=18, margin_end=18) grid.set_column_spacing(18) grid.get_style_context().add_class('app-notification') spinner = Gtk.Spinner() spinner.start() grid.add(spinner) label = Gtk.Label.new(_("Loading")) grid.add(label) self._loading_notification.add(grid) self._loading_notification.show_all() @log def _setup_playlist_notification(self): self._playlist_notification_timeout_id = 0 self._playlist_notification = Gtk.Revealer(halign=Gtk.Align.CENTER, valign=Gtk.Align.START) self._playlist_notification.set_transition_type( Gtk.RevealerTransitionType.SLIDE_DOWN) self._overlay.add_overlay(self._playlist_notification) grid = Gtk.Grid(margin_bottom=18, margin_start=18, margin_end=18) grid.set_column_spacing(12) grid.get_style_context().add_class('app-notification') def remove_notification_timeout(self): # Remove the timeout if any if self._playlist_notification_timeout_id > 0: GLib.source_remove(self._playlist_notification_timeout_id) self._playlist_notification_timeout_id = 0 # Undo playlist removal def undo_remove_cb(button, self): self._playlist_notification.set_reveal_child(False) self.views[3].undo_playlist_deletion() remove_notification_timeout(self) # Playlist name label self._playlist_notification.label = Gtk.Label('') grid.add(self._playlist_notification.label) # Undo button undo_button = Gtk.Button.new_with_mnemonic(_("_Undo")) undo_button.connect("clicked", undo_remove_cb, self) grid.add(undo_button) self._playlist_notification.add(grid) self._playlist_notification.show_all() @log def _on_changes_pending(self, data=None): def songs_available_cb(available): if available: if self.views[0] == self.views[-1]: view = self.views.pop() view.destroy() self._switch_to_player_view() self.toolbar._search_button.set_sensitive(True) self.toolbar._select_button.set_sensitive(True) self.toolbar.show_stack() elif (self.toolbar._selectionMode is False and len(self.views) != 1): self._stack.disconnect(self._on_notify_model_id) self.disconnect(self._key_press_event_id) view_count = len(self.views) for i in range(0, view_count): view = self.views.pop() view.destroy() self.toolbar.hide_stack() self._switch_to_empty_view() grilo.songs_available(songs_available_cb) @log def _on_configure_event(self, widget, event): if self.window_size_update_timeout is None: self.window_size_update_timeout = GLib.timeout_add( 500, self.store_window_size_and_position, widget) @log def store_window_size_and_position(self, widget): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) GLib.source_remove(self.window_size_update_timeout) self.window_size_update_timeout = None return False @log def _on_window_state_event(self, widget, event): self.settings.set_boolean( 'window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) @log def _grab_media_player_keys(self): self.proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon.MediaKeys', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) self.proxy.connect('g-signal', self._handle_media_keys) @log def _windows_focus_cb(self, window, event): try: self._grab_media_player_keys() except GLib.GError: # We cannot grab media keys if no settings daemon is running pass @log def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'. format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() @log def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) # Add the 'background' styleclass so it properly hides the # bottom line of the searchbar self._stack.get_style_context().add_class('background') self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) def songs_available_cb(available): if available: self._switch_to_player_view() else: self._switch_to_empty_view() Query() if Query.music_folder: grilo.songs_available(songs_available_cb) else: self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show() @log def _switch_to_empty_view(self): did_initial_state = self.settings.get_boolean('did-initial-state') view_class = None if did_initial_state: view_class = EmptyView else: view_class = InitialStateView self.views.append(view_class(self, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) @log def _switch_to_player_view(self): self.settings.set_boolean('did-initial-state', True) self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self._key_press_event_id = self.connect('key_press_event', self._on_key_press) self.views.append(AlbumsView(self, self.player)) self.views.append(ArtistsView(self, self.player)) self.views.append(SongsView(self, self.player)) self.views.append(PlaylistView(self, self.player)) self.views.append(SearchView(self, self.player)) self.views.append(EmptySearchView(self, self.player)) for i in self.views: if i.title: self._stack.add_titled(i, i.name, i.title) else: self._stack.add_named(i, i.name) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self.toolbar.dropdown.show() for i in self.views: GLib.idle_add(i.populate) @log def _on_select_all(self, action, param): if self.toolbar._selectionMode is False: return if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() else: view = self._stack.get_visible_child().get_visible_child() view.select_all() @log def _on_select_none(self, action, param): if self.toolbar._state == ToolbarState.MAIN: view = self._stack.get_visible_child() view.unselect_all() else: view = self._stack.get_visible_child().get_visible_child() view.select_none() @log def show_playlist_notification(self): """Show a notification on playlist removal and provide an option to undo for 5 seconds. """ # Callback to remove playlists def remove_playlist_timeout_cb(self): # Remove the playlist playlist.delete_playlist(self.views[3].pl_todelete) # Hide the notification self._playlist_notification.set_reveal_child(False) # Stop the timeout self._playlist_notification_timeout_id = 0 return GLib.SOURCE_REMOVE # If a notification is already visible, remove that playlist if self._playlist_notification_timeout_id > 0: GLib.source_remove(self._playlist_notification_timeout_id) remove_playlist_timeout_cb(self) playlist_title = self.views[3].current_playlist.get_title() label = _("Playlist {} removed".format(playlist_title)) self._playlist_notification.label.set_label(label) self._playlist_notification.set_reveal_child(True) timeout_id = GLib.timeout_add_seconds(5, remove_playlist_timeout_cb, self) self._playlist_notification_timeout_id = timeout_id @log def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() event_and_modifiers = (event.state & modifiers) if event_and_modifiers != 0: # Open search bar on Ctrl + F if (event.keyval == Gdk.KEY_f and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.toolbar.searchbar.toggle_bar() # Play / Pause on Ctrl + SPACE if (event.keyval == Gdk.KEY_space and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_pause() # Play previous on Ctrl + B if (event.keyval == Gdk.KEY_b and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_previous() # Play next on Ctrl + N if (event.keyval == Gdk.KEY_n and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.player.play_next() # Toggle repeat on Ctrl + R if (event.keyval == Gdk.KEY_r and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): if self.player.get_repeat_mode() == RepeatType.SONG: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SONG) # Toggle shuffle on Ctrl + S if (event.keyval == Gdk.KEY_s and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): if self.player.get_repeat_mode() == RepeatType.SHUFFLE: self.player.set_repeat_mode(RepeatType.NONE) else: self.player.set_repeat_mode(RepeatType.SHUFFLE) # Go back from Album view on Alt + Left if (event.keyval == Gdk.KEY_Left and event_and_modifiers == Gdk.ModifierType.MOD1_MASK): if (self.toolbar._state != ToolbarState.MAIN): self.curr_view.set_visible_child(self.curr_view._grid) self.toolbar.set_state(ToolbarState.MAIN) # Go to Albums view on Ctrl + 1 if (event.keyval == Gdk.KEY_1 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 0) # Go to Artists view on Ctrl + 2 if (event.keyval == Gdk.KEY_2 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 1) # Go to Songs view on Ctrl + 3 if (event.keyval == Gdk.KEY_3 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 2) # Go to Playlists view on Ctrl + 4 if (event.keyval == Gdk.KEY_4 and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self._toggle_view(0, 3) else: if (event.keyval == Gdk.KEY_Delete): if self._stack.get_visible_child() == self.views[3]: self.views[3].remove_playlist() # Close search bar after Esc is pressed if event.keyval == Gdk.KEY_Escape: self.toolbar.searchbar.show_bar(False) # Also disable selection if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) # Open the search bar when typing printable chars. key_unic = Gdk.keyval_to_unicode(event.keyval) if ((not self.toolbar.searchbar.get_search_mode() and not event.keyval == Gdk.KEY_space) and GLib.unichar_isprint(chr(key_unic)) and (event_and_modifiers == Gdk.ModifierType.SHIFT_MASK or event_and_modifiers == 0)): self.toolbar.searchbar.show_bar(True) @log def _notify_mode_disconnect(self, data=None): self.player.Stop() self._stack.disconnect(self._on_notify_model_id) @log def _on_notify_mode(self, stack, param): self.prev_view = self.curr_view self.curr_view = stack.get_visible_child() # Switch to all albums view when we're clicking Albums if self.curr_view == self.views[0] and not ( self.prev_view == self.views[4] or self.prev_view == self.views[5]): self.curr_view.set_visible_child(self.curr_view._grid) # Slide out sidebar on switching to Artists or Playlists view if self.curr_view == self.views[1] or \ self.curr_view == self.views[3]: self.curr_view.stack.set_visible_child_name('dummy') self.curr_view.stack.set_visible_child_name('sidebar') if self.curr_view != self.views[4] and self.curr_view != self.views[5]: self.toolbar.searchbar.show_bar(False) # Toggle the selection button for the EmptySearch view if self.curr_view == self.views[5] or \ self.prev_view == self.views[5]: self.toolbar._select_button.set_sensitive( not self.toolbar._select_button.get_sensitive()) @log def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) @log def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.show_bar(button.get_active(), self.curr_view != self.views[4]) if (not button.get_active() and (self.curr_view == self.views[4] or self.curr_view == self.views[5])): if self.toolbar._state == ToolbarState.MAIN: # We should get back to the view before the search self._stack.set_visible_child(self.views[4].previous_view) elif (self.views[4].previous_view == self.views[0] and self.curr_view.get_visible_child() != self.curr_view._albumWidget and self.curr_view.get_visible_child() != self.curr_view._artistAlbumsWidget): self._stack.set_visible_child(self.views[0]) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) @log def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode is False: self._on_changes_pending() else: in_playlist = self._stack.get_visible_child() == self.views[3] self.selection_toolbar._add_to_playlist_button.set_visible( not in_playlist) self.selection_toolbar._remove_from_playlist_button.set_visible( in_playlist) @log def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[3]: return def callback(selected_songs): if len(selected_songs) < 1: return playlist_dialog = PlaylistDialog(self, self.views[3].pl_todelete) if playlist_dialog.run() == Gtk.ResponseType.ACCEPT: playlist.add_to_playlist(playlist_dialog.get_selected(), selected_songs) self.toolbar.set_selection_mode(False) playlist_dialog.destroy() self._stack.get_visible_child().get_selected_songs(callback) @log def _on_remove_from_playlist_button_clicked(self, widget): if self._stack.get_visible_child() != self.views[3]: return def callback(selected_songs): if len(selected_songs) < 1: return playlist.remove_from_playlist(self.views[3].current_playlist, selected_songs) self.toolbar.set_selection_mode(False) self._stack.get_visible_child().get_selected_songs(callback) @log def push_loading_notification(self): """ Increases the counter of loading notification triggers running. If there is no notification is visible, the loading notification is started. """ def show_notification_cb(self): self._loading_notification.set_reveal_child(True) self._show_notification_timeout_id = 0 return GLib.SOURCE_REMOVE if self._loading_counter == 0: # Only show the notification after a small delay, thus # add a timeout. 500ms feels good enough. self._show_notification_timeout_id = GLib.timeout_add( 500, show_notification_cb, self) self._loading_counter = self._loading_counter + 1 @log def pop_loading_notification(self): """ Decreases the counter of loading notification triggers running. If it reaches zero, the notification is withdrawn. """ self._loading_counter = self._loading_counter - 1 if self._loading_counter == 0: # Remove the previously set timeout, if any if self._show_notification_timeout_id > 0: GLib.source_remove(self._show_notification_timeout_id) self._show_notification_timeout_id = 0 self._loading_notification.set_reveal_child(False)
class Window(Gtk.ApplicationWindow): def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) self.set_size_request(887, 640) size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self.connect("window-state-event", self.on_window_state_event) self.connect("configure-event", self.on_configure_event) self._setup_view() self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() self.proxy.connect('g-signal', self._handle_media_keys) def on_configure_event(self, widget, event): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) def on_window_state_event(self, widget, event): self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) def _grab_media_player_keys(self): self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) def _windows_focus_cb(self, window, event): self._grab_media_player_keys() def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Stack( transition_type=StackTransitionType.CROSSFADE, transition_duration=100, visible=True) if Gtk.get_minor_version() > 8: self.set_titlebar(self.toolbar.header_bar) else: self._box.pack_start(self.toolbar.header_bar, False, False, 0) self.set_hide_titlebar_when_maximized(True) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) #self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self.connect('key_press_event', self._on_key_press) self.views[0].populate() #To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show() def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() if (event.keyval == Gdk.KEY_f and (event.state & modifiers) == Gdk.ModifierType.CONTROL_MASK): self._show_searchbar(not self.toolbar.searchbar.get_child_revealed()) elif (event.keyval == Gdk.KEY_Escape and (event.state & modifiers) == 0): self._show_searchbar(False) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) def _notify_mode_disconnect(self, data=None): self._stack.disconnect(self._on_notify_model_id) def _on_notify_mode(self, stack, param): #Slide out artist list on switching to artists view if stack.get_visible_child() == self.views[1]: stack.get_visible_child().stack.set_visible_child_name('dummy') stack.get_visible_child().stack.set_visible_child_name('artists') self._show_searchbar(False) def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) def _on_search_toggled(self, button, data=None): self._show_searchbar(button.get_active()) def _show_searchbar(self, show): self.toolbar.searchbar.set_reveal_child(show) self.toolbar._search_button.set_active(show) if show: self.toolbar.searchbar._search_entry.grab_focus() else: self.toolbar.searchbar._search_entry.set_text('')
class Window(Gtk.ApplicationWindow): def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) selectAll = Gio.SimpleAction.new('selectAll', None) selectAll.connect('activate', self._on_select_all) self.add_action(selectAll) selectNone = Gio.SimpleAction.new('selectNone', None) selectNone.connect('activate', self._on_select_none) self.add_action(selectNone) self.set_size_request(200, 100) size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) self._setup_view() self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() try: self.proxy.connect('g-signal', self._handle_media_keys) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass def _on_configure_event(self, widget, event): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) def _on_window_state_event(self, widget, event): self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) def _grab_media_player_keys(self): try: self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass def _windows_focus_cb(self, window, event): self._grab_media_player_keys() def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Stack( transition_type=StackTransitionType.CROSSFADE, transition_duration=100, visible=True) if Gtk.get_minor_version() > 8: self.set_titlebar(self.toolbar.header_bar) else: self._box.pack_start(self.toolbar.header_bar, False, False, 0) self.set_hide_titlebar_when_maximized(True) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self.connect('key_press_event', self._on_key_press) self.views[0].populate() #To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show() def _on_select_all(self, action, param): if self.toolbar._state != ToolbarState.SINGLE: model = self._stack.get_visible_child()._model else: model = self._stack.get_visible_child()._albumWidget.model _iter = model.get_iter_first() count = 0 while _iter is not None: model.set(_iter, [6], [True]) _iter = model.iter_next(_iter) count = count + 1 if count > 0: self.toolbar._selection_menu_label.set_text( ngettext(_("Selected %d item"), _("Selected %d items"), count) % count) self.selection_toolbar._add_to_playlist_button.set_sensitive(True) self.selection_toolbar._remove_from_playlist_button.set_sensitive(True) elif count == 0: self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) def _on_select_none(self, action, param): if self.toolbar._state != ToolbarState.SINGLE: model = self._stack.get_visible_child()._model else: model = self._stack.get_visible_child()._albumWidget.model _iter = model.get_iter_first() self.selection_toolbar._add_to_playlist_button.set_sensitive(False) self.selection_toolbar._remove_from_playlist_button.set_sensitive(False) while _iter is not None: model.set(_iter, [6], [False]) _iter = model.iter_next(_iter) self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() if (event.keyval == Gdk.KEY_f and (event.state & modifiers) == Gdk.ModifierType.CONTROL_MASK): self.toolbar.searchbar.toggle_bar() elif (event.keyval == Gdk.KEY_Escape and (event.state & modifiers) == 0): self.toolbar.searchbar.show_bar(False) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) elif (event.state & modifiers) == 0 and \ event.keyval in range(33, 126) and \ not self.toolbar.searchbar.get_reveal_child(): self.toolbar.searchbar.show_bar(True) def _notify_mode_disconnect(self, data=None): self._stack.disconnect(self._on_notify_model_id) def _on_notify_mode(self, stack, param): #Slide out artist list on switching to artists view if stack.get_visible_child() == self.views[1] or \ stack.get_visible_child() == self.views[3]: stack.get_visible_child().stack.set_visible_child_name('dummy') stack.get_visible_child().stack.set_visible_child_name('sidebar') self.toolbar.searchbar.show_bar(False) def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.show_bar(button.get_active()) def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode: in_playlist = self._stack.get_visible_child() == self.views[3] self.selection_toolbar._add_to_playlist_button.set_visible(not in_playlist) self.selection_toolbar._remove_from_playlist_button.set_visible(in_playlist) def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[3]: return def callback(selected_uris): if len(selected_uris) < 1: return add_to_playlist = Widgets.PlaylistDialog(self) if add_to_playlist.dialog_box.run() == Gtk.ResponseType.ACCEPT: playlist.add_to_playlist( add_to_playlist.get_selected(), selected_uris) self.toolbar.set_selection_mode(False) add_to_playlist.dialog_box.destroy() self._stack.get_visible_child().get_selected_track_uris(callback) def _on_remove_from_playlist_button_clicked(self, widget): if self._stack.get_visible_child() != self.views[3]: return def callback(selected_uris): if len(selected_uris) < 1: return playlist.remove_from_playlist( self.views[3].current_playlist, selected_uris) self.toolbar.set_selection_mode(False) self._stack.get_visible_child().get_selected_track_uris(callback)
class Window(Gtk.ApplicationWindow): def __repr__(self): return '<Window>' @log def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) self.connect('focus-in-event', self._windows_focus_cb) self.settings = Gio.Settings.new('org.gnome.Music') self.add_action(self.settings.create_action('repeat')) selectAll = Gio.SimpleAction.new('selectAll', None) app.add_accelerator('<Primary>a', 'win.selectAll', None) selectAll.connect('activate', self._on_select_all) self.add_action(selectAll) selectNone = Gio.SimpleAction.new('selectNone', None) selectNone.connect('activate', self._on_select_none) self.add_action(selectNone) self.set_size_request(200, 100) self.set_icon_name('gnome-music') self.notification_handler = None self.prev_view = None self.curr_view = None self.pl_todelete_notification = None size_setting = self.settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self.settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self.settings.get_value('window-maximized'): self.maximize() self._setup_view() self.window_size_update_timeout = None self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) grilo.connect('changes-pending', self._on_changes_pending) @log def _on_changes_pending(self, data=None): count = 1 cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if not count > 0: if self.toolbar._selectionMode is False and len(self.views) != 1: self._stack.disconnect(self._on_notify_model_id) self.disconnect(self._key_press_event_id) view_count = len(self.views) for i in range(0, view_count): view = self.views.pop() view.destroy() self.toolbar.hide_stack() self._switch_to_empty_view() else: if (self.views[0] == self.views[-1]): view = self.views.pop() view.destroy() self._switch_to_player_view() self.toolbar._search_button.set_sensitive(True) self.toolbar._select_button.set_sensitive(True) self.toolbar.show_stack() def _on_configure_event(self, widget, event): if self.window_size_update_timeout is None: self.window_size_update_timeout = GLib.timeout_add(500, self.store_window_size_and_position, widget) @log def store_window_size_and_position(self, widget): size = widget.get_size() self.settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self.settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) GLib.source_remove(self.window_size_update_timeout) self.window_size_update_timeout = None return False @log def _on_window_state_event(self, widget, event): self.settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) @log def _grab_media_player_keys(self): self.proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self.proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Music', 0)), Gio.DBusCallFlags.NONE, -1, None) self.proxy.connect('g-signal', self._handle_media_keys) @log def _windows_focus_cb(self, window, event): try: self._grab_media_player_keys() except GLib.GError: # We cannot grab media keys if no settings daemon is running pass @log def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self.player.play_pause() elif 'Stop' in response: self.player.Stop() elif 'Next' in response: self.player.play_next() elif 'Previous' in response: self.player.play_previous() @log def _setup_view(self): self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.player = Player(self) self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True, can_focus=False) self._overlay = Gtk.Overlay(child=self._stack) self._overlay.add_overlay(self.toolbar.dropdown) self.set_titlebar(self.toolbar.header_bar) self._box.pack_start(self.toolbar.searchbar, False, False, 0) self._box.pack_start(self._overlay, True, True, 0) self._box.pack_start(self.player.actionbar, False, False, 0) self._box.pack_start(self.selection_toolbar.actionbar, False, False, 0) self.add(self._box) count = 0 cursor = None Query() if Query.music_folder: try: cursor = tracker.query(Query.all_songs_count(), None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) except Exception as e: logger.error("Tracker query crashed: %s", e) count = 0 if count > 0: self._switch_to_player_view() # To revert to the No Music View when no songs are found else: if self.toolbar._selectionMode is False: self._switch_to_empty_view() else: # Revert to No Music view if XDG dirs are not set self._switch_to_empty_view() self.toolbar._search_button.connect('toggled', self._on_search_toggled) self.toolbar.connect('selection-mode-changed', self._on_selection_mode_changed) self.selection_toolbar._add_to_playlist_button.connect( 'clicked', self._on_add_to_playlist_button_clicked) self.selection_toolbar._remove_from_playlist_button.connect( 'clicked', self._on_remove_from_playlist_button_clicked) self.toolbar.set_state(ToolbarState.MAIN) self.toolbar.header_bar.show() self._overlay.show() self.player.actionbar.show_all() self._box.show() self.show() @log def _switch_to_empty_view(self): did_initial_state = self.settings.get_boolean('did-initial-state') view_class = None if did_initial_state: view_class = Views.Empty else: view_class = Views.InitialState self.views.append(view_class(self, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar._search_button.set_sensitive(False) self.toolbar._select_button.set_sensitive(False) @log def _switch_to_player_view(self): self.settings.set_boolean('did-initial-state', True) self._on_notify_model_id = self._stack.connect('notify::visible-child', self._on_notify_mode) self.connect('destroy', self._notify_mode_disconnect) self._key_press_event_id = self.connect('key_press_event', self._on_key_press) self.views.append(Views.Albums(self, self.player)) self.views.append(Views.Artists(self, self.player)) self.views.append(Views.Songs(self, self.player)) self.views.append(Views.Playlist(self, self.player)) self.views.append(Views.Search(self, self.player)) self.views.append(Views.EmptySearch(self, self.player)) for i in self.views: if i.title: self._stack.add_titled(i, i.name, i.title) else: self._stack.add_named(i, i.name) self.toolbar.set_stack(self._stack) self.toolbar.searchbar.show() self.toolbar.dropdown.show() self.views[0].populate() @log def _set_selection(self, model, value, parent=None): count = 0 _iter = model.iter_children(parent) while _iter is not None: if model.iter_has_child(_iter): count += self._set_selection(model, value, _iter) if model[_iter][5]: model.set(_iter, [6], [value]) count += 1 _iter = model.iter_next(_iter) return count @log def _on_select_all(self, action, param): if self.toolbar._selectionMode is False: return if self.toolbar._state == ToolbarState.MAIN: model = self._stack.get_visible_child().model else: model = self._stack.get_visible_child().get_visible_child().model count = self._set_selection(model, True) if count > 0: self.toolbar._selection_menu_label.set_text( ngettext("Selected %d item", "Selected %d items", count) % count) self.selection_toolbar._add_to_playlist_button.set_sensitive(True) self.selection_toolbar._remove_from_playlist_button.set_sensitive(True) elif count == 0: self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) self._stack.get_visible_child().queue_draw() @log def _on_select_none(self, action, param): if self.toolbar._state == ToolbarState.MAIN: model = self._stack.get_visible_child().model else: model = self._stack.get_visible_child().get_visible_child().model self._set_selection(model, False) self.selection_toolbar._add_to_playlist_button.set_sensitive(False) self.selection_toolbar._remove_from_playlist_button.set_sensitive(False) self.toolbar._selection_menu_label.set_text(_("Click on items to select them")) self._stack.get_visible_child().queue_draw() def _show_notification(self): self.notification_handler = None self.notification.show_all() return False @log def _init_loading_notification(self): self.notification = Gd.Notification() self.notification.set_timeout(5) grid = Gtk.Grid(valign=Gtk.Align.CENTER, margin_end=8) grid.set_column_spacing(8) spinner = Gtk.Spinner() spinner.start() grid.add(spinner) label = Gtk.Label.new(_("Loading")) grid.add(label) self.notification.add(grid) self._overlay.add_overlay(self.notification) if self.notification_handler: GLib.Source.remove(self.notification_handler) self.notification_handler = None self.notification_handler = GLib.timeout_add(1000, self._show_notification) @log def _init_playlist_removal_notification(self): if self.pl_todelete_notification: self.views[3].really_delete = False self.pl_todelete_notification.destroy() Views.playlists.delete_playlist(self.views[3].pl_todelete) self.notification = Gd.Notification() self.notification.set_timeout(20) grid = Gtk.Grid(valign=Gtk.Align.CENTER, margin_right=8) grid.set_column_spacing(8) self.notification.add(grid) undo_button = Gtk.Button.new_with_mnemonic(_("_Undo")) label = _("Playlist %s removed" % ( self.views[3].current_playlist.get_title())) grid.add(Gtk.Label.new(label)) grid.add(undo_button) self.notification.show_all() self._overlay.add_overlay(self.notification) self.pl_todelete_notification = self.notification self.notification.connect("dismissed", self._playlist_removal_notification_dismissed) undo_button.connect("clicked", self._undo_deletion) @log def _playlist_removal_notification_dismissed(self, widget): self.pl_todelete_notification = None if self.views[3].really_delete: Views.playlists.delete_playlist(self.views[3].pl_todelete) else: self.views[3].really_delete = True @log def _undo_deletion(self, widget): self.views[3].really_delete = False self.notification.dismiss() self.views[3].undo_playlist_deletion() @log def _on_key_press(self, widget, event): modifiers = Gtk.accelerator_get_default_mod_mask() event_and_modifiers = (event.state & modifiers) if event_and_modifiers != 0: # Open search bar on Ctrl + F if (event.keyval == Gdk.KEY_f and event_and_modifiers == Gdk.ModifierType.CONTROL_MASK): self.toolbar.searchbar.toggle_bar() # Go back from Album view on Alt + Left if (event.keyval == Gdk.KEY_Left and event_and_modifiers == Gdk.ModifierType.MOD1_MASK): if (self.toolbar._state != ToolbarState.MAIN): self.curr_view.set_visible_child(self.curr_view._grid) self.toolbar.set_state(ToolbarState.MAIN) else: if (event.keyval == Gdk.KEY_Delete): if self._stack.get_visible_child() == self.views[3]: self.views[3]._on_delete_activate(None) # Close search bar after Esc is pressed if event.keyval == Gdk.KEY_Escape: self.toolbar.searchbar.show_bar(False) # Also disable selection if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) # Open search bar when typing printable chars if it not opened # Make sure we skip unprintable chars and don't grab space press # (this is used for play/pause) if not self.toolbar.searchbar.get_reveal_child() and not event.keyval == Gdk.KEY_space: if (event_and_modifiers == Gdk.ModifierType.SHIFT_MASK or event_and_modifiers == 0) and \ GLib.unichar_isprint(chr(Gdk.keyval_to_unicode(event.keyval))): self.toolbar.searchbar.show_bar(True) else: if not self.toolbar.searchbar.get_reveal_child(): if event.keyval == Gdk.KEY_space and self.player.actionbar.get_visible(): if self.get_focus() != self.player.playBtn: self.player.play_pause() @log def _notify_mode_disconnect(self, data=None): self.player.Stop() self._stack.disconnect(self._on_notify_model_id) @log def _on_notify_mode(self, stack, param): self.prev_view = self.curr_view self.curr_view = stack.get_visible_child() # Switch to all albums view when we're clicking Albums if self.curr_view == self.views[0] and not (self.prev_view == self.views[4] or self.prev_view == self.views[5]): self.curr_view.set_visible_child(self.curr_view._grid) # Slide out sidebar on switching to Artists or Playlists view if self.curr_view == self.views[1] or \ self.curr_view == self.views[3]: self.curr_view.stack.set_visible_child_name('dummy') self.curr_view.stack.set_visible_child_name('sidebar') if self.curr_view != self.views[4] and self.curr_view != self.views[5]: self.toolbar.searchbar.show_bar(False) # Toggle the selection button for the EmptySearch view if self.curr_view == self.views[5] or \ self.prev_view == self.views[5]: self.toolbar._select_button.set_sensitive( not self.toolbar._select_button.get_sensitive()) @log def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i]) @log def _on_search_toggled(self, button, data=None): self.toolbar.searchbar.show_bar(button.get_active(), self.curr_view != self.views[4]) if (not button.get_active() and (self.curr_view == self.views[4] or self.curr_view == self.views[5])): if self.toolbar._state == ToolbarState.MAIN: # We should get back to the view before the search self._stack.set_visible_child(self.views[4].previous_view) elif (self.views[4].previous_view == self.views[0] and self.curr_view.get_visible_child() != self.curr_view._albumWidget and self.curr_view.get_visible_child() != self.curr_view._artistAlbumsWidget): self._stack.set_visible_child(self.views[0]) if self.toolbar._selectionMode: self.toolbar.set_selection_mode(False) @log def _on_selection_mode_changed(self, widget, data=None): if self.toolbar._selectionMode is False: self._on_changes_pending() else: in_playlist = self._stack.get_visible_child() == self.views[3] self.selection_toolbar._add_to_playlist_button.set_visible(not in_playlist) self.selection_toolbar._remove_from_playlist_button.set_visible(in_playlist) @log def _on_add_to_playlist_button_clicked(self, widget): if self._stack.get_visible_child() == self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return add_to_playlist = Widgets.PlaylistDialog(self) if add_to_playlist.dialog_box.run() == Gtk.ResponseType.ACCEPT: playlist.add_to_playlist( add_to_playlist.get_selected(), selected_tracks) self.toolbar.set_selection_mode(False) add_to_playlist.dialog_box.destroy() self._stack.get_visible_child().get_selected_tracks(callback) @log def _on_remove_from_playlist_button_clicked(self, widget): if self._stack.get_visible_child() != self.views[3]: return def callback(selected_tracks): if len(selected_tracks) < 1: return playlist.remove_from_playlist( self.views[3].current_playlist, selected_tracks) self.toolbar.set_selection_mode(False) self._stack.get_visible_child().get_selected_tracks(callback)
class Window(Gtk.ApplicationWindow): def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Music")) settings = Gio.Settings("org.gnome.Music") self.add_action(settings.create_action("repeat")) self.set_size_request(887, 640) self._setup_view() self.proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, "org.gnome.SettingsDaemon", "/org/gnome/SettingsDaemon/MediaKeys", "org.gnome.SettingsDaemon.MediaKeys", None, ) self.proxy.call_sync( "GrabMediaPlayerKeys", GLib.Variant("(su)", ("Music", 0)), Gio.DBusCallFlags.NONE, -1, None ) self.proxy.connect("g-signal", self._handle_media_keys) def _windows_focus_cb(self, window, event): self.proxy.call_sync( "GrabMediaPlayerKeys", GLib.Variant("(su)", ("Music", 0)), Gio.DBusCallFlags.NONE, -1, None ) def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != "MediaPlayerKeyPressed": print("Received an unexpected signal '%s' from media player".format(signal)) return response = parameters.get_child_value(1).get_string() if "Play" in response: self.player.play_pause() elif "Stop" in response: self.player.Stop() elif "Next" in response: self.player.play_next() elif "Previous" in response: self.player.play_previous() def _setup_view(self): self._box = Gtk.VBox() self.player = Player() self.selection_toolbar = SelectionToolbar() self.toolbar = Toolbar() self.set_titlebar(self.toolbar.header_bar) self.views = [] self._stack = Gtk.Stack( transition_type=Gtk.StackTransitionType.CROSSFADE, transition_duration=100, visible=True ) self._box.pack_start(self._stack, True, True, 0) self._box.pack_start(self.player.eventBox, False, False, 0) self._box.pack_start(self.selection_toolbar.eventbox, False, False, 0) self.add(self._box) count = 1 cursor = tracker.query(Query.SONGS_COUNT, None) if cursor is not None and cursor.next(None): count = cursor.get_integer(0) if count > 0: self.views.append(Views.Albums(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Artists(self.toolbar, self.selection_toolbar, self.player)) self.views.append(Views.Songs(self.toolbar, self.selection_toolbar, self.player)) # self.views.append(Views.Playlist(self.toolbar, self.selection_toolbar, self.player)) for i in self.views: self._stack.add_titled(i, i.title, i.title) self.toolbar.set_stack(self._stack) self._on_notify_model_id = self._stack.connect("notify::visible-child", self._on_notify_mode) self.connect("destroy", self._notify_mode_disconnect) self.views[0].populate() # To revert to the No Music View when no songs are found else: self.views.append(Views.Empty(self.toolbar, self.player)) self._stack.add_titled(self.views[0], _("Empty"), _("Empty")) self.toolbar.set_state(ToolbarState.ALBUMS) self.toolbar.header_bar.show() self.player.eventBox.show_all() self._box.show() self.show() def _notify_mode_disconnect(self, data=None): self._stack.disconnect(self._on_notify_model_id) def _on_notify_mode(self, stack, param): # Slide out artist list on switching to artists view if stack.get_visible_child() == self.views[1]: stack.get_visible_child().stack.set_visible_child_name("dummy") stack.get_visible_child().stack.set_visible_child_name("artists") def _toggle_view(self, btn, i): self._stack.set_visible_child(self.views[i])