Added support for saving/loading 'animate' option.
[drlaunch] / src / icon.py
1 #!/usr/bin/env python
2 # coding=UTF-8
3 #
4 # Copyright (C) 2010 Stefanos Harhalakis
5 #
6 # This file is part of wifieye.
7 #
8 # wifieye is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # wifieye is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with wifieye.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 # $Id: 0.py 2265 2010-02-21 19:16:26Z v13 $
22
23 __version__ = "$Id: 0.py 2265 2010-02-21 19:16:26Z v13 $"
24
25 import gtk
26 import gobject
27 import hildon
28 from hildondesktop import *
29 from gtk import gdk
30 from math import pi
31 import cairo
32 import time
33
34 from portrait import FremantleRotation
35 import launcher
36 from xdg.IconTheme import getIconPath
37
38
39 #import config
40 import apps
41
42 # Background surface for icons
43 iconbg=None
44
45 # Load an icon
46 # Fall-back to default/blue if not found or name==None
47 def getIcon(name, iconsize):
48     # Default icon
49     idef='tasklaunch_default_application'
50
51     # If name==None then use the default icon
52     if name==None or name=='':
53         iname=idef
54     else:
55         iname=name
56
57     ico=getIconPath(iname, iconsize)
58
59     # If not found then use the default icon
60     if ico==None:
61         ico=getIconPath(idef, iconsize)
62
63     ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize)
64
65     return(ret)
66
67 class Icon(gobject.GObject):
68     def __init__(self, isconfig, config):
69         self.__gobject_init__()
70
71         self.isconfig=isconfig
72         self.config=config
73
74         self.name=None
75         self.icon=None
76         self.sicon=None
77         self.lastpress=0
78         self.ispressed=False
79
80         self.x=0
81         self.y=0
82
83         self.presstime=0.25
84
85         self.window=None
86
87         self.clickcount=0
88
89         self.angle=0
90
91         self.cached_icons={}
92
93     def timePressed(self):
94         """ return how much time a button is pressed """
95         dt=time.time() - self.lastpress
96
97         return(dt)
98
99     def setApp(self, dt):
100         if dt==None:
101             self.name=None
102             self.icon=None
103             self.sicon=None
104         else:
105             self.name=dt['id']
106             self.icon=dt['icon2']
107             self.sicon=None
108             self.cached_icons={}
109         self.invalidate()
110
111     def getSize(self):
112         return(self.config.iconsize+self.config.iconspace)
113
114     def setAngle(self, angle):
115         """ Set the angle. Return True if the angle changed or False if it
116         didn't. The caller should invalidate the icon """
117
118         # The step in degrees
119         step=15
120
121         angle2=int(angle/step)*step
122
123         if angle2==self.angle:
124             return(False)
125
126         self.angle=angle2
127
128         # The caller should be responsible for redrawing.
129         # If we call invalidate() here there is the risk of having
130         # icons rotate individually using different angles
131 #       self.invalidate()
132
133         return(True)
134
135     def mkbg(self, t_pressed):
136         """ Create the background of the icon and cache it as a global
137         variable among all icons and widget instances """
138         global iconbg
139
140         if iconbg!=None and t_pressed<=0.001:
141             return(iconbg)
142
143         w=self.config.iconsize + self.config.iconspace
144         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
145         cr0=cairo.Context(s)
146         cr=gtk.gdk.CairoContext(cr0)
147
148         cr.set_source_rgba(0.1, 0.1, 0.1, 1)
149         cr.set_line_width(5)
150
151         #if self.ispressed:
152         if t_pressed>0.001 and \
153             (t_pressed <= self.presstime or self.ispressed):
154             t=1.0 * min(t_pressed, self.presstime) / self.presstime
155             g=0.3+0.5*t
156             b=0.3+0.7*t
157             cr.set_source_rgba(0, g, b, 0.7)
158         else:
159             cr.set_source_rgba(0.3, 0.3, 0.3, 0.7)
160
161         x=0
162         y=0
163         x3=x + (self.config.iconspace/6)
164         y3=y + (self.config.iconspace/6)
165
166         r=10    # Radius
167         w=self.config.iconsize+(self.config.iconspace*2/3)
168
169         cr.move_to(x3+r, y3)
170         cr.arc(x3+w-r,  y3+r,   r,          pi*1.5, pi*2)
171         cr.arc(x3+w-r,  y3+w-r, r,          0,      pi*0.5)
172         cr.arc(x3+r,    y3+w-r, r,          pi*0.5, pi)
173         cr.arc(x3+r,    y3+r,   r,          pi,     pi*1.5)
174
175         cr.stroke_preserve()
176         cr.fill()
177         cr.clip()
178         cr.paint()
179 #       cr.restore()
180
181         if t_pressed<0.001:
182             iconbg=s
183
184         return(s)
185
186     def get_sicon(self):
187         """ Return the icon as a surface. Cache it. """
188         if self.sicon!=None:
189             return(self.sicon)
190
191         w=self.config.iconsize
192         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
193         cr0=cairo.Context(s)
194         cr=gtk.gdk.CairoContext(cr0)
195
196         cr.set_source_pixbuf(self.icon, 0, 0)
197         cr.paint()
198
199         self.sicon=s
200
201         return(s)
202
203     def get_paint_icon(self):
204         """ Return the icon to paint as a surface. The icon is rotated
205         as needed. The result is cached. """
206         angle=self.angle
207
208         if self.timePressed() <= self.presstime or self.ispressed:
209             t=self.timePressed()
210             pressed=True
211         else:
212             t=0
213             pressed=False
214
215         if not pressed and self.cached_icons.has_key(angle):
216             return(self.cached_icons[angle])
217
218         w=self.config.iconsize + self.config.iconspace
219         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
220         cr0=cairo.Context(s)
221         cr=gtk.gdk.CairoContext(cr0)
222
223         # Paint the background
224         s2=self.mkbg(t)
225         cr.save()
226         cr.set_source_surface(s2, 0, 0)
227         cr.paint()
228         cr.restore()
229
230         # If there is no icon then don't do anything more
231         if self.icon!=None:
232             # Get the icon as a surface (get_sicon() will cache the surface)
233             sicon=self.get_sicon()
234
235             # Width is the iconsize plus the empty border around the icon
236             #w=self.config.iconsize + self.config.iconspace
237
238             # This is used to locate the center of the surface
239             dx=int(w/2)
240
241             # This is the delta from the center where icons are drawn
242             dx2=int(self.config.iconsize/2)
243
244 #           cr.save()
245
246             # A transformation matrix with dx/dy set to point to the center
247             m=cairo.Matrix(1, 0, 0, 1, dx, dx)
248             cr.set_matrix(m)
249             # Transform degrees to rads
250             rot=-1 * pi * 2 * self.angle / 360
251             cr.rotate(rot)
252             # Draw the icon
253             cr.set_source_surface(sicon, -dx2, -dx2)    # Faster than pixbuf
254 #       cr.set_source_pixbuf(icon2, -dx2, -dx2)
255             cr.paint()
256
257 #           cr.restore()
258
259         if not pressed:
260             self.cached_icons[angle]=s
261
262         return(s)
263
264
265     def draw(self, cr, x, y):
266         self.draw_queued=False
267         self.x=x
268         self.y=y
269
270         if self.icon==None and not self.isconfig:
271             return
272
273         cr.save()
274         s=self.get_paint_icon()
275         cr.set_source_surface(s, x, y)
276         cr.paint()
277
278         cr.restore()
279
280         return(False)
281
282     def timerPressed(self):
283 #       if not self.ispressed:
284 #           return(False)
285
286         self.invalidate()
287
288         if self.timePressed()>self.presstime:
289             ret=False
290         else:
291             ret=True
292
293         return(ret)
294
295     def doPress(self):
296         # Double-time: time for pressed and time for not-pressed
297         if time.time() - self.lastpress > self.presstime*2:
298             self.clickcount=0
299
300         self.lastpress=time.time()
301         self.ispressed=True
302         gobject.timeout_add(20, self.timerPressed)
303
304     def doRelease(self):
305         dt=time.time() - self.lastpress
306         self.ispressed=False
307         self.invalidate()
308         if dt<=self.presstime:
309             self.clickcount+=1
310             if self.clickcount==1:
311                 self.emit('click')
312             elif self.clickcount==2:
313                 self.emit('double-click')
314             if self.clickcount==3:
315                 self.emit('tripple-click')
316                 self.clickcount=0
317         elif dt>self.presstime and dt<2:
318             self.emit('long-press')
319
320     def doCancel(self):
321         self.ispressed=False
322
323     def setWindow(self, window):
324         self.window=window
325
326     def invalidate(self, window=None):
327         if window==None:
328             window=self.window
329         else:
330             self.window=window
331
332         if window==None:
333             return
334
335         if self.draw_queued:
336             print "queued"
337             return
338         self.draw_queued=True
339         w=self.config.iconsize + self.config.iconspace
340         rect=gdk.Rectangle(self.x, self.y, w, w)
341         gdk.Window.invalidate_rect(window, rect, True)
342
343 gobject.type_register(Icon)
344 signals=['click', 'double-click', 'tripple-click', 'long-press']
345 for s in signals:
346     gobject.signal_new(s, Icon, gobject.SIGNAL_RUN_FIRST,
347         gobject.TYPE_NONE, ())
348
349 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
350