2982bf3ec3d466bd9697e2145854716028e73be9
[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.clearAnimationCache()
109         self.invalidate()
110
111     def clearAnimationCache(self):
112         self.cached_icons={}
113
114     def getSize(self):
115         return(self.config.iconsize+self.config.iconspace)
116
117     def setAngle(self, angle):
118         """ Set the angle. Return True if the angle changed or False if it
119         didn't. The caller should invalidate the icon """
120
121         # The step in degrees
122         step=9
123
124         angle2=int(angle/step)*step
125
126         if angle2==self.angle:
127             return(False)
128
129         self.angle=angle2
130
131         # The caller should be responsible for redrawing.
132         # If we call invalidate() here there is the risk of having
133         # icons rotate individually using different angles
134 #       self.invalidate()
135
136         return(True)
137
138     def mkbg(self, t_pressed):
139         """ Create the background of the icon and cache it as a global
140         variable among all icons and widget instances """
141         global iconbg
142
143         if iconbg!=None and t_pressed<=0.001:
144             return(iconbg)
145
146         w=self.config.iconsize + self.config.iconspace
147         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
148         cr0=cairo.Context(s)
149         cr=gtk.gdk.CairoContext(cr0)
150
151         cr.set_source_rgba(0.1, 0.1, 0.1, 1)
152         cr.set_line_width(5)
153
154         #if self.ispressed:
155         if t_pressed>0.001 and \
156             (t_pressed <= self.presstime or self.ispressed):
157             t=1.0 * min(t_pressed, self.presstime) / self.presstime
158             g=0.3+0.5*t
159             b=0.3+0.7*t
160             cr.set_source_rgba(0, g, b, 0.7)
161         else:
162             cr.set_source_rgba(0.3, 0.3, 0.3, 0.7)
163
164         x=0
165         y=0
166         x3=x + (self.config.iconspace/6)
167         y3=y + (self.config.iconspace/6)
168
169         r=10    # Radius
170         w=self.config.iconsize+(self.config.iconspace*2/3)
171
172         cr.move_to(x3+r, y3)
173         cr.arc(x3+w-r,  y3+r,   r,          pi*1.5, pi*2)
174         cr.arc(x3+w-r,  y3+w-r, r,          0,      pi*0.5)
175         cr.arc(x3+r,    y3+w-r, r,          pi*0.5, pi)
176         cr.arc(x3+r,    y3+r,   r,          pi,     pi*1.5)
177
178         cr.stroke_preserve()
179         cr.fill()
180         cr.clip()
181         cr.paint()
182 #       cr.restore()
183
184         if t_pressed<0.001:
185             iconbg=s
186
187         return(s)
188
189     def get_sicon(self):
190         """ Return the icon as a surface. Cache it. """
191         if self.sicon!=None:
192             return(self.sicon)
193
194         w=self.config.iconsize
195         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
196         cr0=cairo.Context(s)
197         cr=gtk.gdk.CairoContext(cr0)
198
199         cr.set_source_pixbuf(self.icon, 0, 0)
200         cr.paint()
201
202         self.sicon=s
203
204         return(s)
205
206     def get_paint_icon(self):
207         """ Return the icon to paint as a surface. The icon is rotated
208         as needed. The result is cached. """
209         angle=self.angle
210
211         if self.timePressed() <= self.presstime or self.ispressed:
212             t=self.timePressed()
213             pressed=True
214         else:
215             t=0
216             pressed=False
217
218         if not pressed and self.cached_icons.has_key(angle):
219             return(self.cached_icons[angle])
220
221         w=self.config.iconsize + self.config.iconspace
222         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
223         cr0=cairo.Context(s)
224         cr=gtk.gdk.CairoContext(cr0)
225
226         # Paint the background
227         if not self.config.getNoBg():
228             s2=self.mkbg(t)
229             cr.save()
230             cr.set_source_surface(s2, 0, 0)
231             cr.paint()
232             cr.restore()
233
234         # If there is no icon then don't do anything more
235         if self.icon!=None:
236             # Get the icon as a surface (get_sicon() will cache the surface)
237             sicon=self.get_sicon()
238
239             # Width is the iconsize plus the empty border around the icon
240             #w=self.config.iconsize + self.config.iconspace
241
242             # This is used to locate the center of the surface
243             dx=int(w/2)
244
245             # This is the delta from the center where icons are drawn
246             dx2=int(self.config.iconsize/2)
247
248 #           cr.save()
249
250             # A transformation matrix with dx/dy set to point to the center
251             m=cairo.Matrix(1, 0, 0, 1, dx, dx)
252             cr.set_matrix(m)
253             # Transform degrees to rads
254             rot=-1 * pi * 2 * self.angle / 360
255             cr.rotate(rot)
256             # Draw the icon
257             cr.set_source_surface(sicon, -dx2, -dx2)    # Faster than pixbuf
258 #       cr.set_source_pixbuf(icon2, -dx2, -dx2)
259             cr.paint()
260
261 #           cr.restore()
262
263         if not pressed:
264             self.cached_icons[angle]=s
265
266         return(s)
267
268
269     def draw(self, cr, x, y):
270         self.draw_queued=False
271         self.x=x
272         self.y=y
273
274         if self.icon==None and not self.isconfig:
275             return
276
277         cr.save()
278         s=self.get_paint_icon()
279         cr.set_source_surface(s, x, y)
280         cr.paint()
281
282         cr.restore()
283
284         return(False)
285
286     def timerPressed(self):
287 #       if not self.ispressed:
288 #           return(False)
289
290         self.invalidate()
291
292         if self.timePressed()>self.presstime:
293             ret=False
294         else:
295             ret=True
296
297         return(ret)
298
299     def doPress(self):
300         # Double-time: time for pressed and time for not-pressed
301         if time.time() - self.lastpress > self.presstime*2:
302             self.clickcount=0
303
304         self.lastpress=time.time()
305         self.ispressed=True
306         gobject.timeout_add(20, self.timerPressed)
307
308     def doRelease(self):
309         dt=time.time() - self.lastpress
310         self.ispressed=False
311         self.invalidate()
312         if dt<=self.presstime:
313             self.clickcount+=1
314             if self.clickcount==1:
315                 self.emit('click')
316             elif self.clickcount==2:
317                 self.emit('double-click')
318             if self.clickcount==3:
319                 self.emit('tripple-click')
320                 self.clickcount=0
321         elif dt>self.presstime and dt<2:
322             self.emit('long-press')
323
324     def doCancel(self):
325         self.ispressed=False
326
327     def setWindow(self, window):
328         self.window=window
329
330     def invalidate(self, window=None):
331         if window==None:
332             window=self.window
333         else:
334             self.window=window
335
336         if window==None:
337             return
338
339         if self.draw_queued:
340 #           print "queued"
341             return
342
343         self.draw_queued=True
344         w=self.config.iconsize + self.config.iconspace
345         rect=gdk.Rectangle(self.x, self.y, w, w)
346         gdk.Window.invalidate_rect(window, rect, True)
347
348 gobject.type_register(Icon)
349 signals=['click', 'double-click', 'tripple-click', 'long-press']
350 for s in signals:
351     gobject.signal_new(s, Icon, gobject.SIGNAL_RUN_FIRST,
352         gobject.TYPE_NONE, ())
353
354 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
355