X-Git-Url: http://git.maemo.org/git/?p=drlaunch;a=blobdiff_plain;f=src%2Ficon.py;h=c4ea529038f3073027feed1a4679f14e1e57f89c;hp=c1a8096be96a2cd65d79778ec0d76b22a35e6127;hb=75247cf58d8d7f5a420aa300a01b1f50e4bffb23;hpb=c8a10df6fd650fb9fabd9b9ac2c69c5e2e4985a8 diff --git a/src/icon.py b/src/icon.py index c1a8096..c4ea529 100755 --- a/src/icon.py +++ b/src/icon.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # coding=UTF-8 -# +# # Copyright (C) 2010 Stefanos Harhalakis # # This file is part of wifieye. @@ -34,26 +34,71 @@ import time from portrait import FremantleRotation import launcher from xdg.IconTheme import getIconPath - +from sig import Disconnector #import config import apps +# Background surface for icons +iconbg=None +sthemebg1=None +sthemebg2=None + +# Load an icon +# Fall-back to default/blue if not found or name==None def getIcon(name, iconsize): - ico=getIconPath(name, iconsize) - ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize) + # Default icon + idef='tasklaunch_default_application' + + # If name==None then use the default icon + if name==None or name=='': + iname=idef + else: + iname=name + + ico=getIconPath(iname, iconsize) + + # If not found then use the default icon + if ico==None: + ico=getIconPath(idef, iconsize) + + try: + ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize) + except: + # On error use the default icon + ico=getIconPath(idef, iconsize) + ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize) + print "Icon with unhandled format:", iname return(ret) -class Icon(gobject.GObject): +class Icon(Disconnector, gobject.GObject): +#class Icon(gtk.Widget, Disconnector): + + __v_t=(gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION, + gobject.TYPE_NONE, ()) + + gsignals={ + 'click': __v_t, + 'double-click': __v_t, + 'tripple-click': __v_t, + 'long-press': __v_t, + } + + __gsignals__=gsignals + def __init__(self, isconfig, config): - self.__gobject_init__() +# self.__gobject_init__() + gobject.GObject.__init__(self) +# gtk.Widget.__init__(self) + Disconnector.__init__(self) self.isconfig=isconfig self.config=config - self.name=None + self.appname=None self.icon=None + self.sicon=None self.lastpress=0 self.ispressed=False @@ -62,55 +107,114 @@ class Icon(gobject.GObject): self.presstime=0.25 - self.window=None + self.window_=None self.clickcount=0 + self.angle=0 + + self.cached_icons={} + + self.draw_queued=False + + #print "icon-init" + + def __del__(self): + #print "icon-del" + pass + def timePressed(self): """ return how much time a button is pressed """ dt=time.time() - self.lastpress return(dt) + def reload(self): + self.clearAnimationCache() + self.clearBgCache() + self.invalidate() + def setApp(self, dt): if dt==None: - self.name=None + self.appname=None self.icon=None + self.sicon=None else: - self.name=dt['id'] + self.appname=dt['id'] self.icon=dt['icon2'] + self.sicon=None + self.clearAnimationCache() + self.clearBgCache() self.invalidate() + def clearAnimationCache(self): + self.cached_icons={} + + def clearBgCache(self): + global iconbg, sthemebg1, sthemebg2 + + iconbg=None + sthemebg1=None + sthemebg2=None + def getSize(self): - return(self.config.iconsize+self.config.iconspace) + return(self.config.getIconSizeFull()) + # return(self.config.iconsize+self.config.iconspace) - def draw(self, cr, x, y, mode): - self.x=x - self.y=y + def setAngle(self, angle): + """ Set the angle. Return True if the angle changed or False if it + didn't. The caller should invalidate the icon """ - if self.icon==None and not self.isconfig: - return + # The step in degrees + step=9 - cr.save() - cr.set_source_rgba(0.1, 0.1, 0.1, 1) + angle2=int(angle/step)*step + + if angle2==self.angle: + return(False) + + self.angle=angle2 + + # The caller should be responsible for redrawing. + # If we call invalidate() here there is the risk of having + # icons rotate individually using different angles +# self.invalidate() + + return(True) + + def mkbg(self, t_pressed): + """ Create the background of the icon and cache it as a global + variable among all icons and widget instances """ + global iconbg + + if iconbg!=None and t_pressed<=0.001: + return(iconbg) + + w=self.getSize() + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) + + cr.set_source_rgba(0.1, 0.1, 0.1, 1) cr.set_line_width(5) #if self.ispressed: - if self.timePressed() <= self.presstime or self.ispressed: - t=1.0 * min(self.timePressed(), self.presstime) / self.presstime + if t_pressed>0.001 and \ + (t_pressed <= self.presstime or self.ispressed): + t=1.0 * min(t_pressed, self.presstime) / self.presstime g=0.3+0.5*t b=0.3+0.7*t cr.set_source_rgba(0, g, b, 0.7) - print "t:", t - else: cr.set_source_rgba(0.3, 0.3, 0.3, 0.7) - x3=x + (self.config.iconspace/6) - y3=y + (self.config.iconspace/6) + x=0 + y=0 + x3=x + (self.config.iconmargin) + y3=y + (self.config.iconmargin) r=10 # Radius - w=self.config.iconsize+(self.config.iconspace*2/3) + w=self.config.iconsize+(self.config.iconpadding*2) cr.move_to(x3+r, y3) cr.arc(x3+w-r, y3+r, r, pi*1.5, pi*2) @@ -122,23 +226,152 @@ class Icon(gobject.GObject): cr.fill() cr.clip() cr.paint() - cr.restore() +# cr.restore() - if self.icon==None: - return + if t_pressed<0.001: + iconbg=s + + return(s) + + def get_sthemebg(self, pressed): + """ Return the theme's background icon as a surface. Cache it. """ + global sthemebg1, sthemebg2 - icon=self.icon + if not pressed and sthemebg1!=None: + return(sthemebg1) + if pressed and sthemebg2!=None: + return(sthemebg2) - if mode=='l': - icon2=icon + fn="/etc/hildon/theme/images/" + if pressed: + fn+="ApplicationShortcutAppletPressed.png" else: - icon2=icon.rotate_simple(gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) + fn+="ApplicationShortcutApplet.png" + + w=self.config.iconsize + (self.config.iconpadding*2) + buf=gtk.gdk.pixbuf_new_from_file_at_size(fn, w, w) + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) + + cr.set_source_pixbuf(buf, 0, 0) + cr.paint() + + if not pressed: + sthemebg1=s + else: + sthemebg2=s + + return(s) + + def get_sicon(self): + """ Return the icon as a surface. Cache it. """ + if self.sicon!=None: + return(self.sicon) + + w=self.config.iconsize + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) + + cr.set_source_pixbuf(self.icon, 0, 0) + cr.paint() + + self.sicon=s + + return(s) + + def get_paint_icon(self): + """ Return the icon to paint as a surface. The icon is rotated + as needed. The result is cached. """ + angle=self.angle + + if self.timePressed() <= self.presstime or self.ispressed: + t=self.timePressed() + pressed=True + else: + t=0 + pressed=False + + if not pressed and self.cached_icons.has_key(angle): + return(self.cached_icons[angle]) + + w=self.config.getIconSizeFull() + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) + + # Paint the background + if self.config.getNoBg(): + pass + elif self.config.getThemeBg(): # Use theme bg + s2=self.get_sthemebg(pressed) + + # have in mind the size difference of iconsize+iconspace with + # the fixed themebgsize + #xy0=int((w-self.config.themebgsize)/2) + #xy0=int((w-self.config.iconsize)/2) + xy0=self.config.iconmargin + + cr.save() + cr.set_source_surface(s2, xy0, xy0) + cr.paint() + cr.restore() + else: + s2=self.mkbg(t) + cr.save() + cr.set_source_surface(s2, 0, 0) + cr.paint() + cr.restore() + + # If there is no icon then don't do anything more + if self.icon!=None: + # Get the icon as a surface (get_sicon() will cache the surface) + sicon=self.get_sicon() + + # Width is the iconsize plus the empty border around the icon + #w=self.config.iconsize + self.config.iconspace + + # This is used to locate the center of the surface + dx=int(w/2) + + # This is the delta from the center where icons are drawn + dx2=int(self.config.iconsize/2) + +# cr.save() + + # A transformation matrix with dx/dy set to point to the center + m=cairo.Matrix(1, 0, 0, 1, dx, dx) + cr.set_matrix(m) + # Transform degrees to rads + rot=-1 * pi * 2 * self.angle / 360 + cr.rotate(rot) + # Draw the icon + cr.set_source_surface(sicon, -dx2, -dx2) # Faster than pixbuf +# cr.set_source_pixbuf(icon2, -dx2, -dx2) + cr.paint() + +# cr.restore() + + if not pressed: + self.cached_icons[angle]=s + + return(s) + + + def draw(self, cr, x, y): + self.draw_queued=False + self.x=x + self.y=y + + if self.icon==None and not self.isconfig: + return cr.save() - x3=x + (self.config.iconspace/2) - y3=y + (self.config.iconspace/2) - cr.set_source_pixbuf(icon2, x3, y3) + s=self.get_paint_icon() + cr.set_source_surface(s, x, y) cr.paint() + cr.restore() return(False) @@ -147,8 +380,6 @@ class Icon(gobject.GObject): # if not self.ispressed: # return(False) - print "timerPressed" - self.invalidate() if self.timePressed()>self.presstime: @@ -175,6 +406,7 @@ class Icon(gobject.GObject): self.clickcount+=1 if self.clickcount==1: self.emit('click') +# print "emit click", self elif self.clickcount==2: self.emit('double-click') if self.clickcount==3: @@ -182,28 +414,37 @@ class Icon(gobject.GObject): self.clickcount=0 elif dt>self.presstime and dt<2: self.emit('long-press') +# print "Emit lp" def doCancel(self): self.ispressed=False + def setWindow(self, window): + self.window_=window + def invalidate(self, window=None): if window==None: - window=self.window + window=self.window_ else: - self.window=window - + self.window_=window + if window==None: return - w=self.config.iconsize + self.config.iconspace + if self.draw_queued: +# print "queued" + return + + self.draw_queued=True + w=self.getSize() rect=gdk.Rectangle(self.x, self.y, w, w) gdk.Window.invalidate_rect(window, rect, True) -gobject.type_register(Icon) -signals=['click', 'double-click', 'tripple-click', 'long-press'] -for s in signals: - gobject.signal_new(s, Icon, gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ()) +#gobject.type_register(Icon) +#signals=['click', 'double-click', 'tripple-click', 'long-press'] +#for s in signals: +# gobject.signal_new(s, Icon, gobject.SIGNAL_RUN_FIRST | \ +# gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ()) # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent: