Formatting
Esse commit está contido em:
+345
-351
@@ -31,6 +31,7 @@ from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Gio, Peas
|
||||
from gi.repository import RB
|
||||
|
||||
import gettext
|
||||
|
||||
gettext.install('rhythmbox', RB.locale_dir())
|
||||
|
||||
import urllib.request
|
||||
@@ -44,379 +45,372 @@ THROBBER = 'process-working'
|
||||
ASPECT_RATIO_MIN = 0.9
|
||||
ASPECT_RATIO_MAX = 1.1
|
||||
|
||||
def merge_pixbufs (old_pb, new_pb, reserve_pb, step, width, height, mode=GdkPixbuf.InterpType.BILINEAR):
|
||||
if width <= 1 and height <= 1:
|
||||
return None
|
||||
if old_pb is None:
|
||||
if new_pb is None:
|
||||
return reserve_pb
|
||||
else:
|
||||
return new_pb.scale_simple (width, height, mode)
|
||||
elif step == 0.0:
|
||||
return old_pb.scale_simple (width, height, mode)
|
||||
elif new_pb is None:
|
||||
if reserve_pb is None:
|
||||
return None
|
||||
new_pb = reserve_pb
|
||||
sw, sh = (float (width)) / new_pb.props.width, (float (height)) / new_pb.props.height
|
||||
alpha = int (step * 255)
|
||||
ret = old_pb.scale_simple (width, height, mode)
|
||||
new_pb.composite (ret, 0, 0, width, height, 0, 0, sw, sh, mode, alpha)
|
||||
return ret
|
||||
|
||||
def merge_with_background (pixbuf, bgcolor, pad_if_not_near_square):
|
||||
|
||||
if pixbuf is None:
|
||||
return pixbuf
|
||||
has_alpha = pixbuf.get_has_alpha ()
|
||||
width, height = pixbuf.props.width, pixbuf.props.height
|
||||
if pad_if_not_near_square and (height < width * ASPECT_RATIO_MIN or
|
||||
height > width * ASPECT_RATIO_MAX):
|
||||
rw, rh = max (width, height), max (width, height)
|
||||
left, top = (rw - width) // 2, (rh - height) // 2
|
||||
else:
|
||||
if not has_alpha:
|
||||
return pixbuf
|
||||
rw, rh, left, top = width, height, 0, 0
|
||||
ret = GdkPixbuf.Pixbuf.new (GdkPixbuf.Colorspace.RGB, False, 8, rw, rh)
|
||||
ret.fill ((int(bgcolor.red * 255) << 24) | (int(bgcolor.green * 255) << 16) | (int(bgcolor.blue * 255) << 8) | int(bgcolor.alpha * 255))
|
||||
if has_alpha:
|
||||
pixbuf.composite (ret, left, top, width, height, left, top, 1.0, 1.0, GdkPixbuf.InterpType.NEAREST, 255)
|
||||
else:
|
||||
pixbuf.copy_area (0, 0, width, height, ret, left, top)
|
||||
return ret
|
||||
|
||||
class FadingImage (Gtk.Misc):
|
||||
__gsignals__ = {
|
||||
'get-max-size' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_INT, ())
|
||||
}
|
||||
def __init__ (self, missing_image):
|
||||
Gtk.Misc.__init__(self)
|
||||
self.sc_id = self.connect('screen-changed', self.screen_changed)
|
||||
self.sa_id = self.connect('size-allocate', self.size_allocate_cb)
|
||||
self.resize_id, self.fade_id, self.anim_id = 0, 0, 0
|
||||
self.missing_image = missing_image
|
||||
self.size = 100
|
||||
self.screen_changed (self, None)
|
||||
self.old_pixbuf, self.new_pixbuf = None, None
|
||||
self.merged_pixbuf, self.missing_pixbuf = None, None
|
||||
self.fade_step = 0.0
|
||||
self.anim, self.anim_frames, self.anim_size = None, None, 0
|
||||
|
||||
def disconnect_handlers (self):
|
||||
self.disconnect(self.sc_id)
|
||||
self.disconnect(self.sa_id)
|
||||
self.icon_theme.disconnect(self.tc_id)
|
||||
for id in self.resize_id, self.fade_id, self.anim_id:
|
||||
if id != 0:
|
||||
GObject.source_remove (id)
|
||||
|
||||
def screen_changed (self, widget, old_screen):
|
||||
if old_screen:
|
||||
self.icon_theme.disconnect (self.tc_id)
|
||||
self.icon_theme = Gtk.IconTheme.get_for_screen (self.get_screen ())
|
||||
self.tc_id = self.icon_theme.connect ('changed', self.theme_changed)
|
||||
self.theme_changed (self.icon_theme)
|
||||
|
||||
def reload_anim_frames (self):
|
||||
icon_info = self.icon_theme.lookup_icon (THROBBER, -1, 0)
|
||||
size = icon_info.get_base_size ()
|
||||
icon = GdkPixbuf.Pixbuf.new_from_file (icon_info.get_filename ())
|
||||
self.anim_frames = [ # along, then down
|
||||
icon.new_subpixbuf (x * size, y * size, size, size)
|
||||
for y in range (int (icon.props.height / size))
|
||||
for x in range (int (icon.props.width / size))]
|
||||
self.anim_size = size
|
||||
|
||||
def theme_changed (self, icon_theme):
|
||||
try:
|
||||
self.reload_anim_frames ()
|
||||
except Exception as e:
|
||||
warn ("Throbber animation not loaded: %s" % e, Warning)
|
||||
self.reload_util_pixbufs ()
|
||||
|
||||
def reload_util_pixbufs (self):
|
||||
if self.size <= 1:
|
||||
return
|
||||
try:
|
||||
missing_pixbuf = self.icon_theme.load_icon (ART_MISSING_ICON, self.size, 0)
|
||||
except:
|
||||
try:
|
||||
missing_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size (self.missing_image, self.size, self.size)
|
||||
except Exception as e:
|
||||
warn ("Missing artwork icon not found: %s" % e, Warning)
|
||||
return
|
||||
|
||||
bgcolor = self.get_style_context().get_background_color(Gtk.StateType.NORMAL)
|
||||
self.missing_pixbuf = merge_with_background (missing_pixbuf, bgcolor, False)
|
||||
|
||||
def size_allocate_cb (self, widget, allocation):
|
||||
if self.resize_id == 0:
|
||||
self.resize_id = GObject.idle_add (self.after_resize)
|
||||
|
||||
max_size = self.emit ('get-max-size')
|
||||
self.size = min (self.get_allocated_width (), max_size)
|
||||
|
||||
def after_resize (self):
|
||||
self.reload_util_pixbufs ()
|
||||
self.merged_pixbuf = None
|
||||
self.resize_id = 0
|
||||
self.queue_draw ()
|
||||
return False
|
||||
|
||||
def do_get_request_mode(self):
|
||||
return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
|
||||
|
||||
def do_get_preferred_width(self):
|
||||
# maybe set minimum width here?
|
||||
return (0, 0)
|
||||
|
||||
def do_get_preferred_height_for_width(self, width):
|
||||
max_size = self.emit ('get-max-size')
|
||||
size = min(self.get_allocated_width(), max_size)
|
||||
return (size, size)
|
||||
|
||||
def do_draw (self, cr):
|
||||
if not self.ensure_merged_pixbuf ():
|
||||
return False
|
||||
|
||||
if self.merged_pixbuf.props.width != self.size:
|
||||
draw_pb = self.merged_pixbuf.scale_simple (self.size, self.size, GdkPixbuf.InterpType.NEAREST)
|
||||
else:
|
||||
draw_pb = self.merged_pixbuf
|
||||
|
||||
# center the image if we're wider than we are tall
|
||||
pad = (self.get_allocation().width - self.size) / 2
|
||||
|
||||
left = pad
|
||||
right = pad + self.size
|
||||
top = 0
|
||||
bottom = self.size
|
||||
if right > left and bottom > top:
|
||||
Gdk.cairo_set_source_pixbuf(cr, draw_pb, pad, 0)
|
||||
cr.rectangle(left, top, right - left, bottom - top)
|
||||
cr.fill()
|
||||
|
||||
if self.anim:
|
||||
x, y, w, h = self.anim_rect ()
|
||||
Gdk.cairo_set_source_pixbuf(cr, self.anim, max(0, x), max(0, y))
|
||||
cr.rectangle(max(0, x), max(0, y), w, h)
|
||||
cr.fill()
|
||||
|
||||
return False
|
||||
|
||||
def anim_rect (self):
|
||||
alloc_width = self.get_allocated_width()
|
||||
alloc_height = self.get_allocated_height()
|
||||
return ((alloc_width - self.anim_size) / 2,
|
||||
(alloc_height - self.anim_size) / 2,
|
||||
min (self.anim_size, alloc_width),
|
||||
min (self.anim_size, alloc_height))
|
||||
|
||||
def ensure_merged_pixbuf (self):
|
||||
if self.merged_pixbuf is None:
|
||||
alloc_width = self.get_allocated_width()
|
||||
alloc_height = self.get_allocated_height()
|
||||
self.merged_pixbuf = merge_pixbufs (self.old_pixbuf, self.new_pixbuf, self.missing_pixbuf, self.fade_step, alloc_width, alloc_height)
|
||||
return self.merged_pixbuf
|
||||
|
||||
def render_overlay (self):
|
||||
ret = self.ensure_merged_pixbuf ()
|
||||
if ret and self.anim:
|
||||
if ret is self.missing_pixbuf: ret = ret.copy ()
|
||||
x, y, w, h = self.anim_rect ()
|
||||
self.anim.composite (ret, max (x, 0), max (y, 0), w, h, x, y, 1, 1, GdkPixbuf.InterpType.BILINEAR, 255)
|
||||
return ret
|
||||
|
||||
def fade_art (self, first_time):
|
||||
self.fade_step += 1.0 / FADE_STEPS
|
||||
if self.fade_step > 0.999:
|
||||
self.old_pixbuf = None
|
||||
self.fade_id = 0
|
||||
self.merged_pixbuf = None
|
||||
if first_time:
|
||||
self.fade_id = GObject.timeout_add ((FADE_TOTAL_TIME / FADE_STEPS), self.fade_art, False)
|
||||
return False
|
||||
self.queue_resize ()
|
||||
return (self.fade_step <= 0.999)
|
||||
|
||||
def animation_advance (self, counter, first_time):
|
||||
self.anim = self.anim_frames[counter[0]]
|
||||
counter[0] = (counter[0] + 1) % len(self.anim_frames)
|
||||
x, y, w, h = self.anim_rect ()
|
||||
self.queue_draw_area (max (x, 0), max (y, 0), w, h)
|
||||
if first_time:
|
||||
self.anim_id = GObject.timeout_add (int (1000 / THROBBER_RATE), self.animation_advance, counter, False)
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_current_art (self, pixbuf, working):
|
||||
if self.props.visible and self.get_allocated_width() > 1:
|
||||
self.old_pixbuf = self.render_overlay ()
|
||||
else:
|
||||
self.old_pixbuf = None # don't fade
|
||||
|
||||
bgcolor = self.get_style_context().get_background_color(Gtk.StateType.NORMAL)
|
||||
self.new_pixbuf = merge_with_background (pixbuf, bgcolor, True)
|
||||
self.merged_pixbuf = None
|
||||
self.fade_step = 0.0
|
||||
self.anim = None
|
||||
if self.fade_id != 0:
|
||||
GObject.source_remove (self.fade_id)
|
||||
self.fade_id = 0
|
||||
if self.old_pixbuf is not None:
|
||||
self.fade_id = GObject.timeout_add (working and WORKING_DELAY or (FADE_TOTAL_TIME / FADE_STEPS), self.fade_art, working)
|
||||
if working and self.anim_id == 0 and self.anim_frames:
|
||||
self.anim_id = GObject.timeout_add (WORKING_DELAY, self.animation_advance, [0], True)
|
||||
if not working and self.anim_id != 0:
|
||||
GObject.source_remove (self.anim_id)
|
||||
self.anim_id = 0
|
||||
self.queue_resize ()
|
||||
|
||||
GObject.type_register (FadingImage)
|
||||
def merge_pixbufs(old_pb, new_pb, reserve_pb, step, width, height, mode=GdkPixbuf.InterpType.BILINEAR):
|
||||
if width <= 1 and height <= 1:
|
||||
return None
|
||||
if old_pb is None:
|
||||
if new_pb is None:
|
||||
return reserve_pb
|
||||
else:
|
||||
return new_pb.scale_simple(width, height, mode)
|
||||
elif step == 0.0:
|
||||
return old_pb.scale_simple(width, height, mode)
|
||||
elif new_pb is None:
|
||||
if reserve_pb is None:
|
||||
return None
|
||||
new_pb = reserve_pb
|
||||
sw, sh = (float(width)) / new_pb.props.width, (float(height)) / new_pb.props.height
|
||||
alpha = int(step * 255)
|
||||
ret = old_pb.scale_simple(width, height, mode)
|
||||
new_pb.composite(ret, 0, 0, width, height, 0, 0, sw, sh, mode, alpha)
|
||||
return ret
|
||||
|
||||
|
||||
class ArtDisplayWidget (FadingImage):
|
||||
|
||||
def __init__ (self, missing_image):
|
||||
super (ArtDisplayWidget, self).__init__ (missing_image)
|
||||
self.set_padding (0, 5)
|
||||
self.qt_id = self.connect ('query-tooltip', self.query_tooltip)
|
||||
self.props.has_tooltip = True
|
||||
self.current_entry, self.working = None, False
|
||||
self.current_pixbuf, self.current_uri = None, None
|
||||
|
||||
def disconnect_handlers (self):
|
||||
super (ArtDisplayWidget, self).disconnect_handlers ()
|
||||
|
||||
def query_tooltip (self, widget, x, y, keyboard_mode, tooltip):
|
||||
if (self.tooltip_image, self.tooltip_text) != (None, None):
|
||||
tooltip.set_text(self.tooltip_text)
|
||||
tooltip.set_icon(self.tooltip_image)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def set (self, entry, pixbuf, uri, tooltip_image, tooltip_text, working):
|
||||
self.current_entry = entry
|
||||
self.current_pixbuf = pixbuf
|
||||
self.current_uri = uri
|
||||
self.set_current_art (pixbuf, working)
|
||||
|
||||
self.tooltip_image = None
|
||||
if not self.current_entry:
|
||||
self.tooltip_text = None
|
||||
elif working:
|
||||
self.tooltip_text = _("Searching...")
|
||||
elif (tooltip_image, tooltip_text) != (None, None):
|
||||
self.tooltip_image = tooltip_image
|
||||
self.tooltip_text = tooltip_text
|
||||
else:
|
||||
self.tooltip_text = None
|
||||
def merge_with_background(pixbuf, bgcolor, pad_if_not_near_square):
|
||||
if pixbuf is None:
|
||||
return pixbuf
|
||||
has_alpha = pixbuf.get_has_alpha()
|
||||
width, height = pixbuf.props.width, pixbuf.props.height
|
||||
if pad_if_not_near_square and (height < width * ASPECT_RATIO_MIN or
|
||||
height > width * ASPECT_RATIO_MAX):
|
||||
rw, rh = max(width, height), max(width, height)
|
||||
left, top = (rw - width) // 2, (rh - height) // 2
|
||||
else:
|
||||
if not has_alpha:
|
||||
return pixbuf
|
||||
rw, rh, left, top = width, height, 0, 0
|
||||
ret = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, rw, rh)
|
||||
ret.fill((int(bgcolor.red * 255) << 24) | (int(bgcolor.green * 255) << 16) | (int(bgcolor.blue * 255) << 8) | int(
|
||||
bgcolor.alpha * 255))
|
||||
if has_alpha:
|
||||
pixbuf.composite(ret, left, top, width, height, left, top, 1.0, 1.0, GdkPixbuf.InterpType.NEAREST, 255)
|
||||
else:
|
||||
pixbuf.copy_area(0, 0, width, height, ret, left, top)
|
||||
return ret
|
||||
|
||||
|
||||
GObject.type_register (ArtDisplayWidget)
|
||||
class FadingImage(Gtk.Misc):
|
||||
__gsignals__ = {
|
||||
'get-max-size': (GObject.SIGNAL_RUN_LAST, GObject.TYPE_INT, ())
|
||||
}
|
||||
|
||||
def __init__(self, missing_image):
|
||||
Gtk.Misc.__init__(self)
|
||||
self.sc_id = self.connect('screen-changed', self.screen_changed)
|
||||
self.sa_id = self.connect('size-allocate', self.size_allocate_cb)
|
||||
self.resize_id, self.fade_id, self.anim_id = 0, 0, 0
|
||||
self.missing_image = missing_image
|
||||
self.size = 100
|
||||
self.screen_changed(self, None)
|
||||
self.old_pixbuf, self.new_pixbuf = None, None
|
||||
self.merged_pixbuf, self.missing_pixbuf = None, None
|
||||
self.fade_step = 0.0
|
||||
self.anim, self.anim_frames, self.anim_size = None, None, 0
|
||||
|
||||
def disconnect_handlers(self):
|
||||
self.disconnect(self.sc_id)
|
||||
self.disconnect(self.sa_id)
|
||||
self.icon_theme.disconnect(self.tc_id)
|
||||
for id in self.resize_id, self.fade_id, self.anim_id:
|
||||
if id != 0:
|
||||
GObject.source_remove(id)
|
||||
|
||||
def screen_changed(self, widget, old_screen):
|
||||
if old_screen:
|
||||
self.icon_theme.disconnect(self.tc_id)
|
||||
self.icon_theme = Gtk.IconTheme.get_for_screen(self.get_screen())
|
||||
self.tc_id = self.icon_theme.connect('changed', self.theme_changed)
|
||||
self.theme_changed(self.icon_theme)
|
||||
|
||||
def reload_anim_frames(self):
|
||||
icon_info = self.icon_theme.lookup_icon(THROBBER, -1, 0)
|
||||
size = icon_info.get_base_size()
|
||||
icon = GdkPixbuf.Pixbuf.new_from_file(icon_info.get_filename())
|
||||
self.anim_frames = [ # along, then down
|
||||
icon.new_subpixbuf(x * size, y * size, size, size)
|
||||
for y in range(int(icon.props.height / size))
|
||||
for x in range(int(icon.props.width / size))]
|
||||
self.anim_size = size
|
||||
|
||||
def theme_changed(self, icon_theme):
|
||||
try:
|
||||
self.reload_anim_frames()
|
||||
except Exception as e:
|
||||
warn("Throbber animation not loaded: %s" % e, Warning)
|
||||
self.reload_util_pixbufs()
|
||||
|
||||
def reload_util_pixbufs(self):
|
||||
if self.size <= 1:
|
||||
return
|
||||
try:
|
||||
missing_pixbuf = self.icon_theme.load_icon(ART_MISSING_ICON, self.size, 0)
|
||||
except:
|
||||
try:
|
||||
missing_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(self.missing_image, self.size, self.size)
|
||||
except Exception as e:
|
||||
warn("Missing artwork icon not found: %s" % e, Warning)
|
||||
return
|
||||
|
||||
bgcolor = self.get_style_context().get_background_color(Gtk.StateType.NORMAL)
|
||||
self.missing_pixbuf = merge_with_background(missing_pixbuf, bgcolor, False)
|
||||
|
||||
def size_allocate_cb(self, widget, allocation):
|
||||
if self.resize_id == 0:
|
||||
self.resize_id = GObject.idle_add(self.after_resize)
|
||||
|
||||
max_size = self.emit('get-max-size')
|
||||
self.size = min(self.get_allocated_width(), max_size)
|
||||
|
||||
def after_resize(self):
|
||||
self.reload_util_pixbufs()
|
||||
self.merged_pixbuf = None
|
||||
self.resize_id = 0
|
||||
self.queue_draw()
|
||||
return False
|
||||
|
||||
def do_get_request_mode(self):
|
||||
return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
|
||||
|
||||
def do_get_preferred_width(self):
|
||||
# maybe set minimum width here?
|
||||
return (0, 0)
|
||||
|
||||
def do_get_preferred_height_for_width(self, width):
|
||||
max_size = self.emit('get-max-size')
|
||||
size = min(self.get_allocated_width(), max_size)
|
||||
return (size, size)
|
||||
|
||||
def do_draw(self, cr):
|
||||
if not self.ensure_merged_pixbuf():
|
||||
return False
|
||||
|
||||
if self.merged_pixbuf.props.width != self.size:
|
||||
draw_pb = self.merged_pixbuf.scale_simple(self.size, self.size, GdkPixbuf.InterpType.NEAREST)
|
||||
else:
|
||||
draw_pb = self.merged_pixbuf
|
||||
|
||||
# center the image if we're wider than we are tall
|
||||
pad = (self.get_allocation().width - self.size) / 2
|
||||
|
||||
left = pad
|
||||
right = pad + self.size
|
||||
top = 0
|
||||
bottom = self.size
|
||||
if right > left and bottom > top:
|
||||
Gdk.cairo_set_source_pixbuf(cr, draw_pb, pad, 0)
|
||||
cr.rectangle(left, top, right - left, bottom - top)
|
||||
cr.fill()
|
||||
|
||||
if self.anim:
|
||||
x, y, w, h = self.anim_rect()
|
||||
Gdk.cairo_set_source_pixbuf(cr, self.anim, max(0, x), max(0, y))
|
||||
cr.rectangle(max(0, x), max(0, y), w, h)
|
||||
cr.fill()
|
||||
|
||||
return False
|
||||
|
||||
def anim_rect(self):
|
||||
alloc_width = self.get_allocated_width()
|
||||
alloc_height = self.get_allocated_height()
|
||||
return ((alloc_width - self.anim_size) / 2,
|
||||
(alloc_height - self.anim_size) / 2,
|
||||
min(self.anim_size, alloc_width),
|
||||
min(self.anim_size, alloc_height))
|
||||
|
||||
def ensure_merged_pixbuf(self):
|
||||
if self.merged_pixbuf is None:
|
||||
alloc_width = self.get_allocated_width()
|
||||
alloc_height = self.get_allocated_height()
|
||||
self.merged_pixbuf = merge_pixbufs(self.old_pixbuf, self.new_pixbuf, self.missing_pixbuf, self.fade_step,
|
||||
alloc_width, alloc_height)
|
||||
return self.merged_pixbuf
|
||||
|
||||
def render_overlay(self):
|
||||
ret = self.ensure_merged_pixbuf()
|
||||
if ret and self.anim:
|
||||
if ret is self.missing_pixbuf: ret = ret.copy()
|
||||
x, y, w, h = self.anim_rect()
|
||||
self.anim.composite(ret, max(x, 0), max(y, 0), w, h, x, y, 1, 1, GdkPixbuf.InterpType.BILINEAR, 255)
|
||||
return ret
|
||||
|
||||
def fade_art(self, first_time):
|
||||
self.fade_step += 1.0 / FADE_STEPS
|
||||
if self.fade_step > 0.999:
|
||||
self.old_pixbuf = None
|
||||
self.fade_id = 0
|
||||
self.merged_pixbuf = None
|
||||
if first_time:
|
||||
self.fade_id = GObject.timeout_add((FADE_TOTAL_TIME / FADE_STEPS), self.fade_art, False)
|
||||
return False
|
||||
self.queue_resize()
|
||||
return (self.fade_step <= 0.999)
|
||||
|
||||
def animation_advance(self, counter, first_time):
|
||||
self.anim = self.anim_frames[counter[0]]
|
||||
counter[0] = (counter[0] + 1) % len(self.anim_frames)
|
||||
x, y, w, h = self.anim_rect()
|
||||
self.queue_draw_area(max(x, 0), max(y, 0), w, h)
|
||||
if first_time:
|
||||
self.anim_id = GObject.timeout_add(int(1000 / THROBBER_RATE), self.animation_advance, counter, False)
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_current_art(self, pixbuf, working):
|
||||
if self.props.visible and self.get_allocated_width() > 1:
|
||||
self.old_pixbuf = self.render_overlay()
|
||||
else:
|
||||
self.old_pixbuf = None # don't fade
|
||||
|
||||
bgcolor = self.get_style_context().get_background_color(Gtk.StateType.NORMAL)
|
||||
self.new_pixbuf = merge_with_background(pixbuf, bgcolor, True)
|
||||
self.merged_pixbuf = None
|
||||
self.fade_step = 0.0
|
||||
self.anim = None
|
||||
if self.fade_id != 0:
|
||||
GObject.source_remove(self.fade_id)
|
||||
self.fade_id = 0
|
||||
if self.old_pixbuf is not None:
|
||||
self.fade_id = GObject.timeout_add(working and WORKING_DELAY or (FADE_TOTAL_TIME / FADE_STEPS),
|
||||
self.fade_art, working)
|
||||
if working and self.anim_id == 0 and self.anim_frames:
|
||||
self.anim_id = GObject.timeout_add(WORKING_DELAY, self.animation_advance, [0], True)
|
||||
if not working and self.anim_id != 0:
|
||||
GObject.source_remove(self.anim_id)
|
||||
self.anim_id = 0
|
||||
self.queue_resize()
|
||||
|
||||
|
||||
class ArtDisplayPlugin (GObject.GObject, Peas.Activatable):
|
||||
__gtype_name__ = 'ArtDisplayPlugin'
|
||||
object = GObject.property(type=GObject.GObject)
|
||||
GObject.type_register(FadingImage)
|
||||
|
||||
def __init__ (self):
|
||||
GObject.GObject.__init__ (self)
|
||||
|
||||
def do_activate (self):
|
||||
shell = self.object
|
||||
sp = shell.props.shell_player
|
||||
self.player_cb_ids = (
|
||||
sp.connect ('playing-song-changed', self.playing_entry_changed),
|
||||
sp.connect ('playing-changed', self.playing_changed)
|
||||
)
|
||||
self.art_store = RB.ExtDB(name="album-art")
|
||||
self.art_widget = ArtDisplayWidget (rb.find_plugin_file (self, ART_MISSING_ICON + ".svg"))
|
||||
self.art_widget.connect ('get-max-size', self.get_max_art_size)
|
||||
self.art_widget.connect ('button-press-event', self.on_button_press)
|
||||
self.art_container = Gtk.VBox ()
|
||||
self.art_container.pack_start (self.art_widget, True, True, 6)
|
||||
shell.add_widget (self.art_container, RB.ShellUILocation.SIDEBAR, False, True)
|
||||
self.current_entry, self.current_pixbuf = None, None
|
||||
self.playing_entry_changed (sp, sp.get_playing_entry ())
|
||||
class ArtDisplayWidget(FadingImage):
|
||||
def __init__(self, missing_image):
|
||||
super(ArtDisplayWidget, self).__init__(missing_image)
|
||||
self.set_padding(0, 5)
|
||||
self.qt_id = self.connect('query-tooltip', self.query_tooltip)
|
||||
self.props.has_tooltip = True
|
||||
self.current_entry, self.working = None, False
|
||||
self.current_pixbuf, self.current_uri = None, None
|
||||
|
||||
def do_deactivate (self):
|
||||
def disconnect_handlers(self):
|
||||
super(ArtDisplayWidget, self).disconnect_handlers()
|
||||
|
||||
shell = self.object
|
||||
sp = shell.props.shell_player
|
||||
for id in self.player_cb_ids:
|
||||
sp.disconnect (id)
|
||||
self.player_cb_ids = ()
|
||||
def query_tooltip(self, widget, x, y, keyboard_mode, tooltip):
|
||||
if (self.tooltip_image, self.tooltip_text) != (None, None):
|
||||
tooltip.set_text(self.tooltip_text)
|
||||
tooltip.set_icon(self.tooltip_image)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
shell.remove_widget (self.art_container, RB.ShellUILocation.SIDEBAR)
|
||||
self.art_widget.disconnect_handlers ()
|
||||
self.art_widget = None
|
||||
self.art_container = None
|
||||
def set(self, entry, pixbuf, uri, tooltip_image, tooltip_text, working):
|
||||
self.current_entry = entry
|
||||
self.current_pixbuf = pixbuf
|
||||
self.current_uri = uri
|
||||
self.set_current_art(pixbuf, working)
|
||||
|
||||
def playing_changed (self, sp, playing):
|
||||
self.set_entry(sp.get_playing_entry ())
|
||||
self.tooltip_image = None
|
||||
if not self.current_entry:
|
||||
self.tooltip_text = None
|
||||
elif working:
|
||||
self.tooltip_text = _("Searching...")
|
||||
elif (tooltip_image, tooltip_text) != (None, None):
|
||||
self.tooltip_image = tooltip_image
|
||||
self.tooltip_text = tooltip_text
|
||||
else:
|
||||
self.tooltip_text = None
|
||||
|
||||
def playing_entry_changed (self, sp, entry):
|
||||
self.set_entry(entry)
|
||||
|
||||
def set_entry (self, entry):
|
||||
if rb.entry_equal(entry, self.current_entry):
|
||||
return
|
||||
GObject.type_register(ArtDisplayWidget)
|
||||
|
||||
self.art_widget.set (entry, None, None, None, None, True)
|
||||
self.art_container.show_all ()
|
||||
self.current_entry = entry
|
||||
self.current_pixbuf = None
|
||||
|
||||
if entry is not None:
|
||||
key = entry.create_ext_db_key (RB.RhythmDBPropType.ALBUM)
|
||||
self.art_store.request(key, self.art_store_request_cb, entry)
|
||||
class ArtDisplayPlugin(GObject.GObject, Peas.Activatable):
|
||||
__gtype_name__ = 'ArtDisplayPlugin'
|
||||
object = GObject.property(type=GObject.GObject)
|
||||
|
||||
def art_store_request_cb(self, *args): # key, filename, data, entry):
|
||||
|
||||
# rhythmbox 3.2 breaks the API -
|
||||
# since there isnt a way to say "we are using RB 3.2" we have to hack this...
|
||||
# need to find the args filename(str), data (pixbuf) and entry (RB.RhythmDBEntry) manually
|
||||
data = None
|
||||
for arg in args:
|
||||
if isinstance(arg, str):
|
||||
filename = arg
|
||||
if isinstance(arg, GdkPixbuf.Pixbuf):
|
||||
data = arg
|
||||
if isinstance(arg, RB.RhythmDBEntry):
|
||||
entry = arg
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
if rb.entry_equal(entry, self.current_entry) is False:
|
||||
# track changed while we were searching
|
||||
return
|
||||
def do_activate(self):
|
||||
shell = self.object
|
||||
sp = shell.props.shell_player
|
||||
self.player_cb_ids = (
|
||||
sp.connect('playing-song-changed', self.playing_entry_changed),
|
||||
sp.connect('playing-changed', self.playing_changed)
|
||||
)
|
||||
self.art_store = RB.ExtDB(name="album-art")
|
||||
self.art_widget = ArtDisplayWidget(rb.find_plugin_file(self, ART_MISSING_ICON + ".svg"))
|
||||
self.art_widget.connect('get-max-size', self.get_max_art_size)
|
||||
self.art_widget.connect('button-press-event', self.on_button_press)
|
||||
self.art_container = Gtk.VBox()
|
||||
self.art_container.pack_start(self.art_widget, True, True, 6)
|
||||
shell.add_widget(self.art_container, RB.ShellUILocation.SIDEBAR, False, True)
|
||||
self.current_entry, self.current_pixbuf = None, None
|
||||
self.playing_entry_changed(sp, sp.get_playing_entry())
|
||||
|
||||
if isinstance(data, GdkPixbuf.Pixbuf):
|
||||
self.current_pixbuf = data
|
||||
uri = "file://" + urllib.request.pathname2url(filename)
|
||||
self.art_widget.set (entry, self.current_pixbuf, uri, None, None, False)
|
||||
else:
|
||||
self.art_widget.set (entry, None, None, None, None, False)
|
||||
def do_deactivate(self):
|
||||
|
||||
def get_max_art_size (self, widget):
|
||||
# limit the art image to a third of the window height to prevent it from
|
||||
# forcing the window to resize, obscuring everything else, and so on.
|
||||
shell = self.object
|
||||
(width, height) = shell.props.window.get_size()
|
||||
return height / 3
|
||||
shell = self.object
|
||||
sp = shell.props.shell_player
|
||||
for id in self.player_cb_ids:
|
||||
sp.disconnect(id)
|
||||
self.player_cb_ids = ()
|
||||
|
||||
def on_button_press (self, widget, event):
|
||||
# on double clicks, open the cover image (if there is one) in the default
|
||||
# image viewer
|
||||
shell.remove_widget(self.art_container, RB.ShellUILocation.SIDEBAR)
|
||||
self.art_widget.disconnect_handlers()
|
||||
self.art_widget = None
|
||||
self.art_container = None
|
||||
|
||||
doubleclick = Gdk.EventType._2BUTTON_PRESS
|
||||
if event.type != doubleclick or event.button != 1:
|
||||
return
|
||||
def playing_changed(self, sp, playing):
|
||||
self.set_entry(sp.get_playing_entry())
|
||||
|
||||
if self.art_widget.current_uri is None:
|
||||
return
|
||||
def playing_entry_changed(self, sp, entry):
|
||||
self.set_entry(entry)
|
||||
|
||||
f = Gio.file_new_for_uri(self.art_widget.current_uri)
|
||||
shell = self.object
|
||||
Gtk.show_uri(shell.props.window.get_screen(), f.get_uri(), event.time)
|
||||
def set_entry(self, entry):
|
||||
if rb.entry_equal(entry, self.current_entry):
|
||||
return
|
||||
|
||||
self.art_widget.set(entry, None, None, None, None, True)
|
||||
self.art_container.show_all()
|
||||
self.current_entry = entry
|
||||
self.current_pixbuf = None
|
||||
|
||||
if entry is not None:
|
||||
key = entry.create_ext_db_key(RB.RhythmDBPropType.ALBUM)
|
||||
self.art_store.request(key, self.art_store_request_cb, entry)
|
||||
|
||||
def art_store_request_cb(self, key, filename, data, entry):
|
||||
if rb.entry_equal(entry, self.current_entry) is False:
|
||||
# track changed while we were searching
|
||||
return
|
||||
|
||||
if isinstance(data, GdkPixbuf.Pixbuf):
|
||||
self.current_pixbuf = data
|
||||
uri = "file://" + urllib.request.pathname2url(filename)
|
||||
self.art_widget.set(entry, self.current_pixbuf, uri, None, None, False)
|
||||
else:
|
||||
self.art_widget.set(entry, None, None, None, None, False)
|
||||
|
||||
def get_max_art_size(self, widget):
|
||||
# limit the art image to a third of the window height to prevent it from
|
||||
# forcing the window to resize, obscuring everything else, and so on.
|
||||
shell = self.object
|
||||
(width, height) = shell.props.window.get_size()
|
||||
return height / 3
|
||||
|
||||
def on_button_press(self, widget, event):
|
||||
# on double clicks, open the cover image (if there is one) in the default
|
||||
# image viewer
|
||||
|
||||
doubleclick = Gdk.EventType._2BUTTON_PRESS
|
||||
if event.type != doubleclick or event.button != 1:
|
||||
return
|
||||
|
||||
if self.art_widget.current_uri is None:
|
||||
return
|
||||
|
||||
f = Gio.file_new_for_uri(self.art_widget.current_uri)
|
||||
shell = self.object
|
||||
Gtk.show_uri(shell.props.window.get_screen(), f.get_uri(), event.time)
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário