38275a77ec
Although usage tips are handy and useful, they look clunky in the tooltip. They also lack localization and this project doesn't provide translations. Show artist and song name during playback and "Rhythmbox" the rest of the time.
298 linhas
9.7 KiB
Python
Arquivo Executável
298 linhas
9.7 KiB
Python
Arquivo Executável
#!/usr/bin/python3
|
|
# coding=utf-8
|
|
|
|
from gi.repository import Gtk, Gdk, GdkPixbuf, Peas, GObject, RB
|
|
import os
|
|
import sys
|
|
import math
|
|
|
|
|
|
class TrayIcon(GObject.Object, Peas.Activatable):
|
|
|
|
__gtype_name = 'TrayIcon'
|
|
object = GObject.property(type=GObject.Object)
|
|
|
|
rhythmbox_icon = os.path.join(sys.path[0], "tray_stopped.png")
|
|
play_icon = os.path.join(sys.path[0], "tray_playing.png")
|
|
menu = None
|
|
|
|
def position_menu_cb(self, m, x, y=None, i=None):
|
|
try:
|
|
return Gtk.StatusIcon.position_menu(self.menu, x, y, self.icon)
|
|
except (AttributeError, TypeError):
|
|
return Gtk.StatusIcon.position_menu(self.menu, self.icon)
|
|
|
|
def show_popup_menu(self, icon, button, time, data = None):
|
|
"""
|
|
Called when the icon is right clicked, displays the menu
|
|
"""
|
|
|
|
self.create_popup_menu()
|
|
device = Gdk.Display.get_default().get_device_manager().get_client_pointer()
|
|
self.menu.popup_for_device(device, None, None, self.position_menu_cb, self.icon, 3, time)
|
|
|
|
def create_popup_menu(self):
|
|
"""
|
|
Creates menu items for popup menu, including star rating
|
|
"""
|
|
|
|
if not self.menu:
|
|
self.set_menu_css()
|
|
|
|
self.menu = Gtk.Menu()
|
|
|
|
if self.playing:
|
|
menuitem_playpause = Gtk.ImageMenuItem(_("Pause"))
|
|
menuitem_playpause.set_image(Gtk.Image.new_from_icon_name("media-playback-pause-symbolic", Gtk.IconSize.MENU))
|
|
else:
|
|
menuitem_playpause = Gtk.ImageMenuItem(_("Play"))
|
|
menuitem_playpause.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.MENU))
|
|
menuitem_next = Gtk.ImageMenuItem(_("Next"))
|
|
menuitem_next.set_image(Gtk.Image.new_from_icon_name("media-skip-forward-symbolic", Gtk.IconSize.MENU))
|
|
menuitem_prev = Gtk.ImageMenuItem(_("Previous"))
|
|
menuitem_prev.set_image(Gtk.Image.new_from_icon_name("media-skip-backward-symbolic", Gtk.IconSize.MENU))
|
|
menuitem_quit = Gtk.ImageMenuItem(_("Close"))
|
|
menuitem_quit.set_image(Gtk.Image.new_from_icon_name("window-close-symbolic", Gtk.IconSize.MENU))
|
|
|
|
menuitem_star = self.get_rating_menuitem()
|
|
if menuitem_star:
|
|
self.menu.append(menuitem_star)
|
|
|
|
menuitem_playpause.connect("activate", self.play)
|
|
menuitem_next.connect("activate", self.next)
|
|
menuitem_prev.connect("activate", self.previous)
|
|
menuitem_quit.connect("activate", self.quit)
|
|
|
|
self.menu.append(menuitem_playpause)
|
|
self.menu.append(menuitem_next)
|
|
self.menu.append(menuitem_prev)
|
|
self.menu.append(menuitem_quit)
|
|
|
|
self.menu.show_all()
|
|
|
|
def set_menu_css(self):
|
|
"""
|
|
Sets style for popup menu, hides hover background for stars
|
|
"""
|
|
|
|
#Prevent background color when mouse hovers
|
|
screen = Gdk.Screen.get_default()
|
|
css_provider = Gtk.CssProvider()
|
|
|
|
# This line breaks the plugin with Rhythmbox 3, so I've commented it out. Seems to have absolutely
|
|
# no effect on the plugin.
|
|
# css_provider.load_from_data("GtkMenuItem { border:@bg_color; background:@bg_color; } GtkMenuItem:hover { background:@selected_bg_color; } GtkWidget{ border: @bg_color; } #starMenu:hover { color:@fg_color;background: @bg_color; -unico-inner-stroke-width: 0; }")
|
|
|
|
context = Gtk.StyleContext()
|
|
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
|
|
|
|
|
def get_rating_menuitem(self):
|
|
"""
|
|
Gets a Gtk.MenuItem with the current song's ratings in filled stars
|
|
"""
|
|
|
|
menuitem_star = Gtk.MenuItem(self.get_stars_markup(0,5))
|
|
self.star_value = self.get_song_rating()
|
|
label = menuitem_star.get_children()[0]
|
|
label.set_markup(self.get_stars_markup(self.star_value,5))
|
|
|
|
menuitem_star.set_name('starMenu')
|
|
|
|
menuitem_star.connect("motion_notify_event", self.on_star_mouseover)
|
|
menuitem_star.connect("button_press_event", self.on_star_click)
|
|
menuitem_star.connect("leave_notify_event", self.on_star_mouseout)
|
|
|
|
if self.star_value >= 0:
|
|
return menuitem_star
|
|
else:
|
|
return None
|
|
|
|
def get_song_rating(self):
|
|
"""
|
|
Gets the current song's user rating from Rhythmbox.
|
|
"""
|
|
current_entry = self.shell.props.shell_player.get_playing_entry()
|
|
|
|
if current_entry:
|
|
return int(current_entry.get_double(RB.RhythmDBPropType.RATING))
|
|
else:
|
|
return -1
|
|
|
|
def on_star_click(self, widget, event):
|
|
"""
|
|
Method called when stars are clicked on. Determines chosen stars and sets song rating.
|
|
"""
|
|
label = widget.get_children()[0]
|
|
self.star_value = self.get_chosen_stars(label, event.x)
|
|
self.set_song_rating(self.star_value)
|
|
|
|
def set_song_rating(self, rating):
|
|
"""
|
|
Sets the current song rating in Rhythmbox.
|
|
"""
|
|
current_entry = self.shell.props.shell_player.get_playing_entry()
|
|
self.db.entry_set(current_entry, RB.RhythmDBPropType.RATING, rating)
|
|
|
|
|
|
def get_chosen_stars(self, label, mouseX):
|
|
"""
|
|
Calculates the number of chosen stars to show based on the mouse's X position
|
|
"""
|
|
star_width = int(label.get_layout().get_pixel_size()[0]/5)
|
|
chosen = math.ceil((mouseX-label.get_layout_offsets()[0])/star_width)
|
|
if chosen <= 0:
|
|
chosen = 0
|
|
|
|
if chosen >= 5:
|
|
chosen = 5
|
|
|
|
return chosen
|
|
|
|
def on_star_mouseout(self, widget, event):
|
|
"""
|
|
Method called when mouse leaves the rating stars. Resets stars to original value.
|
|
"""
|
|
label = widget.get_children()[0]
|
|
label.set_markup(self.get_stars_markup(self.star_value, 5))
|
|
|
|
|
|
def on_star_mouseover(self, widget, event):
|
|
"""
|
|
Method called when mouse hovers over the rating stars. Shows filled stars as mouse hovers.
|
|
"""
|
|
label = widget.get_children()[0]
|
|
label.set_markup(self.get_stars_markup(self.get_chosen_stars(label,event.x), 5))
|
|
|
|
def get_stars_markup(self, filled_stars, total_stars):
|
|
"""
|
|
Gets the Pango Markup for the star rating label
|
|
"""
|
|
|
|
if filled_stars is None or filled_stars <= 0:
|
|
filled_stars = 0
|
|
|
|
if filled_stars >= total_stars:
|
|
filled_stars = total_stars
|
|
|
|
filled_stars = int(math.ceil(filled_stars))
|
|
total_stars = int(total_stars)
|
|
|
|
starString = '★' * filled_stars + '☆' * (total_stars-filled_stars)
|
|
return "<span size='x-large' foreground='#000000'>" + starString + "</span>"
|
|
|
|
def toggle_player_visibility(self, icon, event, data = None):
|
|
"""
|
|
Toggles visibility of Rhythmbox player
|
|
"""
|
|
if event.button == 1: # left button
|
|
if self.wind.get_visible():
|
|
self.wind.hide()
|
|
else:
|
|
self.wind.show()
|
|
self.wind.present()
|
|
elif event.button == 2: # middle button
|
|
self.player.do_next()
|
|
|
|
def play(self, widget):
|
|
"""
|
|
Starts playing
|
|
"""
|
|
try:
|
|
self.player.playpause() # does nothing argument
|
|
except:
|
|
# Rhythmbox 3.3 support
|
|
self.player.playpause(True)
|
|
|
|
def next(self, widget):
|
|
"""
|
|
Goes to next song
|
|
"""
|
|
self.player.do_next()
|
|
|
|
def previous(self, widget):
|
|
"""
|
|
Goes to previous song
|
|
"""
|
|
self.player.do_previous()
|
|
|
|
def quit(self, widget):
|
|
"""
|
|
Exits Rhythmbox
|
|
"""
|
|
self.shell.quit()
|
|
|
|
def hide_on_delete(self, widget, event):
|
|
self.wind.hide()
|
|
return True # don't actually delete
|
|
|
|
def on_playing_changed(self, player, playing):
|
|
"""
|
|
Sets icon and tooltip when playing status changes
|
|
"""
|
|
|
|
self.playing = playing
|
|
|
|
if playing:
|
|
self.icon.set_from_file(self.play_icon)
|
|
current_entry = self.shell.props.shell_player.get_playing_entry()
|
|
self.set_tooltip_text("%s\n%s" % (current_entry.get_string(RB.RhythmDBPropType.ARTIST), current_entry.get_string(RB.RhythmDBPropType.TITLE)))
|
|
else:
|
|
self.icon.set_from_file(self.rhythmbox_icon)
|
|
self.set_tooltip_text("Rhythmbox")
|
|
|
|
def set_tooltip_text(self, message=""):
|
|
"""
|
|
Sets tooltip to given message
|
|
"""
|
|
self.icon.set_tooltip_text(message)
|
|
|
|
def do_activate(self):
|
|
"""
|
|
Called when the plugin is activated
|
|
"""
|
|
self.shell = self.object
|
|
self.wind = self.shell.get_property("window")
|
|
self.player = self.shell.props.shell_player
|
|
self.db = self.shell.props.db
|
|
self.playing = False
|
|
|
|
self.wind.connect("delete-event", self.hide_on_delete)
|
|
self.create_popup_menu()
|
|
|
|
self.icon = Gtk.StatusIcon()
|
|
self.icon.set_from_file(self.rhythmbox_icon)
|
|
self.icon.connect("scroll-event", self.on_scroll)
|
|
self.icon.connect("popup-menu", self.show_popup_menu)
|
|
self.icon.connect("button-press-event", self.toggle_player_visibility)
|
|
self.player.connect("playing-changed", self.on_playing_changed)
|
|
|
|
self.set_tooltip_text("Rhythmbox")
|
|
|
|
def on_scroll(self, widget, event):
|
|
"""
|
|
Lowers or raises Rhythmbox's volume
|
|
"""
|
|
vol = round(self.player.get_volume()[1],1)
|
|
|
|
if event.direction == Gdk.ScrollDirection.UP:
|
|
vol+=0.1
|
|
elif event.direction == Gdk.ScrollDirection.DOWN:
|
|
vol-=0.1
|
|
|
|
if vol <= 0:
|
|
vol = 0
|
|
|
|
if vol >=1:
|
|
vol = 1
|
|
|
|
self.player.set_volume(vol)
|
|
|
|
|
|
def do_deactivate(self):
|
|
"""
|
|
Called when plugin is deactivated
|
|
"""
|
|
self.icon.set_visible(False)
|
|
del self.icon
|