| Home | Trees | Indices | Help |
|
|---|
|
|
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 #-------------------------------------------------------------------------------
33 # Imports!
34 #-------------------------------------------------------------------------------
35
36 import os
37 import sys
38 from optparse import OptionParser
39
40 import gtk
41 import gettext
42
43 #-------------------------------------------------------------------------------
44 # Find Install Prefix
45 # Note: It's important to do this before we import the Screenlets submodules
46 #-------------------------------------------------------------------------------
47 try:
48 INSTALL_PREFIX = open("/etc/screenlets/prefix").read()[:-1]
49 except:
50 INSTALL_PREFIX = '/usr'
51
52 # translation stuff
53 gettext.textdomain('screenlets')
54 gettext.bindtextdomain('screenlets', INSTALL_PREFIX + '/share/locale')
55
56 #-------------------------------------------------------------------------------
57 # Parse command line options
58 # Note: This must be done before we import any submodules
59 # TODO: Fix up command line parsing. Right now, the same options are parsed for
60 # both screenlets, the manager, the daemon, and melange.
61 #-------------------------------------------------------------------------------
62 # Define command line related constants.
63 # These are used as the default values for command line options that aren't overriden
64 LOG_DISABLED = False # Disable log
65 LOG_LEVEL = 4 # TODO: change to 3 for stable version to show only warning, error and critical messages
66 LOG_OUTPUT = "FILE" # default output, allowed FILE, STDERR, STDOUT
67 LOG_NAME = "screenlets" # Log name
68 LOG_FILE = "/tmp/%s.log" % os.path.basename(sys.argv[0]) # full path to log file; only used if LOG_OUTPUT is FILE
69
70 SESSION_NAME = "default"
71 SESSION_REPLACE = "False"
72 SERVER_NAME = "none"
73
74 # Create the parser
75 parser = OptionParser()
76 # Add options used by all of the various modules
77 parser.add_option("-l", "--logging-level", dest = "LOG_LEVEL", default = LOG_LEVEL,
78 help = ("set logging level.\n0 - log disabled, 1 - only critical errors, 2 - all errors,\
79 3 - all errors and warnings, 4 - all errors, warnings, and info messages, 5 - all messages \
80 including debug output.\ndefault: %s") %(LOG_LEVEL))
81 parser.add_option("-f", "--logging-file", dest = "LOG_FILE", default = LOG_FILE,
82 help = ("write log to LOG_FILE. Default: %s") %(LOG_FILE))
83 parser.add_option("-s", "--server", dest = "SERVER_NAME", default = SERVER_NAME,
84 help = "server name. default options are Melange and Sidebar")
85 parser.add_option("-o", "--output", dest = "LOG_OUTPUT", default = LOG_OUTPUT,
86 help = ("set output. allowed outputs: FILE, STDOUT and STDERR. Default: %s") %(LOG_OUTPUT))
87 parser.add_option("-q", "--quiet", action="store_true", dest = "LOG_DISABLED",
88 default = LOG_DISABLED, help = "Disable log. The same as log level 0")
89 parser.add_option("-r", "--replace", action="store_true", dest = "SESSION_REPLACE",
90 default = SESSION_REPLACE, help = "Replace old session. Default: %s" %(SESSION_REPLACE))
91 parser.add_option("-e", "--session", action="store_true", dest = "SESSION_NAME",
92 default = SESSION_NAME, help = "Name of session. Default: %s" %(SESSION_NAME))
93 # Parse the options and store them in global module variables
94 COMMAND_LINE_OPTIONS, COMMAND_LINE_ARGS = parser.parse_args()
95
96 #-------------------------------------------------------------------------------
97 # Finish with the imports and import all of the submodules
98 #-------------------------------------------------------------------------------
99
100 import pygtk
101 pygtk.require('2.0')
102 import cairo, pango
103 import gobject
104 import glib
105 try:
106 import rsvg
107 except ImportError: print 'No module RSVG , graphics will not be so good'
108 import subprocess
109 import glob
110 import math
111
112 # import screenlet-submodules
113 from options import *
114 import services
115 import utils
116 import sensors
117 # TEST
118 import menu
119 from menu import DefaultMenuItem, add_menuitem
120 from drawing import Drawing
121 # /TEST
122
123 import logger
124
127
128 #-------------------------------------------------------------------------------
129 # CONSTANTS
130 #-------------------------------------------------------------------------------
131
132 # the application name
133 APP_NAME = "Screenlets"
134
135 # the version of the Screenlets-baseclass in use
136 VERSION = "0.1.4"
137
138 # the application copyright
139 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>"
140
141 # the application authors
142 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"]
143
144 # the application comments
145 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"
146
147 DOCUMENTERS = ["Documentation generated by epydoc"]
148
149 ARTISTS = ["ODD radio screenlet theme by ODDie\nPasodoble mail theme by jEsuSdA\nSome themes by RYX\nSome themes by Whise\nMore to come..."]
150
151 TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/"
152
153 # the application website
154 WEBSITE = 'http://www.screenlets.org'
155
156 # 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!)
157 THIRD_PARTY_DOWNLOAD = _("http://www.screenlets.org/index.php/Get_more_screenlets")
158
159
160 #-------------------------------------------------------------------------------
161 # PATHS
162 #-------------------------------------------------------------------------------
163 DIR_TMP = '/tmp/screenlets/'
164
165 TMP_DIR = DIR_TMP
166
167 TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running'
168
169 DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets'
170
171 DIR_USER = os.environ['HOME'] + '/.screenlets'
172
173 DIR_CONFIG = os.environ['HOME'] + '/.config/Screenlets'
174
175 # note that this is the order how themes are preferred to each other
176 # don't change the order just like that
177 SCREENLETS_PATH = [DIR_USER, DIR_USER_ROOT]
178
179 SCREENLETS_PACK_PREFIX = "screenlets-pack-"
180
181 #-------------------------------------------------------------------------------
182 # DBUS
183 #-------------------------------------------------------------------------------
184
185 DAEMON_BUS = 'org.screenlets.ScreenletsDaemon'
186
187 DAEMON_PATH = '/org/screenlets/ScreenletsDaemon'
188
189 DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon'
190
191 #Other stuff
192
193 DEBUG_MODE = True
194
195 DEBIAN = True
196 try:
197 subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
198 except OSError:
199 DEBIAN = False
200
201 UBUNTU = True
202 try:
203 subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
204 except OSError:
205 UBUNTU = False
206
207 #-------------------------------------------------------------------------------
208 # CLASSES
209 #-------------------------------------------------------------------------------
210
212 """A container with constants for the default menuitems"""
213
214 # default menuitem constants (is it right to increase like this?)
215 NONE = 0
216 DELETE = 1
217 THEMES = 2
218 INFO = 4
219 SIZE = 8
220 WINDOW_MENU = 16
221 PROPERTIES = 32
222 DELETE = 64
223 QUIT = 128
224 QUIT_ALL = 256
225 # EXPERIMENTAL!! If you use this, the file menu.xml in the
226 # Screenlet's data-dir is used for generating the menu ...
227 XML = 512
228 ADD = 1024
229 # the default items
230 STANDARD = 1|2|8|16|32|64|128|256|1024
231
232
234 """ScreenletThemes are simple storages that allow loading files
235 as svg-handles within a theme-directory. Each Screenlet can have
236 its own theme-directory. It is up to the Screenlet-developer if he
237 wants to let his Screenlet support themes or not. Themes are
238 turned off by default - if your Screenlet uses Themes, just set the
239 attribute 'theme_name' to the name of the theme's dir you want to use.
240 TODO: remove dict-inheritance"""
241
242 # meta-info (set through theme.conf)
243 __name__ = ''
244 __author__ = ''
245 __version__ = ''
246 __info__ = ''
247
248 # attributes
249 path = ""
250 loaded = False
251 width = 0
252 height = 0
253 option_overrides = {}
254 p_fdesc = None
255 p_layout = None
256 tooltip = None
257 notify = None
258
259
261 # set theme-path and load all files in path
262 self.path = path
263 self.svgs = {}
264 self.pngs = {}
265 self.option_overrides = {}
266 self.loaded = self.__load_all()
267 if self.loaded == False:
268 raise Exception("Error while loading ScreenletTheme in: " + path)
269
271 if name in ("width", "height"):
272 if self.loaded and len(self)>0:
273 size=self[0].get_dimension_data()
274 if name=="width":
275 return size[0]
276 else:
277 return size[1]
278 else:
279 return object.__getattr__(self, name)
280
282 """Apply this theme's overridden options to the given Screenlet."""
283 # disable the canvas-updates in the screenlet
284 screenlet.disable_updates = True
285 # theme_name needs special care (must be applied last)
286 theme_name = ''
287 # loop through overrides and appply them
288 for name in self.option_overrides:
289 print "Override: " + name
290 o = screenlet.get_option_by_name(name)
291 if o and not o.protected:
292 if name == 'theme_name':
293 # import/remember theme-name, but not apply yet
294 theme_name = o.on_import(self.option_overrides[name])
295 else:
296 # set option in screenlet
297 setattr(screenlet, name,
298 o.on_import(self.option_overrides[name]))
299 else:
300 print "WARNING: Option '%s' not found or protected." % name
301 # now apply theme
302 if theme_name != '':
303 screenlet.theme_name = theme_name
304 # re-enable updates and call redraw/reshape
305 screenlet.disable_updates = False
306 screenlet.redraw_canvas()
307 screenlet.update_shape()
308
310 """Checks if a file with filename is loaded in this theme."""
311 try:
312 if self[filename]:
313 return True
314 except:
315 #raise Exception
316 return False
317
319 """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
320 ctx.save()
321
322 if self.p_layout == None :
323
324 self.p_layout = ctx.create_layout()
325 else:
326
327 ctx.update_layout(self.p_layout)
328 self.p_fdesc = pango.FontDescription(font)
329 self.p_layout.set_font_description(self.p_fdesc)
330 self.p_layout.set_text(text)
331 extents, lextents = self.p_layout.get_pixel_extents()
332 ctx.restore()
333 return extents[2]
334
336 """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
337 ctx.save()
338
339 if self.p_layout == None :
340
341 self.p_layout = ctx.create_layout()
342 else:
343
344 ctx.update_layout(self.p_layout)
345 self.p_fdesc = pango.FontDescription(font)
346 self.p_layout.set_font_description(self.p_fdesc)
347 self.p_layout.set_text(text)
348 extents, lextents = self.p_layout.get_pixel_extents()
349 ctx.restore()
350 return extents
351
352 - def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
353 """@DEPRECATED Moved to Screenlets class: Draws text"""
354 ctx.save()
355 ctx.translate(x, y)
356 if self.p_layout == None :
357
358 self.p_layout = ctx.create_layout()
359 else:
360
361 ctx.update_layout(self.p_layout)
362 self.p_fdesc = pango.FontDescription()
363 self.p_fdesc.set_family_static(font)
364 self.p_fdesc.set_size(size * pango.SCALE)
365 self.p_fdesc.set_weight(weight)
366 self.p_layout.set_font_description(self.p_fdesc)
367 self.p_layout.set_width(width * pango.SCALE)
368 self.p_layout.set_alignment(allignment)
369 self.p_layout.set_ellipsize(ellipsize)
370 self.p_layout.set_markup(text)
371 ctx.show_layout(self.p_layout)
372 ctx.restore()
373
374
376 """@DEPRECATED Moved to Screenlets class: Draws a circule"""
377 ctx.save()
378 ctx.translate(x, y)
379 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
380 if fill: ctx.fill()
381 else: ctx.stroke()
382 ctx.restore()
383
384 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
385 """@DEPRECATED Moved to Screenlets class: Draws a line"""
386 ctx.save()
387 ctx.move_to(start_x, start_y)
388 ctx.set_line_width(line_width)
389 ctx.rel_line_to(end_x, end_y)
390 if close : ctx.close_path()
391 if preserve: ctx.stroke_preserve()
392 else: ctx.stroke()
393 ctx.restore()
394
396 """@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
397 ctx.save()
398 ctx.translate(x, y)
399 ctx.rectangle (0,0,width,height)
400 if fill:ctx.fill()
401 else: ctx.stroke()
402 ctx.restore()
403
405 """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
406 ctx.save()
407 ctx.translate(x, y)
408 padding=0 # Padding from the edges of the window
409 rounded=rounded_angle # How round to make the edges 20 is ok
410 w = width
411 h = height
412
413 # Move to top corner
414 ctx.move_to(0+padding+rounded, 0+padding)
415
416 # Top right corner and round the edge
417 ctx.line_to(w-padding-rounded, 0+padding)
418 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
419
420 # Bottom right corner and round the edge
421 ctx.line_to(w-padding, h-padding-rounded)
422 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
423
424 # Bottom left corner and round the edge.
425 ctx.line_to(0+padding+rounded, h-padding)
426 ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
427
428 # Top left corner and round the edge
429 ctx.line_to(0+padding, 0+padding+rounded)
430 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
431
432 # Fill in the shape.
433 if fill:ctx.fill()
434 else: ctx.stroke()
435 ctx.restore()
436
438 """@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
439
440 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
441 iw = pixbuf.get_width()
442 ih = pixbuf.get_height()
443 puxbuf = None
444 return iw,ih
445
447 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
448
449 ctx.save()
450 ctx.translate(x, y)
451 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
452 format = cairo.FORMAT_RGB24
453 if pixbuf.get_has_alpha():
454 format = cairo.FORMAT_ARGB32
455
456 iw = pixbuf.get_width()
457 ih = pixbuf.get_height()
458 image = cairo.ImageSurface(format, iw, ih)
459 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
460
461 ctx.paint()
462 puxbuf = None
463 image = None
464 ctx.restore()
465
466
467
469 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
470
471 ctx.save()
472 ctx.translate(x, y)
473 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
474 format = cairo.FORMAT_RGB24
475 if pixbuf.get_has_alpha():
476 format = cairo.FORMAT_ARGB32
477
478 iw = pixbuf.get_width()
479 ih = pixbuf.get_height()
480 image = cairo.ImageSurface(format, iw, ih)
481
482 matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
483 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
484 if image != None :image.set_matrix(matrix)
485 ctx.paint()
486 puxbuf = None
487 image = None
488 ctx.restore()
489
491 """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
492 if self.notify == None:
493 self.notify = Notify()
494 self.notify.text = text
495 self.notify.show()
496
498 """@DEPRECATED Moved to Screenlets class: hide notification window"""
499 if self.notify != None:
500 self.notify.hide()
501 self.notify = None
502
504 """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
505 if self.tooltip == None:
506 self.tooltip = Tooltip(300, 400)
507 self.tooltip.text = text
508 self.tooltip.x = tooltipx
509 self.tooltip.y = tooltipy
510 self.tooltip.show()
511
513 """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
514 if self.tooltip != None:
515 self.tooltip.hide()
516 self.tooltip = None
517
519 """Check if this theme contains overrides for options."""
520 return len(self.option_overrides) > 0
521
523 """Load a config-file from this theme's dir and save vars in list."""
524 ini = utils.IniReader()
525 if ini.load(filename):
526 if ini.has_section('Theme'):
527 self.__name__ = ini.get_option('name', section='Theme')
528 self.__author__ = ini.get_option('author', section='Theme')
529 self.__version__ = ini.get_option('version', section='Theme')
530 self.__info__ = ini.get_option('info', section='Theme')
531 if ini.has_section('Options'):
532 opts = ini.list_options(section='Options')
533 if opts:
534 for o in opts:
535 self.option_overrides[o[0]] = o[1]
536 print "Loaded theme config from:", filename
537 print "\tName: " + str(self.__name__)
538 print "\tAuthor: " +str(self.__author__)
539 print "\tVersion: " +str(self.__version__)
540 print "\tInfo: " +str(self.__info__)
541 else:
542 print "Failed to theme config from", filename
543
544
546 """Load an SVG-file into this theme and reference it as ref_name."""
547 if self.has_key(filename):
548 del self[filename]
549 try:
550 self[filename] = rsvg.Handle(self.path + "/" + filename)
551 self.svgs[filename[:-4]] = self[filename]
552 if self[filename] != None:
553 # set width/height
554 size=self[filename].get_dimension_data()
555 if size:
556 self.width = size[0]
557 self.height = size[1]
558 return True
559 except NameError, ex:
560 self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
561 self.svgs[filename[:-4]] = self[filename]
562 if self[filename] != None:
563 # set width/height
564 self.width = self[filename].get_width()
565 self.height = self[filename].get_height()
566 print str(ex)
567 return True
568
569 else:
570 return False
571 #self[filename] = None
572
574 """Load a PNG-file into this theme and reference it as ref_name."""
575 if self.has_key(filename):
576 del self[filename]
577 self[filename] = cairo.ImageSurface.create_from_png(self.path +
578 "/" + filename)
579 self.pngs[filename[:-4]] = self[filename]
580 if self[filename] != None:
581 return True
582 else:
583 return False
584 #self[filename] = None
585
587 """Load all files in the theme's path. Currently only loads SVGs and
588 PNGs."""
589 # clear overrides
590 #self.__option_overrides = {}
591 # read dir
592 dirlst = glob.glob(self.path + '/*')
593 if len(dirlst)==0:
594 return False
595 plen = len(self.path) + 1
596 for file in dirlst:
597 fname = file[plen:]
598 if fname.endswith('.svg'):
599 # svg file
600 if self.load_svg(fname) == False:
601 return False
602 elif fname.endswith('.png'):
603 # svg file
604 if self.load_png(fname) == False:
605 return False
606 elif fname == "theme.conf":
607 print "theme.conf found! Loading option-overrides."
608 # theme.conf
609 if self.load_conf(file) == False:
610 return False
611 # print "Theme %s loaded from %s" % (self.__name__, self.path)
612 return True
613
618
619 # TODO: fix function, rsvg handles are not freed properly
621 """Deletes the Theme's contents and frees all rsvg-handles.
622 TODO: freeing rsvg-handles does NOT work for some reason"""
623 self.option_overrides.clear()
624 for filename in self:
625 try:
626 self[filename].free()
627 except AttributeError:pass
628 #self[filename].close()
629 del filename
630 self.clear()
631
632 # TEST: render-function
633 # should be used like "theme.render(context, 'notes-bg')" and then use
634 # either an svg or png image
636 """Render an image from within this theme to the given context. This
637 function can EITHER use png OR svg images, so it is possible to
638 create themes using both image-formats when a Screenlet uses this
639 function for drawing its images. The image name has to be defined
640 without the extension and the function will automatically select
641 the available one (SVG is prefered over PNG)."""
642
643 ### Render Graphics even if rsvg is not available###
644 if os.path.isfile (self.path + '/' + name + '.svg'):
645
646 try:
647 self.svgs[name].render_cairo(ctx)
648 except:
649 try:
650 ctx.set_source_pixbuf(self.svgs[name], 0, 0)
651
652 ctx.paint()
653 pixbuf = None
654 except TypeError:
655 ctx.set_source_surface(self.pngs[name], 0, 0)
656 ctx.paint()
657
658 elif os.path.isfile (self.path + '/' + name + '.png'):
659 ctx.set_source_surface(self.pngs[name], 0, 0)
660 ctx.paint()
661
662
663
670
671
672
674 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
675 fully invisible by default. Subclasses of Screenlet can render
676 their owner-drawn graphics on fully transparent background."""
677
678 # default meta-info for Screenlets
679 __name__ = _('No name set for this Screenlet')
680 __version__ = '0.0'
681 __author__ = _('No author defined for this Screenlet')
682 __desc__ = _('No info set for this Screenlet')
683 __requires__ = []
684 #__target_version__ = '0.0.0'
685 #__backend_version__ = '0.0.1'
686
687 # attributes (TODO: remove them here and add them to the constructor,
688 # because they only should exist per instance)
689 id = '' # id-attribute for handling instances
690 window = None # the gtk.Window behind the scenes
691 theme = None # the assigned ScreenletTheme
692 uses_theme = True # flag indicating whether Screenlet uses themes
693 draw_buttons = True
694 show_buttons = True
695 menu = None # the right-click gtk.Menu
696 is_dragged = False # TODO: make this work
697 quit_on_close = True # if True, closing this instance quits gtk
698 saving_enabled = True # if False, saving is disabled
699 dragging_over = False # true if something is dragged over
700 disable_updates = False # to temporarily avoid refresh/reshape
701 p_context = None # PangoContext
702 p_layout = None # PangoLayout
703
704 # default editable options, available for all Screenlets
705 x = 200
706 y = 150
707 mousex = 0
708 mousey = 0
709 mouse_is_over = False
710 width = 100
711 height = 100
712 scale = 1.0
713 opacity = 1.0
714 theme_name = ""
715 is_visible = True
716 is_sticky = False
717 is_widget = False
718 keep_above = False
719 keep_below = True
720 skip_pager = True
721 first_run = False
722 skip_taskbar = True
723 lock_position = False
724 allow_option_override = True # if False, overrides are ignored
725 ask_on_option_override = True # if True, overrides need confirmation
726 ignore_requirements = False # if True, DEB requirements are ignored
727 resize_on_scroll = True
728 has_started = False
729 has_focus = False
730 # internals (deprecated? we still don't get the end of a begin_move_drag)
731 gtk_icon_theme = None
732 __lastx = 0
733 __lasty = 0
734 p_fdesc = None
735 p_layout = None
736 tooltip = None
737 notify = None
738 # some menuitems (needed for checking/unchecking)
739 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
740 __mi_keep_above = None
741 __mi_keep_below = None
742 __mi_widget = None
743 __mi_sticky = None
744 __mi_lock = None
745 # for custom signals (which aren't acutally used ... yet)
746 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
747 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
748
749 - def __init__ (self, id='', width=100, height=100, parent_window=None,
750 show_window=True, is_widget=False, is_sticky=False,
751 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
752 enable_saving=True, service_class=services.ScreenletService,
753 uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
754 """Constructor - should only be subclassed"""
755
756 # call gobject and EditableOptions superclasses
757 super(Screenlet, self).__init__()
758 EditableOptions.__init__(self)
759 # init properties
760 self.id = id
761 self.session = session
762 self.service = None
763 self.__desc__ = self.__doc__
764
765 # if we have an id and a service-class, register our service
766 if self.id and service_class:
767 self.register_service(service_class)
768 # notify service about adding this instance
769 self.service.instance_added(self.id)
770 self.width = width
771 self.height = height
772 self.is_dragged = False
773 self.__path__ = path
774 self.saving_enabled = enable_saving # used by session
775 # set some attributes without calling __setattr__
776 self.__dict__['theme_name'] = ""
777 self.__dict__['is_widget'] = is_widget
778 self.__dict__['is_sticky'] = is_sticky
779 self.__dict__['draw_buttons'] = draw_buttons
780 self.resize_on_scroll = resize_on_scroll
781 self.__dict__['x'] = 200
782 self.__dict__['y'] = 150
783 # TEST: set scale relative to theme size (NOT WORKING)
784 #self.__dict__['scale'] = width/100.0
785 # /TEST
786 # shape bitmap
787 self.__shape_bitmap = None
788 self.__shape_bitmap_width = 0
789 self.__shape_bitmap_height = 0
790 # "editable" options, first create a group
791 self.add_options_group('Screenlet',
792 _('The basic settings for this Screenlet-instance.'))
793 # if this Screenlet uses themes, add theme-specific options
794 # (NOTE: this option became hidden with 0.0.9 and doesn't use
795 # get_available_themes anymore for showing the choices)
796 self.gtk_icon_theme = gtk.icon_theme_get_default()
797 self.load_buttons(None)
798 self.gtk_icon_theme.connect("changed", self.load_buttons)
799 if draw_buttons: self.draw_buttons = True
800 else: self.draw_buttons = False
801 if uses_theme:
802 self.uses_theme = True
803 self.add_option(StringOption('Screenlet', 'theme_name',
804 default='default', hidden=True))
805 # create/add options
806 self.add_option(IntOption('Screenlet', 'x',
807 default=200, label=_('X-Position'),
808 desc=_('The X-position of this Screenlet ...'),
809 min=0, max=gtk.gdk.screen_width()))
810 self.add_option(IntOption('Screenlet', 'y',
811 default=150, label=_('Y-Position'),
812 desc=_('The Y-position of this Screenlet ...'),
813 min=0, max=gtk.gdk.screen_height()))
814 self.add_option(IntOption('Screenlet', 'width',
815 default=width, label=_('Width'),
816 desc=_('The width of this Screenlet ...'),
817 min=16, max=1000, hidden=True))
818 self.add_option(IntOption('Screenlet', 'height',
819 default=height, label=_('Height'),
820 desc=_('The height of this Screenlet ...'),
821 min=16, max=1000, hidden=True))
822 self.add_option(FloatOption('Screenlet', 'scale',
823 default=self.scale, label=_('Scale'),
824 desc=_('The scale-factor of this Screenlet ...'),
825 min=0.1, max=10.0, digits=2, increment=0.1))
826 self.add_option(FloatOption('Screenlet', 'opacity',
827 default=self.opacity, label=_('Opacity'),
828 desc=_('The opacity of the Screenlet window ...'),
829 min=0.1, max=1.0, digits=2, increment=0.1))
830 self.add_option(BoolOption('Screenlet', 'is_sticky',
831 default=is_sticky, label=_('Stick to Desktop'),
832 desc=_('Show this Screenlet on all workspaces ...')))
833 self.add_option(BoolOption('Screenlet', 'is_widget',
834 default=is_widget, label=_('Treat as Widget'),
835 desc=_('Treat this Screenlet as a "Widget" ...')))
836 self.add_option(BoolOption('Screenlet', 'is_dragged',
837 default=self.is_dragged, label="Is the screenlet dragged",
838 desc="Is the screenlet dragged", hidden=True))
839 self.add_option(BoolOption('Screenlet', 'is_sizable',
840 default=is_sizable, label="Can the screenlet be resized",
841 desc="is_sizable", hidden=True))
842 self.add_option(BoolOption('Screenlet', 'is_visible',
843 default=self.is_visible, label="Usefull to use screenlets as gnome panel applets",
844 desc="is_visible", hidden=True))
845 self.add_option(BoolOption('Screenlet', 'lock_position',
846 default=self.lock_position, label=_('Lock position'),
847 desc=_('Stop the screenlet from being moved...')))
848 self.add_option(BoolOption('Screenlet', 'keep_above',
849 default=self.keep_above, label=_('Keep above'),
850 desc=_('Keep this Screenlet above other windows ...')))
851 self.add_option(BoolOption('Screenlet', 'keep_below',
852 default=self.keep_below, label=_('Keep below'),
853 desc=_('Keep this Screenlet below other windows ...')))
854 self.add_option(BoolOption('Screenlet', 'draw_buttons',
855 default=self.draw_buttons, label=_('Draw button controls'),
856 desc=_('Draw buttons in top right corner')))
857 self.add_option(BoolOption('Screenlet', 'skip_pager',
858 default=self.skip_pager, label=_('Skip Pager'),
859 desc=_('Set this Screenlet to show/hide in pagers ...')))
860 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
861 default=self.skip_pager, label=_('Skip Taskbar'),
862 desc=_('Set this Screenlet to show/hide in taskbars ...')))
863 self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
864 default=self.resize_on_scroll, label=_("Resize on mouse scroll"),
865 desc="resize_on_scroll"))
866 self.add_option(BoolOption('Screenlet', 'ignore_requirements',
867 self.ignore_requirements, _('Ignore requirements'),
868 _('Set this Screenlet to ignore/demand DEB requirements ...')))
869 if uses_theme:
870 self.ask_on_option_override = ask_on_option_override
871 self.add_option(BoolOption('Screenlet', 'allow_option_override',
872 default=self.allow_option_override, label=_('Allow overriding Options'),
873 desc=_('Allow themes to override options in this screenlet ...')))
874 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
875 default=self.ask_on_option_override, label=_('Ask on Override'),
876 desc=_('Show a confirmation-dialog when a theme wants to override ')+\
877 _('the current options of this Screenlet ...')))
878 # disable width/height
879 self.disable_option('width')
880 self.disable_option('height')
881 # create window
882 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
883 if parent_window:
884 self.window.set_parent_window(parent_window)
885 self.window.set_transient_for(parent_window)
886 self.window.set_destroy_with_parent(True)
887 self.window.resize(width, height)
888 self.window.set_decorated(False)
889 try: # Workaround for Ubuntu Natty
890 self.window.set_property('has-resize-grip', False)
891 except TypeError:
892 pass
893 self.window.set_app_paintable(True)
894 # create pango layout, if active
895 if uses_pango:
896 self.p_context = self.window.get_pango_context()
897 if self.p_context:
898 self.p_layout = pango.Layout(self.p_context)
899 self.p_layout.set_font_description(\
900 pango.FontDescription("Sans 12"))
901 # set type hint
902
903 if str(sensors.sys_get_window_manager()).lower() == 'kwin':
904 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
905 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
906 elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
907 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
908 else:
909 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
910 self.window.set_keep_above(self.keep_above)
911 self.window.set_keep_below(self.keep_below)
912 self.window.set_skip_taskbar_hint(True)
913 self.window.set_skip_pager_hint(True)
914 if is_sticky:
915 self.window.stick()
916 self.alpha_screen_changed(self.window)
917 self.update_shape()
918 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
919 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
920 self.window.connect("composited-changed", self.composite_changed)
921 self.window.connect("delete_event", self.delete_event)
922 self.window.connect("destroy", self.destroy)
923 self.window.connect("expose_event", self.expose)
924 self.window.connect("button-press-event", self.button_press)
925 self.window.connect("button-release-event", self.button_release)
926 self.window.connect("configure-event", self.configure_event)
927 self.window.connect("screen-changed", self.alpha_screen_changed)
928 self.window.connect("realize", self.realize_event)
929 self.window.connect("enter-notify-event", self.enter_notify_event)
930 self.window.connect("leave-notify-event", self.leave_notify_event)
931 self.window.connect("focus-in-event", self.focus_in_event)
932 self.window.connect("focus-out-event", self.focus_out_event)
933 self.window.connect("scroll-event", self.scroll_event)
934 self.window.connect("motion-notify-event",self.motion_notify_event)
935 self.window.connect("map-event", self.map_event)
936 self.window.connect("unmap-event", self.unmap_event)
937 # add key-handlers (TODO: use keyword-attrib to activate?)
938 self.window.connect("key-press-event", self.key_press)
939 # drag/drop support (NOTE: still experimental and incomplete)
940 if drag_drop:
941 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
942 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
943 [("text/plain", 0, 0),
944 ("image", 0, 1),
945 ("text/uri-list", 0, 2)],
946 gtk.gdk.ACTION_COPY)
947 self.window.connect("drag_data_received", self.drag_data_received)
948 self.window.connect("drag-begin", self.drag_begin)
949 self.window.connect("drag-end", self.drag_end)
950 self.window.connect("drag-motion", self.drag_motion)
951 self.window.connect("drag-leave", self.drag_leave)
952 # create menu
953 self.menu = gtk.Menu()
954 # 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
955
956
957 if show_window:
958 self.window.show()
959 if self.__name__.endswith("Screenlet"):
960 short_name = self.__name__[:-9]
961 else:
962 short_name = self.__name__
963 if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + short_name + '/default/'+ self.id + '.ini'):
964 self.first_run = True
965 self.window.hide()
966
967 #Make opacity available only when composite is enabled
968 if not self.window.is_composited () :
969 self.disable_option('opacity')
970
972 # set the value in GObject (ESSENTIAL!!!!)
973 self.on_before_set_atribute(name, value)
974 gobject.GObject.__setattr__(self, name, value)
975 # And do other actions
976 if name=="x" or name=="y":
977 if self.has_started:
978 self.window.move(self.x, self.y)
979 elif name == 'opacity':
980 self.window.set_opacity(value)
981 elif name == 'scale':
982 self.window.resize(int(self.width * self.scale),
983 int(self.height * self.scale))
984 # TODO: call on_resize-handler here !!!!
985 self.on_scale()
986 self.redraw_canvas()
987 self.update_shape()
988
989
990 elif name == "theme_name":
991 #self.__dict__ ['theme_name'] = value
992 #self.load_theme(self.get_theme_dir() + value)
993 # load theme
994 print "Theme set to: '%s'" % value
995 path = self.find_theme(value)
996 if path:
997 self.load_theme(path)
998 #self.load_first_theme(value)
999 self.redraw_canvas()
1000 self.update_shape()
1001 elif name in ("width", "height"):
1002 #self.__dict__ [name] = value
1003 if self.window:
1004 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
1005 #self.redraw_canvas()
1006 self.update_shape()
1007 elif name == "is_widget":
1008 if self.has_started:
1009 self.set_is_widget(value)
1010 elif name == "is_visible":
1011 if self.has_started:
1012 if value == True:
1013 self.reshow()
1014 else:
1015 self.window.hide()
1016 elif name == "is_sticky":
1017 if value == True:
1018 self.window.stick()
1019 else:
1020 self.window.unstick()
1021 #if self.__mi_sticky:
1022 # self.__mi_sticky.set_active(value)
1023 elif name == "keep_above":
1024 if self.has_started == True:
1025 self.window.set_keep_above(bool(value))
1026 #self.__mi_keep_above.set_active(value)
1027 elif name == "keep_below":
1028 if self.has_started == True:
1029 self.window.set_keep_below(bool(value))
1030 #self.__mi_keep_below.set_active(value)
1031 elif name == "skip_pager":
1032 if self.window.window:
1033 self.window.window.set_skip_pager_hint(bool(value))
1034 elif name == "skip_taskbar":
1035 if self.window.window:
1036 self.window.window.set_skip_taskbar_hint(bool(value))
1037 # NOTE: This is the new recommended way of storing options in real-time
1038 # (we access the backend through the session here)
1039 if self.saving_enabled:
1040 o = self.get_option_by_name(name)
1041 if o != None:
1042 self.session.backend.save_option(self.id, o.name,
1043 o.on_export(value))
1044 self.on_after_set_atribute(name, value)
1045 # /TEST
1046
1047 #-----------------------------------------------------------------------
1048 # Screenlet's public functions
1049 #-----------------------------------------------------------------------
1050
1052 '''Checks if required DEB packages are installed'''
1053
1054 req_feedback = ""
1055 fail = False
1056
1057 # operators=['>', '=', '<']
1058
1059 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/"'
1060 for req in self.__requires__:
1061 operator = None
1062 # req = req.replace(' ', '')
1063 if req.find('(') != -1:
1064 # package version is specified with an operator (no logical operators supported yet!)
1065 pos = req.find('(')
1066 package = req[:pos].strip()
1067 version_str = req[pos+1:]
1068 version_str = version_str[:version_str.find(')')]
1069 while version_str.find(' ') != -1:
1070 version_str = req.replace(' ', ' ')
1071 res = version_str.split(' ')
1072 version = res[1]
1073 operator = res[0]
1074 else:
1075 # when only package name is specified
1076 package = req
1077 # version of the deb package if unspecified
1078 version = _("?")
1079
1080 installed_version = os.popen(commandstr % package).readline().replace('\n', '')
1081
1082 if len(installed_version) < 1:
1083 req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version}
1084 fail = True
1085 else:
1086 req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version}
1087 # will fail only if dpkg says that version is too old
1088 # otherwise it's responsibility of developer to provide
1089 # correct version id and operator (won't detect problems with these)
1090 if operator is not None:
1091 comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\""
1092 # print comp_command
1093 if subprocess.call(comp_command, shell=True) != 0:
1094 fail = True
1095 if fail:
1096 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")
1097
1210
1221
1241
1242
1243
1247
1268
1270 """Fills the given cairo.Context with fully transparent white."""
1271 ctx.save()
1272 ctx.set_source_rgba(1, 1, 1, 0)
1273 ctx.set_operator (cairo.OPERATOR_SOURCE)
1274 ctx.paint()
1275 ctx.restore()
1276
1278 """Close this Screenlet
1279 TODO: send close-notify instead of destroying window?"""
1280 #self.save_settings()
1281 self.window.unmap()
1282 self.window.destroy()
1283 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
1284
1286 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1287 with the icon and the mask. To supply your own icon you can use the
1288 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1289 w = self.width
1290 h = self.height
1291 icon, mask = self.on_create_drag_icon()
1292 if icon == None:
1293 # create icon
1294 icon = gtk.gdk.Pixmap(self.window.window, w, h)
1295 ctx = icon.cairo_create()
1296 self.clear_cairo_context(ctx)
1297 self.on_draw(ctx)
1298 if mask == None:
1299 # create mask
1300 mask = gtk.gdk.Pixmap(self.window.window, w, h)
1301 ctx = mask.cairo_create()
1302 self.clear_cairo_context(ctx)
1303 self.on_draw_shape(ctx)
1304 return (icon, mask)
1305
1309
1311 """Find the best occurence of a theme and return its global path."""
1312 sn = self.get_short_name()
1313 utils.refresh_available_screenlet_paths()
1314 for p in SCREENLETS_PATH:
1315 fpath = p + '/' + sn + '/themes/' + name
1316 if os.path.isdir(fpath):
1317 return fpath
1318 return None
1319
1321 """Return the short name of this screenlet. This returns the classname
1322 of the screenlet without trailing "Screenlet". Please always use
1323 this function if you want to retrieve the short name of a Screenlet."""
1324 return self.__class__.__name__[:-9]
1325
1327 """Return the name of this screenlet's personal directory."""
1328 p = utils.find_first_screenlet_path(self.get_short_name())
1329 if p:
1330 return p
1331 else:
1332 if self.__path__ != '':
1333 return self.__path__
1334 else:
1335 return os.getcwd()
1336
1338 """Return the name of this screenlet's personal theme-dir.
1339 (Only returns the dir under the screenlet's location"""
1340 return self.get_screenlet_dir() + "/themes/"
1341
1343 """Returns a list with the names of all available themes in this
1344 Screenlet's theme-directories."""
1345 lst = []
1346 utils.refresh_available_screenlet_paths()
1347 for p in SCREENLETS_PATH:
1348 d = p + '/' + self.get_short_name() + '/themes/'
1349 if os.path.isdir(d):
1350 #dirname = self.get_theme_dir()
1351 dirlst = glob.glob(d + '*')
1352 dirlst.sort()
1353 tdlen = len(d)
1354 for fname in dirlst:
1355 if os.path.isdir(fname):
1356 dname = fname[tdlen:]
1357 if not dname in lst:
1358 lst.append(dname)
1359 return lst
1360
1362 self.window.present()
1363 self.has_started = True
1364 self.is_dragged = False
1365 self.keep_above= self.keep_above
1366 self.keep_below= self.keep_below
1367 self.skip_taskbar = self.skip_taskbar
1368 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1369 self.window.set_keep_above(self.keep_above)
1370 self.window.set_keep_below(self.keep_below)
1371 if self.is_widget:
1372 self.set_is_widget(True)
1373 self.has_focus = False
1374
1376 """Called when screenlet finishes loading"""
1377
1378
1379 self.window.present()
1380
1381
1382 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary
1383 self.window.hide()
1384 self.window.move(self.x, self.y)
1385
1386 if DEBIAN and not self.ignore_requirements:
1387 self.check_requirements()
1388
1389 self.window.show()
1390 self.has_started = True
1391 self.is_dragged = False
1392 self.keep_above= self.keep_above
1393 self.keep_below= self.keep_below
1394 self.is_sticky = self.is_sticky
1395 self.skip_taskbar = self.skip_taskbar
1396 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1397 self.window.set_keep_above(self.keep_above)
1398 self.window.set_keep_below(self.keep_below)
1399
1400 self.on_init()
1401 if self.is_widget:
1402 self.set_is_widget(True)
1403 self.has_focus = False
1404 ini = utils.IniReader()
1405 if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run:
1406
1407 if ini.get_option('Lock', section='Options') == 'True':
1408 self.lock_position = True
1409 elif ini.get_option('Lock', section='Options') == 'False':
1410 self.lock_position = False
1411 if ini.get_option('Sticky', section='Options') == 'True':
1412 self.is_sticky = True
1413 elif ini.get_option('Sticky', section='Options') == 'False':
1414 self.is_sticky = False
1415 if ini.get_option('Widget', section='Options') == 'True':
1416 self.is_widget = True
1417 elif ini.get_option('Widget', section='Options') == 'False':
1418 self.is_widget = False
1419 if ini.get_option('Keep_above', section='Options') == 'True':
1420 self.keep_above = True
1421 elif ini.get_option('Keep_above', section='Options') == 'False':
1422 self.keep_above = False
1423 if ini.get_option('Keep_below', section='Options') == 'True':
1424 self.keep_below = True
1425 elif ini.get_option('Keep_below', section='Options') == 'False':
1426 self.keep_below = False
1427 if ini.get_option('draw_buttons', section='Options') == 'True':
1428 self.draw_buttons = True
1429 elif ini.get_option('draw_buttons', section='Options') == 'False':
1430 self.draw_buttons = False
1431
1436
1437 # EXPERIMENTAL:
1438 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
1439 # To do all in one, set attribute self.theme_name instead
1441 """Load a theme for this Screenlet from the given path. NOTE:
1442 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
1443 in one call, set the attribute self.theme_name instead."""
1444 if self.theme:
1445 self.theme.free()
1446 del self.theme
1447 self.theme = ScreenletTheme(path)
1448 # check for errors
1449 if self.theme.loaded == False:
1450 print "Error while loading theme: " + path
1451 self.theme = None
1452 else:
1453 # call user-defined handler
1454 self.on_load_theme()
1455 # if override options is allowed, apply them
1456 if self.allow_option_override:
1457 if self.theme.has_overrides():
1458 if self.ask_on_option_override==True and \
1459 show_question(self,
1460 _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
1461 return
1462 self.theme.apply_option_overrides(self)
1463 # /EXPERIMENTAL
1464
1468
1470 """Register or create the given ScreenletService-(sub)class as the new
1471 service for this Screenlet. If self is not the first instance in the
1472 current session, the service from the first instance will be used
1473 instead and no new service is created."""
1474 if self.session:
1475 if len(self.session.instances) == 0:
1476 # if it is the basic service, add name to call
1477 if service_classobj==services.ScreenletService:#BUG
1478 self.service = service_classobj(self, self.get_short_name())
1479 else:
1480 # else only pass this screenlet
1481 self.service = service_classobj(self)
1482 else:
1483 self.service = self.session.instances[0].service
1484 # TODO: throw exception??
1485 return True
1486 return False
1487
1489 """Set this window to be treated as a Widget (only supported by
1490 compiz using the widget-plugin yet)"""
1491 if value==True:
1492 # set window type to utility
1493 #self.window.window.set_type_hint(
1494 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1495 # set _compiz_widget-property on window
1496 self.window.window.property_change("_COMPIZ_WIDGET",
1497 gtk.gdk.SELECTION_TYPE_WINDOW,
1498 32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1499 else:
1500 # set window type to normal
1501 #self.window.window.set_type_hint(
1502 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1503 # set _compiz_widget-property
1504 self.window.window.property_delete("_COMPIZ_WIDGET")
1505 # notify handler
1506 self.on_switch_widget_state(value)
1507
1509 """Show this Screenlet's underlying gtk.Window"""
1510 self.window.show()
1511 self.window.move(self.x, self.y)
1512 self.on_show()
1513
1515 """Show the EditableSettingsDialog for this Screenlet."""
1516 se = OptionsDialog(490, 450)
1517 img = gtk.Image()
1518 try:
1519 d = self.get_screenlet_dir()
1520 if os.path.isfile(d + '/icon.svg'):
1521 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1522 elif os.path.isfile(d + '/icon.png'):
1523 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1524 img.set_from_pixbuf(icn)
1525 except:
1526 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1527 se.set_title(self.__name__)
1528 se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__),
1529 version='v' + self.__version__, icon=img)
1530 se.show_options_for_object(self)
1531 resp = se.run()
1532 if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1533 se.reset_to_defaults()
1534 else:
1535 self.update_shape()
1536 se.destroy()
1537
1539 """Redraw the entire Screenlet's window area.
1540 TODO: store window alloaction in class and change when size changes."""
1541 # if updates are disabled, just exit
1542 if self.disable_updates:
1543 return
1544 if self.window:
1545 x, y, w, h = self.window.get_allocation()
1546 rect = gtk.gdk.Rectangle(x, y, w, h)
1547 if self.window.window:
1548 self.window.window.invalidate_rect(rect, True)
1549 self.window.window.process_updates(True)
1550 # if self.has_focus and self.draw_buttons and self.show_buttons:
1551 # self.create_buttons()
1552
1553
1555 """Redraw the given Rectangle (x, y, width, height) within the
1556 current Screenlet's window."""
1557 # if updates are disabled, just exit
1558 if self.disable_updates:
1559 return
1560 if self.window:
1561 rect = gtk.gdk.Rectangle(x, y, width, height)
1562 if self.window.window:
1563 self.window.window.invalidate_rect(rect, True)
1564 self.window.window.process_updates(True)
1565
1567 """Removed shaped window , in case the nom composited shape has been set"""
1568 if self.window.window:
1569 self.window.window.shape_combine_mask(None,0,0)
1570
1571 w = self.window.allocation.width
1572 h = self.window.allocation.height
1573
1574 # if 0 return to avoid crashing
1575 if w==0 or h==0: return False
1576 # if size changed, recreate shape bitmap
1577 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1578 self.__shape_bitmap = screenlets.create_empty_bitmap(w, h)
1579 self.__shape_bitmap_width = w
1580 self.__shape_bitmap_height = h
1581
1582 # create context
1583 ctx = self.__shape_bitmap.cairo_create()
1584 self.clear_cairo_context(ctx)
1585
1586 # shape the window acording if the window is composited or not
1587 if self.window.is_composited():
1588 # log.debug(_("Updating input shape"))
1589 self.on_draw_shape(ctx)
1590 # self.main_view.set_shape(self.__shape_bitmap, True)
1591 else:
1592 try:
1593 self.on_draw_shape(ctx)
1594 except:
1595 self.on_draw(ctx)
1596 # log.debug(_("Updating window shape"))
1597 # self.main_view.set_shape(self.__shape_bitmap, False)
1598
1600 """Update window shape (only call this when shape has changed
1601 because it is very ressource intense if ran too often)."""
1602 # if updates are disabled, just exit
1603 if self.disable_updates:
1604 return
1605 #print "UPDATING SHAPE"
1606 # TODO:
1607 #if not self.window.is_composited():
1608 # self.update_shape_non_composited()
1609 # calculate new width/height of shape bitmap
1610 w = int(self.width * self.scale)
1611 h = int(self.height * self.scale)
1612 # if 0 set it to 100 to avoid crashes and stay interactive
1613 if w==0: w = 100
1614 if h==0: h = 100
1615 # if size changed, recreate shape bitmap
1616 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1617 data = ''.zfill(w*h)
1618 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1619 w, h)
1620 self.__shape_bitmap_width = w
1621 self.__shape_bitmap_height = h
1622 # create context and draw shape
1623 ctx = self.__shape_bitmap.cairo_create()
1624 self.clear_cairo_context(ctx) #TEST
1625 if self.has_focus and self.draw_buttons and self.show_buttons:
1626 ctx.save()
1627 #theme1 = gtk.icon_theme_get_default()
1628 #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1629 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1630 #close = theme1.load_icon ("gtk-close", 16, 0)
1631 #prop = theme1.load_icon ("gtk-properties", 16, 0)
1632 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1633 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1634 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1635 ctx.translate((self.width*self.scale)-16,0)
1636 ctx.set_source_pixbuf(self.closeb, 0, 0)
1637 ctx.paint()
1638 ctx.restore()
1639 ctx.save()
1640 ctx.translate((self.width*self.scale)-32,0)
1641 ctx.set_source_pixbuf(self.prop, 0, 0)
1642 ctx.paint()
1643 ctx.restore()
1644 # shape the window acording if the window is composited or not
1645
1646 if self.window.is_composited():
1647
1648 self.on_draw_shape(ctx)
1649 # and cut window with mask
1650 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1651 else:
1652 try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1653 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1654 # and cut window with mask
1655 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1656 self.on_update_shape()
1657
1659 """TEST: This function is intended to shape the window whenever no
1660 composited environment can be found. (NOT WORKING YET!!!!)"""
1661 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1662 # calculate new width/height of shape bitmap
1663 w = int(self.width * self.scale)
1664 h = int(self.height * self.scale)
1665 # if 0 set it to 100 to avoid crashes and stay interactive
1666 if w==0: w = 100
1667 if h==0: h = 100
1668 # if size changed, recreate shape bitmap
1669 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1670 data = ''.zfill(w*h)
1671 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1672 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1673 self.__shape_bitmap_width = w
1674 self.__shape_bitmap_height = h
1675 # and render window contents to it
1676 # TOOD!!
1677 if self.__shape_bitmap:
1678 # create new mask
1679 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1680 # apply new mask to window
1681 self.window.shape_combine_mask(mask)
1682
1686
1687 # ----------------------------------------------------------------------
1688 # Screenlet's event-handler dummies
1689 # ----------------------------------------------------------------------
1690
1692 """Called when the Screenlet gets deleted. Return True to cancel.
1693 TODO: sometimes not properly called"""
1694 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1695 _('Really delete this %s and its settings?') % self.get_short_name())
1696 """return not show_question(self, 'Deleting this instance of the '+\
1697 self.__name__ + ' will also delete all your personal '+\
1698 'changes you made to it!! If you just want to close the '+\
1699 'application, use "Quit" instead. Are you sure you want to '+\
1700 'delete this instance?')
1701 return False"""
1702
1703 # TODO: on_drag
1704 # TODO: on_drag_end
1705
1709
1713
1714
1716 """Called when the screenlet's drag-icon is created. You can supply
1717 your own icon and mask by returning them as a 2-tuple."""
1718 return (None, None)
1719
1723
1727
1731
1732
1736
1740
1744
1746 """Callback for drawing the Screenlet's window - override
1747 in subclasses to implement your own drawing."""
1748 pass
1749
1751 """Callback for drawing the Screenlet's shape - override
1752 in subclasses to draw the window's input-shape-mask."""
1753 pass
1754
1758
1762
1766
1768 """Called when the Screenlet's options have been applied and the
1769 screenlet finished its initialization. If you want to have your
1770 Screenlet do things on startup you should use this handler."""
1771 pass
1772
1776
1780
1784
1786 """Called when a buttonpress-event occured in Screenlet's window.
1787 Returning True causes the event to be not further propagated."""
1788 return False
1789
1793
1797
1801
1803 """Called when a buttonrelease-event occured in Screenlet's window.
1804 Returning True causes the event to be not further propagated."""
1805 return False
1806
1810
1813
1817
1821
1825
1829
1833
1837
1841 # ----------------------------------------------------------------------
1842 # Screenlet's event-handlers for GTK-events
1843 # ----------------------------------------------------------------------
1844
1846 """set colormap for window"""
1847 if screen==None:
1848 screen = window.get_screen()
1849 map = screen.get_rgba_colormap()
1850 if map:
1851 pass
1852 else:
1853 map = screen.get_rgb_colormap()
1854 window.set_colormap(map)
1855
1894
1903
1905 #this handle is called when composition changed
1906 self.remove_shape() # removing previous set shape , this is absolutly necessary
1907 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1908 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1909 self.keep_above= self.keep_above
1910 self.keep_below= self.keep_below
1911 self.window.show()
1912 #print 'Compositing method changed to %s' % str(self.window.is_composited())
1913 self.update_shape()
1914 self.redraw_canvas()
1915
1916 if not self.window.is_composited () :
1917 self.show_buttons = False
1918 self.disable_option("opacity")
1919 # print 'Warning - Buttons will not be shown until screenlet is restarted'
1920
1921 if self.window.is_composited () :
1922 self.enable_option("opacity")
1923
1924 self.is_sticky = self.is_sticky #and again ...
1925 self.keep_above= self.keep_above
1926 self.keep_below= self.keep_below
1927 self.window.set_keep_above(self.keep_above)
1928 self.window.set_keep_below(self.keep_below)
1929 self.on_composite_changed()
1930
1931 # NOTE: this should somehow handle the end of a move_drag-operation
1933 #print "onConfigure"
1934 #print event
1935 #if self.is_dragged == True:
1936 # set new position and cause a save of this Screenlet (not use
1937 # setattr to avoid conflicts with the window.move in __setattr__)
1938 if event.x != self.x:
1939 self.__dict__['x'] = event.x
1940 if self.session:
1941 self.session.backend.save_option(self.id, 'x', str(event.x))
1942 # self.is_dragged = False
1943 if event.y != self.y:
1944 self.__dict__['y'] = event.y
1945 if self.session:
1946 self.session.backend.save_option(self.id, 'y', str(event.y))
1947 # self.is_dragged = False
1948 return False
1949
1951 # cancel event?
1952 print "delete_event"
1953 if self.on_delete() == True:
1954 print "Cancel delete_event"
1955 return True
1956 else:
1957 self.close()
1958 return False
1959
1961 # call user-defined on_quit-handler
1962 self.on_quit()
1963 #print "destroy signal occurred"
1964 self.emit("screenlet_removed", self)
1965 # close gtk?
1966 if self.quit_on_close:
1967 if self.session: # if we have a session, flush current data
1968 self.session.backend.flush()
1969 gtk.main_quit()
1970 else:
1971 del self # ??? does this really work???
1972
1977 #return False
1978
1981
1986
1988 #print "Drag motion"
1989 if self.dragging_over == False:
1990 self.dragging_over = True
1991 self.on_drag_enter(drag_context, x, y, timestamp)
1992 return False
1993
1998
2000 #self.__mouse_inside = True
2001 self.__dict__['mouse_is_over'] = True
2002 self.on_mouse_enter(event)
2003
2004 #self.redraw_canvas()
2005
2007 ctx = widget.window.cairo_create()
2008 # set a clip region for the expose event
2009 ctx.rectangle(event.area.x, event.area.y,
2010 event.area.width, event.area.height)
2011 ctx.clip()
2012 # clear context
2013 self.clear_cairo_context(ctx)
2014
2015 # scale context
2016 #ctx.scale(self.scale, self.scale)
2017 # call drawing method
2018 self.on_draw(ctx)
2019 if self.show_buttons and self.draw_buttons and self.has_focus:
2020 self.create_buttons()
2021 # and delete context (needed?)
2022 del ctx
2023 return False
2024
2026 if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
2027 #Screenlet always gets focus after being dragged so this is a good method
2028 #to control the end of a move_drag operation!!!!!
2029 #This code happens on the end of a move_drag
2030 self.is_dragged=False
2031 self.has_focus = True
2032 self.on_focus(event)
2033 self.update_shape()
2034 self.redraw_canvas()
2035
2036
2037
2038
2040 if self.is_dragged==False:
2041 self.has_focus = False
2042 self.on_unfocus(event)
2043 self.update_shape()
2044 self.redraw_canvas()
2045
2046
2047
2049 """Handle keypress events, needed for in-place editing."""
2050 self.on_key_down(event.keyval, event.string, event)
2051
2053 #self.__mouse_inside = False
2054 #self.is_dragged = False
2055 self.__dict__['mouse_is_over'] = False
2056 self.on_mouse_leave(event)
2057
2058 #self.redraw_canvas()
2059
2132
2134 self.on_map()
2135
2137 self.on_unmap()
2138
2140 self.__dict__['mousex'] = event.x / self.scale
2141 self.__dict__['mousey'] = event.y / self.scale
2142
2143 self.on_mouse_move(event)
2144
2146 """called when window has been realized"""
2147 if self.window.window:
2148 self.window.window.set_back_pixmap(None, False) # needed?
2149
2150 self.on_realize()
2151
2153 if event.direction == gtk.gdk.SCROLL_UP:
2154 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
2155 self.on_scroll_up()
2156 elif event.direction == gtk.gdk.SCROLL_DOWN:
2157 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
2158 self.on_scroll_down()
2159 return False
2160
2161
2163 """Show notification window at current mouse position."""
2164 if self.notify == None:
2165 self.notify = Notify()
2166 self.notify.text = text
2167 self.notify.show()
2168
2170 """hide notification window"""
2171 if self.notify != None:
2172 self.notify.hide()
2173 self.notify = None
2174
2176 """Show tooltip window at current mouse position."""
2177 if self.tooltip == None:
2178 self.tooltip = Tooltip(300, 400)
2179 self.tooltip.text = text
2180 self.tooltip.x = tooltipx
2181 self.tooltip.y = tooltipy
2182 self.tooltip.show()
2183 else:
2184 #self.tooltip = Tooltip(300, 400)
2185 self.tooltip.text = text
2186 self.tooltip.x = tooltipx
2187 self.tooltip.y = tooltipy
2188 #self.tooltip.show()
2189
2195
2196 # TEST!!!
2198 """A simple base-class for creating owner-drawn gtk-widgets"""
2199
2200 __widget=None
2201
2202 mouse_inside = False
2203 width = 32
2204 height = 32
2205
2207 # call superclass
2208 super(ShapedWidget, self).__init__()
2209 # create/setup widget
2210 #self.__widget = gtk.Widget()
2211 self.set_app_paintable(True)
2212 self.set_size_request(width, height)
2213 # connect handlers
2214 self.set_events(gtk.gdk.ALL_EVENTS_MASK)
2215 self.connect("expose-event", self.expose_event)
2216 self.connect("button-press-event", self.button_press)
2217 self.connect("button-release-event", self.button_release)
2218 self.connect("enter-notify-event", self.enter_notify)
2219 self.connect("leave-notify-event", self.leave_notify)
2220
2221 # EXPERIMENTAL: TODO: cache bitmap until size changes
2223 """update widget's shape (only call this when shape has changed)"""
2224 data = ""
2225 for i in xrange(self.width*self.height):
2226 data += "0"
2227 bitmap = gtk.gdk.bitmap_create_from_data(None,
2228 data, self.width, self.height)
2229 ctx = bitmap.cairo_create()
2230 ctx.set_source_rgba(1, 1, 1, 0)
2231 ctx.set_operator (cairo.OPERATOR_SOURCE)
2232 ctx.paint()
2233 self.draw_shape(ctx)
2234 self.input_shape_combine_mask(bitmap, 0, 0)
2235 print "Updating shape."
2236
2241
2246
2250 #print "mouse enter"
2251
2255 #print "mouse leave"
2256
2259
2261 self.draw(ctx)
2262
2264 ctx = widget.window.cairo_create()
2265 # set a clip region for the expose event
2266 ctx.rectangle(event.area.x, event.area.y,
2267 event.area.width, event.area.height)
2268 ctx.clip()
2269 # clear context
2270 ctx.set_source_rgba(1, 1, 1, 0)
2271 ctx.set_operator (cairo.OPERATOR_SOURCE)
2272 ctx.paint()
2273 # call drawing method
2274 self.draw(ctx)
2275 # and delete context
2276 del ctx
2277 return False
2278
2280 __gtype_name__ = 'WrapLabel'
2281
2283 gtk.Label.__init__(self)
2284
2285 self.__wrap_width = 0
2286 self.layout = self.get_layout()
2287 self.layout.set_wrap(pango.WRAP_WORD_CHAR)
2288
2289 if str != None:
2290 self.set_text(str)
2291
2292 self.set_alignment(0.0, 0.0)
2293
2295 layout = self.get_layout()
2296 width, height = layout.get_pixel_size()
2297 requisition.width = 0
2298 requisition.height = height
2299
2303
2307
2311
2320
2322 """A window that displays a text and serves as Tooltip (very basic yet)."""
2323
2324 # internals
2325 __timeout = None
2326
2327 # attribs
2328 text = ''
2329 font_name = 'FreeSans 9'
2330 width = 100
2331 height = 20
2332 x = 0
2333 y = 0
2334
2336 object.__init__(self)
2337 # init
2338 self.__dict__['width'] = width
2339 self.__dict__['height'] = height
2340 self.window = gtk.Window()
2341 self.window.set_app_paintable(True)
2342 self.window.set_size_request(width, height)
2343 self.window.set_decorated(False)
2344 self.window.set_accept_focus(False)
2345 self.window.set_skip_pager_hint(True)
2346 self.window.set_skip_taskbar_hint(True)
2347 self.window.set_keep_above(True)
2348 self.screen_changed(self.window)
2349 self.window.connect("screen-changed", self.screen_changed)
2350 #self.window.show()
2351
2352 try: # Workaround for Ubuntu Natty
2353 self.window.set_property('has-resize-grip', False)
2354 except TypeError:
2355 pass
2356 self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(65535, 65535, 32767))
2357 self.label = WrapLabel()
2358 self.label.modify_font(pango.FontDescription(self.font_name))
2359 self.label.set_line_wrap(True)
2360 self.label.show()
2361 self.window.add(self.label)
2362
2363 self.p_layout = self.label.get_layout()
2364 self.p_layout.set_width(width * pango.SCALE - 6)
2365
2367 self.__dict__[name] = value
2368 if name in ('width', 'height', 'text'):
2369 if name== 'width':
2370 self.p_layout.set_width(width)
2371 elif name == 'text':
2372 value = utils.html_to_pango(value)
2373 self.p_layout.set_markup(value)
2374 self.label.set_markup(value)
2375 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2376 self.height = min(max(logical_rect[3], 16), 400) + 6
2377 self.window.set_size_request(self.width, self.height)
2378 self.window.queue_draw()
2379 elif name == 'x':
2380 self.window.move(int(value), int(self.y))
2381 elif name == 'y':
2382 self.window.move(int(self.x), int(value))
2383
2385 """Show the Tooltip window."""
2386 self.cancel_show()
2387 self.window.show()
2388 self.window.set_keep_above(True)
2389
2391 """Show the Tooltip window after a given delay."""
2392 self.cancel_show()
2393 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2394
2399
2401 """Cancel showing of the Tooltip."""
2402 if self.__timeout:
2403 gobject.source_remove(self.__timeout)
2404 self.p_context = None
2405 self.p_layout = None
2406
2408 self.show()
2409
2417
2419 """A window that displays a text and serves as Notification (very basic yet)."""
2420
2421 # internals
2422 __timeout = None
2423
2424 # attribs
2425 text = ''
2426 font_name = 'FreeSans 9'
2427 width = 200
2428 height = 100
2429 x = 0
2430 y = 0
2431 gradient = cairo.LinearGradient(0, 100,0, 0)
2432
2434 object.__init__(self)
2435 # init
2436 self.window = gtk.Window()
2437 self.window.set_app_paintable(True)
2438 self.window.set_size_request(self.width, self.height)
2439 self.window.set_decorated(False)
2440 self.window.set_accept_focus(False)
2441 self.window.set_skip_pager_hint(True)
2442 self.window.set_skip_taskbar_hint(True)
2443 self.window.set_keep_above(True)
2444 self.screen_changed(self.window)
2445 self.window.connect("expose_event", self.expose)
2446 self.window.connect("screen-changed", self.screen_changed)
2447 #self.window.show()
2448 self.p_context = self.window.get_pango_context()
2449 self.p_layout = pango.Layout(self.p_context)
2450 self.p_layout.set_font_description(\
2451 pango.FontDescription(self.font_name))
2452 #self.p_layout.set_width(-1)
2453 self.p_layout.set_width(self.width * pango.SCALE - 6)
2454
2456 self.__dict__[name] = value
2457 if name in ('text'):
2458 if name == 'text':
2459 self.p_layout.set_markup(value)
2460 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2461 self.window.queue_draw()
2462
2464 """Show the Notify window."""
2465 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
2466 self.cancel_show()
2467 self.window.show()
2468 self.window.set_keep_above(True)
2469
2471 """Show the Notify window after a given delay."""
2472 self.cancel_show()
2473 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2474
2479
2481 """Cancel showing of the Notify."""
2482 if self.__timeout:
2483 gobject.source_remove(self.__timeout)
2484 self.p_context = None
2485 self.p_layout = None
2486
2488 self.show()
2489
2491 if screen == None:
2492 screen = window.get_screen()
2493 map = screen.get_rgba_colormap()
2494 if not map:
2495 map = screen.get_rgb_colormap()
2496 window.set_colormap(map)
2497
2499 ctx = self.window.window.cairo_create()
2500 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2501 # set a clip region for the expose event
2502 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2503 ctx.clip()
2504 # clear context
2505 ctx.set_source_rgba(1, 1, 1, 0)
2506 ctx.set_operator (cairo.OPERATOR_SOURCE)
2507 ctx.paint()
2508 # draw rectangle
2509 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2510 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2511 ctx.set_source(self.gradient)
2512 ctx.rectangle(0, 0, self.width, self.height)
2513 ctx.fill()
2514 # draw text
2515 ctx.save()
2516 ctx.translate(3, 3)
2517 ctx.set_source_rgba(1, 1, 1, 1)
2518 ctx.show_layout(self.p_layout)
2519 ctx.fill()
2520 ctx.restore()
2521 ctx.rectangle(0, 0, self.width, self.height)
2522 ctx.set_source_rgba(0, 0, 0, 0.7)
2523 ctx.stroke()
2524
2525 # TEST (as the name implies)
2526 """class TestWidget(ShapedWidget):
2527
2528 def __init__(self, width, height):
2529 #ShapedWidget.__init__(self, width, height)
2530 super(TestWidget, self).__init__(width, height)
2531
2532 def draw(self, ctx):
2533 if self.mouse_inside:
2534 ctx.set_source_rgba(1, 0, 0, 0.8)
2535 else:
2536 ctx.set_source_rgba(1, 1, 0, 0.8)
2537 ctx.rectangle(0, 0, 32, 32)
2538 ctx.fill()
2539 """
2540
2541
2542 # ------------------------------------------------------------------------------
2543 # MODULE-FUNCTIONS
2544 # ------------------------------------------------------------------------------
2545
2546 # the new recommended way of launching a screenlet from the "outside"
2548 """Launch a screenlet, either through its service or by launching a new
2549 process of the given screenlet. Name has to be the name of the Screenlet's
2550 class without trailing 'Screenlet'.
2551 NOTE: we could only launch the file here"""
2552 # check for service
2553 if services.service_is_running(name):
2554 # add screenlet through service, if running
2555 srvc = services.get_service_by_name(name)
2556 if srvc:
2557 try:
2558 srvc.add('') # empty string for auto-creating ID
2559 return True
2560 except Exception, ex:
2561 print "Error while adding instance by service: %s" % ex
2562 # service not running or error? launch screenlet's file
2563 path = utils.find_first_screenlet_path(name)
2564 if path:
2565 # get full path of screenlet's file
2566 slfile = path + '/' + name + 'Screenlet.py'
2567 # launch screenlet as separate process
2568 print "Launching Screenlet from: %s" % slfile
2569 if debug:
2570 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
2571 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
2572 else:
2573 out = '/dev/null'
2574 os.system('python -u %s > %s &' % (slfile, out))
2575 return True
2576 else:
2577 print "Screenlet '%s' could not be launched." % name
2578 return False
2579
2581 """Show a message for the given Screenlet (may contain Pango-Markup).
2582 If screenlet is None, this function can be used by other objects as well."""
2583 if screenlet == None:
2584 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2585 buttons=gtk.BUTTONS_OK)
2586 md.set_title(title)
2587 else:
2588 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2589 buttons=gtk.BUTTONS_OK)
2590 md.set_title(screenlet.__name__)
2591 md.set_markup(message)
2592 md.run()
2593 md.destroy()
2594
2596 """Show a question for the given Screenlet (may contain Pango-Markup)."""
2597 if screenlet == None:
2598 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2599 buttons=gtk.BUTTONS_YES_NO)
2600 md.set_title(title)
2601 else:
2602 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2603 buttons=gtk.BUTTONS_YES_NO)
2604 md.set_title(screenlet.__name__)
2605 md.set_markup(message)
2606 response = md.run()
2607 md.destroy()
2608 if response == gtk.RESPONSE_YES:
2609 return True
2610 return False
2611
2613 """Show an error for the given Screenlet (may contain Pango-Markup)."""
2614 if screenlet == None:
2615 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2616 buttons=gtk.BUTTONS_OK)
2617 md.set_title(title)
2618 else:
2619 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2620 buttons=gtk.BUTTONS_OK)
2621 md.set_title(screenlet.__name__)
2622 md.set_markup(message)
2623 md.run()
2624 md.destroy()
2625
2627 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
2628 import sys
2629 msg = 'FATAL ERROR: %s\n' % message
2630 sys.stdout.write(msg)
2631 sys.stderr.write(msg)
2632 sys.exit(1)
2633
2634 # LEGACY support: functions that are not used any longer (raise fatal error)
2635
2637 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2638
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 6 10:56:40 2011 | http://epydoc.sourceforge.net |