#!/usr/bin/env python
# coding=UTF-8
-#
+#
# Copyright (C) 2010 Stefanos Harhalakis
#
# This file is part of wifieye.
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
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)
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)
# if not self.ispressed:
# return(False)
- print "timerPressed"
-
self.invalidate()
if self.timePressed()>self.presstime:
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:
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: