示例#1
0
文件: window.py 项目: erwanj/lollypop
class Window(Gtk.ApplicationWindow):

	"""
		Init window objects
	"""	
	def __init__(self, app):
		Gtk.ApplicationWindow.__init__(self,
					       application=app,
					       title=_("Lollypop"))

		self._timeout = None
		self._scanner = CollectionScanner()
		self._scanner.connect("scan-finished", self._setup_list_one, True)

		self._setup_window()				
		self._setup_view()

		self._setup_media_keys()

		party_settings = Objects["settings"].get_value('party-ids')
		ids = []
		for setting in party_settings:
			if isinstance(setting, int):
				ids.append(setting)	
		Objects["player"].set_party_ids(ids)
		self.connect("destroy", self._on_destroyed_window)

	"""
		Run collection update if needed
	"""	
	def setup_view(self):
		if Objects["tracks"].is_empty():
			self._scanner.update(self._progress, False)
			return
		elif Objects["settings"].get_value('startup-scan'):
			self._scanner.update(self._progress, True)
			
		self._setup_list_one()
		self._update_view_albums(POPULARS)

	"""
		Update music database
	"""
	def update_db(self):
		self._list_one.widget.hide()
		self._list_two.widget.hide()

		old_view = self._stack.get_visible_child()		
		self._loading = True
		view = LoadingView()
		self._stack.add(view)
		self._stack.set_visible_child(view)
		self._scanner.update(self._progress, False)
		if old_view:
			self._stack.remove(old_view)
			old_view.remove_signals()


	"""
		Update view class
		@param bool
	"""
	def update_view_class(self, dark):
		current_view = self._stack.get_visible_child()
		if dark:
			current_view.get_style_context().add_class('black')
		else:
			current_view.get_style_context().remove_class('black')

############
# Private  #
############

	"""
		Setup media player keys
	"""
	def _setup_media_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._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

	"""
		Do key grabbing
	"""
	def _grab_media_player_keys(self):
		try:
			self._proxy.call_sync('GrabMediaPlayerKeys',
								 GLib.Variant('(su)', ('Lollypop', 0)),
								 Gio.DBusCallFlags.NONE,
								 -1,
								 None)
		except GLib.GError:
			# We cannot grab media keys if no settings daemon is running
			pass

	"""
		Do player actions in response to media key pressed
	"""
	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:
			Objects["player"].play_pause()
		elif 'Stop' in response:
			Objects["player"].stop()
		elif 'Next' in response:
			Objects["player"].next()
		elif 'Previous' in response:
			Objects["player"].prev()
	
	"""
		Setup window icon, position and size, callback for updating this values
	"""
	def _setup_window(self):
		self.set_icon_name('lollypop')
		size_setting = Objects["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])
		else:
			self.set_size_request(800, 600)
		position_setting = Objects["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 Objects["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)

	"""
		Setup window main view:
			- genre list
			- artist list
			- main view as artist view or album view
	"""
	def _setup_view(self):
		self._paned_main_list = Gtk.HPaned()
		self._paned_list_view = Gtk.HPaned()
		vgrid = Gtk.Grid()
		vgrid.set_orientation(Gtk.Orientation.VERTICAL)
	
		self._toolbar = Toolbar()
		self._toolbar.header_bar.show()
		self._toolbar.get_view_genres_btn().connect("toggled", self._setup_list_one)

		self._list_one = SelectionList("Genre")
		self._list_two = SelectionList("Artist")
		self._list_one_signal = None
		self._list_two_signal = None
		
		self._loading = True
		loading_view = LoadingView()

		self._stack = Gtk.Stack()
		self._stack.add(loading_view)
		self._stack.set_visible_child(loading_view)
		self._stack.set_transition_duration(500)
		self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
		self._stack.show()

		self._progress = Gtk.ProgressBar()

		vgrid.add(self._stack)
		vgrid.add(self._progress)
		vgrid.show()

		DESKTOP = environ.get("XDG_CURRENT_DESKTOP")
		if DESKTOP and ("GNOME" in DESKTOP or "Pantheon" in DESKTOP):
			self.set_titlebar(self._toolbar.header_bar)
			self._toolbar.header_bar.set_show_close_button(True)
			self.add(self._paned_main_list)
		else:
			hgrid = Gtk.Grid()
			hgrid.set_orientation(Gtk.Orientation.VERTICAL)
			hgrid.add(self._toolbar.header_bar)
			hgrid.add(self._paned_main_list)
			hgrid.show()
			self.add(hgrid)

		separator = Gtk.Separator()
		separator.show()
		self._paned_list_view.add1(self._list_two.widget)
		self._paned_list_view.add2(vgrid)
		self._paned_main_list.add1(self._list_one.widget)
		self._paned_main_list.add2(self._paned_list_view)
		self._paned_main_list.set_position(Objects["settings"].get_value("paned-mainlist-width").get_int32())
		self._paned_list_view.set_position(Objects["settings"].get_value("paned-listview-width").get_int32())
		self._paned_main_list.show()
		self._paned_list_view.show()
		self.show()

	"""
		Init the filter list
		@param widget as unused
	"""
	def _init_main_list(self, widget):
		if self._list_one.widget.is_visible():
			self._update_genres()
		else:
			self._init_genres()
	"""
		Init list with genres or artist
		If update, only update list content
		@param obj as unused, bool
	"""
	def _setup_list_one(self, obj = None, update = None):
		if self._list_one_signal:
			self._list_one.disconnect(self._list_one_signal)
		active = self._toolbar.get_view_genres_btn().get_active()
		if active:
			items = Objects["genres"].get_ids()
		else:
			self._list_two.widget.hide()
			items = Objects["artists"].get_ids(ALL)
			if len(Objects["albums"].get_compilations(ALL)) > 0:
				items.insert(0, (COMPILATIONS, _("Compilations")))

		items.insert(0, (ALL, _("All artists")))
		items.insert(0, (POPULARS, _("Popular albums")))

		if update:
			self._list_one.update(items, not active)
		else:
			self._list_one.populate(items, not active)		

		# If was empty
		if not self._list_one_signal:
			self._list_one.select_first()

		if self._loading:
			self._stack.get_visible_child().hide()
			self._list_one.select_first()
			self._update_view_albums(POPULARS)
			self._loading = False

		self._list_one.widget.show()
		if active:
			self._list_one_signal = self._list_one.connect('item-selected', self._setup_list_two)
		else:
			self._list_one_signal = self._list_one.connect('item-selected', self._update_view_artists, None)

	"""
		Init list two with artist based on genre
		@param obj as unused, genre id as int
	"""
	def _setup_list_two(self, obj, genre_id):
		if self._list_two_signal:
			self._list_two.disconnect(self._list_two_signal)

		if genre_id == POPULARS:
			self._list_two.widget.hide()
			self._list_two_signal = None
		else:
			
			values = Objects["artists"].get_ids(genre_id)
			if len(Objects["albums"].get_compilations(genre_id)) > 0:
				values.insert(0, (COMPILATIONS, _("Compilations")))
			self._list_two.populate(values, True)
			self._list_two.widget.show()
			self._list_two_signal = self._list_two.connect('item-selected', self._update_view_artists, genre_id)
		self._update_view_albums(genre_id)
		
	"""
		Update artist view
		@param artist id as int, genre id as int
	"""
	def _update_view_artists(self, obj, artist_id, genre_id):
		if artist_id == ALL or artist_id == POPULARS:
			self._update_view_albums(artist_id)
		else:
			old_view = self._stack.get_visible_child()
			view = ArtistView(artist_id, genre_id, False)
			self._stack.add(view)
			start_new_thread(view.populate, ())
			self._stack.set_visible_child(view)
			if old_view:
				self._stack.remove(old_view)
				old_view.remove_signals()

	"""
		Update albums view
		@param genre id as int
	"""
	def _update_view_albums(self, genre_id):
		old_view = self._stack.get_visible_child()
		view = AlbumView(genre_id)
		self._stack.add(view)
		start_new_thread(view.populate, ())
		self._stack.set_visible_child(view)
		if old_view:
			self._stack.remove(old_view)
			old_view.remove_signals()

	"""
		Delay event
		@param: widget as Gtk.Window
		@param: event as Gtk.Event
	"""		
	def _on_configure_event(self, widget, event):
		if self._timeout:
			GLib.source_remove(self._timeout)
		self._timeout = GLib.timeout_add(500, self._save_size_position, widget)
	
	"""
		Save window state, update current view content size
		@param: widget as Gtk.Window
	"""
	def _save_size_position(self, widget):
		self._timeout = None
		size = widget.get_size()
		Objects["settings"].set_value('window-size', GLib.Variant('ai', [size[0], size[1]]))
		position = widget.get_position()
		Objects["settings"].set_value('window-position', GLib.Variant('ai', [position[0], position[1]]))

	"""
		Save maximised state
	"""
	def _on_window_state_event(self, widget, event):
		Objects["settings"].set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names)

	"""
		Save paned widget width
		@param widget as unused, data as unused
	"""	
	def _on_destroyed_window(self, widget):
		Objects["settings"].set_value("paned-mainlist-width", GLib.Variant('i', self._paned_main_list.get_position()))
		Objects["settings"].set_value("paned-listview-width", GLib.Variant('i', self._paned_list_view.get_position()))
示例#2
0
class Window(Gtk.ApplicationWindow):

	"""
		Init window objects
	"""
	def __init__(self, app, db, player):
		Gtk.ApplicationWindow.__init__(self,
					       application=app,
					       title=_("Lollypop"))
		
		self._db = db
		self._player = player
		self._scanner = CollectionScanner()
		self._settings = Gio.Settings.new('org.gnome.Lollypop')

		self._artist_signal_id = 0

		self._setup_window()				
		self._setup_view()
		self._setup_media_keys()

		party_settings = self._settings.get_value('party-ids')
		ids = []
		for setting in party_settings:
			if isinstance(setting, int):
				ids.append(setting)	
		self._player.set_party_ids(ids)
		
		self.connect("map-event", self._on_mapped_window)



	def edit_party(self):
		builder = Gtk.Builder()
		builder.add_from_resource('/org/gnome/Lollypop/PartyDialog.ui')
		self._party_dialog = builder.get_object('party_dialog')
		self._party_dialog.set_transient_for(self)
		self._party_dialog.set_title(_("Select what will be available in party mode"))
		party_button = builder.get_object('button1')
		party_button.connect("clicked", self._edit_party_close)
		scrolled = builder.get_object('scrolledwindow1')
		genres = self._db.get_all_genres()
		genres.insert(0, (-1, "Populars"))
		self._party_grid = Gtk.Grid()
		self._party_grid.set_orientation(Gtk.Orientation.VERTICAL)
		self._party_grid.set_property("column-spacing", 10)
		ids = self._player.get_party_ids()
		i = 0
		x = 0
		for genre_id, genre in genres:
			label = Gtk.Label()
			label.set_text(genre)
			switch = Gtk.Switch()
			if genre_id in ids:
				switch.set_state(True)
			switch.connect("state-set", self._party_switch_state, genre_id)
			self._party_grid.attach(label, x, i, 1, 1)
			self._party_grid.attach(switch, x+1, i, 1, 1)
			if x == 0:
				x += 2
			else:
				i += 1
				x = 0
		scrolled.add(self._party_grid)
		self._party_dialog.show_all()

	"""
		Update music database
		Empty database if reinit True
	"""
	def update_db(self, reinit):
		if reinit:
			self._player.stop()
			self._player.clear_albums()
			self._toolbar.update_toolbar(None, None)
			self._db.reset()
		self._list_genres.widget.hide()
		self._list_artists.widget.hide()
		self._box.remove(self._view)
		self._view = LoadingView()
		self._box.add(self._view)
		self._scanner.update(self._update_genres)

############
# Private  #
############

	"""
		Update party ids when use change a switch in dialog
	"""
	def _party_switch_state(self, widget, state, genre_id):
		ids = self._player.get_party_ids()
		if state:
			try:
				ids.append(genre_id)
			except:
				pass
		else:
			try:
				ids.remove(genre_id)
			except:
				pass
		self._player.set_party_ids(ids)
		self._settings.set_value('party-ids',  GLib.Variant('ai', ids))
		

	"""
		Close edit party dialog
	"""
	def _edit_party_close(self, widget):
		self._party_dialog.hide()
		self._party_dialog.destroy()

	"""
		Setup media player keys
	"""
	def _setup_media_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._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

	"""
		Do key grabbing
	"""
	def _grab_media_player_keys(self):
		try:
			self._proxy.call_sync('GrabMediaPlayerKeys',
								 GLib.Variant('(su)', ('Lollypop', 0)),
								 Gio.DBusCallFlags.NONE,
								 -1,
								 None)
		except GLib.GError:
			# We cannot grab media keys if no settings daemon is running
			pass

	"""
		Do player actions in response to media key pressed
	"""
	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.next()
		elif 'Previous' in response:
			self._player.prev()
	
	"""
		Setup window icon, position and size, callback for updating this values
	"""
	def _setup_window(self):
		self.set_size_request(200, 100)
		self.set_icon_name('lollypop')
		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)

	"""
		Setup window main view:
			- genre list
			- artist list
			- main view as artist view or album view
	"""
	def _setup_view(self):
		self._box = Gtk.Grid()
		self._toolbar = Toolbar(self._db, self._player)
		self.set_titlebar(self._toolbar.header_bar)
		self._toolbar.header_bar.show()
		self._toolbar.get_infobox().connect("button-press-event", self._show_current_album)

		self._list_genres = SelectionList("Genre", 150)
		self._list_artists = SelectionList("Artist", 200)
		
		self._view = LoadingView()

		separator = Gtk.Separator()
		separator.show()
		self._box.add(self._list_genres.widget)
		self._box.add(separator)
		self._box.add(self._list_artists.widget)
		self._box.add(self._view)
		self.add(self._box)
		self._box.show()
		self.show()
	
	"""
		Run collection update on mapped window
		Pass _update_genre() as collection scanned callback
	"""	
	def _on_mapped_window(self, obj, data):
		if self._db.is_empty():
			self._scanner.update(self._update_genres)
		else:
			genres = self._db.get_all_genres()
			self._update_genres(genres)
		
	"""
		Update genres list with genres
	"""
	def _update_genres(self, genres):
		genres.insert(0, (-1, _("All genres")))
		genres.insert(0, (-2, _("Populars albums")))
		self._list_genres.populate(genres)
		self._list_genres.connect('item-selected', self._update_artists)
		self._list_genres.widget.show()
		self._list_genres.select_first()

	"""
		Update artist list for genre_id
	"""
	def _update_artists(self, obj, genre_id):
		if self._artist_signal_id:
			self._list_artists.disconnect(self._artist_signal_id)
		if genre_id == -1:
			self._list_artists.populate(self._db.get_all_artists(), True)
			self._update_view_albums(self, -1)
			self._list_artists.widget.show()
		elif genre_id == -2:
			self._update_view_populars_albums()
			self._list_artists.widget.hide()
		else:
			self._list_artists.populate(self._db.get_artists_by_genre_id(genre_id), True)
			self._update_view_albums(self, genre_id)
			self._list_artists.widget.show()
		self._artist_signal_id = self._list_artists.connect('item-selected', self._update_view_artist)
		self._genre_id = genre_id

	"""
		Update artist view for artist_id
	"""
	def _update_view_artist(self, obj, artist_id):
		self._box.remove(self._view)
		self._view.destroy()
		self._view = ArtistView(self._db, self._player, self._genre_id, artist_id)
		self._box.add(self._view)
		self._view.populate()
	
	"""
		Update albums view with populars albums
	"""
	def _update_view_populars_albums(self):
		self._box.remove(self._view)
		self._view.destroy()
		self._view = AlbumView(self._db, self._player, None)
		self._box.add(self._view)
		self._view.populate_popular()
	"""
		Update albums view for genre_id
	"""
	def _update_view_albums(self, obj, genre_id):
		self._box.remove(self._view)
		self._view.destroy()
		self._view = AlbumView(self._db, self._player, genre_id)
		self._box.add(self._view)
		self._view.populate()
	
	"""
		Save new window size/position
	"""		
	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]]))

	"""
		Save maximised state
	"""
	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)

	"""
		Show current album context/content
	"""
	def _show_current_album(self, obj, data):
		track_id = self._player.get_current_track_id()
		if  track_id != -1:
			self._view.current_changed(False, track_id)