Fix old-config loading.
[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     try:
64         ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize)
65     except:
66         # On error use the default icon
67         ico=getIconPath(idef, iconsize)
68         ret=gtk.gdk.pixbuf_new_from_file_at_size(ico, iconsize, iconsize)
69         print "Icon with unhandled format:", iname
70
71     return(ret)
72
73 class Icon(gobject.GObject):
74     def __init__(self, isconfig, config):
75         self.__gobject_init__()
76
77         self.isconfig=isconfig
78         self.config=config
79
80         self.name=None
81         self.icon=None
82         self.sicon=None
83         self.sthemebg1=None
84         self.sthemebg2=None
85         self.lastpress=0
86         self.ispressed=False
87
88         self.x=0
89         self.y=0
90
91         self.presstime=0.25
92
93         self.window=None
94
95         self.clickcount=0
96
97         self.angle=0
98
99         self.cached_icons={}
100
101     def timePressed(self):
102         """ return how much time a button is pressed """
103         dt=time.time() - self.lastpress
104
105         return(dt)
106
107     def setApp(self, dt):
108         if dt==None:
109             self.name=None
110             self.icon=None
111             self.sicon=None
112         else:
113             self.name=dt['id']
114             self.icon=dt['icon2']
115             self.sicon=None
116         self.clearAnimationCache()
117         self.invalidate()
118
119     def clearAnimationCache(self):
120         self.cached_icons={}
121
122     def getSize(self):
123         return(self.config.iconsize+self.config.iconspace)
124
125     def setAngle(self, angle):
126         """ Set the angle. Return True if the angle changed or False if it
127         didn't. The caller should invalidate the icon """
128
129         # The step in degrees
130         step=9
131
132         angle2=int(angle/step)*step
133
134         if angle2==self.angle:
135             return(False)
136
137         self.angle=angle2
138
139         # The caller should be responsible for redrawing.
140         # If we call invalidate() here there is the risk of having
141         # icons rotate individually using different angles
142 #       self.invalidate()
143
144         return(True)
145
146     def mkbg(self, t_pressed):
147         """ Create the background of the icon and cache it as a global
148         variable among all icons and widget instances """
149         global iconbg
150
151         if iconbg!=None and t_pressed<=0.001:
152             return(iconbg)
153
154         w=self.config.iconsize + self.config.iconspace
155         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
156         cr0=cairo.Context(s)
157         cr=gtk.gdk.CairoContext(cr0)
158
159         cr.set_source_rgba(0.1, 0.1, 0.1, 1)
160         cr.set_line_width(5)
161
162         #if self.ispressed:
163         if t_pressed>0.001 and \
164             (t_pressed <= self.presstime or self.ispressed):
165             t=1.0 * min(t_pressed, self.presstime) / self.presstime
166             g=0.3+0.5*t
167             b=0.3+0.7*t
168             cr.set_source_rgba(0, g, b, 0.7)
169         else:
170             cr.set_source_rgba(0.3, 0.3, 0.3, 0.7)
171
172         x=0
173         y=0
174         x3=x + (self.config.iconspace/6)
175         y3=y + (self.config.iconspace/6)
176
177         r=10    # Radius
178         w=self.config.iconsize+(self.config.iconspace*2/3)
179
180         cr.move_to(x3+r, y3)
181         cr.arc(x3+w-r,  y3+r,   r,          pi*1.5, pi*2)
182         cr.arc(x3+w-r,  y3+w-r, r,          0,      pi*0.5)
183         cr.arc(x3+r,    y3+w-r, r,          pi*0.5, pi)
184         cr.arc(x3+r,    y3+r,   r,          pi,     pi*1.5)
185
186         cr.stroke_preserve()
187         cr.fill()
188         cr.clip()
189         cr.paint()
190 #       cr.restore()
191
192         if t_pressed<0.001:
193             iconbg=s
194
195         return(s)
196
197     def get_sthemebg(self, pressed):
198         """ Return the theme's background icon as a surface. Cache it. """
199         if not pressed and self.sthemebg1!=None:
200             return(self.sthemebg1)
201         if pressed and self.sthemebg2!=None:
202             return(self.sthemebg2)
203
204         fn="/etc/hildon/theme/images/"
205         if pressed:
206             fn+="ApplicationShortcutAppletPressed.png"
207         else:
208             fn+="ApplicationShortcutApplet.png"
209
210         w=self.config.themebgsize
211         buf=gtk.gdk.pixbuf_new_from_file_at_size(fn, w, w)
212         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
213         cr0=cairo.Context(s)
214         cr=gtk.gdk.CairoContext(cr0)
215
216         cr.set_source_pixbuf(buf, 0, 0)
217         cr.paint()
218
219         if not pressed:
220             self.sthemebg1=s
221         else:
222             self.sthemebg2=s
223
224         return(s)
225
226     def get_sicon(self):
227         """ Return the icon as a surface. Cache it. """
228         if self.sicon!=None:
229             return(self.sicon)
230
231         w=self.config.iconsize
232         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
233         cr0=cairo.Context(s)
234         cr=gtk.gdk.CairoContext(cr0)
235
236         cr.set_source_pixbuf(self.icon, 0, 0)
237         cr.paint()
238
239         self.sicon=s
240
241         return(s)
242
243     def get_paint_icon(self):
244         """ Return the icon to paint as a surface. The icon is rotated
245         as needed. The result is cached. """
246         angle=self.angle
247
248         if self.timePressed() <= self.presstime or self.ispressed:
249             t=self.timePressed()
250             pressed=True
251         else:
252             t=0
253             pressed=False
254
255         if not pressed and self.cached_icons.has_key(angle):
256             return(self.cached_icons[angle])
257
258         w=self.config.iconsize + self.config.iconspace
259         s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w)
260         cr0=cairo.Context(s)
261         cr=gtk.gdk.CairoContext(cr0)
262
263         # Paint the background
264         if self.config.getNoBg():
265             pass
266         elif self.config.getThemeBg():  # Use theme bg
267             s2=self.get_sthemebg(pressed)
268
269             # have in mind the size difference of iconsize+iconspace with
270             # the fixed themebgsize
271             xy0=int((w-self.config.themebgsize)/2)
272
273             cr.save()
274             cr.set_source_surface(s2, xy0, xy0)
275             cr.paint()
276             cr.restore()
277         else:
278             s2=self.mkbg(t)
279             cr.save()
280             cr.set_source_surface(s2, 0, 0)
281             cr.paint()
282             cr.restore()
283
284         # If there is no icon then don't do anything more
285         if self.icon!=None:
286             # Get the icon as a surface (get_sicon() will cache the surface)
287             sicon=self.get_sicon()
288
289             # Width is the iconsize plus the empty border around the icon
290             #w=self.config.iconsize + self.config.iconspace
291
292             # This is used to locate the center of the surface
293             dx=int(w/2)
294
295             # This is the delta from the center where icons are drawn
296             dx2=int(self.config.iconsize/2)
297
298 #           cr.save()
299
300             # A transformation matrix with dx/dy set to point to the center
301             m=cairo.Matrix(1, 0, 0, 1, dx, dx)
302             cr.set_matrix(m)
303             # Transform degrees to rads
304             rot=-1 * pi * 2 * self.angle / 360
305             cr.rotate(rot)
306             # Draw the icon
307             cr.set_source_surface(sicon, -dx2, -dx2)    # Faster than pixbuf
308 #       cr.set_source_pixbuf(icon2, -dx2, -dx2)
309             cr.paint()
310
311 #           cr.restore()
312
313         if not pressed:
314             self.cached_icons[angle]=s
315
316         return(s)
317
318
319     def draw(self, cr, x, y):
320         self.draw_queued=False
321         self.x=x
322         self.y=y
323
324         if self.icon==None and not self.isconfig:
325             return
326
327         cr.save()
328         s=self.get_paint_icon()
329         cr.set_source_surface(s, x, y)
330         cr.paint()
331
332         cr.restore()
333
334         return(False)
335
336     def timerPressed(self):
337 #       if not self.ispressed:
338 #           return(False)
339
340         self.invalidate()
341
342         if self.timePressed()>self.presstime:
343             ret=False
344         else:
345             ret=True
346
347         return(ret)
348
349     def doPress(self):
350         # Double-time: time for pressed and time for not-pressed
351         if time.time() - self.lastpress > self.presstime*2:
352             self.clickcount=0
353
354         self.lastpress=time.time()
355         self.ispressed=True
356         gobject.timeout_add(20, self.timerPressed)
357
358     def doRelease(self):
359         dt=time.time() - self.lastpress
360         self.ispressed=False
361         self.invalidate()
362         if dt<=self.presstime:
363             self.clickcount+=1
364             if self.clickcount==1:
365                 self.emit('click')
366             elif self.clickcount==2:
367                 self.emit('double-click')
368             if self.clickcount==3:
369                 self.emit('tripple-click')
370                 self.clickcount=0
371         elif dt>self.presstime and dt<2:
372             self.emit('long-press')
373
374     def doCancel(self):
375         self.ispressed=False
376
377     def setWindow(self, window):
378         self.window=window
379
380     def invalidate(self, window=None):
381         if window==None:
382             window=self.window
383         else:
384             self.window=window
385
386         if window==None:
387             return
388
389         if self.draw_queued:
390 #           print "queued"
391             return
392
393         self.draw_queued=True
394         w=self.config.iconsize + self.config.iconspace
395         rect=gdk.Rectangle(self.x, self.y, w, w)
396         gdk.Window.invalidate_rect(window, rect, True)
397
398 gobject.type_register(Icon)
399 signals=['click', 'double-click', 'tripple-click', 'long-press']
400 for s in signals:
401     gobject.signal_new(s, Icon, gobject.SIGNAL_RUN_FIRST,
402         gobject.TYPE_NONE, ())
403
404 # vim: set ts=8 sts=4 sw=4 noet formatoptions=r ai nocindent:
405