~ubuntu-branches/ubuntu/trusty/screenlets/trusty

« back to all changes in this revision

Viewing changes to .pc/90-disable-resize-grip.patch/src/lib/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2011-04-23 19:40:12 UTC
  • Revision ID: james.westby@ubuntu.com-20110423194012-x63zdevf47fi0wst
Tags: 0.1.3-0ubuntu2
* debian/patches/90-disable-resize-grip.patch:
 - From upstream (rev 646), disable resize grip (LP: #717989)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This application is released under the GNU General Public License 
 
2
# v3 (or, at your option, any later version). You can find the full 
 
3
# text of the license under http://www.gnu.org/licenses/gpl.txt. 
 
4
# By using, editing and/or distributing this software you agree to 
 
5
# the terms and conditions of this license. 
 
6
# Thank you for using free software!
 
7
 
 
8
# Screenlets main module (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> , 
 
9
# Whise aka Helder Fraga <helder.fraga@hotmail.com>
 
10
#
 
11
##@mainpage
 
12
#
 
13
##@section intro_sec General Information
 
14
#
 
15
# INFO:
 
16
# - Screenlets are small owner-drawn applications that can be described as
 
17
#  " the virtual representation of things lying/standing around on your desk".
 
18
#   Sticknotes, clocks, rulers, ... the possibilities are endless. The goal of 
 
19
#   the Screenlets is to simplify the creation of fully themeable mini-apps that
 
20
#   each solve basic desktop-work-related needs and generally improve the 
 
21
#   usability and eye-candy of the modern Linux-desktop.
 
22
#
 
23
# TODO: (possible improvements, not essential)
 
24
# - still more error-handling and maybe custom exceptions!!!
 
25
# - improve xml-based menu (is implemented, but I'm not happy with it)
 
26
# - switching themes slowly increases the memory usage (possible leak)
 
27
# - maybe attributes for dependancies/requirements (e.g. special 
 
28
#   python-libs or certain Screenlets)
 
29
# -
 
30
#
 
31
 
 
32
try:
 
33
        INSTALL_PREFIX = open("/etc/screenlets/prefix").read()[:-1] 
 
34
except:
 
35
        INSTALL_PREFIX = '/usr'
 
36
 
 
37
import pygtk
 
38
pygtk.require('2.0')
 
39
import gtk
 
40
import cairo, pango
 
41
import gobject
 
42
import glib
 
43
try:
 
44
        import rsvg
 
45
except ImportError: print 'No module RSVG , graphics will not be so good'
 
46
import os
 
47
import subprocess
 
48
import glob
 
49
import gettext
 
50
import math
 
51
 
 
52
# import screenlet-submodules
 
53
from options import *
 
54
import services
 
55
import utils
 
56
import sensors
 
57
# TEST
 
58
import menu
 
59
from menu import DefaultMenuItem, add_menuitem
 
60
from drawing import Drawing
 
61
# /TEST
 
62
 
 
63
# translation stuff
 
64
gettext.textdomain('screenlets')
 
65
gettext.bindtextdomain('screenlets', INSTALL_PREFIX +  '/share/locale')
 
66
 
 
67
def _(s):
 
68
        return gettext.gettext(s)
 
69
 
 
70
#-------------------------------------------------------------------------------
 
71
# CONSTANTS
 
72
#-------------------------------------------------------------------------------
 
73
 
 
74
# the application name
 
75
APP_NAME = "Screenlets"
 
76
 
 
77
# the version of the Screenlets-baseclass in use
 
78
VERSION = "0.1.3"
 
79
 
 
80
# the application copyright
 
81
COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>"
 
82
 
 
83
# the application authors
 
84
AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"]
 
85
 
 
86
# the application comments
 
87
COMMENTS = "Screenlets is a widget framework that consists of small owner-drawn applications (written in Python, a very simple object-oriented programming-language) that can be described as 'the virtual representation of things lying/standing around on your desk'. Sticknotes, clocks, rulers, ... the possibilities are endless. Screenlet also tries to include some compatibility with other widget frameworks,like web widgets and super karamba themes"
 
88
 
 
89
DOCUMENTERS = ["Documentation generated by epydoc"]
 
90
 
 
91
ARTISTS = ["ODD radio screenlet theme by ODDie\nPasodoble mail theme by jEsuSdA\nSome themes by RYX\nSome themes by Whise\nMore to come..."]
 
92
 
 
93
TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/"
 
94
 
 
95
# the application website
 
96
WEBSITE = 'http://www.screenlets.org'
 
97
 
 
98
# The Screenlets download page. Notice that if you translate this, you also have to create/translate the page for your language on the Screenlets.org (it's a Wiki!)
 
99
THIRD_PARTY_DOWNLOAD = _("http://www.screenlets.org/index.php/Get_more_screenlets")
 
100
 
 
101
 
 
102
#-------------------------------------------------------------------------------
 
103
# PATHS
 
104
#-------------------------------------------------------------------------------
 
105
DIR_TMP                 = '/tmp/screenlets/'
 
106
 
 
107
TMP_DIR = DIR_TMP
 
108
 
 
109
TMP_FILE        = 'screenlets.' + os.environ['USER'] + '.running'
 
110
 
 
111
DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets'
 
112
 
 
113
DIR_USER = os.environ['HOME'] + '/.screenlets'
 
114
 
 
115
DIR_CONFIG = os.environ['HOME'] + '/.config/Screenlets'
 
116
 
 
117
# note that this is the order how themes are preferred to each other
 
118
# don't change the order just like that
 
119
SCREENLETS_PATH = [DIR_USER, DIR_USER_ROOT]
 
120
 
 
121
SCREENLETS_PACK_PREFIX = "screenlets-pack-"
 
122
 
 
123
#-------------------------------------------------------------------------------
 
124
# DBUS
 
125
#-------------------------------------------------------------------------------
 
126
 
 
127
DAEMON_BUS = 'org.screenlets.ScreenletsDaemon'
 
128
 
 
129
DAEMON_PATH = '/org/screenlets/ScreenletsDaemon'
 
130
 
 
131
DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon'
 
132
 
 
133
#Other stuff
 
134
 
 
135
DEBUG_MODE              = True
 
136
 
 
137
DEBIAN = True
 
138
try:
 
139
    subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
 
140
except OSError:
 
141
    DEBIAN = False
 
142
 
 
143
UBUNTU = True
 
144
try:
 
145
    subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
 
146
except OSError:
 
147
    UBUNTU = False
 
148
 
 
149
#-------------------------------------------------------------------------------
 
150
# CLASSES
 
151
#-------------------------------------------------------------------------------
 
152
 
 
153
class DefaultMenuItem(object):
 
154
        """A container with constants for the default menuitems"""
 
155
        
 
156
        # default menuitem constants (is it right to increase like this?)
 
157
        NONE            = 0
 
158
        DELETE          = 1
 
159
        THEMES          = 2
 
160
        INFO            = 4
 
161
        SIZE            = 8
 
162
        WINDOW_MENU     = 16
 
163
        PROPERTIES      = 32
 
164
        DELETE          = 64
 
165
        QUIT            = 128
 
166
        QUIT_ALL        = 256
 
167
        # EXPERIMENTAL!! If you use this, the file menu.xml in the 
 
168
        # Screenlet's data-dir is used for generating the menu ...
 
169
        XML                     = 512
 
170
        ADD                     = 1024
 
171
        # the default items
 
172
        STANDARD        = 1|2|8|16|32|64|128|256|1024
 
173
 
 
174
 
 
175
class ScreenletTheme (dict):
 
176
        """ScreenletThemes are simple storages that allow loading files
 
177
        as svg-handles within a theme-directory. Each Screenlet can have 
 
178
        its own theme-directory. It is up to the Screenlet-developer if he
 
179
        wants to let his Screenlet support themes or not. Themes are 
 
180
        turned off by default - if your Screenlet uses Themes, just set the 
 
181
        attribute 'theme_name' to the name of the theme's dir you want to use.
 
182
        TODO: remove dict-inheritance"""
 
183
        
 
184
        # meta-info (set through theme.conf)
 
185
        __name__        = ''
 
186
        __author__      = ''
 
187
        __version__     = ''
 
188
        __info__        = ''
 
189
        
 
190
        # attributes
 
191
        path            = ""
 
192
        loaded          = False
 
193
        width           = 0
 
194
        height          = 0
 
195
        option_overrides = {}
 
196
        p_fdesc = None
 
197
        p_layout = None
 
198
        tooltip = None
 
199
        notify = None
 
200
        
 
201
 
 
202
        def __init__ (self, path):
 
203
                # set theme-path and load all files in path
 
204
                self.path = path
 
205
                self.svgs = {}
 
206
                self.pngs = {}  
 
207
                self.option_overrides = {}
 
208
                self.loaded = self.__load_all()
 
209
                if self.loaded == False:
 
210
                        raise Exception("Error while loading ScreenletTheme in: " + path)
 
211
        
 
212
        def __getattr__ (self, name):
 
213
                if name in ("width", "height"):
 
214
                        if self.loaded and len(self)>0:
 
215
                                size=self[0].get_dimension_data()
 
216
                                if name=="width":
 
217
                                        return size[0]
 
218
                                else:
 
219
                                        return size[1]
 
220
                else:
 
221
                        return object.__getattr__(self, name)
 
222
        
 
223
        def apply_option_overrides (self, screenlet):
 
224
                """Apply this theme's overridden options to the given Screenlet."""
 
225
                # disable the canvas-updates in the screenlet
 
226
                screenlet.disable_updates = True
 
227
                # theme_name needs special care (must be applied last)
 
228
                theme_name = ''
 
229
                # loop through overrides and appply them
 
230
                for name in self.option_overrides:
 
231
                        print "Override: " + name
 
232
                        o = screenlet.get_option_by_name(name)
 
233
                        if o and not o.protected:
 
234
                                if name == 'theme_name':
 
235
                                        # import/remember theme-name, but not apply yet
 
236
                                        theme_name = o.on_import(self.option_overrides[name])
 
237
                                else:
 
238
                                        # set option in screenlet
 
239
                                        setattr(screenlet, name, 
 
240
                                                o.on_import(self.option_overrides[name]))
 
241
                        else:
 
242
                                print "WARNING: Option '%s' not found or protected." % name
 
243
                # now apply theme
 
244
                if theme_name != '':
 
245
                        screenlet.theme_name = theme_name
 
246
                # re-enable updates and call redraw/reshape
 
247
                screenlet.disable_updates = False
 
248
                screenlet.redraw_canvas()
 
249
                screenlet.update_shape()
 
250
                
 
251
        def check_entry (self, filename):
 
252
                """Checks if a file with filename is loaded in this theme."""
 
253
                try:
 
254
                        if self[filename]:
 
255
                                return True
 
256
                except:
 
257
                        #raise Exception
 
258
                        return False
 
259
 
 
260
        def get_text_width(self, ctx, text, font):
 
261
                """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
 
262
                ctx.save()
 
263
                
 
264
                if self.p_layout == None :
 
265
        
 
266
                        self.p_layout = ctx.create_layout()
 
267
                else:
 
268
                        
 
269
                        ctx.update_layout(self.p_layout)
 
270
                self.p_fdesc = pango.FontDescription(font)
 
271
                self.p_layout.set_font_description(self.p_fdesc)
 
272
                self.p_layout.set_text(text)
 
273
                extents, lextents = self.p_layout.get_pixel_extents()
 
274
                ctx.restore()
 
275
                return extents[2]
 
276
 
 
277
        def get_text_extents(self, ctx, text, font):
 
278
                """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
 
279
                ctx.save()
 
280
                
 
281
                if self.p_layout == None :
 
282
        
 
283
                        self.p_layout = ctx.create_layout()
 
284
                else:
 
285
                        
 
286
                        ctx.update_layout(self.p_layout)
 
287
                self.p_fdesc = pango.FontDescription(font)
 
288
                self.p_layout.set_font_description(self.p_fdesc)
 
289
                self.p_layout.set_text(text)
 
290
                extents, lextents = self.p_layout.get_pixel_extents()
 
291
                ctx.restore()
 
292
                return extents
 
293
 
 
294
        def draw_text(self, ctx, text, x, y,  font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
 
295
                """@DEPRECATED Moved to Screenlets class: Draws text"""
 
296
                ctx.save()
 
297
                ctx.translate(x, y)
 
298
                if self.p_layout == None :
 
299
        
 
300
                        self.p_layout = ctx.create_layout()
 
301
                else:
 
302
                        
 
303
                        ctx.update_layout(self.p_layout)
 
304
                self.p_fdesc = pango.FontDescription()
 
305
                self.p_fdesc.set_family_static(font)
 
306
                self.p_fdesc.set_size(size * pango.SCALE)
 
307
                self.p_fdesc.set_weight(weight)
 
308
                self.p_layout.set_font_description(self.p_fdesc)
 
309
                self.p_layout.set_width(width * pango.SCALE)
 
310
                self.p_layout.set_alignment(allignment)
 
311
                self.p_layout.set_ellipsize(ellipsize)
 
312
                self.p_layout.set_markup(text)
 
313
                ctx.show_layout(self.p_layout)
 
314
                ctx.restore()
 
315
 
 
316
 
 
317
        def draw_circle(self,ctx,x,y,width,height,fill=True):
 
318
                """@DEPRECATED Moved to Screenlets class: Draws a circule"""
 
319
                ctx.save()
 
320
                ctx.translate(x, y)
 
321
                ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
 
322
                if fill:ctx.fill()
 
323
                else: ctx.stroke()
 
324
                ctx.restore()
 
325
 
 
326
        def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
 
327
                """@DEPRECATED Moved to Screenlets class: Draws a line"""
 
328
                ctx.save()
 
329
                ctx.move_to(start_x, start_y)
 
330
                ctx.set_line_width(line_width)
 
331
                ctx.rel_line_to(end_x, end_y)
 
332
                if close : ctx.close_path()
 
333
                if preserve: ctx.stroke_preserve()
 
334
                else: ctx.stroke()
 
335
                ctx.restore()
 
336
 
 
337
        def draw_rectangle(self,ctx,x,y,width,height,fill=True):
 
338
                """@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
 
339
                ctx.save()
 
340
                ctx.translate(x, y)
 
341
                ctx.rectangle (0,0,width,height)
 
342
                if fill:ctx.fill()
 
343
                else: ctx.stroke()
 
344
                ctx.restore()
 
345
 
 
346
        def draw_rounded_rectangle(self,ctx,x,y,rounded_angle,width,height,fill=True):
 
347
                """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
 
348
                ctx.save()
 
349
                ctx.translate(x, y)
 
350
                padding=0 # Padding from the edges of the window
 
351
                rounded=rounded_angle # How round to make the edges 20 is ok
 
352
                w = width
 
353
                h = height
 
354
 
 
355
                # Move to top corner
 
356
                ctx.move_to(0+padding+rounded, 0+padding)
 
357
                
 
358
                # Top right corner and round the edge
 
359
                ctx.line_to(w-padding-rounded, 0+padding)
 
360
                ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
 
361
        
 
362
                # Bottom right corner and round the edge
 
363
                ctx.line_to(w-padding, h-padding-rounded)
 
364
                ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
 
365
        
 
366
                # Bottom left corner and round the edge.
 
367
                ctx.line_to(0+padding+rounded, h-padding)
 
368
                ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
 
369
        
 
370
                # Top left corner and round the edge
 
371
                ctx.line_to(0+padding, 0+padding+rounded)
 
372
                ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
 
373
                
 
374
                # Fill in the shape.
 
375
                if fill:ctx.fill()
 
376
                else: ctx.stroke()
 
377
                ctx.restore()
 
378
 
 
379
        def get_image_size(self,pix):
 
380
                """@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
 
381
 
 
382
                pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
 
383
                iw = pixbuf.get_width()
 
384
                ih = pixbuf.get_height()
 
385
                puxbuf = None
 
386
                return iw,ih
 
387
 
 
388
        def draw_image(self,ctx,x,y, pix):
 
389
                """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
 
390
 
 
391
                ctx.save()
 
392
                ctx.translate(x, y)     
 
393
                pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
 
394
                format = cairo.FORMAT_RGB24
 
395
                if pixbuf.get_has_alpha():
 
396
                        format = cairo.FORMAT_ARGB32
 
397
 
 
398
                iw = pixbuf.get_width()
 
399
                ih = pixbuf.get_height()
 
400
                image = cairo.ImageSurface(format, iw, ih)
 
401
                image = ctx.set_source_pixbuf(pixbuf, 0, 0)
 
402
                
 
403
                ctx.paint()
 
404
                puxbuf = None
 
405
                image = None
 
406
                ctx.restore()
 
407
 
 
408
 
 
409
 
 
410
        def draw_scaled_image(self,ctx,x,y, pix, w, h):
 
411
                """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
 
412
 
 
413
                ctx.save()
 
414
                ctx.translate(x, y)     
 
415
                pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
 
416
                format = cairo.FORMAT_RGB24
 
417
                if pixbuf.get_has_alpha():
 
418
                        format = cairo.FORMAT_ARGB32
 
419
 
 
420
                iw = pixbuf.get_width()
 
421
                ih = pixbuf.get_height()
 
422
                image = cairo.ImageSurface(format, iw, ih)
 
423
 
 
424
                matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
 
425
                image = ctx.set_source_pixbuf(pixbuf, 0, 0)
 
426
                if image != None :image.set_matrix(matrix)
 
427
                ctx.paint()
 
428
                puxbuf = None
 
429
                image = None
 
430
                ctx.restore()
 
431
 
 
432
        def show_notification (self,text):
 
433
                """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
 
434
                if self.notify == None:
 
435
                        self.notify = Notify()
 
436
                        self.notify.text = text
 
437
                        self.notify.show()
 
438
 
 
439
        def hide_notification (self):
 
440
                """@DEPRECATED Moved to Screenlets class: hide notification window"""
 
441
                if self.notify != None:
 
442
                        self.notify.hide()
 
443
                        self.notify = None
 
444
 
 
445
        def show_tooltip (self,text,tooltipx,tooltipy):
 
446
                """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
 
447
                if self.tooltip == None:
 
448
                        self.tooltip = Tooltip(300, 400)
 
449
                        self.tooltip.text = text
 
450
                        self.tooltip.x    = tooltipx
 
451
                        self.tooltip.y    = tooltipy
 
452
                        self.tooltip.show()
 
453
 
 
454
        def hide_tooltip (self):
 
455
                """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
 
456
                if self.tooltip != None:
 
457
                        self.tooltip.hide()
 
458
                        self.tooltip = None             
 
459
 
 
460
        def has_overrides (self):
 
461
                """Check if this theme contains overrides for options."""
 
462
                return len(self.option_overrides) > 0
 
463
        
 
464
        def load_conf (self, filename):
 
465
                """Load a config-file from this theme's dir and save vars in list."""
 
466
                ini = utils.IniReader()
 
467
                if ini.load(filename):
 
468
                        if ini.has_section('Theme'):
 
469
                                self.__name__ = ini.get_option('name', section='Theme')
 
470
                                self.__author__ = ini.get_option('author', section='Theme')
 
471
                                self.__version__ = ini.get_option('version', section='Theme')
 
472
                                self.__info__ = ini.get_option('info', section='Theme')
 
473
                        if ini.has_section('Options'):
 
474
                                opts = ini.list_options(section='Options')
 
475
                                if opts:
 
476
                                        for o in opts:
 
477
                                                self.option_overrides[o[0]] = o[1]
 
478
                        print "Loaded theme config from:", filename
 
479
                        print "\tName: " + str(self.__name__)
 
480
                        print "\tAuthor: " +str(self.__author__)
 
481
                        print "\tVersion: " +str(self.__version__)
 
482
                        print "\tInfo: " +str(self.__info__)
 
483
                else:
 
484
                        print "Failed to theme config from", filename
 
485
        
 
486
 
 
487
        def load_svg (self, filename):
 
488
                """Load an SVG-file into this theme and reference it as ref_name."""
 
489
                if self.has_key(filename):
 
490
                        del self[filename]
 
491
                try:
 
492
                        self[filename] = rsvg.Handle(self.path + "/" + filename)
 
493
                        self.svgs[filename[:-4]] = self[filename]
 
494
                        if self[filename] != None:
 
495
                                # set width/height
 
496
                                size=self[filename].get_dimension_data()
 
497
                                if size:
 
498
                                        self.width = size[0]
 
499
                                        self.height = size[1]
 
500
                        return True
 
501
                except NameError, ex: 
 
502
                        self[filename] =  gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
 
503
                        self.svgs[filename[:-4]] = self[filename]
 
504
                        if self[filename] != None:
 
505
                                # set width/height
 
506
                                self.width = self[filename].get_width()
 
507
                                self.height = self[filename].get_height()
 
508
                        print str(ex)
 
509
                        return True
 
510
 
 
511
                else:
 
512
                        return False
 
513
                #self[filename] = None
 
514
        
 
515
        def load_png (self, filename):
 
516
                """Load a PNG-file into this theme and reference it as ref_name."""
 
517
                if self.has_key(filename):
 
518
                        del self[filename]
 
519
                self[filename] = cairo.ImageSurface.create_from_png(self.path + 
 
520
                        "/" + filename)
 
521
                self.pngs[filename[:-4]] = self[filename]
 
522
                if self[filename] != None:
 
523
                        return True
 
524
                else:
 
525
                        return False
 
526
                #self[filename] = None
 
527
        
 
528
        def __load_all (self):
 
529
                """Load all files in the theme's path. Currently only loads SVGs and
 
530
                PNGs."""
 
531
                # clear overrides
 
532
                #self.__option_overrides = {}
 
533
                # read dir
 
534
                dirlst = glob.glob(self.path + '/*')
 
535
                if len(dirlst)==0:
 
536
                        return False
 
537
                plen = len(self.path) + 1
 
538
                for file in dirlst:
 
539
                        fname = file[plen:]
 
540
                        if fname.endswith('.svg'):
 
541
                                # svg file
 
542
                                if self.load_svg(fname) == False:
 
543
                                        return False
 
544
                        elif fname.endswith('.png'):
 
545
                                # svg file
 
546
                                if self.load_png(fname) == False:
 
547
                                        return False
 
548
                        elif fname == "theme.conf":
 
549
                                print "theme.conf found! Loading option-overrides."
 
550
                                # theme.conf
 
551
                                if self.load_conf(file) == False:
 
552
                                        return False
 
553
#               print "Theme %s loaded from %s" % (self.__name__, self.path) 
 
554
                return True
 
555
        
 
556
        def reload (self):
 
557
                """Re-Load all files in the theme's path."""
 
558
                self.free()
 
559
                self.__load_all()
 
560
        
 
561
        # TODO: fix function, rsvg handles are not freed properly
 
562
        def free (self):
 
563
                """Deletes the Theme's contents and frees all rsvg-handles.
 
564
                TODO: freeing rsvg-handles does NOT work for some reason"""
 
565
                self.option_overrides.clear()
 
566
                for filename in self:
 
567
                        try:
 
568
                                self[filename].free()
 
569
                        except AttributeError:pass
 
570
                        #self[filename].close()
 
571
                        del filename
 
572
                self.clear()
 
573
        
 
574
        # TEST: render-function
 
575
        # should be used like "theme.render(context, 'notes-bg')" and then use
 
576
        # either an svg or png image
 
577
        def render (self, ctx, name):
 
578
                """Render an image from within this theme to the given context. This
 
579
                function can EITHER use png OR svg images, so it is possible to 
 
580
                create themes using both image-formats when a Screenlet uses this
 
581
                function for drawing its images. The image name has to be defined
 
582
                without the extension and the function will automatically select 
 
583
                the available one (SVG is prefered over PNG)."""
 
584
 
 
585
                ### Render Graphics even if rsvg is not available###
 
586
                if os.path.isfile (self.path + '/' + name + '.svg'):
 
587
 
 
588
                        try:
 
589
                                self.svgs[name].render_cairo(ctx)
 
590
                        except:
 
591
                                try:
 
592
                                        ctx.set_source_pixbuf(self.svgs[name], 0, 0)
 
593
                                
 
594
                                        ctx.paint()
 
595
                                        pixbuf = None
 
596
                                except TypeError:       
 
597
                                        ctx.set_source_surface(self.pngs[name], 0, 0)
 
598
                                        ctx.paint()
 
599
 
 
600
                elif os.path.isfile (self.path + '/' + name + '.png'):
 
601
                        ctx.set_source_surface(self.pngs[name], 0, 0)
 
602
                        ctx.paint()
 
603
                        
 
604
 
 
605
 
 
606
        def render_png_colorized(self, ctx, name,color):
 
607
                # Scale the pixmap
 
608
                ctx.set_source_rgba(color[0], color[1], color[2], color[3])
 
609
                ctx.set_source_surface(self.pngs[name], 0, 0)
 
610
                ctx.mask_surface(image, 0, 0)
 
611
                ctx.stroke()
 
612
 
 
613
 
 
614
 
 
615
class Screenlet (gobject.GObject, EditableOptions, Drawing):
 
616
        """A Screenlet is a (i.e. contains a) shaped gtk-window that is
 
617
        fully invisible by default. Subclasses of Screenlet can render 
 
618
        their owner-drawn graphics on fully transparent background."""
 
619
        
 
620
        # default meta-info for Screenlets
 
621
        __name__        = _('No name set for this Screenlet')
 
622
        __version__     = '0.0'
 
623
        __author__      = _('No author defined for this Screenlet')
 
624
        __desc__        = _('No info set for this Screenlet')
 
625
        __requires__    = []
 
626
        #__target_version__ = '0.0.0'
 
627
        #__backend_version__ = '0.0.1'
 
628
        
 
629
        # attributes (TODO: remove them here and add them to the constructor,
 
630
        # because they only should exist per instance)
 
631
        id                              = ''            # id-attribute for handling instances
 
632
        window                  = None          # the gtk.Window behind the scenes
 
633
        theme                   = None          # the assigned ScreenletTheme
 
634
        uses_theme              = True          # flag indicating whether Screenlet uses themes
 
635
        draw_buttons            = True          
 
636
        show_buttons            = True
 
637
        menu                    = None          # the right-click gtk.Menu
 
638
        is_dragged              = False         # TODO: make this work
 
639
        quit_on_close   = True          # if True, closing this instance quits gtk
 
640
        saving_enabled  = True          # if False, saving is disabled
 
641
        dragging_over   = False         # true if something is dragged over
 
642
        disable_updates = False         # to temporarily avoid refresh/reshape
 
643
        p_context               = None          # PangoContext
 
644
        p_layout                = None          # PangoLayout
 
645
        
 
646
        # default editable options, available for all Screenlets
 
647
        x = 0
 
648
        y = 0
 
649
        mousex = 0
 
650
        mousey = 0
 
651
        mouse_is_over = False
 
652
        width   = 100
 
653
        height  = 100
 
654
        scale   = 1.0
 
655
        opacity = 1.0
 
656
        theme_name              = ""
 
657
        is_visible              = True
 
658
        is_sticky               = False
 
659
        is_widget               = False
 
660
        keep_above              = True
 
661
        keep_below              = False
 
662
        skip_pager              = True
 
663
        first_run               = False
 
664
        skip_taskbar    = True
 
665
        lock_position   = False
 
666
        allow_option_override   = True          # if False, overrides are ignored
 
667
        ask_on_option_override  = True          # if True, overrides need confirmation
 
668
        ignore_requirements     = False         # if True, DEB requirements are ignored
 
669
        resize_on_scroll = True
 
670
        has_started = False
 
671
        has_focus = False
 
672
        # internals (deprecated? we still don't get the end of a begin_move_drag)
 
673
        gtk_icon_theme = None
 
674
        __lastx = 0
 
675
        __lasty = 0
 
676
        p_fdesc = None
 
677
        p_layout = None
 
678
        tooltip = None
 
679
        notify = None
 
680
        # some menuitems (needed for checking/unchecking)
 
681
        # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
 
682
        __mi_keep_above = None
 
683
        __mi_keep_below = None
 
684
        __mi_widget = None
 
685
        __mi_sticky = None
 
686
        __mi_lock = None        
 
687
        # for custom signals (which aren't acutally used ... yet)
 
688
        __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
 
689
                gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
 
690
 
 
691
        def __init__ (self, id='', width=100, height=100, parent_window=None, 
 
692
                show_window=True, is_widget=False, is_sticky=False, 
 
693
                uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None, 
 
694
                enable_saving=True, service_class=services.ScreenletService,
 
695
                uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
 
696
                """Constructor - should only be subclassed"""
 
697
                
 
698
                # call gobject and EditableOptions superclasses
 
699
                super(Screenlet, self).__init__()
 
700
                EditableOptions.__init__(self)
 
701
                # init properties
 
702
                self.id                         = id
 
703
                self.session            = session
 
704
                self.service            = None
 
705
                self.__desc__           = self.__doc__
 
706
 
 
707
                # if we have an id and a service-class, register our service
 
708
                if self.id and service_class:
 
709
                        self.register_service(service_class)
 
710
                        # notify service about adding this instance
 
711
                        self.service.instance_added(self.id)
 
712
                self.width                      = width
 
713
                self.height             = height
 
714
                self.is_dragged         = False
 
715
                self.__path__           = path
 
716
                self.saving_enabled     = enable_saving         # used by session
 
717
                # set some attributes without calling __setattr__
 
718
                self.__dict__['theme_name'] = ""
 
719
                self.__dict__['is_widget']      = is_widget
 
720
                self.__dict__['is_sticky']      = is_sticky
 
721
                self.__dict__['draw_buttons']   = draw_buttons
 
722
                self.resize_on_scroll = resize_on_scroll
 
723
                self.__dict__['x'] = 0
 
724
                self.__dict__['y'] = 0
 
725
                # TEST: set scale relative to theme size (NOT WORKING)
 
726
                #self.__dict__['scale'] = width/100.0
 
727
                # /TEST
 
728
                # shape bitmap
 
729
                self.__shape_bitmap = None
 
730
                self.__shape_bitmap_width = 0
 
731
                self.__shape_bitmap_height = 0
 
732
                # "editable" options, first create a group
 
733
                self.add_options_group('Screenlet', 
 
734
                        _('The basic settings for this Screenlet-instance.'))
 
735
                # if this Screenlet uses themes, add theme-specific options
 
736
                # (NOTE: this option became hidden with 0.0.9 and doesn't use
 
737
                # get_available_themes anymore for showing the choices)
 
738
                self.gtk_icon_theme = gtk.icon_theme_get_default()
 
739
                self.load_buttons(None)
 
740
                self.gtk_icon_theme.connect("changed", self.load_buttons)
 
741
                if draw_buttons: self.draw_buttons = True
 
742
                else: self.draw_buttons = False
 
743
                if uses_theme:
 
744
                        self.uses_theme = True
 
745
                        self.add_option(StringOption('Screenlet', 'theme_name', 
 
746
                                'default', '', '', hidden=True))
 
747
                # create/add options
 
748
                self.add_option(IntOption('Screenlet', 'x', 
 
749
                        0, _('X-Position'), _('The X-position of this Screenlet ...'), 
 
750
                        min=0, max=gtk.gdk.screen_width()))
 
751
                self.add_option(IntOption('Screenlet', 'y', 
 
752
                        0, _('Y-Position'), _('The Y-position of this Screenlet ...'), 
 
753
                        min=0, max=gtk.gdk.screen_height()))
 
754
                self.add_option(IntOption('Screenlet', 'width', 
 
755
                        width, _('Width'), _('The width of this Screenlet ...'), 
 
756
                        min=16, max=1000, hidden=True))
 
757
                self.add_option(IntOption('Screenlet', 'height', 
 
758
                        height, _('Height'), _('The height of this Screenlet ...'), 
 
759
                        min=16, max=1000, hidden=True))
 
760
                self.add_option(FloatOption('Screenlet', 'scale', 
 
761
                        self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'), 
 
762
                        min=0.1, max=10.0, digits=2, increment=0.1))
 
763
                self.add_option(FloatOption('Screenlet', 'opacity', 
 
764
                        self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'), 
 
765
                        min=0.1, max=1.0, digits=2, increment=0.1))
 
766
                self.add_option(BoolOption('Screenlet', 'is_sticky', 
 
767
                        is_sticky, _('Stick to Desktop'), 
 
768
                        _('Show this Screenlet on all workspaces ...')))
 
769
                self.add_option(BoolOption('Screenlet', 'is_widget', 
 
770
                        is_widget, _('Treat as Widget'), 
 
771
                        _('Treat this Screenlet as a "Widget" ...')))
 
772
                self.add_option(BoolOption('Screenlet', 'is_dragged', 
 
773
                        self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True))
 
774
                self.add_option(BoolOption('Screenlet', 'is_sizable', 
 
775
                        is_sizable, "Can the screenlet be resized","is_sizable", hidden=True))
 
776
                self.add_option(BoolOption('Screenlet', 'is_visible', 
 
777
                        self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True))
 
778
                self.add_option(BoolOption('Screenlet', 'lock_position', 
 
779
                        self.lock_position, _('Lock position'), 
 
780
                        _('Stop the screenlet from being moved...')))
 
781
                self.add_option(BoolOption('Screenlet', 'keep_above', 
 
782
                        self.keep_above, _('Keep above'), 
 
783
                        _('Keep this Screenlet above other windows ...')))
 
784
                self.add_option(BoolOption('Screenlet', 'keep_below', 
 
785
                        self.keep_below, _('Keep below'), 
 
786
                        _('Keep this Screenlet below other windows ...')))
 
787
                self.add_option(BoolOption('Screenlet', 'draw_buttons', 
 
788
                        self.draw_buttons, _('Draw button controls'), 
 
789
                        _('Draw buttons in top right corner')))
 
790
                self.add_option(BoolOption('Screenlet', 'skip_pager', 
 
791
                        self.skip_pager, _('Skip Pager'), 
 
792
                        _('Set this Screenlet to show/hide in pagers ...')))
 
793
                self.add_option(BoolOption('Screenlet', 'skip_taskbar', 
 
794
                        self.skip_pager, _('Skip Taskbar'), 
 
795
                        _('Set this Screenlet to show/hide in taskbars ...')))
 
796
                self.add_option(BoolOption('Screenlet', 'resize_on_scroll', 
 
797
                        self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll"))
 
798
                self.add_option(BoolOption('Screenlet', 'ignore_requirements', 
 
799
                        self.ignore_requirements, _('Ignore requirements'), 
 
800
                        _('Set this Screenlet to ignore/demand DEB requirements ...')))
 
801
                if uses_theme:
 
802
                        self.ask_on_option_override = ask_on_option_override
 
803
                        self.add_option(BoolOption('Screenlet', 'allow_option_override', 
 
804
                        self.allow_option_override, _('Allow overriding Options'), 
 
805
                                _('Allow themes to override options in this screenlet ...')))
 
806
                        self.add_option(BoolOption('Screenlet', 'ask_on_option_override', 
 
807
                                self.ask_on_option_override, _('Ask on Override'), 
 
808
                                _('Show a confirmation-dialog when a theme wants to override ')+\
 
809
                                _('the current options of this Screenlet ...')))
 
810
                # disable width/height
 
811
                self.disable_option('width')
 
812
                self.disable_option('height')
 
813
                # create window
 
814
                self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
 
815
                if parent_window:
 
816
                        self.window.set_parent_window(parent_window)
 
817
                        self.window.set_transient_for(parent_window)
 
818
                        self.window.set_destroy_with_parent(True)
 
819
                self.window.resize(width, height)
 
820
                self.window.set_decorated(False)
 
821
                self.window.set_app_paintable(True)
 
822
                # create pango layout, if active
 
823
                if uses_pango:
 
824
                        self.p_context = self.window.get_pango_context()
 
825
                        if self.p_context:
 
826
                                self.p_layout = pango.Layout(self.p_context)
 
827
                                self.p_layout.set_font_description(\
 
828
                                        pango.FontDescription("Sans 12"))
 
829
                # set type hint
 
830
 
 
831
                if str(sensors.sys_get_window_manager()).lower() == 'kwin':
 
832
                        print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
 
833
                        #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
 
834
                elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
 
835
                        print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
 
836
                else:
 
837
                        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
 
838
                self.window.set_keep_above(True)
 
839
                self.window.set_skip_taskbar_hint(True)
 
840
                self.window.set_skip_pager_hint(True)
 
841
                if is_sticky:
 
842
                        self.window.stick()
 
843
                self.alpha_screen_changed(self.window)
 
844
                self.update_shape()
 
845
                #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
 
846
                self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
 
847
                self.window.connect("composited-changed", self.composite_changed)
 
848
                self.window.connect("delete_event", self.delete_event)
 
849
                self.window.connect("destroy", self.destroy)
 
850
                self.window.connect("expose_event", self.expose)
 
851
                self.window.connect("button-press-event", self.button_press)
 
852
                self.window.connect("button-release-event", self.button_release)
 
853
                self.window.connect("configure-event", self.configure_event)
 
854
                self.window.connect("screen-changed", self.alpha_screen_changed)
 
855
                self.window.connect("realize", self.realize_event)
 
856
                self.window.connect("enter-notify-event", self.enter_notify_event)
 
857
                self.window.connect("leave-notify-event", self.leave_notify_event)
 
858
                self.window.connect("focus-in-event", self.focus_in_event)
 
859
                self.window.connect("focus-out-event", self.focus_out_event)
 
860
                self.window.connect("scroll-event", self.scroll_event)
 
861
                self.window.connect("motion-notify-event",self.motion_notify_event)
 
862
                self.window.connect("map-event", self.map_event)
 
863
                self.window.connect("unmap-event", self.unmap_event)
 
864
                # add key-handlers (TODO: use keyword-attrib to activate?)
 
865
                self.window.connect("key-press-event", self.key_press)
 
866
                # drag/drop support (NOTE: still experimental and incomplete)
 
867
                if drag_drop:
 
868
                        self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
 
869
                                gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL, 
 
870
                                [("text/plain", 0, 0), 
 
871
                                ("image", 0, 1),
 
872
                                ("text/uri-list", 0, 2)], 
 
873
                                gtk.gdk.ACTION_COPY)
 
874
                        self.window.connect("drag_data_received", self.drag_data_received)
 
875
                        self.window.connect("drag-begin", self.drag_begin)
 
876
                        self.window.connect("drag-end", self.drag_end)
 
877
                        self.window.connect("drag-motion", self.drag_motion)
 
878
                        self.window.connect("drag-leave", self.drag_leave)
 
879
                # create menu
 
880
                self.menu = gtk.Menu()
 
881
                # show window so it can realize , but hiding it so we can show it only when atributes have been set , this fixes some placement errors arround the screen egde
 
882
 
 
883
                        
 
884
                if show_window:
 
885
                        self.window.show()
 
886
#                       print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id
 
887
                        if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'):
 
888
                                self.first_run = True
 
889
                        self.window.hide()      
 
890
 
 
891
                #Make opacity available only when composite is enabled
 
892
                if not self.window.is_composited () :
 
893
                        self.disable_option('opacity')
 
894
 
 
895
        def __setattr__ (self, name, value):
 
896
                # set the value in GObject (ESSENTIAL!!!!)
 
897
                self.on_before_set_atribute(name, value)
 
898
                gobject.GObject.__setattr__(self, name, value)
 
899
                # And do other actions
 
900
                if name=="x" or name=="y":
 
901
                        if self.has_started:
 
902
                                self.window.move(self.x, self.y)
 
903
                elif name == 'opacity':
 
904
                        self.window.set_opacity(value)
 
905
                elif name == 'scale':
 
906
                        self.window.resize(int(self.width * self.scale), 
 
907
                                int(self.height * self.scale))
 
908
                        # TODO: call on_resize-handler here !!!!
 
909
                        self.on_scale()
 
910
                        self.redraw_canvas()
 
911
                        self.update_shape()
 
912
 
 
913
 
 
914
                elif name == "theme_name":
 
915
                        #self.__dict__ ['theme_name'] = value
 
916
                        #self.load_theme(self.get_theme_dir() + value)
 
917
                        # load theme
 
918
                        print "Theme set to: '%s'" % value
 
919
                        path = self.find_theme(value)
 
920
                        if path:
 
921
                                self.load_theme(path)
 
922
                        #self.load_first_theme(value)
 
923
                        self.redraw_canvas()
 
924
                        self.update_shape()
 
925
                elif name in ("width", "height"):
 
926
                        #self.__dict__ [name] = value
 
927
                        if self.window:
 
928
                                self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
 
929
                                #self.redraw_canvas()
 
930
                                self.update_shape()
 
931
                elif name == "is_widget":
 
932
                        if self.has_started:
 
933
                                self.set_is_widget(value)
 
934
                elif name == "is_visible":
 
935
                        if self.has_started:
 
936
                                if value == True:
 
937
                                        self.reshow()
 
938
                                else:
 
939
                                        self.window.hide()
 
940
                elif name == "is_sticky":
 
941
                        if value == True:
 
942
                                self.window.stick()
 
943
                        else:
 
944
                                self.window.unstick()
 
945
                        #if self.__mi_sticky:
 
946
                        #       self.__mi_sticky.set_active(value)
 
947
                elif name == "keep_above":
 
948
                        if self.has_started == True:
 
949
                                self.window.set_keep_above(bool(value))
 
950
                        #self.__mi_keep_above.set_active(value)
 
951
                elif name == "keep_below":
 
952
                        if self.has_started == True:
 
953
                                self.window.set_keep_below(bool(value))
 
954
                        #self.__mi_keep_below.set_active(value)
 
955
                elif name == "skip_pager":
 
956
                        if self.window.window:
 
957
                                self.window.window.set_skip_pager_hint(bool(value))
 
958
                elif name == "skip_taskbar":
 
959
                        if self.window.window:
 
960
                                self.window.window.set_skip_taskbar_hint(bool(value))
 
961
                # NOTE: This is the new recommended way of storing options in real-time
 
962
                #       (we access the backend through the session here)
 
963
                if self.saving_enabled:
 
964
                        o = self.get_option_by_name(name)
 
965
                        if o != None:
 
966
                                self.session.backend.save_option(self.id, o.name, 
 
967
                                        o.on_export(value))
 
968
                self.on_after_set_atribute(name, value)
 
969
                # /TEST
 
970
        
 
971
        #-----------------------------------------------------------------------
 
972
        # Screenlet's public functions
 
973
        #-----------------------------------------------------------------------
 
974
        
 
975
        def check_requirements (self):
 
976
                '''Checks if required DEB packages are installed'''
 
977
 
 
978
                req_feedback = ""
 
979
                fail = False
 
980
 
 
981
#               operators=['>', '=', '<']
 
982
 
 
983
                commandstr = 'apt-cache policy %s 2>/dev/null | sed -n "2 p" | grep -v ":[ \t]*([a-z \t]*)" | sed -r -e "s/(\s*[^\s]+:\s*)(.*)/\\2/"'
 
984
                for req in self.__requires__:
 
985
                        operator = None
 
986
#                       req = req.replace(' ', '')
 
987
                        if req.find('(') != -1:
 
988
                                # package version is specified with an operator (no logical operators supported yet!)
 
989
                                pos = req.find('(')
 
990
                                package = req[:pos].strip()
 
991
                                version_str = req[pos+1:]
 
992
                                version_str = version_str[:version_str.find(')')]
 
993
                                while version_str.find('  ') != -1:
 
994
                                        version_str = req.replace('  ', ' ')
 
995
                                res = version_str.split(' ')
 
996
                                version = res[1]
 
997
                                operator = res[0]
 
998
                        else:
 
999
                                # when only package name is specified
 
1000
                                package = req
 
1001
                                # version of the deb package if unspecified
 
1002
                                version = _("?")
 
1003
 
 
1004
                        installed_version = os.popen(commandstr % package).readline().replace('\n', '')
 
1005
 
 
1006
                        if len(installed_version) < 1:
 
1007
                                req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version}
 
1008
                                fail = True
 
1009
                        else:
 
1010
                                req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version}
 
1011
                                # will fail only if dpkg says that version is too old
 
1012
                                # otherwise it's responsibility of developer to provide
 
1013
                                # correct version id and operator (won't detect problems with these)
 
1014
                                if operator is not None:
 
1015
                                        comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\""
 
1016
#                                       print comp_command
 
1017
                                        if subprocess.call(comp_command, shell=True) != 0:
 
1018
                                                fail = True
 
1019
                if fail:
 
1020
                        screenlets.show_message (self,_("Requirements for the Screenlet are not satisfied! Use the package manager of your system to install required packages.\n\nREQUIREMENTS:\n%s") % req_feedback, "Requirements not satisfied")
 
1021
        
 
1022
        def add_default_menuitems (self, flags=DefaultMenuItem.STANDARD):
 
1023
                """Appends the default menu-items to self.menu. You can add on OR'ed
 
1024
                   flag with DefaultMenuItems you want to add."""
 
1025
                if not self.has_started: print 'WARNING - add_default_menuitems and add_menuitems should be set in on_init ,menu values will be displayed incorrectly'
 
1026
                
 
1027
                menu = self.menu
 
1028
                
 
1029
                # children already exist? add separator
 
1030
                if len(menu.get_children()) > 0:
 
1031
                        self.add_menuitem("", "-")
 
1032
                # EXPERIMENTAL:
 
1033
                if flags & DefaultMenuItem.XML:
 
1034
                        # create XML-menu from screenletpath/menu.xml
 
1035
                        xfile = self.get_screenlet_dir() + "/menu.xml"
 
1036
                        xmlmenu = screenlets.menu.create_menu_from_file(xfile, 
 
1037
                                self.menuitem_callback)
 
1038
                        if xmlmenu:
 
1039
                                self.menu = xmlmenu
 
1040
                # add size-selection
 
1041
                if flags & DefaultMenuItem.SIZE:
 
1042
                        size_item = gtk.MenuItem(_("Size"))
 
1043
                        size_item.show()
 
1044
                        size_menu = gtk.Menu()
 
1045
                        menu.append(size_item)
 
1046
                        size_item.set_submenu(size_menu)
 
1047
                        #for i in xrange(10):
 
1048
                        for i in (0.2,0.3,0.4, 0.5,0.6, 0.7,0.8,0.9, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 7.5, 10):
 
1049
                                s = str(int(i * 100))
 
1050
                                item = gtk.MenuItem(s + " %")
 
1051
                                item.connect("activate", self.menuitem_callback, 
 
1052
                                        "scale:"+str(i))
 
1053
                                item.show()
 
1054
                                size_menu.append(item)
 
1055
                # create theme-selection menu
 
1056
                if flags & DefaultMenuItem.THEMES:
 
1057
                        themes_item = gtk.MenuItem(_("Theme"))
 
1058
                        themes_item.show()
 
1059
                        themes_menu = gtk.Menu()
 
1060
                        menu.append(themes_item)
 
1061
                        themes_item.set_submenu(themes_menu)
 
1062
                        # create theme-list from theme-directory
 
1063
                        lst = self.get_available_themes()
 
1064
                        for tname in lst:
 
1065
                                item = gtk.MenuItem(tname)
 
1066
                                item.connect("activate", self.menuitem_callback, "theme:"+tname)
 
1067
                                item.show()
 
1068
                                themes_menu.append(item)
 
1069
 
 
1070
                # add window-options menu
 
1071
                if flags & DefaultMenuItem.WINDOW_MENU:
 
1072
                        winmenu_item = gtk.MenuItem(_("Window"))
 
1073
                        winmenu_item.show()
 
1074
                        winmenu_menu = gtk.Menu()
 
1075
                        menu.append(winmenu_item)
 
1076
                        winmenu_item.set_submenu(winmenu_menu)
 
1077
                        # add "lock"-menuitem
 
1078
                        self.__mi_lock = item = gtk.CheckMenuItem(_("Lock"))
 
1079
                        item.set_active(self.lock_position)
 
1080
                        item.connect("activate", self.menuitem_callback, 
 
1081
                                "option:lock")
 
1082
                        item.show()
 
1083
                        winmenu_menu.append(item)
 
1084
                        # add "Sticky"-menuitem
 
1085
                        self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky"))
 
1086
                        item.set_active(self.is_sticky)
 
1087
                        item.connect("activate", self.menuitem_callback, 
 
1088
                                "option:sticky")
 
1089
                        item.show()
 
1090
                        winmenu_menu.append(item)
 
1091
                        # add "Widget"-menuitem
 
1092
                        self.__mi_widget = item = gtk.CheckMenuItem(_("Widget"))
 
1093
                        item.set_active(self.is_widget)
 
1094
                        item.connect("activate", self.menuitem_callback, 
 
1095
                                "option:widget")
 
1096
                        item.show()
 
1097
                        winmenu_menu.append(item)
 
1098
                        # add "Keep above"-menuitem
 
1099
                        self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above"))
 
1100
                        item.set_active(self.keep_above)
 
1101
                        item.connect("activate", self.menuitem_callback, 
 
1102
                                "option:keep_above")
 
1103
                        item.show()
 
1104
                        winmenu_menu.append(item)
 
1105
                        # add "Keep Below"-menuitem
 
1106
                        self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below"))
 
1107
                        item.set_active(self.keep_below)
 
1108
                        item.connect("activate", self.menuitem_callback, 
 
1109
                                "option:keep_below")
 
1110
                        item.show()
 
1111
                        winmenu_menu.append(item)
 
1112
 
 
1113
                # add Settings item
 
1114
                if flags & DefaultMenuItem.PROPERTIES:
 
1115
                        add_menuitem(menu, "-", self.menuitem_callback, "")
 
1116
                        add_menuitem(menu, _("Properties..."), self.menuitem_callback, "options")
 
1117
                # add info item
 
1118
                if flags & DefaultMenuItem.INFO:
 
1119
                        add_menuitem(menu, _("Info..."), self.menuitem_callback, "info")
 
1120
                # add delete item
 
1121
                if flags & DefaultMenuItem.ADD:
 
1122
                        add_menuitem(menu, "-", self.menuitem_callback, "")
 
1123
                        add_menuitem(menu, _("Add one more %s") % self.get_short_name(), self.menuitem_callback, "add")
 
1124
                # add delete item
 
1125
                if flags & DefaultMenuItem.DELETE:
 
1126
                        add_menuitem(menu, _("Delete this %s") % self.get_short_name(), self.menuitem_callback, "delete")
 
1127
                # add Quit item
 
1128
                if flags & DefaultMenuItem.QUIT:
 
1129
                        add_menuitem(menu, "-", self.menuitem_callback, "")
 
1130
                        add_menuitem(menu, _("Quit this %s") % self.get_short_name(), self.menuitem_callback, "quit_instance")
 
1131
                # add Quit-all item
 
1132
                if flags & DefaultMenuItem.QUIT_ALL:
 
1133
                        add_menuitem(menu, _("Quit all %ss") % self.get_short_name(), self.menuitem_callback, "quit")
 
1134
 
 
1135
        def add_menuitem (self, id, label, callback=None):
 
1136
                """Simple way to add menuitems to a right-click menu.
 
1137
                This function wraps screenlets.menu.add_menuitem.
 
1138
                For backwards compatibility, the order of the parameters
 
1139
                to this function is switched."""
 
1140
                if not self.has_started: print 'WARNING - add_default_menuitems and add_menuitems should be set in on_init ,menu values will be displayed incorrectly'
 
1141
                if callback is None:
 
1142
                        callback = self.menuitem_callback
 
1143
                # call menu.add_menuitem
 
1144
                return add_menuitem(self.menu, label, callback, id)
 
1145
        
 
1146
        def add_submenuitem (self, id, label, lst, callback=None):
 
1147
                """Simple way to add submenuitems to the right-click menu through a list."""
 
1148
                if not self.has_started: print 'WARNING - add_default_menuitems and add_menuitems should be set in on_init ,menu values will be displayed incorrectly'
 
1149
 
 
1150
                submenu = gtk.MenuItem(label)
 
1151
                submenu.show()
 
1152
                sub_menu = gtk.Menu()
 
1153
                self.menu.append(submenu)
 
1154
                submenu.set_submenu(sub_menu)
 
1155
                        # create theme-list from theme-directory
 
1156
                
 
1157
                for tname in lst:
 
1158
                        item = gtk.MenuItem(tname)
 
1159
                        item.connect("activate", self.menuitem_callback, 
 
1160
                                tname)
 
1161
                        item.show()
 
1162
                        sub_menu.append(item)
 
1163
 
 
1164
                return submenu
 
1165
 
 
1166
 
 
1167
 
 
1168
        def load_buttons(self, event):
 
1169
                self.closeb = self.gtk_icon_theme.load_icon ("gtk-close", 16, 0)
 
1170
                self.prop = self.gtk_icon_theme.load_icon ("gtk-properties", 16, 0)
 
1171
 
 
1172
        def create_buttons(self):
 
1173
 
 
1174
                ctx = self.window.window.cairo_create()
 
1175
                ctx.save()
 
1176
                #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
 
1177
                #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
 
1178
                #close = theme1.load_icon ("gtk-close", 16, 0)
 
1179
                #prop = theme1.load_icon ("gtk-properties", 16, 0)
 
1180
                #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
 
1181
                #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
 
1182
                #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
 
1183
                ctx.translate((self.width*self.scale)-16,0)
 
1184
                ctx.set_source_pixbuf(self.closeb, 0, 0)
 
1185
                ctx.paint()
 
1186
                ctx.restore()
 
1187
                ctx.save()      
 
1188
                ctx.translate((self.width*self.scale)-32,0)
 
1189
                ctx.set_source_pixbuf(self.prop, 0, 0)
 
1190
                ctx.paint()
 
1191
                ctx.restore()
 
1192
 
 
1193
        def clear_cairo_context (self, ctx):
 
1194
                """Fills the given cairo.Context with fully transparent white."""
 
1195
                ctx.save()
 
1196
                ctx.set_source_rgba(1, 1, 1, 0)
 
1197
                ctx.set_operator (cairo.OPERATOR_SOURCE)
 
1198
                ctx.paint()
 
1199
                ctx.restore()
 
1200
 
 
1201
        def close (self):
 
1202
                """Close this Screenlet
 
1203
                   TODO: send close-notify instead of destroying window?"""
 
1204
                #self.save_settings()
 
1205
                self.window.unmap()
 
1206
                self.window.destroy()
 
1207
                #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
 
1208
        
 
1209
        def create_drag_icon (self):
 
1210
                """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
 
1211
                with the icon and the mask. To supply your own icon you can use the
 
1212
                on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
 
1213
                w = self.width
 
1214
                h = self.height
 
1215
                icon, mask = self.on_create_drag_icon()
 
1216
                if icon == None:
 
1217
                        # create icon
 
1218
                        icon = gtk.gdk.Pixmap(self.window.window, w, h)
 
1219
                        ctx = icon.cairo_create()
 
1220
                        self.clear_cairo_context(ctx)
 
1221
                        self.on_draw(ctx)
 
1222
                if mask == None:
 
1223
                        # create mask
 
1224
                        mask = gtk.gdk.Pixmap(self.window.window, w, h)
 
1225
                        ctx = mask.cairo_create()
 
1226
                        self.clear_cairo_context(ctx)
 
1227
                        self.on_draw_shape(ctx)
 
1228
                return (icon, mask)
 
1229
        
 
1230
        def enable_saving (self, enabled=True):
 
1231
                """Enable/Disable realtime-saving of options."""
 
1232
                self.saving_enabled = enabled
 
1233
        
 
1234
        def find_theme (self, name):
 
1235
                """Find the best occurence of a theme and return its global path."""
 
1236
                sn = self.get_short_name()
 
1237
                utils.refresh_available_screenlet_paths()
 
1238
                for p in SCREENLETS_PATH:
 
1239
                        fpath = p + '/' + sn + '/themes/' + name
 
1240
                        if os.path.isdir(fpath):
 
1241
                                return fpath
 
1242
                return None
 
1243
        
 
1244
        def get_short_name (self):
 
1245
                """Return the short name of this screenlet. This returns the classname
 
1246
                of the screenlet without trailing "Screenlet". Please always use
 
1247
                this function if you want to retrieve the short name of a Screenlet."""
 
1248
                return self.__class__.__name__[:-9]
 
1249
                
 
1250
        def get_screenlet_dir (self):
 
1251
                """Return the name of this screenlet's personal directory."""
 
1252
                p = utils.find_first_screenlet_path(self.get_short_name())
 
1253
                if p:
 
1254
                        return p
 
1255
                else:
 
1256
                        if self.__path__ != '':
 
1257
                                return self.__path__
 
1258
                        else:
 
1259
                                return os.getcwd()
 
1260
        
 
1261
        def get_theme_dir (self):
 
1262
                """Return the name of this screenlet's personal theme-dir.
 
1263
                (Only returns the dir under the screenlet's location"""
 
1264
                return self.get_screenlet_dir() + "/themes/"
 
1265
        
 
1266
        def get_available_themes (self):
 
1267
                """Returns a list with the names of all available themes in this
 
1268
                        Screenlet's theme-directories."""
 
1269
                lst = []
 
1270
                utils.refresh_available_screenlet_paths()
 
1271
                for p in SCREENLETS_PATH:
 
1272
                        d = p + '/' + self.get_short_name() + '/themes/'
 
1273
                        if os.path.isdir(d):
 
1274
                                #dirname = self.get_theme_dir()
 
1275
                                dirlst = glob.glob(d + '*')
 
1276
                                dirlst.sort()
 
1277
                                tdlen = len(d)
 
1278
                                for fname in dirlst:
 
1279
                                        if os.path.isdir(fname):
 
1280
                                                dname = fname[tdlen:]
 
1281
                                                if not dname in lst:
 
1282
                                                        lst.append(dname)
 
1283
                return lst
 
1284
 
 
1285
        def reshow(self):
 
1286
                self.window.present()   
 
1287
                self.has_started = True 
 
1288
                self.is_dragged = False
 
1289
                self.keep_above= self.keep_above
 
1290
                self.keep_below= self.keep_below
 
1291
                self.skip_taskbar = self.skip_taskbar
 
1292
                self.window.set_skip_taskbar_hint(self.skip_taskbar)
 
1293
                self.window.set_keep_above(self.keep_above)
 
1294
                self.window.set_keep_below(self.keep_below)
 
1295
                if self.is_widget:
 
1296
                        self.set_is_widget(True)
 
1297
                self.has_focus = False
 
1298
 
 
1299
        def finish_loading(self):
 
1300
                """Called when screenlet finishes loading"""
 
1301
                
 
1302
 
 
1303
                self.window.present()                   
 
1304
                
 
1305
                
 
1306
                # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary 
 
1307
                self.window.hide()
 
1308
                self.window.move(self.x, self.y)
 
1309
 
 
1310
                if DEBIAN and not self.ignore_requirements:
 
1311
                        self.check_requirements()
 
1312
 
 
1313
                self.window.show()      
 
1314
                self.has_started = True 
 
1315
                self.is_dragged = False
 
1316
                self.keep_above= self.keep_above
 
1317
                self.keep_below= self.keep_below
 
1318
                self.is_sticky = self.is_sticky
 
1319
                self.skip_taskbar = self.skip_taskbar
 
1320
                self.window.set_skip_taskbar_hint(self.skip_taskbar)
 
1321
                self.window.set_keep_above(self.keep_above)
 
1322
                self.window.set_keep_below(self.keep_below)
 
1323
 
 
1324
                self.on_init()
 
1325
                if self.is_widget:
 
1326
                        self.set_is_widget(True)
 
1327
                self.has_focus = False
 
1328
                ini = utils.IniReader()
 
1329
                if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run:
 
1330
                                
 
1331
                        if ini.get_option('Lock', section='Options') == 'True':
 
1332
                                self.lock_position = True
 
1333
                        elif ini.get_option('Lock', section='Options') == 'False':              
 
1334
                                self.lock_position = False
 
1335
                        if ini.get_option('Sticky', section='Options') == 'True':
 
1336
                                self.is_sticky = True
 
1337
                        elif ini.get_option('Sticky', section='Options') == 'False':            
 
1338
                                self.is_sticky = False
 
1339
                        if ini.get_option('Widget', section='Options') == 'True':
 
1340
                                self.is_widget = True
 
1341
                        elif ini.get_option('Widget', section='Options') == 'False':            
 
1342
                                self.is_widget = False
 
1343
                        if ini.get_option('Keep_above', section='Options') == 'True':
 
1344
                                self.keep_above = True
 
1345
                        elif ini.get_option('Keep_above', section='Options') == 'False':                        
 
1346
                                self.keep_above =  False
 
1347
                        if ini.get_option('Keep_below', section='Options') == 'True':
 
1348
                                self.keep_below = True
 
1349
                        elif ini.get_option('Keep_below', section='Options') == 'False':
 
1350
                                self.keep_below = False
 
1351
                        if ini.get_option('draw_buttons', section='Options') == 'True':
 
1352
                                self.draw_buttons = True                        
 
1353
                        elif ini.get_option('draw_buttons', section='Options') == 'False':
 
1354
                                self.draw_buttons = False
 
1355
        
 
1356
        def hide (self):
 
1357
                """Hides this Screenlet's underlying gtk.Window"""
 
1358
                self.window.hide()
 
1359
                self.on_hide()
 
1360
        
 
1361
        # EXPERIMENTAL:
 
1362
        # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
 
1363
        # To do all in one, set attribute self.theme_name instead
 
1364
        def load_theme (self, path):
 
1365
                """Load a theme for this Screenlet from the given path. NOTE: 
 
1366
                load_theme does NOT call redraw_canvas and update_shape!!!!! To do all 
 
1367
                in one call, set the attribute self.theme_name instead."""
 
1368
                if self.theme:
 
1369
                        self.theme.free()
 
1370
                        del self.theme
 
1371
                self.theme = ScreenletTheme(path)
 
1372
                # check for errors
 
1373
                if self.theme.loaded == False:
 
1374
                        print "Error while loading theme: " + path
 
1375
                        self.theme = None
 
1376
                else:
 
1377
                        # call user-defined handler
 
1378
                        self.on_load_theme()
 
1379
                        # if override options is allowed, apply them
 
1380
                        if self.allow_option_override:
 
1381
                                if self.theme.has_overrides():
 
1382
                                        if self.ask_on_option_override==True and \
 
1383
                                                show_question(self, 
 
1384
                                                _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
 
1385
                                                return
 
1386
                                        self.theme.apply_option_overrides(self)
 
1387
        # /EXPERIMENTAL
 
1388
        
 
1389
        def main (self):
 
1390
                """If the Screenlet runs as stand-alone app, starts gtk.main()"""
 
1391
                gtk.main()
 
1392
        
 
1393
        def register_service (self, service_classobj):
 
1394
                """Register or create the given ScreenletService-(sub)class as the new 
 
1395
                service for this Screenlet. If self is not the first instance in the
 
1396
                current session, the service from the first instance will be used 
 
1397
                instead and no new service is created."""
 
1398
                if self.session:
 
1399
                        if len(self.session.instances) == 0:
 
1400
                                # if it is the basic service, add name to call
 
1401
                                if service_classobj==services.ScreenletService:#BUG
 
1402
                                        self.service = service_classobj(self, self.get_short_name())
 
1403
                                else:
 
1404
                                        # else only pass this screenlet
 
1405
                                        self.service = service_classobj(self)
 
1406
                        else:
 
1407
                                self.service = self.session.instances[0].service
 
1408
                        # TODO: throw exception??
 
1409
                        return True
 
1410
                return False
 
1411
        
 
1412
        def set_is_widget (self, value):
 
1413
                """Set this window to be treated as a Widget (only supported by
 
1414
                compiz using the widget-plugin yet)"""
 
1415
                if value==True:
 
1416
                        # set window type to utility
 
1417
                        #self.window.window.set_type_hint(
 
1418
                        #       gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
 
1419
                        # set _compiz_widget-property on window
 
1420
                        self.window.window.property_change("_COMPIZ_WIDGET", 
 
1421
                                gtk.gdk.SELECTION_TYPE_WINDOW,
 
1422
                                32, gtk.gdk.PROP_MODE_REPLACE, (True,))
 
1423
                else:
 
1424
                        # set window type to normal
 
1425
                        #self.window.window.set_type_hint(
 
1426
                        #       gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
 
1427
                        # set _compiz_widget-property
 
1428
                        self.window.window.property_delete("_COMPIZ_WIDGET")
 
1429
                # notify handler
 
1430
                self.on_switch_widget_state(value)
 
1431
        
 
1432
        def show (self):
 
1433
                """Show this Screenlet's underlying gtk.Window"""
 
1434
                self.window.show()
 
1435
                self.window.move(self.x, self.y)
 
1436
                self.on_show()
 
1437
        
 
1438
        def show_settings_dialog (self):
 
1439
                """Show the EditableSettingsDialog for this Screenlet."""
 
1440
                se = OptionsDialog(490, 450)
 
1441
                img = gtk.Image()
 
1442
                try:
 
1443
                        d = self.get_screenlet_dir()
 
1444
                        if os.path.isfile(d + '/icon.svg'):
 
1445
                                icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
 
1446
                        elif os.path.isfile(d + '/icon.png'):
 
1447
                                icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
 
1448
                        img.set_from_pixbuf(icn)
 
1449
                except:
 
1450
                        img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
 
1451
                se.set_title(self.__name__)
 
1452
                se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__), 
 
1453
                        version='v' + self.__version__, icon=img)
 
1454
                se.show_options_for_object(self)
 
1455
                resp = se.run()
 
1456
                if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
 
1457
                        se.reset_to_defaults()
 
1458
                else:
 
1459
                        self.update_shape()
 
1460
                se.destroy()
 
1461
        
 
1462
        def redraw_canvas (self):
 
1463
                """Redraw the entire Screenlet's window area.
 
1464
                TODO: store window alloaction in class and change when size changes."""
 
1465
                # if updates are disabled, just exit
 
1466
                if self.disable_updates:
 
1467
                        return
 
1468
                if self.window:
 
1469
                        x, y, w, h = self.window.get_allocation()
 
1470
                        rect = gtk.gdk.Rectangle(x, y, w, h)
 
1471
                        if self.window.window:
 
1472
                                self.window.window.invalidate_rect(rect, True)
 
1473
                                self.window.window.process_updates(True)
 
1474
        #                       if self.has_focus and self.draw_buttons and self.show_buttons:
 
1475
        #                               self.create_buttons()
 
1476
 
 
1477
        
 
1478
        def redraw_canvas_area (self, x, y, width, height):
 
1479
                """Redraw the given Rectangle (x, y, width, height) within the 
 
1480
                current Screenlet's window."""
 
1481
                # if updates are disabled, just exit
 
1482
                if self.disable_updates:
 
1483
                        return
 
1484
                if self.window:
 
1485
                        rect = gtk.gdk.Rectangle(x, y, width, height)
 
1486
                        if self.window.window:
 
1487
                                self.window.window.invalidate_rect(rect, True)
 
1488
                                self.window.window.process_updates(True)
 
1489
 
 
1490
        def remove_shape(self):
 
1491
                """Removed shaped window , in case the nom composited shape has been set"""
 
1492
                if self.window.window:
 
1493
                        self.window.window.shape_combine_mask(None,0,0) 
 
1494
 
 
1495
                w = self.window.allocation.width
 
1496
                h = self.window.allocation.height
 
1497
                
 
1498
                # if 0 return to avoid crashing
 
1499
                if w==0 or h==0: return False
 
1500
                # if size changed, recreate shape bitmap
 
1501
                if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
 
1502
                        self.__shape_bitmap = screenlets.create_empty_bitmap(w, h)
 
1503
                        self.__shape_bitmap_width = w
 
1504
                        self.__shape_bitmap_height = h
 
1505
                        
 
1506
                # create context
 
1507
                ctx = self.__shape_bitmap.cairo_create()
 
1508
                self.clear_cairo_context(ctx)
 
1509
                
 
1510
                # shape the window acording if the window is composited  or not
 
1511
                if self.window.is_composited():
 
1512
#                       log.debug(_("Updating input shape"))
 
1513
                        self.on_draw_shape(ctx)
 
1514
                        self.main_view.set_shape(self.__shape_bitmap, True)
 
1515
                else:
 
1516
                        try:
 
1517
                                self.on_draw_shape(ctx)
 
1518
                        except:
 
1519
                                self.on_draw(ctx)
 
1520
#                       log.debug(_("Updating window shape"))
 
1521
                        self.main_view.set_shape(self.__shape_bitmap, False)
 
1522
 
 
1523
        def update_shape (self):
 
1524
                """Update window shape (only call this when shape has changed
 
1525
                because it is very ressource intense if ran too often)."""
 
1526
                # if updates are disabled, just exit
 
1527
                if self.disable_updates:
 
1528
                        return
 
1529
                #print "UPDATING SHAPE"
 
1530
                # TODO:
 
1531
                #if not self.window.is_composited():
 
1532
                #       self.update_shape_non_composited()
 
1533
                # calculate new width/height of shape bitmap
 
1534
                w = int(self.width * self.scale)
 
1535
                h = int(self.height * self.scale)
 
1536
                # if 0 set it to 100 to avoid crashes and stay interactive
 
1537
                if w==0: w = 100
 
1538
                if h==0: h = 100
 
1539
                # if size changed, recreate shape bitmap
 
1540
                if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
 
1541
                        data = ''.zfill(w*h)
 
1542
                        self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data, 
 
1543
                                w, h)
 
1544
                        self.__shape_bitmap_width = w
 
1545
                        self.__shape_bitmap_height = h
 
1546
                # create context and draw shape
 
1547
                ctx = self.__shape_bitmap.cairo_create()
 
1548
                self.clear_cairo_context(ctx)           #TEST
 
1549
                if self.has_focus and self.draw_buttons and self.show_buttons:
 
1550
                        ctx.save()
 
1551
                        #theme1 = gtk.icon_theme_get_default()
 
1552
                        #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
 
1553
                        #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
 
1554
                        #close = theme1.load_icon ("gtk-close", 16, 0)
 
1555
                        #prop = theme1.load_icon ("gtk-properties", 16, 0)
 
1556
                        #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
 
1557
                        #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
 
1558
                        #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
 
1559
                        ctx.translate((self.width*self.scale)-16,0)
 
1560
                        ctx.set_source_pixbuf(self.closeb, 0, 0)
 
1561
                        ctx.paint()
 
1562
                        ctx.restore()
 
1563
                        ctx.save()      
 
1564
                        ctx.translate((self.width*self.scale)-32,0)
 
1565
                        ctx.set_source_pixbuf(self.prop, 0, 0)
 
1566
                        ctx.paint()
 
1567
                        ctx.restore()
 
1568
                # shape the window acording if the window is composited  or not
 
1569
 
 
1570
                if self.window.is_composited():
 
1571
 
 
1572
                        self.on_draw_shape(ctx)
 
1573
                        # and cut window with mask      
 
1574
                        self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
 
1575
                else:
 
1576
                        try: self.on_draw(ctx) #Works better then the shape method on non composited windows
 
1577
                        except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
 
1578
                        # and cut window with mask 
 
1579
                        self.window.shape_combine_mask(self.__shape_bitmap,0,0)
 
1580
                self.on_update_shape()
 
1581
 
 
1582
        def update_shape_non_composited (self):
 
1583
                """TEST: This function is intended to shape the window whenever no
 
1584
                composited environment can be found. (NOT WORKING YET!!!!)"""
 
1585
                #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
 
1586
                # calculate new width/height of shape bitmap
 
1587
                w = int(self.width * self.scale)
 
1588
                h = int(self.height * self.scale)
 
1589
                # if 0 set it to 100 to avoid crashes and stay interactive
 
1590
                if w==0: w = 100
 
1591
                if h==0: h = 100
 
1592
                # if size changed, recreate shape bitmap
 
1593
                if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
 
1594
                        data = ''.zfill(w*h)
 
1595
                        self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
 
1596
                                gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
 
1597
                        self.__shape_bitmap_width = w
 
1598
                        self.__shape_bitmap_height = h
 
1599
                        # and render window contents to it
 
1600
                        # TOOD!!
 
1601
                        if self.__shape_bitmap:
 
1602
                                # create new mask
 
1603
                                (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
 
1604
                                # apply new mask to window
 
1605
                                self.window.shape_combine_mask(mask)
 
1606
 
 
1607
        def redraw_canvas_and_update_shape (self):
 
1608
                self.redraw_canvas()
 
1609
                self.update_shape()
 
1610
 
 
1611
        # ----------------------------------------------------------------------
 
1612
        # Screenlet's event-handler dummies
 
1613
        # ----------------------------------------------------------------------
 
1614
        
 
1615
        def on_delete (self):
 
1616
                """Called when the Screenlet gets deleted. Return True to cancel.
 
1617
                TODO: sometimes not properly called"""
 
1618
                return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
 
1619
                        _('Really delete this %s and its settings?') % self.get_short_name())
 
1620
                """return not show_question(self, 'Deleting this instance of the '+\
 
1621
                                self.__name__ + ' will also delete all your personal '+\
 
1622
                                'changes you made to it!! If you just want to close the '+\
 
1623
                                'application, use "Quit" instead. Are you sure you want to '+\
 
1624
                                'delete this instance?')
 
1625
                return False"""
 
1626
        
 
1627
        # TODO: on_drag
 
1628
        # TODO: on_drag_end
 
1629
 
 
1630
        def on_after_set_atribute(self,name, value):
 
1631
                """Called after setting screenlet atributes"""
 
1632
                pass
 
1633
 
 
1634
        def on_before_set_atribute(self,name, value):
 
1635
                """Called before setting screenlet atributes"""
 
1636
                pass
 
1637
 
 
1638
 
 
1639
        def on_create_drag_icon (self):
 
1640
                """Called when the screenlet's drag-icon is created. You can supply
 
1641
                your own icon and mask by returning them as a 2-tuple."""
 
1642
                return (None, None)
 
1643
 
 
1644
        def on_map(self):
 
1645
                """Called when screenlet was mapped"""
 
1646
                pass
 
1647
 
 
1648
        def on_unmap(self):
 
1649
                """Called when screenlet was unmapped"""
 
1650
                pass
 
1651
 
 
1652
        def on_composite_changed(self):
 
1653
                """Called when composite state has changed"""
 
1654
                pass
 
1655
 
 
1656
 
 
1657
        def on_drag_begin (self, drag_context):
 
1658
                """Called when the Screenlet gets dragged."""
 
1659
                pass
 
1660
        
 
1661
        def on_drag_enter (self, drag_context, x, y, timestamp):
 
1662
                """Called when something gets dragged into the Screenlets area."""
 
1663
                pass
 
1664
        
 
1665
        def on_drag_leave (self, drag_context, timestamp):
 
1666
                """Called when something gets dragged out of the Screenlets area."""
 
1667
                pass
 
1668
        
 
1669
        def on_draw (self, ctx):
 
1670
                """Callback for drawing the Screenlet's window - override
 
1671
                in subclasses to implement your own drawing."""
 
1672
                pass
 
1673
        
 
1674
        def on_draw_shape (self, ctx):
 
1675
                """Callback for drawing the Screenlet's shape - override
 
1676
                in subclasses to draw the window's input-shape-mask."""
 
1677
                pass
 
1678
        
 
1679
        def on_drop (self, x, y, sel_data, timestamp):
 
1680
                """Called when a selection is dropped on this Screenlet."""
 
1681
                return False
 
1682
                
 
1683
        def on_focus (self, event):
 
1684
                """Called when the Screenlet's window receives focus."""
 
1685
                pass
 
1686
        
 
1687
        def on_hide (self):
 
1688
                """Called when the Screenlet gets hidden."""
 
1689
                pass
 
1690
        
 
1691
        def on_init (self):
 
1692
                """Called when the Screenlet's options have been applied and the 
 
1693
                screenlet finished its initialization. If you want to have your
 
1694
                Screenlet do things on startup you should use this handler."""
 
1695
                pass
 
1696
        
 
1697
        def on_key_down (self, keycode, keyvalue, event=None):
 
1698
                """Called when a key is pressed within the screenlet's window."""
 
1699
                pass
 
1700
        
 
1701
        def on_load_theme (self):
 
1702
                """Called when the theme is reloaded (after loading, before redraw)."""
 
1703
                pass
 
1704
        
 
1705
        def on_menuitem_select (self, id):
 
1706
                """Called when a menuitem is selected."""
 
1707
                pass
 
1708
        
 
1709
        def on_mouse_down (self, event):
 
1710
                """Called when a buttonpress-event occured in Screenlet's window. 
 
1711
                Returning True causes the event to be not further propagated."""
 
1712
                return False
 
1713
        
 
1714
        def on_mouse_enter (self, event):
 
1715
                """Called when the mouse enters the Screenlet's window."""
 
1716
                pass
 
1717
                
 
1718
        def on_mouse_leave (self, event):
 
1719
                """Called when the mouse leaves the Screenlet's window."""
 
1720
                pass
 
1721
 
 
1722
        def on_mouse_move(self, event):
 
1723
                """Called when the mouse moves in the Screenlet's window."""
 
1724
                pass
 
1725
        
 
1726
        def on_mouse_up (self, event):
 
1727
                """Called when a buttonrelease-event occured in Screenlet's window. 
 
1728
                Returning True causes the event to be not further propagated."""
 
1729
                return False
 
1730
        
 
1731
        def on_quit (self):
 
1732
                """Callback for handling destroy-event. Perform your cleanup here!"""
 
1733
                return True
 
1734
                
 
1735
        def on_realize (self):
 
1736
                """"Callback for handling the realize-event."""
 
1737
        
 
1738
        def on_scale (self):
 
1739
                """Called when Screenlet.scale is changed."""
 
1740
                pass
 
1741
        
 
1742
        def on_scroll_up (self):
 
1743
                """Called when mousewheel is scrolled up (button4)."""
 
1744
                pass
 
1745
 
 
1746
        def on_scroll_down (self):
 
1747
                """Called when mousewheel is scrolled down (button5)."""
 
1748
                pass
 
1749
        
 
1750
        def on_show (self):
 
1751
                """Called when the Screenlet gets shown after being hidden."""
 
1752
                pass
 
1753
        
 
1754
        def on_switch_widget_state (self, state):
 
1755
                """Called when the Screenlet enters/leaves "Widget"-state."""
 
1756
                pass
 
1757
        
 
1758
        def on_unfocus (self, event):
 
1759
                """Called when the Screenlet's window loses focus."""
 
1760
                pass
 
1761
 
 
1762
        def on_update_shape(self):
 
1763
                """Called when the Screenlet's window is updating shape"""
 
1764
                pass
 
1765
        # ----------------------------------------------------------------------
 
1766
        # Screenlet's event-handlers for GTK-events
 
1767
        # ----------------------------------------------------------------------
 
1768
        
 
1769
        def alpha_screen_changed (self, window, screen=None):
 
1770
                """set colormap for window"""
 
1771
                if screen==None:
 
1772
                        screen = window.get_screen()
 
1773
                map = screen.get_rgba_colormap()
 
1774
                if map:
 
1775
                        pass
 
1776
                else:
 
1777
                        map = screen.get_rgb_colormap()
 
1778
                window.set_colormap(map)                
 
1779
        
 
1780
        def button_press (self, widget, event):
 
1781
 
 
1782
                #print "Button press"
 
1783
                # set flags for user-handler
 
1784
 
 
1785
 
 
1786
                # call user-handler for onmousedownbegin_move_drag
 
1787
                if self.on_mouse_down(event) == True:
 
1788
                        return True
 
1789
                # unhandled? continue
 
1790
                
 
1791
                if self.mousex >= self.width - (32/self.scale) and self.mousey <= (16/self.scale) and self.draw_buttons and self.show_buttons and self.has_focus:
 
1792
                        if self.mousex >=  self.width - (16/self.scale):
 
1793
                                self.menuitem_callback(widget,'quit_instance')
 
1794
                        elif self.mousex <=  self.width -(16/self.scale):
 
1795
                                self.menuitem_callback(widget,'info')
 
1796
                elif self.lock_position == False:
 
1797
                        if event.button == 1:
 
1798
                                self.is_dragged = True
 
1799
                                widget.begin_move_drag(event.button, int(event.x_root), 
 
1800
                                        int(event.y_root), event.time)
 
1801
                
 
1802
                if event.button == 3:
 
1803
                        try:
 
1804
                                self.__mi_lock.set_active(self.lock_position)
 
1805
                                self.__mi_sticky.set_active(self.is_sticky)
 
1806
                                self.__mi_widget.set_active(self.is_widget)
 
1807
                                self.__mi_keep_above.set_active(self.keep_above)
 
1808
                                self.__mi_keep_below.set_active(self.keep_below)
 
1809
                        except : pass
 
1810
                        self.menu.popup(None, None, None, event.button, event.time)
 
1811
                #elif event.button == 4:
 
1812
                #       print "MOUSEWHEEL"
 
1813
                #       self.scale -= 0.1
 
1814
                #elif event.button == 5:
 
1815
                #       print "MOUSEWHEEL"
 
1816
                #       self.scale += 0.1
 
1817
                return False
 
1818
        
 
1819
        def button_release (self, widget, event):
 
1820
                print "Button release"
 
1821
                if event.button==1:
 
1822
                        self.focus_in_event(self, None)
 
1823
                self.is_dragged = False # doesn't work!!! we don't get an event when move_drag ends :( ...
 
1824
                if self.on_mouse_up(event):
 
1825
                        return True
 
1826
                return False
 
1827
 
 
1828
        def composite_changed(self,widget):
 
1829
                #this handle is called when composition changed
 
1830
                self.remove_shape() # removing previous set shape , this is absolutly necessary
 
1831
                self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
 
1832
                self.is_sticky = self.is_sticky  #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
 
1833
                self.keep_above= self.keep_above
 
1834
                self.keep_below= self.keep_below
 
1835
                self.window.show()
 
1836
                #print 'Compositing method changed to %s' % str(self.window.is_composited())
 
1837
                self.update_shape()
 
1838
                self.redraw_canvas()
 
1839
 
 
1840
                if not self.window.is_composited () :
 
1841
                        self.show_buttons = False
 
1842
                        self.disable_option("opacity")
 
1843
                #       print 'Warning - Buttons will not be shown until screenlet is restarted'
 
1844
 
 
1845
                if self.window.is_composited () :
 
1846
                        self.enable_option("opacity")
 
1847
 
 
1848
                self.is_sticky = self.is_sticky #and again ...
 
1849
                self.keep_above= self.keep_above
 
1850
                self.keep_below= self.keep_below
 
1851
                self.window.set_keep_above(self.keep_above)
 
1852
                self.window.set_keep_below(self.keep_below)
 
1853
                self.on_composite_changed()
 
1854
 
 
1855
        # NOTE: this should somehow handle the end of a move_drag-operation
 
1856
        def configure_event (self, widget, event):
 
1857
                #print "onConfigure"
 
1858
                #print event
 
1859
                #if self.is_dragged == True:
 
1860
                # set new position and cause a save of this Screenlet (not use 
 
1861
                # setattr to avoid conflicts with the window.move in __setattr__)
 
1862
                if event.x != self.x:
 
1863
                        self.__dict__['x'] = event.x
 
1864
                        if self.session:
 
1865
                                self.session.backend.save_option(self.id, 'x', str(event.x))
 
1866
                        #       self.is_dragged = False
 
1867
                if event.y != self.y:
 
1868
                        self.__dict__['y'] = event.y
 
1869
                        if self.session:
 
1870
                                self.session.backend.save_option(self.id, 'y', str(event.y))
 
1871
                        #       self.is_dragged = False
 
1872
                return False
 
1873
        
 
1874
        def delete_event (self, widget, event, data=None):
 
1875
                # cancel event?
 
1876
                print "delete_event"
 
1877
                if self.on_delete() == True:
 
1878
                        print "Cancel delete_event"
 
1879
                        return True
 
1880
                else:
 
1881
                        self.close()
 
1882
                return False
 
1883
 
 
1884
        def destroy (self, widget, data=None):
 
1885
                # call user-defined on_quit-handler
 
1886
                self.on_quit()
 
1887
                #print "destroy signal occurred"
 
1888
                self.emit("screenlet_removed", self)
 
1889
                # close gtk?
 
1890
                if self.quit_on_close:
 
1891
                        if self.session:        # if we have a session, flush current data
 
1892
                                self.session.backend.flush()
 
1893
                        gtk.main_quit()
 
1894
                else:
 
1895
                        del self                # ??? does this really work???
 
1896
        
 
1897
        def drag_begin (self, widget, drag_context):
 
1898
                print "Start drag"
 
1899
                self.is_dragged = True
 
1900
                self.on_drag_begin(drag_context)
 
1901
                #return False
 
1902
        
 
1903
        def drag_data_received (self, widget, dc, x, y, sel_data, info, timestamp):
 
1904
                return self.on_drop(x, y, sel_data, timestamp)
 
1905
        
 
1906
        def drag_end (self, widget, drag_context):
 
1907
                print "End drag"
 
1908
                self.is_dragged = False
 
1909
                return False
 
1910
        
 
1911
        def drag_motion (self, widget, drag_context, x, y, timestamp):
 
1912
                #print "Drag motion"
 
1913
                if self.dragging_over == False:
 
1914
                        self.dragging_over = True
 
1915
                        self.on_drag_enter(drag_context, x, y, timestamp)
 
1916
                return False
 
1917
        
 
1918
        def drag_leave (self, widget, drag_context, timestamp):
 
1919
                self.dragging_over = False
 
1920
                self.on_drag_leave(drag_context, timestamp)
 
1921
                return
 
1922
        
 
1923
        def enter_notify_event (self, widget, event):
 
1924
                #self.__mouse_inside = True
 
1925
                self.__dict__['mouse_is_over'] = True
 
1926
                self.on_mouse_enter(event)
 
1927
                
 
1928
                #self.redraw_canvas()
 
1929
        
 
1930
        def expose (self, widget, event):
 
1931
                ctx = widget.window.cairo_create()
 
1932
                # clear context
 
1933
                self.clear_cairo_context(ctx)
 
1934
                # set a clip region for the expose event
 
1935
                ctx.rectangle(event.area.x, event.area.y,
 
1936
                        event.area.width, event.area.height)
 
1937
                ctx.clip()
 
1938
                
 
1939
                # scale context
 
1940
                #ctx.scale(self.scale, self.scale)
 
1941
                # call drawing method
 
1942
                self.on_draw(ctx)
 
1943
                if self.show_buttons and self.draw_buttons and self.has_focus:
 
1944
                        self.create_buttons()
 
1945
                # and delete context (needed?)
 
1946
                del ctx
 
1947
                return False
 
1948
        
 
1949
        def focus_in_event (self, widget, event):
 
1950
                if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
 
1951
                        #Screenlet always gets focus after being dragged so this is a good method
 
1952
                        #to control the end of a move_drag operation!!!!!
 
1953
                        #This code happens on the end of a move_drag
 
1954
                        self.is_dragged=False
 
1955
                        self.has_focus = True
 
1956
                        self.on_focus(event)
 
1957
                        self.update_shape()
 
1958
                        self.redraw_canvas()
 
1959
 
 
1960
 
 
1961
 
 
1962
 
 
1963
        def focus_out_event (self, widget, event):
 
1964
                if self.is_dragged==False:
 
1965
                        self.has_focus = False
 
1966
                        self.on_unfocus(event)
 
1967
                        self.update_shape()
 
1968
                        self.redraw_canvas()
 
1969
 
 
1970
 
 
1971
        
 
1972
        def key_press (self, widget, event):
 
1973
                """Handle keypress events, needed for in-place editing."""
 
1974
                self.on_key_down(event.keyval, event.string, event)
 
1975
        
 
1976
        def leave_notify_event (self, widget, event):
 
1977
                #self.__mouse_inside = False
 
1978
                #self.is_dragged = False
 
1979
                self.__dict__['mouse_is_over'] = False
 
1980
                self.on_mouse_leave(event)
 
1981
        
 
1982
                #self.redraw_canvas()
 
1983
        
 
1984
        def menuitem_callback (self, widget, id):
 
1985
                if id == "delete":
 
1986
                        if not self.on_delete():
 
1987
                                # remove instance
 
1988
                                self.session.delete_instance (self.id)
 
1989
                                # notify about being rmeoved (does this get send???)
 
1990
                                self.service.instance_removed(self.id)
 
1991
                elif id == "quit_instance":
 
1992
                        print 'Quitting current screenlet instance'
 
1993
                        self.session.quit_instance (self.id)
 
1994
                        self.service.instance_removed(self.id)
 
1995
                elif id == "quit":
 
1996
                        self.close()
 
1997
                elif id == "add":
 
1998
                        self.service.add("")
 
1999
                elif id in ("info", "about", "settings", "options", "properties"):
 
2000
                        # show settings dialog
 
2001
                        self.show_settings_dialog()
 
2002
                elif id.startswith('scale:'):
 
2003
                        self.scale = float(id[6:])
 
2004
                elif id[:5] == "size:": # DEPRECATED??
 
2005
                        # set size and update shape (redraw is done by setting height)
 
2006
                        #self.__dict__['width'] = int(id[5:])
 
2007
                        self.width = int(id[5:])
 
2008
                        self.height = int(id[5:])
 
2009
                        self.update_shape()
 
2010
                elif id[:6]=="theme:":
 
2011
                        print "Screenlet: Set theme %s" % id[6:]
 
2012
                        # set theme
 
2013
                        self.theme_name = id[6:]
 
2014
                elif id[:8] == "setting:":
 
2015
                        # set a boolean option to the opposite state
 
2016
                        try:
 
2017
                                if type(self.__dict__[id[8:]]) == bool:
 
2018
                                        self.__dict__[id[8:]] = not self.__dict__[id[8:]]       # UNSAFE!!
 
2019
                        except:
 
2020
                                print "Error: Cannot set missing or non-boolean value '"\
 
2021
                                        + id[8:] + "'"
 
2022
                elif id[:7] == "option:":
 
2023
                        # NOTE: this part should be removed and XML-menus
 
2024
                        #               should be used by default ... maybe
 
2025
                        # set option
 
2026
                        if id[7:]=="lock":
 
2027
                                if self.__mi_lock.get_active () != self.lock_position:
 
2028
                                        self.lock_position = not self.lock_position
 
2029
                        elif id[7:]=="sticky":
 
2030
                                if self.__mi_sticky.get_active () != self.is_sticky:
 
2031
                                        self.is_sticky = not self.is_sticky
 
2032
                                #widget.toggle()
 
2033
                        elif id[7:]=="widget":
 
2034
                                if self.__mi_widget.get_active () != self.is_widget:
 
2035
                                        self.is_widget = not self.is_widget
 
2036
                        elif id[7:]=="keep_above":
 
2037
                                if self.__mi_keep_above.get_active () != self.keep_above:
 
2038
                                        self.keep_above = not self.keep_above
 
2039
                                        self.__mi_keep_above.set_active(self.keep_above)
 
2040
                                        if self.keep_below and self.keep_above : 
 
2041
                                                self.keep_below = False
 
2042
                                                self.__mi_keep_below.set_active(False)
 
2043
                        elif id[7:]=="keep_below":
 
2044
                                if self.__mi_keep_below.get_active () != self.keep_below:
 
2045
                                        self.keep_below = not self.keep_below
 
2046
                                        self.__mi_keep_below.set_active(self.keep_below)
 
2047
                                        if self.keep_below and self.keep_above : 
 
2048
                                                self.keep_above = False
 
2049
                                                self.__mi_keep_above.set_active(False)
 
2050
                else:
 
2051
                        #print "Item: " + string
 
2052
                        pass
 
2053
                # call user-handler
 
2054
                self.on_menuitem_select(id)
 
2055
                return False
 
2056
 
 
2057
        def map_event(self, widget, event):
 
2058
                self.on_map()
 
2059
 
 
2060
        def unmap_event(self, widget, event):
 
2061
                self.on_unmap()
 
2062
 
 
2063
        def motion_notify_event(self, widget, event):
 
2064
                self.__dict__['mousex'] = event.x / self.scale
 
2065
                self.__dict__['mousey'] = event.y / self.scale
 
2066
                
 
2067
                self.on_mouse_move(event)
 
2068
        
 
2069
        def realize_event (self, widget):
 
2070
                """called when window has been realized"""
 
2071
                if self.window.window:
 
2072
                        self.window.window.set_back_pixmap(None, False) # needed?
 
2073
 
 
2074
                self.on_realize()
 
2075
        
 
2076
        def scroll_event (self, widget, event):
 
2077
                if event.direction == gtk.gdk.SCROLL_UP:
 
2078
                        if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
 
2079
                        self.on_scroll_up()
 
2080
                elif event.direction == gtk.gdk.SCROLL_DOWN:
 
2081
                        if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
 
2082
                        self.on_scroll_down()
 
2083
                return False
 
2084
 
 
2085
 
 
2086
        def show_notification (self,text):
 
2087
                """Show notification window at current mouse position."""
 
2088
                if self.notify == None:
 
2089
                        self.notify = Notify()
 
2090
                        self.notify.text = text
 
2091
                        self.notify.show()
 
2092
 
 
2093
        def hide_notification (self):
 
2094
                """hide notification window"""
 
2095
                if self.notify != None:
 
2096
                        self.notify.hide()
 
2097
                        self.notify = None
 
2098
 
 
2099
        def show_tooltip (self,text,tooltipx,tooltipy):
 
2100
                """Show tooltip window at current mouse position."""
 
2101
                if self.tooltip == None:
 
2102
                        self.tooltip = Tooltip(300, 400)
 
2103
                        self.tooltip.text = text
 
2104
                        self.tooltip.x    = tooltipx
 
2105
                        self.tooltip.y    = tooltipy
 
2106
                        self.tooltip.show()
 
2107
                else:
 
2108
                        #self.tooltip = Tooltip(300, 400)
 
2109
                        self.tooltip.text = text
 
2110
                        self.tooltip.x    = tooltipx
 
2111
                        self.tooltip.y    = tooltipy
 
2112
                        #self.tooltip.show()
 
2113
 
 
2114
        def hide_tooltip (self):
 
2115
                """hide tooltip window"""
 
2116
                if self.tooltip != None:
 
2117
                        self.tooltip.hide()
 
2118
                        self.tooltip = None             
 
2119
 
 
2120
# TEST!!!
 
2121
class ShapedWidget (gtk.DrawingArea):
 
2122
        """A simple base-class for creating owner-drawn gtk-widgets"""
 
2123
        
 
2124
        __widget=None
 
2125
        
 
2126
        mouse_inside = False
 
2127
        width = 32
 
2128
        height = 32
 
2129
        
 
2130
        def __init__ (self, width, height):
 
2131
                # call superclass
 
2132
                super(ShapedWidget, self).__init__()
 
2133
                # create/setup widget
 
2134
                #self.__widget = gtk.Widget()
 
2135
                self.set_app_paintable(True)
 
2136
                self.set_size_request(width, height)
 
2137
                # connect handlers
 
2138
                self.set_events(gtk.gdk.ALL_EVENTS_MASK)
 
2139
                self.connect("expose-event", self.expose_event)
 
2140
                self.connect("button-press-event", self.button_press)
 
2141
                self.connect("button-release-event", self.button_release)
 
2142
                self.connect("enter-notify-event", self.enter_notify)
 
2143
                self.connect("leave-notify-event", self.leave_notify)
 
2144
        
 
2145
        # EXPERIMENTAL: TODO: cache bitmap until size changes
 
2146
        def update_shape (self):
 
2147
                """update widget's shape (only call this when shape has changed)"""
 
2148
                data = ""
 
2149
                for i in xrange(self.width*self.height):
 
2150
                        data += "0"
 
2151
                bitmap = gtk.gdk.bitmap_create_from_data(None, 
 
2152
                        data, self.width, self.height)
 
2153
                ctx = bitmap.cairo_create()
 
2154
                ctx.set_source_rgba(1, 1, 1, 0)
 
2155
                ctx.set_operator (cairo.OPERATOR_SOURCE)
 
2156
                ctx.paint()
 
2157
                self.draw_shape(ctx)
 
2158
                self.input_shape_combine_mask(bitmap, 0, 0)
 
2159
                print "Updating shape."
 
2160
        
 
2161
        def button_press (self, widget, event):
 
2162
                if event.button==1:
 
2163
                        print "left button pressed!"
 
2164
                return False
 
2165
                
 
2166
        def button_release (self, widget, event):
 
2167
                #if event.button==1:
 
2168
                        #print "left button release!"
 
2169
                return False
 
2170
        
 
2171
        def enter_notify (self, widget, event):
 
2172
                self.mouse_inside = True
 
2173
                self.queue_draw()
 
2174
                #print "mouse enter"
 
2175
        
 
2176
        def leave_notify (self, widget, event):
 
2177
                self.mouse_inside = False
 
2178
                self.queue_draw()
 
2179
                #print "mouse leave"
 
2180
        
 
2181
        def draw (self, ctx):
 
2182
                pass
 
2183
                
 
2184
        def draw_shape (self, ctx):
 
2185
                self.draw(ctx)
 
2186
        
 
2187
        def expose_event (self, widget, event):
 
2188
                ctx = widget.window.cairo_create()
 
2189
                # set a clip region for the expose event
 
2190
                ctx.rectangle(event.area.x, event.area.y,
 
2191
                        event.area.width, event.area.height)
 
2192
                ctx.clip()
 
2193
                # clear context
 
2194
                ctx.set_source_rgba(1, 1, 1, 0)
 
2195
                ctx.set_operator (cairo.OPERATOR_SOURCE)
 
2196
                ctx.paint()
 
2197
                # call drawing method
 
2198
                self.draw(ctx)
 
2199
                # and delete context
 
2200
                del ctx
 
2201
                return False
 
2202
 
 
2203
class Tooltip(object):
 
2204
        """A window that displays a text and serves as Tooltip (very basic yet)."""
 
2205
        
 
2206
        # internals
 
2207
        __timeout    = None
 
2208
    
 
2209
        # attribs
 
2210
        text        = ''
 
2211
        font_name    = 'FreeSans 9'
 
2212
        width        = 100
 
2213
        height        = 20
 
2214
        x             = 0
 
2215
        y             = 0
 
2216
    
 
2217
        def __init__ (self, width, height):
 
2218
                object.__init__(self)
 
2219
                # init
 
2220
                self.__dict__['width']    = width
 
2221
                self.__dict__['height']    = height
 
2222
                self.window = gtk.Window()
 
2223
                self.window.set_app_paintable(True)
 
2224
                self.window.set_size_request(width, height)
 
2225
                self.window.set_decorated(False)
 
2226
                self.window.set_accept_focus(False)
 
2227
                self.window.set_skip_pager_hint(True)
 
2228
                self.window.set_skip_taskbar_hint(True)
 
2229
                self.window.set_keep_above(True)
 
2230
                self.screen_changed(self.window)
 
2231
                self.window.connect("expose_event", self.expose)
 
2232
                self.window.connect("screen-changed", self.screen_changed)
 
2233
                #self.window.show()
 
2234
                self.p_context = self.window.get_pango_context()
 
2235
                self.p_layout = pango.Layout(self.p_context)
 
2236
                self.p_layout.set_font_description(\
 
2237
                pango.FontDescription(self.font_name))
 
2238
                #self.p_layout.set_width(-1)
 
2239
                self.p_layout.set_width(width * pango.SCALE - 6)
 
2240
    
 
2241
        def __setattr__ (self, name, value):
 
2242
                self.__dict__[name] = value
 
2243
                if name in ('width', 'height', 'text'):
 
2244
                        if name== 'width':
 
2245
                                self.p_layout.set_width(width)
 
2246
                        elif name == 'text':
 
2247
                                self.p_layout.set_markup(value)
 
2248
                                ink_rect, logical_rect = self.p_layout.get_pixel_extents()
 
2249
                                self.height = min(max(logical_rect[3], 16), 400) + 6
 
2250
                                self.window.set_size_request(self.width, self.height)
 
2251
                        self.window.queue_draw()
 
2252
                elif name == 'x':
 
2253
                        self.window.move(int(value), int(self.y))
 
2254
                elif name == 'y':
 
2255
                        self.window.move(int(self.x), int(value))
 
2256
    
 
2257
        def show (self):
 
2258
                """Show the Tooltip window."""
 
2259
                self.cancel_show()
 
2260
                self.window.show()
 
2261
                self.window.set_keep_above(True)
 
2262
   
 
2263
        def show_delayed (self, delay):
 
2264
                """Show the Tooltip window after a given delay."""
 
2265
                self.cancel_show()
 
2266
                self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
 
2267
    
 
2268
        def hide (self):
 
2269
                """Hide the Tooltip window."""
 
2270
                self.cancel_show()
 
2271
                self.window.destroy()
 
2272
    
 
2273
        def cancel_show (self):
 
2274
                """Cancel showing of the Tooltip."""
 
2275
                if self.__timeout:
 
2276
                        gobject.source_remove(self.__timeout)
 
2277
                        self.p_context = None
 
2278
                        self.p_layout = None
 
2279
    
 
2280
        def __show_timeout (self):
 
2281
                self.show()
 
2282
    
 
2283
        def screen_changed (self, window, screen=None):
 
2284
                if screen == None:
 
2285
                        screen = window.get_screen()
 
2286
                map = screen.get_rgba_colormap()
 
2287
                if not map:
 
2288
                        map = screen.get_rgb_colormap()
 
2289
                window.set_colormap(map)
 
2290
    
 
2291
        def expose (self, widget, event):
 
2292
                ctx = self.window.window.cairo_create()
 
2293
                ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)    # ?
 
2294
                # set a clip region for the expose event
 
2295
                ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
 
2296
                ctx.clip()
 
2297
                # clear context
 
2298
                ctx.set_source_rgba(1, 1, 1, 0)
 
2299
                ctx.set_operator (cairo.OPERATOR_SOURCE)
 
2300
                ctx.paint()
 
2301
                # draw rectangle
 
2302
                ctx.set_source_rgba(1, 1, 0.5, 1)
 
2303
                ctx.rectangle(0, 0, self.width, self.height)
 
2304
                ctx.fill()
 
2305
                # draw text
 
2306
                ctx.save()
 
2307
                ctx.translate(3, 3)
 
2308
                ctx.set_source_rgba(0, 0, 0, 1) 
 
2309
                ctx.show_layout(self.p_layout)
 
2310
                ctx.fill()
 
2311
                ctx.restore()
 
2312
                ctx.rectangle(0, 0, self.width, self.height)
 
2313
                ctx.set_source_rgba(0, 0, 0, 0.7)
 
2314
                ctx.stroke()
 
2315
 
 
2316
class Notify(object):
 
2317
        """A window that displays a text and serves as Notification (very basic yet)."""
 
2318
        
 
2319
        # internals
 
2320
        __timeout    = None
 
2321
    
 
2322
        # attribs
 
2323
        text        = ''
 
2324
        font_name    = 'FreeSans 9'
 
2325
        width        = 200
 
2326
        height        = 100
 
2327
        x             = 0
 
2328
        y             = 0
 
2329
        gradient = cairo.LinearGradient(0, 100,0, 0)
 
2330
    
 
2331
        def __init__ (self):
 
2332
                object.__init__(self)
 
2333
                # init
 
2334
                self.window = gtk.Window()
 
2335
                self.window.set_app_paintable(True)
 
2336
                self.window.set_size_request(self.width, self.height)
 
2337
                self.window.set_decorated(False)
 
2338
                self.window.set_accept_focus(False)
 
2339
                self.window.set_skip_pager_hint(True)
 
2340
                self.window.set_skip_taskbar_hint(True)
 
2341
                self.window.set_keep_above(True)
 
2342
                self.screen_changed(self.window)
 
2343
                self.window.connect("expose_event", self.expose)
 
2344
                self.window.connect("screen-changed", self.screen_changed)
 
2345
                #self.window.show()
 
2346
                self.p_context = self.window.get_pango_context()
 
2347
                self.p_layout = pango.Layout(self.p_context)
 
2348
                self.p_layout.set_font_description(\
 
2349
                pango.FontDescription(self.font_name))
 
2350
                #self.p_layout.set_width(-1)
 
2351
                self.p_layout.set_width(self.width * pango.SCALE - 6)
 
2352
    
 
2353
        def __setattr__ (self, name, value):
 
2354
                self.__dict__[name] = value
 
2355
                if name in ('text'):
 
2356
                        if name == 'text':
 
2357
                                self.p_layout.set_markup(value)
 
2358
                                ink_rect, logical_rect = self.p_layout.get_pixel_extents()
 
2359
                        self.window.queue_draw()
 
2360
 
 
2361
        def show (self):
 
2362
                """Show the Notify window."""
 
2363
                self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
 
2364
                self.cancel_show()
 
2365
                self.window.show()
 
2366
                self.window.set_keep_above(True)
 
2367
   
 
2368
        def show_delayed (self, delay):
 
2369
                """Show the Notify window after a given delay."""
 
2370
                self.cancel_show()
 
2371
                self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
 
2372
    
 
2373
        def hide (self):
 
2374
                """Hide the Notify window."""
 
2375
                self.cancel_show()
 
2376
                self.window.destroy()
 
2377
    
 
2378
        def cancel_show (self):
 
2379
                """Cancel showing of the Notify."""
 
2380
                if self.__timeout:
 
2381
                        gobject.source_remove(self.__timeout)
 
2382
                        self.p_context = None
 
2383
                        self.p_layout = None
 
2384
    
 
2385
        def __show_timeout (self):
 
2386
                self.show()
 
2387
    
 
2388
        def screen_changed (self, window, screen=None):
 
2389
                if screen == None:
 
2390
                        screen = window.get_screen()
 
2391
                map = screen.get_rgba_colormap()
 
2392
                if not map:
 
2393
                        map = screen.get_rgb_colormap()
 
2394
                window.set_colormap(map)
 
2395
    
 
2396
        def expose (self, widget, event):
 
2397
                ctx = self.window.window.cairo_create()
 
2398
                ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)    # ?
 
2399
                # set a clip region for the expose event
 
2400
                ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
 
2401
                ctx.clip()
 
2402
                # clear context
 
2403
                ctx.set_source_rgba(1, 1, 1, 0)
 
2404
                ctx.set_operator (cairo.OPERATOR_SOURCE)
 
2405
                ctx.paint()
 
2406
                # draw rectangle
 
2407
                self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
 
2408
                self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
 
2409
                ctx.set_source(self.gradient)
 
2410
                ctx.rectangle(0, 0, self.width, self.height)
 
2411
                ctx.fill()
 
2412
                # draw text
 
2413
                ctx.save()
 
2414
                ctx.translate(3, 3)
 
2415
                ctx.set_source_rgba(1, 1, 1, 1) 
 
2416
                ctx.show_layout(self.p_layout)
 
2417
                ctx.fill()
 
2418
                ctx.restore()
 
2419
                ctx.rectangle(0, 0, self.width, self.height)
 
2420
                ctx.set_source_rgba(0, 0, 0, 0.7)
 
2421
                ctx.stroke()
 
2422
 
 
2423
# TEST (as the name implies)
 
2424
"""class TestWidget(ShapedWidget):
 
2425
        
 
2426
        def __init__(self, width, height):
 
2427
                #ShapedWidget.__init__(self, width, height)
 
2428
                super(TestWidget, self).__init__(width, height)
 
2429
        
 
2430
        def draw(self, ctx):
 
2431
                if self.mouse_inside:
 
2432
                        ctx.set_source_rgba(1, 0, 0, 0.8)
 
2433
                else:
 
2434
                        ctx.set_source_rgba(1, 1, 0, 0.8)
 
2435
                ctx.rectangle(0, 0, 32, 32)
 
2436
                ctx.fill()
 
2437
"""
 
2438
 
 
2439
 
 
2440
# ------------------------------------------------------------------------------
 
2441
# MODULE-FUNCTIONS
 
2442
# ------------------------------------------------------------------------------
 
2443
 
 
2444
# the new recommended way of launching a screenlet from the "outside"
 
2445
def launch_screenlet (name, debug=False):
 
2446
        """Launch a screenlet, either through its service or by launching a new
 
2447
        process of the given screenlet. Name has to be the name of the Screenlet's
 
2448
        class without trailing 'Screenlet'.
 
2449
        NOTE: we could only launch the file here"""
 
2450
        # check for service
 
2451
        if services.service_is_running(name):
 
2452
                # add screenlet through service, if running
 
2453
                srvc = services.get_service_by_name(name)
 
2454
                if srvc:
 
2455
                        try:
 
2456
                                srvc.add('')    # empty string for auto-creating ID
 
2457
                                return True
 
2458
                        except Exception, ex:
 
2459
                                print "Error while adding instance by service: %s" % ex
 
2460
        # service not running or error? launch screenlet's file
 
2461
        path = utils.find_first_screenlet_path(name)
 
2462
        if path:
 
2463
                # get full path of screenlet's file
 
2464
                slfile = path + '/' + name + 'Screenlet.py'
 
2465
                # launch screenlet as separate process
 
2466
                print "Launching Screenlet from: %s" % slfile
 
2467
                if debug:
 
2468
                        print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
 
2469
                        out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
 
2470
                else:
 
2471
                        out = '/dev/null'
 
2472
                os.system('python -u %s > %s &' % (slfile, out))
 
2473
                return True
 
2474
        else:
 
2475
                print "Screenlet '%s' could not be launched." % name
 
2476
                return False
 
2477
 
 
2478
def show_message (screenlet, message, title=''):
 
2479
        """Show a message for the given Screenlet (may contain Pango-Markup).
 
2480
        If screenlet is None, this function can be used by other objects as well."""
 
2481
        if screenlet == None:
 
2482
                md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO, 
 
2483
                        buttons=gtk.BUTTONS_OK)
 
2484
                md.set_title(title)
 
2485
        else:
 
2486
                md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO, 
 
2487
                        buttons=gtk.BUTTONS_OK)
 
2488
                md.set_title(screenlet.__name__)
 
2489
        md.set_markup(message)
 
2490
        md.run()
 
2491
        md.destroy()
 
2492
 
 
2493
def show_question (screenlet, message, title=''):
 
2494
        """Show a question for the given Screenlet (may contain Pango-Markup)."""
 
2495
        if screenlet == None:
 
2496
                md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION, 
 
2497
                        buttons=gtk.BUTTONS_YES_NO)
 
2498
                md.set_title(title)
 
2499
        else:
 
2500
                md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION, 
 
2501
                        buttons=gtk.BUTTONS_YES_NO)
 
2502
                md.set_title(screenlet.__name__)
 
2503
        md.set_markup(message)
 
2504
        response = md.run()
 
2505
        md.destroy()
 
2506
        if response == gtk.RESPONSE_YES:
 
2507
                return True
 
2508
        return False
 
2509
 
 
2510
def show_error (screenlet, message, title='Error'):
 
2511
        """Show an error for the given Screenlet (may contain Pango-Markup)."""
 
2512
        if screenlet == None:
 
2513
                md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR, 
 
2514
                        buttons=gtk.BUTTONS_OK)
 
2515
                md.set_title(title)
 
2516
        else:
 
2517
                md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR, 
 
2518
                        buttons=gtk.BUTTONS_OK)
 
2519
                md.set_title(screenlet.__name__)
 
2520
        md.set_markup(message)
 
2521
        md.run()
 
2522
        md.destroy()
 
2523
 
 
2524
def fatal_error (message):
 
2525
        """Raise a fatal error to stdout and stderr and exit with an errorcode."""
 
2526
        import sys
 
2527
        msg = 'FATAL ERROR: %s\n' % message
 
2528
        sys.stdout.write(msg)
 
2529
        sys.stderr.write(msg)
 
2530
        sys.exit(1)
 
2531
 
 
2532
# LEGACY support: functions that are not used any longer (raise fatal error)
 
2533
 
 
2534
def create_new_instance (name):
 
2535
        fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
 
2536
        
 
2537