import pango
import cairo
import gtk
import screenlets
import cairo
import string
import xml.dom.minidom
class Skin:
items = []
width = 500
height = 200
name = ""
playername_item = False
albumcover_item = False
titlename_item = False
artistname_item = False
albumname_item = False
playercontrols_item = False
def __init__(self, xmlfile, obj):
self.parseSkinXML(xmlfile, obj.theme, obj.window, obj.player)
def add_item(self, item):
self.items.append(item)
def cleanup(self):
self.items[:] = []
if self.playercontrols_item:
t = self.playercontrols_item
t.remove_old()
t.window.remove(t.window.get_child())
def set_scale(self, scale):
if self.playercontrols_item:
self.playercontrols_item.set_scale(scale)
def set_title(self, text):
if self.titlename_item: self.titlename_item.set_text(text)
def set_artist(self, text):
if self.artistname_item: self.artistname_item.set_text(text)
def set_album(self, text):
if self.albumname_item: self.albumname_item.set_text(text)
def set_player(self, text):
if self.playername_item:
self.playername_item.set_text(text[:text.find("API")])
def set_albumcover(self, pixbuf):
if self.albumcover_item: self.albumcover_item.set_image(pixbuf)
def parseSkinXML(self, filename, theme, window, player):
dom = xml.dom.minidom.parse(filename)
skinobj = dom.getElementsByTagName("skin")[0]
self.name = skinobj.getAttribute("name")
self.width = int(skinobj.getAttribute("width"))
self.height = int(skinobj.getAttribute("height"))
for domitem in skinobj.childNodes:
if domitem.nodeType == 1:
type = domitem.nodeName
x = int(domitem.getAttribute("x"))
y = int(domitem.getAttribute("y"))
w = domitem.getAttribute("width")
h = domitem.getAttribute("height")
display = domitem.getAttribute("display")
if w: w = int(w)
if h: h = int(h)
item = False
if type == "image":
src = theme[domitem.getAttribute("src")]
item = ImageItem(src, x, y, w, h, display)
elif type == "albumcover":
item = ImageItem(None, x, y, w, h, display)
self.albumcover_item = item
elif type in ("titlename", "artistname", "albumname", "playername"):
font = domitem.getAttribute("font")
color = domitem.getAttribute("color")
shadowcolor = domitem.getAttribute("shadowcolor")
align = domitem.getAttribute("align")
maxchars = domitem.getAttribute("maxchars")
direction = domitem.getAttribute("direction")
scrollstr = domitem.getAttribute("scroll")
scroll = False
if scrollstr and (scrollstr=="1" or string.lower(scrollstr)=="true"):
scroll = True
item = TextItem("", font, color, x, y, scroll, display, shadowcolor,
maxchars, align, direction, w, h)
exec "self."+type+"_item = item"
elif type == "playercontrols":
xd = float(x)/self.width
yd = float(y)/self.height
spacing = domitem.getAttribute("spacing")
item = PlayerControls(int(spacing), xd, yd, theme, window, player, display)
for controlitem in domitem.childNodes:
if controlitem.nodeType == 1:
cw = controlitem.getAttribute("width")
ch = controlitem.getAttribute("height")
ctype = controlitem.nodeName
eval("item.add_%s(%s,%s)" %(ctype,cw,ch))
self.playercontrols_item = item
if item: self.items.append(item)
class GenericItem:
type = ""
x = 0
y = 0
width = 0
height = 0
display = True
regular_updates = False
def __init__(self, type, x, y, w, h, display):
self.type = type
self.x = x
self.y = y
self.width = w
self.height = h
if display: self.display = display
def draw(self, ctx):
pass
class ImageItem(GenericItem):
imgobj = None
image_type = None
def __init__(self, imgobj, x, y, w, h, display):
GenericItem.__init__(self, "image", x, y, w, h, display)
self.set_image(imgobj)
def set_image(self, imgobj):
self.imgobj = imgobj
cl = imgobj.__class__.__name__
mod = imgobj.__class__.__module__
if cl=='Handle': self.image_type = "rsvg"
elif cl=='ImageSurface': self.image_type = "surface"
elif cl=='Pixbuf': self.image_type = "pixbuf"
else: self.image_type = None
def draw(self, ctx):
if self.imgobj != None:
if self.image_type == "rsvg":
size=self.imgobj.get_dimension_data()
if size:
ctx.save()
ctx.move_to(self.x,self.y)
if self.width and self.height:
ctx.scale(float(self.width)/size[0], float(self.height)/size[1])
self.imgobj.render_cairo(ctx)
ctx.restore()
elif self.image_type == "surface":
pattern = cairo.SurfacePattern(self.imgobj)
matrix = cairo.Matrix()
if self.width and self.height:
iw = float(self.imgobj.get_width())
ih = float(self.imgobj.get_height())
matrix.scale(iw/self.width, ih/self.height)
matrix.translate(-self.x, -self.y)
pattern.set_matrix(matrix)
ctx.set_source(pattern)
ctx.paint()
elif self.image_type == "pixbuf":
ctx.save()
pw = float(self.imgobj.get_width())
ph = float(self.imgobj.get_height())
ctx.translate(self.x, self.y)
pixscaled = self.imgobj
if self.width and self.height:
pixscaled = self.imgobj.scale_simple(self.width,self.height,1)
ctx.set_source_pixbuf(pixscaled, 0, 0)
ctx.paint()
ctx.restore()
class TextItem(GenericItem):
font = "Sans 10"
text = ""
align = ""
color = (0,0,0,1)
shadowcolor = False
rotation = False
layout = 0
maxchars = False
scroll_desired = False
scrolling = False
cur_index = 0
scroll_wait = 10
scrollfiller = " "
def __init__(self, text, font, color, x, y, scroll_desired=False, display="",
shadowcolor="", maxchars="", align="left",
direction = "", w=0, h=0):
GenericItem.__init__(self, "text", x, y, w, h, display)
if font: self.font = font
if color: self.color = self.get_rgba(color)
if shadowcolor: self.shadowcolor = self.get_rgba(shadowcolor)
if maxchars: self.maxchars = int(maxchars)
if direction: self.rotation = self.get_rotation_from_direction(direction)
self.scroll_desired = scroll_desired
self.align = align
self.set_text(text)
def set_text(self, text):
self.cur_index = 0
self.scrolling = False
self.regular_updates = False
if self.maxchars and len(text) > self.maxchars:
if self.scroll_desired:
self.regular_updates=True
self.scrolling = True
else:
text = text[0:self.maxchars]+"..."
self.text = text
def draw(self, ctx):
text = self.text
if self.scrolling:
if self.cur_index > self.scroll_wait:
text = self.text + self.scrollfiller + self.text
tmp = self.cur_index - self.scroll_wait
text = text[tmp:(tmp+ self.maxchars)]
text = text[0:self.maxchars]
self.cur_index += 1
if self.cur_index == len(self.text+self.scrollfiller)+self.scroll_wait: self.cur_index = 0
ctx.save()
if self.shadowcolor:
self.draw_text(ctx, text, self.shadowcolor, self.x+1, self.y+1)
self.draw_text(ctx, text, self.color, self.x, self.y)
ctx.restore()
def draw_text(self, ctx, text, c, x, y):
ctx.save()
p_layout = ctx.create_layout()
p_fdesc = pango.FontDescription(self.font)
p_layout.set_font_description(p_fdesc)
p_layout.set_text(text)
self.set_clip_region(ctx, p_layout, x, y)
if text:
(x,y) = self.get_xy_from_alignment(p_layout, x, y)
ctx.move_to(x,y)
if self.rotation:
ctx.translate(-x,-y)
ctx.rotate(self.rotation)
ctx.set_source_rgba(c[0],c[1],c[2],c[3])
ctx.show_layout(p_layout)
ctx.restore()
def get_extents(self, p_layout):
(extents, lextents) = p_layout.get_pixel_extents()
return extents
def get_xy_from_alignment(self, p_layout, x, y):
if not self.width: return (x, y)
extents = self.get_extents(p_layout)
fw = extents[2]+extents[0]
fh = extents[3]+extents[1]
if self.align == "left":
pass
elif self.align == "center":
if self.layout==0:
x = x + self.width/2.0 - fw/2.0
elif self.layout==1:
y = y - self.width/2.0 + fw/2.0
elif self.layout==2:
x = x - self.width/2.0 + fw/2.0
elif self.layout==3:
y = y + self.width/2.0 - fw/2.0
elif self.align == "right":
if self.layout==0:
x = x + self.width - fw
elif self.layout==1:
y = y - self.width + fw
elif self.layout==2:
x = x - self.width + fw
elif self.layout==3:
y = y + self.width - fw
return (x, y)
def set_clip_region(self, ctx, p_layout, x, y):
extents = self.get_extents(p_layout)
fw = extents[2]+extents[0]
fh = extents[3]+extents[1]
if self.width:
if self.layout == 0:
ctx.rectangle(x, y, self.width, fh)
if self.layout == 1:
ctx.rectangle(x, y-self.width, fh, self.width)
if self.layout == 2:
ctx.rectangle(x-self.width, y-fh, self.width, fh)
if self.layout == 3:
ctx.rectangle(x-fh, y, fh, self.width)
ctx.clip()
def get_rgba(self, color):
return (
int(color[1:3], 16)/255.0,
int(color[3:5], 16)/255.0,
int(color[5:7], 16)/255.0,
int(color[7:9], 16)/255.0
)
def get_rotation_from_direction(self, dir):
PI = 3.141596
if dir == "left-right":
self.layout = 0
return 0
elif dir == "down-up":
self.layout = 1
return -PI*0.5
elif dir == "right-left":
self.layout = 2
return PI
elif dir == "up-down":
self.layout = 3
return PI*0.5
return False
class PlayerButton(screenlets.ShapedWidget):
width = 20
height = 20
image = None
image_normal = None
image_hover = None
image_pressed = None
callbackfn = None
def __init__(self, w, h):
super(PlayerButton, self).__init__(w,h)
self.width = w
self.height = h
def set_callback_fn(self, fn):
self.callbackfn = fn
def set_images(self, image_normal, image_hover, image_pressed):
self.image = image_normal
self.image_normal = image_normal
self.image_hover = image_hover
self.image_pressed = image_pressed
def button_press(self, widget, event):
if event.button==1:
self.image = self.image_pressed
self.queue_draw()
return True
def button_release(self, widget, event):
if event.button==1:
self.image = self.image_normal
self.queue_draw()
if(self.callbackfn != None): self.callbackfn()
return False
def enter_notify(self, widget, event):
self.image = self.image_hover
self.queue_draw()
def leave_notify(self, widget, event):
self.image = self.image_normal
self.queue_draw()
def draw(self, ctx):
if(self.image != None):
iw = float(self.image.get_width())
ih = float(self.image.get_height())
matrix = cairo.Matrix(xx=iw/self.width, yy=ih/self.height)
pattern = cairo.SurfacePattern(self.image)
pattern.set_matrix(matrix)
ctx.move_to(0,0)
ctx.set_source(pattern)
ctx.paint()
class PlayerControls:
type = "playercontrols"
box = False
window = False
theme = False
player = False
prev_button = False
play_pause_button = False
next_button = False
display = True
dims = [0,0,0,0,0,0]
def __init__(self, spacing, xratio, yratio, theme, window, player, display, w=0, h=0):
self.box = gtk.HBox(spacing=spacing)
alignment = gtk.Alignment(xalign=xratio, yalign=yratio)
alignment.add(self.box)
self.theme = theme
self.window = window
self.player = player
self.window.add(alignment)
if display: self.display = display
def set_images(self, btnimg, img):
eval("self.%s_button.set_images(self.theme['%s.png'], self.theme['%s-hover.png'], self.theme['%s-pressed.png'])" %(btnimg, img, img, img))
def add_prev(self, w, h):
self.add_prev_t(w, h)
self.dims[0]=w; self.dims[1]=h
self.window.show_all()
def add_prev_t(self, w, h):
self.prev_button=PlayerButton(w,h)
self.set_images("prev", "prev")
self.box.add(self.prev_button)
def add_play_pause(self, w, h):
self.add_play_pause_t(w, h)
self.dims[2]=w; self.dims[3]=h
self.window.show_all()
def add_play_pause_t(self, w, h):
self.play_pause_button=PlayerButton(w,h)
image = "play"
if self.player and self.player.is_playing(): image = "pause"
self.set_images("play_pause", image)
self.box.add(self.play_pause_button)
def add_next(self, w, h):
self.add_next_t(w, h)
self.dims[4]=w; self.dims[5]=h
self.window.show_all()
def add_next_t(self, w, h):
self.next_button=PlayerButton(w,h)
self.set_images("next", "next")
self.box.add(self.next_button)
def remove_old(self):
if self.box:
for oldwidget in self.box.get_children():
self.box.remove(oldwidget)
def set_scale(self, scale):
self.remove_old()
b = self.dims
if self.prev_button:
tmp = self.prev_button
fn = tmp.callbackfn
self.add_prev_t(int(b[0]*scale), int(b[1]*scale))
self.prev_button.set_callback_fn(fn)
tmp.destroy()
if self.play_pause_button:
self.play_pause_button.scale = scale
tmp = self.play_pause_button
fn = tmp.callbackfn
self.add_play_pause_t(int(b[2]*scale), int(b[3]*scale))
self.play_pause_button.set_callback_fn(fn)
tmp.destroy()
if self.next_button:
self.next_button.scale = scale
tmp = self.next_button
fn = tmp.callbackfn
self.add_next_t(int(b[4]*scale), int(b[5]*scale))
self.next_button.set_callback_fn(fn)
tmp.destroy()
self.window.show_all()
def draw(self, ctx):
self.window.show_all()