| 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 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
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 #-------------------------------------------------------------------------------
138 # CLASSES
139 #-------------------------------------------------------------------------------
140
142 """A container with constants for the default menuitems"""
143
144 # default menuitem constants (is it right to increase like this?)
145 NONE = 0
146 DELETE = 1
147 THEMES = 2
148 INFO = 4
149 SIZE = 8
150 WINDOW_MENU = 16
151 PROPERTIES = 32
152 DELETE = 64
153 QUIT = 128
154 QUIT_ALL = 256
155 # EXPERIMENTAL!! If you use this, the file menu.xml in the
156 # Screenlet's data-dir is used for generating the menu ...
157 XML = 512
158 ADD = 1024
159 # the default items
160 STANDARD = 1|2|8|16|32|64|128|256|1024
161
162
164 """ScreenletThemes are simple storages that allow loading files
165 as svg-handles within a theme-directory. Each Screenlet can have
166 its own theme-directory. It is up to the Screenlet-developer if he
167 wants to let his Screenlet support themes or not. Themes are
168 turned off by default - if your Screenlet uses Themes, just set the
169 attribute 'theme_name' to the name of the theme's dir you want to use.
170 TODO: remove dict-inheritance"""
171
172 # meta-info (set through theme.conf)
173 __name__ = ''
174 __author__ = ''
175 __version__ = ''
176 __info__ = ''
177
178 # attributes
179 path = ""
180 loaded = False
181 width = 0
182 height = 0
183 option_overrides = {}
184 p_fdesc = None
185 p_layout = None
186 tooltip = None
187 notify = None
188
189
191 # set theme-path and load all files in path
192 self.path = path
193 self.svgs = {}
194 self.pngs = {}
195 self.option_overrides = {}
196 self.loaded = self.__load_all()
197 if self.loaded == False:
198 raise Exception("Error while loading ScreenletTheme in: " + path)
199
201 if name in ("width", "height"):
202 if self.loaded and len(self)>0:
203 size=self[0].get_dimension_data()
204 if name=="width":
205 return size[0]
206 else:
207 return size[1]
208 else:
209 return object.__getattr__(self, name)
210
212 """Apply this theme's overridden options to the given Screenlet."""
213 # disable the canvas-updates in the screenlet
214 screenlet.disable_updates = True
215 # theme_name needs special care (must be applied last)
216 theme_name = ''
217 # loop through overrides and appply them
218 for name in self.option_overrides:
219 print "Override: " + name
220 o = screenlet.get_option_by_name(name)
221 if o and not o.protected:
222 if name == 'theme_name':
223 # import/remember theme-name, but not apply yet
224 theme_name = o.on_import(self.option_overrides[name])
225 else:
226 # set option in screenlet
227 setattr(screenlet, name,
228 o.on_import(self.option_overrides[name]))
229 else:
230 print "WARNING: Option '%s' not found or protected." % name
231 # now apply theme
232 if theme_name != '':
233 screenlet.theme_name = theme_name
234 # re-enable updates and call redraw/reshape
235 screenlet.disable_updates = False
236 screenlet.redraw_canvas()
237 screenlet.update_shape()
238
240 """Checks if a file with filename is loaded in this theme."""
241 try:
242 if self[filename]:
243 return True
244 except:
245 #raise Exception
246 return False
247
249 """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
250 ctx.save()
251
252 if self.p_layout == None :
253
254 self.p_layout = ctx.create_layout()
255 else:
256
257 ctx.update_layout(self.p_layout)
258 self.p_fdesc = pango.FontDescription(font)
259 self.p_layout.set_font_description(self.p_fdesc)
260 self.p_layout.set_text(text)
261 extents, lextents = self.p_layout.get_pixel_extents()
262 ctx.restore()
263 return extents[2]
264
266 """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
267 ctx.save()
268
269 if self.p_layout == None :
270
271 self.p_layout = ctx.create_layout()
272 else:
273
274 ctx.update_layout(self.p_layout)
275 self.p_fdesc = pango.FontDescription(font)
276 self.p_layout.set_font_description(self.p_fdesc)
277 self.p_layout.set_text(text)
278 extents, lextents = self.p_layout.get_pixel_extents()
279 ctx.restore()
280 return extents
281
282 - def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
283 """@DEPRECATED Moved to Screenlets class: Draws text"""
284 ctx.save()
285 ctx.translate(x, y)
286 if self.p_layout == None :
287
288 self.p_layout = ctx.create_layout()
289 else:
290
291 ctx.update_layout(self.p_layout)
292 self.p_fdesc = pango.FontDescription()
293 self.p_fdesc.set_family_static(font)
294 self.p_fdesc.set_size(size * pango.SCALE)
295 self.p_fdesc.set_weight(weight)
296 self.p_layout.set_font_description(self.p_fdesc)
297 self.p_layout.set_width(width * pango.SCALE)
298 self.p_layout.set_alignment(allignment)
299 self.p_layout.set_ellipsize(ellipsize)
300 self.p_layout.set_markup(text)
301 ctx.show_layout(self.p_layout)
302 ctx.restore()
303
304
306 """@DEPRECATED Moved to Screenlets class: Draws a circule"""
307 ctx.save()
308 ctx.translate(x, y)
309 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
310 if fill:ctx.fill()
311 else: ctx.stroke()
312 ctx.restore()
313
314 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
315 """@DEPRECATED Moved to Screenlets class: Draws a line"""
316 ctx.save()
317 ctx.move_to(start_x, start_y)
318 ctx.set_line_width(line_width)
319 ctx.rel_line_to(end_x, end_y)
320 if close : ctx.close_path()
321 if preserve: ctx.stroke_preserve()
322 else: ctx.stroke()
323 ctx.restore()
324
326 """@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
327 ctx.save()
328 ctx.translate(x, y)
329 ctx.rectangle (0,0,width,height)
330 if fill:ctx.fill()
331 else: ctx.stroke()
332 ctx.restore()
333
335 """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
336 ctx.save()
337 ctx.translate(x, y)
338 padding=0 # Padding from the edges of the window
339 rounded=rounded_angle # How round to make the edges 20 is ok
340 w = width
341 h = height
342
343 # Move to top corner
344 ctx.move_to(0+padding+rounded, 0+padding)
345
346 # Top right corner and round the edge
347 ctx.line_to(w-padding-rounded, 0+padding)
348 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
349
350 # Bottom right corner and round the edge
351 ctx.line_to(w-padding, h-padding-rounded)
352 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
353
354 # Bottom left corner and round the edge.
355 ctx.line_to(0+padding+rounded, h-padding)
356 ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
357
358 # Top left corner and round the edge
359 ctx.line_to(0+padding, 0+padding+rounded)
360 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
361
362 # Fill in the shape.
363 if fill:ctx.fill()
364 else: ctx.stroke()
365 ctx.restore()
366
368 """@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
369
370 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
371 iw = pixbuf.get_width()
372 ih = pixbuf.get_height()
373 puxbuf = None
374 return iw,ih
375
377 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
378
379 ctx.save()
380 ctx.translate(x, y)
381 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
382 format = cairo.FORMAT_RGB24
383 if pixbuf.get_has_alpha():
384 format = cairo.FORMAT_ARGB32
385
386 iw = pixbuf.get_width()
387 ih = pixbuf.get_height()
388 image = cairo.ImageSurface(format, iw, ih)
389 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
390
391 ctx.paint()
392 puxbuf = None
393 image = None
394 ctx.restore()
395
396
397
399 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
400
401 ctx.save()
402 ctx.translate(x, y)
403 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
404 format = cairo.FORMAT_RGB24
405 if pixbuf.get_has_alpha():
406 format = cairo.FORMAT_ARGB32
407
408 iw = pixbuf.get_width()
409 ih = pixbuf.get_height()
410 image = cairo.ImageSurface(format, iw, ih)
411
412 matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
413 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
414 if image != None :image.set_matrix(matrix)
415 ctx.paint()
416 puxbuf = None
417 image = None
418 ctx.restore()
419
421 """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
422 if self.notify == None:
423 self.notify = Notify()
424 self.notify.text = text
425 self.notify.show()
426
428 """@DEPRECATED Moved to Screenlets class: hide notification window"""
429 if self.notify != None:
430 self.notify.hide()
431 self.notify = None
432
434 """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
435 if self.tooltip == None:
436 self.tooltip = Tooltip(300, 400)
437 self.tooltip.text = text
438 self.tooltip.x = tooltipx
439 self.tooltip.y = tooltipy
440 self.tooltip.show()
441
443 """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
444 if self.tooltip != None:
445 self.tooltip.hide()
446 self.tooltip = None
447
449 """Check if this theme contains overrides for options."""
450 return len(self.option_overrides) > 0
451
453 """Load a config-file from this theme's dir and save vars in list."""
454 ini = utils.IniReader()
455 if ini.load(filename):
456 if ini.has_section('Theme'):
457 self.__name__ = ini.get_option('name', section='Theme')
458 self.__author__ = ini.get_option('author', section='Theme')
459 self.__version__ = ini.get_option('version', section='Theme')
460 self.__info__ = ini.get_option('info', section='Theme')
461 if ini.has_section('Options'):
462 opts = ini.list_options(section='Options')
463 if opts:
464 for o in opts:
465 self.option_overrides[o[0]] = o[1]
466 print "Loaded theme config from:", filename
467 print "\tName: " + str(self.__name__)
468 print "\tAuthor: " +str(self.__author__)
469 print "\tVersion: " +str(self.__version__)
470 print "\tInfo: " +str(self.__info__)
471 else:
472 print "Failed to theme config from", filename
473
474
476 """Load an SVG-file into this theme and reference it as ref_name."""
477 if self.has_key(filename):
478 del self[filename]
479 try:
480 self[filename] = rsvg.Handle(self.path + "/" + filename)
481 self.svgs[filename[:-4]] = self[filename]
482 if self[filename] != None:
483 # set width/height
484 size=self[filename].get_dimension_data()
485 if size:
486 self.width = size[0]
487 self.height = size[1]
488 return True
489 except NameError, ex:
490 self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
491 self.svgs[filename[:-4]] = self[filename]
492 if self[filename] != None:
493 # set width/height
494 self.width = self[filename].get_width()
495 self.height = self[filename].get_height()
496 print str(ex)
497 return True
498
499 else:
500 return False
501 #self[filename] = None
502
504 """Load a PNG-file into this theme and reference it as ref_name."""
505 if self.has_key(filename):
506 del self[filename]
507 self[filename] = cairo.ImageSurface.create_from_png(self.path +
508 "/" + filename)
509 self.pngs[filename[:-4]] = self[filename]
510 if self[filename] != None:
511 return True
512 else:
513 return False
514 #self[filename] = None
515
517 """Load all files in the theme's path. Currently only loads SVGs and
518 PNGs."""
519 # clear overrides
520 #self.__option_overrides = {}
521 # read dir
522 dirlst = glob.glob(self.path + '/*')
523 if len(dirlst)==0:
524 return False
525 plen = len(self.path) + 1
526 for file in dirlst:
527 fname = file[plen:]
528 if fname.endswith('.svg'):
529 # svg file
530 if self.load_svg(fname) == False:
531 return False
532 elif fname.endswith('.png'):
533 # svg file
534 if self.load_png(fname) == False:
535 return False
536 elif fname == "theme.conf":
537 print "theme.conf found! Loading option-overrides."
538 # theme.conf
539 if self.load_conf(file) == False:
540 return False
541 # print "Theme %s loaded from %s" % (self.__name__, self.path)
542 return True
543
548
549 # TODO: fix function, rsvg handles are not freed properly
551 """Deletes the Theme's contents and frees all rsvg-handles.
552 TODO: freeing rsvg-handles does NOT work for some reason"""
553 self.option_overrides.clear()
554 for filename in self:
555 try:
556 self[filename].free()
557 except AttributeError:pass
558 #self[filename].close()
559 del filename
560 self.clear()
561
562 # TEST: render-function
563 # should be used like "theme.render(context, 'notes-bg')" and then use
564 # either an svg or png image
566 """Render an image from within this theme to the given context. This
567 function can EITHER use png OR svg images, so it is possible to
568 create themes using both image-formats when a Screenlet uses this
569 function for drawing its images. The image name has to be defined
570 without the extension and the function will automatically select
571 the available one (SVG is prefered over PNG)."""
572
573 ### Render Graphics even if rsvg is not available###
574 if os.path.isfile (self.path + '/' + name + '.svg'):
575
576 try:
577 self.svgs[name].render_cairo(ctx)
578 except:
579 try:
580 ctx.set_source_pixbuf(self.svgs[name], 0, 0)
581
582 ctx.paint()
583 pixbuf = None
584 except TypeError:
585 ctx.set_source_surface(self.pngs[name], 0, 0)
586 ctx.paint()
587
588 elif os.path.isfile (self.path + '/' + name + '.png'):
589 ctx.set_source_surface(self.pngs[name], 0, 0)
590 ctx.paint()
591
592
593
595 # Scale the pixmap
596 ctx.set_source_rgba(color[0], color[1], color[2], color[3])
597 ctx.set_source_surface(self.pngs[name], 0, 0)
598 ctx.mask_surface(image, 0, 0)
599 ctx.stroke()
600
601
602
604 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
605 fully invisible by default. Subclasses of Screenlet can render
606 their owner-drawn graphics on fully transparent background."""
607
608 # default meta-info for Screenlets
609 __name__ = _('No name set for this Screenlet')
610 __version__ = '0.0'
611 __author__ = _('No author defined for this Screenlet')
612 __desc__ = _('No info set for this Screenlet')
613 __requires__ = []
614 #__target_version__ = '0.0.0'
615 #__backend_version__ = '0.0.1'
616
617 # attributes (TODO: remove them here and add them to the constructor,
618 # because they only should exist per instance)
619 id = '' # id-attribute for handling instances
620 window = None # the gtk.Window behind the scenes
621 theme = None # the assigned ScreenletTheme
622 uses_theme = True # flag indicating whether Screenlet uses themes
623 draw_buttons = True
624 show_buttons = True
625 menu = None # the right-click gtk.Menu
626 is_dragged = False # TODO: make this work
627 quit_on_close = True # if True, closing this instance quits gtk
628 saving_enabled = True # if False, saving is disabled
629 dragging_over = False # true if something is dragged over
630 disable_updates = False # to temporarily avoid refresh/reshape
631 p_context = None # PangoContext
632 p_layout = None # PangoLayout
633
634 # default editable options, available for all Screenlets
635 x = 0
636 y = 0
637 mousex = 0
638 mousey = 0
639 mouse_is_over = False
640 width = 100
641 height = 100
642 scale = 1.0
643 opacity = 1.0
644 theme_name = ""
645 is_visible = True
646 is_sticky = False
647 is_widget = False
648 keep_above = True
649 keep_below = False
650 skip_pager = True
651 first_run = False
652 skip_taskbar = True
653 lock_position = False
654 allow_option_override = True # if False, overrides are ignored
655 ask_on_option_override = True # if True, overrides need confirmation
656 ignore_requirements = False # if True, DEB requirements are ignored
657 resize_on_scroll = True
658 has_started = False
659 has_focus = False
660 # internals (deprecated? we still don't get the end of a begin_move_drag)
661 gtk_icon_theme = None
662 __lastx = 0
663 __lasty = 0
664 p_fdesc = None
665 p_layout = None
666 tooltip = None
667 notify = None
668 # some menuitems (needed for checking/unchecking)
669 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
670 __mi_keep_above = None
671 __mi_keep_below = None
672 __mi_widget = None
673 __mi_sticky = None
674 __mi_lock = None
675 # for custom signals (which aren't acutally used ... yet)
676 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
677 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
678
679 - def __init__ (self, id='', width=100, height=100, parent_window=None,
680 show_window=True, is_widget=False, is_sticky=False,
681 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
682 enable_saving=True, service_class=services.ScreenletService,
683 uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
684 """Constructor - should only be subclassed"""
685
686 # call gobject and EditableOptions superclasses
687 super(Screenlet, self).__init__()
688 EditableOptions.__init__(self)
689 # init properties
690 self.id = id
691 self.session = session
692 self.service = None
693 # if we have an id and a service-class, register our service
694 if self.id and service_class:
695 self.register_service(service_class)
696 # notify service about adding this instance
697 self.service.instance_added(self.id)
698 self.width = width
699 self.height = height
700 self.is_dragged = False
701 self.__path__ = path
702 self.saving_enabled = enable_saving # used by session
703 # set some attributes without calling __setattr__
704 self.__dict__['theme_name'] = ""
705 self.__dict__['is_widget'] = is_widget
706 self.__dict__['is_sticky'] = is_sticky
707 self.__dict__['draw_buttons'] = draw_buttons
708 self.resize_on_scroll = resize_on_scroll
709 self.__dict__['x'] = 0
710 self.__dict__['y'] = 0
711 # TEST: set scale relative to theme size (NOT WORKING)
712 #self.__dict__['scale'] = width/100.0
713 # /TEST
714 # shape bitmap
715 self.__shape_bitmap = None
716 self.__shape_bitmap_width = 0
717 self.__shape_bitmap_height = 0
718 # "editable" options, first create a group
719 self.add_options_group('Screenlet',
720 _('The basic settings for this Screenlet-instance.'))
721 # if this Screenlet uses themes, add theme-specific options
722 # (NOTE: this option became hidden with 0.0.9 and doesn't use
723 # get_available_themes anymore for showing the choices)
724 self.gtk_icon_theme = gtk.icon_theme_get_default()
725 self.load_buttons(None)
726 self.gtk_icon_theme.connect("changed", self.load_buttons)
727 if draw_buttons: self.draw_buttons = True
728 else: self.draw_buttons = False
729 if uses_theme:
730 self.uses_theme = True
731 self.add_option(StringOption('Screenlet', 'theme_name',
732 'default', '', '', hidden=True))
733 # create/add options
734 self.add_option(IntOption('Screenlet', 'x',
735 0, _('X-Position'), _('The X-position of this Screenlet ...'),
736 min=0, max=gtk.gdk.screen_width()))
737 self.add_option(IntOption('Screenlet', 'y',
738 0, _('Y-Position'), _('The Y-position of this Screenlet ...'),
739 min=0, max=gtk.gdk.screen_height()))
740 self.add_option(IntOption('Screenlet', 'width',
741 width, _('Width'), _('The width of this Screenlet ...'),
742 min=16, max=1000, hidden=True))
743 self.add_option(IntOption('Screenlet', 'height',
744 height, _('Height'), _('The height of this Screenlet ...'),
745 min=16, max=1000, hidden=True))
746 self.add_option(FloatOption('Screenlet', 'scale',
747 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'),
748 min=0.1, max=10.0, digits=2, increment=0.1))
749 self.add_option(FloatOption('Screenlet', 'opacity',
750 self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'),
751 min=0.1, max=1.0, digits=2, increment=0.1))
752 self.add_option(BoolOption('Screenlet', 'is_sticky',
753 is_sticky, _('Stick to Desktop'),
754 _('Show this Screenlet on all workspaces ...')))
755 self.add_option(BoolOption('Screenlet', 'is_widget',
756 is_widget, _('Treat as Widget'),
757 _('Treat this Screenlet as a "Widget" ...')))
758 self.add_option(BoolOption('Screenlet', 'is_dragged',
759 self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True))
760 self.add_option(BoolOption('Screenlet', 'is_sizable',
761 is_sizable, "Can the screenlet be resized","is_sizable", hidden=True))
762 self.add_option(BoolOption('Screenlet', 'is_visible',
763 self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True))
764 self.add_option(BoolOption('Screenlet', 'lock_position',
765 self.lock_position, _('Lock position'),
766 _('Stop the screenlet from being moved...')))
767 self.add_option(BoolOption('Screenlet', 'keep_above',
768 self.keep_above, _('Keep above'),
769 _('Keep this Screenlet above other windows ...')))
770 self.add_option(BoolOption('Screenlet', 'keep_below',
771 self.keep_below, _('Keep below'),
772 _('Keep this Screenlet below other windows ...')))
773 self.add_option(BoolOption('Screenlet', 'draw_buttons',
774 self.draw_buttons, _('Draw button controls'),
775 _('Draw buttons in top right corner')))
776 self.add_option(BoolOption('Screenlet', 'skip_pager',
777 self.skip_pager, _('Skip Pager'),
778 _('Set this Screenlet to show/hide in pagers ...')))
779 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
780 self.skip_pager, _('Skip Taskbar'),
781 _('Set this Screenlet to show/hide in taskbars ...')))
782 self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
783 self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll"))
784 self.add_option(BoolOption('Screenlet', 'ignore_requirements',
785 self.ignore_requirements, _('Ignore requirements'),
786 _('Set this Screenlet to ignore/demand DEB requirements ...')))
787 if uses_theme:
788 self.ask_on_option_override = ask_on_option_override
789 self.add_option(BoolOption('Screenlet', 'allow_option_override',
790 self.allow_option_override, _('Allow overriding Options'),
791 _('Allow themes to override options in this screenlet ...')))
792 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
793 self.ask_on_option_override, _('Ask on Override'),
794 _('Show a confirmation-dialog when a theme wants to override ')+\
795 _('the current options of this Screenlet ...')))
796 # disable width/height
797 self.disable_option('width')
798 self.disable_option('height')
799 # create window
800 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
801 if parent_window:
802 self.window.set_parent_window(parent_window)
803 self.window.set_transient_for(parent_window)
804 self.window.set_destroy_with_parent(True)
805 self.window.resize(width, height)
806 self.window.set_decorated(False)
807 self.window.set_app_paintable(True)
808 # create pango layout, if active
809 if uses_pango:
810 self.p_context = self.window.get_pango_context()
811 if self.p_context:
812 self.p_layout = pango.Layout(self.p_context)
813 self.p_layout.set_font_description(\
814 pango.FontDescription("Sans 12"))
815 # set type hint
816
817 if str(sensors.sys_get_window_manager()).lower() == 'kwin':
818 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
819 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
820 elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
821 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
822 else:
823 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
824 self.window.set_keep_above(True)
825 self.window.set_skip_taskbar_hint(True)
826 self.window.set_skip_pager_hint(True)
827 if is_sticky:
828 self.window.stick()
829 self.alpha_screen_changed(self.window)
830 self.update_shape()
831 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
832 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
833 self.window.connect("composited-changed", self.composite_changed)
834 self.window.connect("delete_event", self.delete_event)
835 self.window.connect("destroy", self.destroy)
836 self.window.connect("expose_event", self.expose)
837 self.window.connect("button-press-event", self.button_press)
838 self.window.connect("button-release-event", self.button_release)
839 self.window.connect("configure-event", self.configure_event)
840 self.window.connect("screen-changed", self.alpha_screen_changed)
841 self.window.connect("realize", self.realize_event)
842 self.window.connect("enter-notify-event", self.enter_notify_event)
843 self.window.connect("leave-notify-event", self.leave_notify_event)
844 self.window.connect("focus-in-event", self.focus_in_event)
845 self.window.connect("focus-out-event", self.focus_out_event)
846 self.window.connect("scroll-event", self.scroll_event)
847 self.window.connect("motion-notify-event",self.motion_notify_event)
848 self.window.connect("map-event", self.map_event)
849 self.window.connect("unmap-event", self.unmap_event)
850 # add key-handlers (TODO: use keyword-attrib to activate?)
851 self.window.connect("key-press-event", self.key_press)
852 # drag/drop support (NOTE: still experimental and incomplete)
853 if drag_drop:
854 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
855 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
856 [("text/plain", 0, 0),
857 ("image", 0, 1),
858 ("text/uri-list", 0, 2)],
859 gtk.gdk.ACTION_COPY)
860 self.window.connect("drag_data_received", self.drag_data_received)
861 self.window.connect("drag-begin", self.drag_begin)
862 self.window.connect("drag-end", self.drag_end)
863 self.window.connect("drag-motion", self.drag_motion)
864 self.window.connect("drag-leave", self.drag_leave)
865 # create menu
866 self.menu = gtk.Menu()
867 # 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
868
869
870 if show_window:
871 self.window.show()
872 # print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id
873 if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'):
874 self.first_run = True
875 self.window.hide()
876
877 #Make opacity available only when composite is enabled
878 if not self.window.is_composited () :
879 self.disable_option('opacity')
880
882 # set the value in GObject (ESSENTIAL!!!!)
883 self.on_before_set_atribute(name, value)
884 gobject.GObject.__setattr__(self, name, value)
885 # And do other actions
886 if name=="x" or name=="y":
887 if self.has_started:
888 self.window.move(self.x, self.y)
889 elif name == 'opacity':
890 self.window.set_opacity(value)
891 elif name == 'scale':
892 self.window.resize(int(self.width * self.scale),
893 int(self.height * self.scale))
894 # TODO: call on_resize-handler here !!!!
895 self.on_scale()
896 self.redraw_canvas()
897 self.update_shape()
898
899
900 elif name == "theme_name":
901 #self.__dict__ ['theme_name'] = value
902 #self.load_theme(self.get_theme_dir() + value)
903 # load theme
904 print "Theme set to: '%s'" % value
905 path = self.find_theme(value)
906 if path:
907 self.load_theme(path)
908 #self.load_first_theme(value)
909 self.redraw_canvas()
910 self.update_shape()
911 elif name in ("width", "height"):
912 #self.__dict__ [name] = value
913 if self.window:
914 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
915 #self.redraw_canvas()
916 self.update_shape()
917 elif name == "is_widget":
918 if self.has_started:
919 self.set_is_widget(value)
920 elif name == "is_visible":
921 if self.has_started:
922 if value == True:
923 self.reshow()
924 else:
925 self.window.hide()
926 elif name == "is_sticky":
927 if value == True:
928 self.window.stick()
929 else:
930 self.window.unstick()
931 #if self.__mi_sticky:
932 # self.__mi_sticky.set_active(value)
933 elif name == "keep_above":
934 if self.has_started == True:
935 self.window.set_keep_above(bool(value))
936 #self.__mi_keep_above.set_active(value)
937 elif name == "keep_below":
938 if self.has_started == True:
939 self.window.set_keep_below(bool(value))
940 #self.__mi_keep_below.set_active(value)
941 elif name == "skip_pager":
942 if self.window.window:
943 self.window.window.set_skip_pager_hint(bool(value))
944 elif name == "skip_taskbar":
945 if self.window.window:
946 self.window.window.set_skip_taskbar_hint(bool(value))
947 # NOTE: This is the new recommended way of storing options in real-time
948 # (we access the backend through the session here)
949 if self.saving_enabled:
950 o = self.get_option_by_name(name)
951 if o != None:
952 self.session.backend.save_option(self.id, o.name,
953 o.on_export(value))
954 self.on_after_set_atribute(name, value)
955 # /TEST
956
957 #-----------------------------------------------------------------------
958 # Screenlet's public functions
959 #-----------------------------------------------------------------------
960
962 '''Checks if required DEB packages are installed'''
963
964 req_feedback = ""
965 fail = False
966
967 # operators=['>', '=', '<']
968
969 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/"'
970 for req in self.__requires__:
971 operator = None
972 # req = req.replace(' ', '')
973 if req.find('(') != -1:
974 # package version is specified with an operator (no logical operators supported yet!)
975 pos = req.find('(')
976 package = req[:pos].strip()
977 version_str = req[pos+1:]
978 version_str = version_str[:version_str.find(')')]
979 while version_str.find(' ') != -1:
980 version_str = req.replace(' ', ' ')
981 res = version_str.split(' ')
982 version = res[1]
983 operator = res[0]
984 else:
985 # when only package name is specified
986 package = req
987 # version of the deb package if unspecified
988 version = _("?")
989
990 installed_version = os.popen(commandstr % package).readline().replace('\n', '')
991
992 if len(installed_version) < 1:
993 req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version}
994 fail = True
995 else:
996 req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version}
997 # will fail only if dpkg says that version is too old
998 # otherwise it's responsibility of developer to provide
999 # correct version id and operator (won't detect problems with these)
1000 if operator is not None:
1001 comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\""
1002 # print comp_command
1003 if subprocess.call(comp_command, shell=True) != 0:
1004 fail = True
1005 if fail:
1006 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")
1007
1120
1131
1151
1152
1153
1157
1178
1180 """Fills the given cairo.Context with fully transparent white."""
1181 ctx.save()
1182 ctx.set_source_rgba(1, 1, 1, 0)
1183 ctx.set_operator (cairo.OPERATOR_SOURCE)
1184 ctx.paint()
1185 ctx.restore()
1186
1188 """Close this Screenlet
1189 TODO: send close-notify instead of destroying window?"""
1190 #self.save_settings()
1191 self.window.unmap()
1192 self.window.destroy()
1193 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
1194
1196 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1197 with the icon and the mask. To supply your own icon you can use the
1198 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1199 w = self.width
1200 h = self.height
1201 icon, mask = self.on_create_drag_icon()
1202 if icon == None:
1203 # create icon
1204 icon = gtk.gdk.Pixmap(self.window.window, w, h)
1205 ctx = icon.cairo_create()
1206 self.clear_cairo_context(ctx)
1207 self.on_draw(ctx)
1208 if mask == None:
1209 # create mask
1210 mask = gtk.gdk.Pixmap(self.window.window, w, h)
1211 ctx = mask.cairo_create()
1212 self.clear_cairo_context(ctx)
1213 self.on_draw_shape(ctx)
1214 return (icon, mask)
1215
1219
1221 """Find the best occurence of a theme and return its global path."""
1222 sn = self.get_short_name()
1223 utils.refresh_available_screenlet_paths()
1224 for p in SCREENLETS_PATH:
1225 fpath = p + '/' + sn + '/themes/' + name
1226 if os.path.isdir(fpath):
1227 return fpath
1228 return None
1229
1231 """Return the short name of this screenlet. This returns the classname
1232 of the screenlet without trailing "Screenlet". Please always use
1233 this function if you want to retrieve the short name of a Screenlet."""
1234 return self.__class__.__name__[:-9]
1235
1237 """Return the name of this screenlet's personal directory."""
1238 p = utils.find_first_screenlet_path(self.get_short_name())
1239 if p:
1240 return p
1241 else:
1242 if self.__path__ != '':
1243 return self.__path__
1244 else:
1245 return os.getcwd()
1246
1248 """Return the name of this screenlet's personal theme-dir.
1249 (Only returns the dir under the screenlet's location"""
1250 return self.get_screenlet_dir() + "/themes/"
1251
1253 """Returns a list with the names of all available themes in this
1254 Screenlet's theme-directories."""
1255 lst = []
1256 utils.refresh_available_screenlet_paths()
1257 for p in SCREENLETS_PATH:
1258 d = p + '/' + self.get_short_name() + '/themes/'
1259 if os.path.isdir(d):
1260 #dirname = self.get_theme_dir()
1261 dirlst = glob.glob(d + '*')
1262 dirlst.sort()
1263 tdlen = len(d)
1264 for fname in dirlst:
1265 if os.path.isdir(fname):
1266 dname = fname[tdlen:]
1267 if not dname in lst:
1268 lst.append(dname)
1269 return lst
1270
1272 self.window.present()
1273 self.has_started = True
1274 self.is_dragged = False
1275 self.keep_above= self.keep_above
1276 self.keep_below= self.keep_below
1277 self.skip_taskbar = self.skip_taskbar
1278 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1279 self.window.set_keep_above(self.keep_above)
1280 self.window.set_keep_below(self.keep_below)
1281 if self.is_widget:
1282 self.set_is_widget(True)
1283 self.has_focus = False
1284
1286 """Called when screenlet finishes loading"""
1287
1288
1289 self.window.present()
1290
1291
1292 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary
1293 self.window.hide()
1294 self.window.move(self.x, self.y)
1295
1296 if not self.ignore_requirements:
1297 self.check_requirements()
1298
1299 self.window.show()
1300 self.has_started = True
1301 self.is_dragged = False
1302 self.keep_above= self.keep_above
1303 self.keep_below= self.keep_below
1304 self.is_sticky = self.is_sticky
1305 self.skip_taskbar = self.skip_taskbar
1306 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1307 self.window.set_keep_above(self.keep_above)
1308 self.window.set_keep_below(self.keep_below)
1309
1310 self.on_init()
1311 if self.is_widget:
1312 self.set_is_widget(True)
1313 self.has_focus = False
1314 ini = utils.IniReader()
1315 if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run:
1316
1317 if ini.get_option('Lock', section='Options') == 'True':
1318 self.lock_position = True
1319 elif ini.get_option('Lock', section='Options') == 'False':
1320 self.lock_position = False
1321 if ini.get_option('Sticky', section='Options') == 'True':
1322 self.is_sticky = True
1323 elif ini.get_option('Sticky', section='Options') == 'False':
1324 self.is_sticky = False
1325 if ini.get_option('Widget', section='Options') == 'True':
1326 self.is_widget = True
1327 elif ini.get_option('Widget', section='Options') == 'False':
1328 self.is_widget = False
1329 if ini.get_option('Keep_above', section='Options') == 'True':
1330 self.keep_above = True
1331 elif ini.get_option('Keep_above', section='Options') == 'False':
1332 self.keep_above = False
1333 if ini.get_option('Keep_below', section='Options') == 'True':
1334 self.keep_below = True
1335 elif ini.get_option('Keep_below', section='Options') == 'False':
1336 self.keep_below = False
1337 if ini.get_option('draw_buttons', section='Options') == 'True':
1338 self.draw_buttons = True
1339 elif ini.get_option('draw_buttons', section='Options') == 'False':
1340 self.draw_buttons = False
1341
1346
1347 # EXPERIMENTAL:
1348 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
1349 # To do all in one, set attribute self.theme_name instead
1351 """Load a theme for this Screenlet from the given path. NOTE:
1352 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
1353 in one call, set the attribute self.theme_name instead."""
1354 if self.theme:
1355 self.theme.free()
1356 del self.theme
1357 self.theme = ScreenletTheme(path)
1358 # check for errors
1359 if self.theme.loaded == False:
1360 print "Error while loading theme: " + path
1361 self.theme = None
1362 else:
1363 # call user-defined handler
1364 self.on_load_theme()
1365 # if override options is allowed, apply them
1366 if self.allow_option_override:
1367 if self.theme.has_overrides():
1368 if self.ask_on_option_override==True and \
1369 show_question(self,
1370 _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
1371 return
1372 self.theme.apply_option_overrides(self)
1373 # /EXPERIMENTAL
1374
1378
1380 """Register or create the given ScreenletService-(sub)class as the new
1381 service for this Screenlet. If self is not the first instance in the
1382 current session, the service from the first instance will be used
1383 instead and no new service is created."""
1384 if self.session:
1385 if len(self.session.instances) == 0:
1386 # if it is the basic service, add name to call
1387 if service_classobj==services.ScreenletService:#BUG
1388 self.service = service_classobj(self, self.get_short_name())
1389 else:
1390 # else only pass this screenlet
1391 self.service = service_classobj(self)
1392 else:
1393 self.service = self.session.instances[0].service
1394 # TODO: throw exception??
1395 return True
1396 return False
1397
1399 """Set this window to be treated as a Widget (only supported by
1400 compiz using the widget-plugin yet)"""
1401 if value==True:
1402 # set window type to utility
1403 #self.window.window.set_type_hint(
1404 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1405 # set _compiz_widget-property on window
1406 self.window.window.property_change("_COMPIZ_WIDGET",
1407 gtk.gdk.SELECTION_TYPE_WINDOW,
1408 32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1409 else:
1410 # set window type to normal
1411 #self.window.window.set_type_hint(
1412 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1413 # set _compiz_widget-property
1414 self.window.window.property_delete("_COMPIZ_WIDGET")
1415 # notify handler
1416 self.on_switch_widget_state(value)
1417
1419 """Show this Screenlet's underlying gtk.Window"""
1420 self.window.show()
1421 self.window.move(self.x, self.y)
1422 self.on_show()
1423
1425 """Show the EditableSettingsDialog for this Screenlet."""
1426 se = OptionsDialog(490, 450)
1427 img = gtk.Image()
1428 try:
1429 d = self.get_screenlet_dir()
1430 if os.path.isfile(d + '/icon.svg'):
1431 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1432 elif os.path.isfile(d + '/icon.png'):
1433 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1434 img.set_from_pixbuf(icn)
1435 except:
1436 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1437 se.set_title(self.__name__)
1438 se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__),
1439 version='v' + self.__version__, icon=img)
1440 se.show_options_for_object(self)
1441 resp = se.run()
1442 if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1443 se.reset_to_defaults()
1444 else:
1445 self.update_shape()
1446 se.destroy()
1447
1449 """Redraw the entire Screenlet's window area.
1450 TODO: store window alloaction in class and change when size changes."""
1451 # if updates are disabled, just exit
1452 if self.disable_updates:
1453 return
1454 if self.window:
1455 x, y, w, h = self.window.get_allocation()
1456 rect = gtk.gdk.Rectangle(x, y, w, h)
1457 if self.window.window:
1458 self.window.window.invalidate_rect(rect, True)
1459 self.window.window.process_updates(True)
1460 # if self.has_focus and self.draw_buttons and self.show_buttons:
1461 # self.create_buttons()
1462
1463
1465 """Redraw the given Rectangle (x, y, width, height) within the
1466 current Screenlet's window."""
1467 # if updates are disabled, just exit
1468 if self.disable_updates:
1469 return
1470 if self.window:
1471 rect = gtk.gdk.Rectangle(x, y, width, height)
1472 if self.window.window:
1473 self.window.window.invalidate_rect(rect, True)
1474 self.window.window.process_updates(True)
1475
1477 """Removed shaped window , in case the nom composited shape has been set"""
1478 if self.window.window:
1479 self.window.window.shape_combine_mask(None,0,0)
1480
1482 """Update window shape (only call this when shape has changed
1483 because it is very ressource intense if ran too often)."""
1484 # if updates are disabled, just exit
1485 if self.disable_updates:
1486 return
1487 #print "UPDATING SHAPE"
1488 # TODO:
1489 #if not self.window.is_composited():
1490 # self.update_shape_non_composited()
1491 # calculate new width/height of shape bitmap
1492 w = int(self.width * self.scale)
1493 h = int(self.height * self.scale)
1494 # if 0 set it to 100 to avoid crashes and stay interactive
1495 if w==0: w = 100
1496 if h==0: h = 100
1497 # if size changed, recreate shape bitmap
1498 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1499 data = ''.zfill(w*h)
1500 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1501 w, h)
1502 self.__shape_bitmap_width = w
1503 self.__shape_bitmap_height = h
1504 # create context and draw shape
1505 ctx = self.__shape_bitmap.cairo_create()
1506 self.clear_cairo_context(ctx) #TEST
1507 if self.has_focus and self.draw_buttons and self.show_buttons:
1508 ctx.save()
1509 #theme1 = gtk.icon_theme_get_default()
1510 #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1511 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1512 #close = theme1.load_icon ("gtk-close", 16, 0)
1513 #prop = theme1.load_icon ("gtk-properties", 16, 0)
1514 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1515 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1516 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1517 ctx.translate((self.width*self.scale)-16,0)
1518 ctx.set_source_pixbuf(self.closeb, 0, 0)
1519 ctx.paint()
1520 ctx.restore()
1521 ctx.save()
1522 ctx.translate((self.width*self.scale)-32,0)
1523 ctx.set_source_pixbuf(self.prop, 0, 0)
1524 ctx.paint()
1525 ctx.restore()
1526 # shape the window acording if the window is composited or not
1527
1528 if self.window.is_composited():
1529
1530 self.on_draw_shape(ctx)
1531 # and cut window with mask
1532 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1533 else:
1534 try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1535 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1536 # and cut window with mask
1537 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1538 self.on_update_shape()
1539
1541 """TEST: This function is intended to shape the window whenever no
1542 composited environment can be found. (NOT WORKING YET!!!!)"""
1543 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1544 # calculate new width/height of shape bitmap
1545 w = int(self.width * self.scale)
1546 h = int(self.height * self.scale)
1547 # if 0 set it to 100 to avoid crashes and stay interactive
1548 if w==0: w = 100
1549 if h==0: h = 100
1550 # if size changed, recreate shape bitmap
1551 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1552 data = ''.zfill(w*h)
1553 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1554 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1555 self.__shape_bitmap_width = w
1556 self.__shape_bitmap_height = h
1557 # and render window contents to it
1558 # TOOD!!
1559 if self.__shape_bitmap:
1560 # create new mask
1561 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1562 # apply new mask to window
1563 self.window.shape_combine_mask(mask)
1564
1565 # ----------------------------------------------------------------------
1566 # Screenlet's event-handler dummies
1567 # ----------------------------------------------------------------------
1568
1570 """Called when the Screenlet gets deleted. Return True to cancel.
1571 TODO: sometimes not properly called"""
1572 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1573 _('Really delete this %s and its settings?') % self.get_short_name())
1574 """return not show_question(self, 'Deleting this instance of the '+\
1575 self.__name__ + ' will also delete all your personal '+\
1576 'changes you made to it!! If you just want to close the '+\
1577 'application, use "Quit" instead. Are you sure you want to '+\
1578 'delete this instance?')
1579 return False"""
1580
1581 # TODO: on_drag
1582 # TODO: on_drag_end
1583
1587
1591
1592
1594 """Called when the screenlet's drag-icon is created. You can supply
1595 your own icon and mask by returning them as a 2-tuple."""
1596 return (None, None)
1597
1601
1605
1609
1610
1614
1618
1622
1624 """Callback for drawing the Screenlet's window - override
1625 in subclasses to implement your own drawing."""
1626 pass
1627
1629 """Callback for drawing the Screenlet's shape - override
1630 in subclasses to draw the window's input-shape-mask."""
1631 pass
1632
1636
1640
1644
1646 """Called when the Screenlet's options have been applied and the
1647 screenlet finished its initialization. If you want to have your
1648 Screenlet do things on startup you should use this handler."""
1649 pass
1650
1654
1658
1662
1664 """Called when a buttonpress-event occured in Screenlet's window.
1665 Returning True causes the event to be not further propagated."""
1666 return False
1667
1671
1675
1679
1681 """Called when a buttonrelease-event occured in Screenlet's window.
1682 Returning True causes the event to be not further propagated."""
1683 return False
1684
1688
1691
1695
1699
1703
1707
1711
1715
1719 # ----------------------------------------------------------------------
1720 # Screenlet's event-handlers for GTK-events
1721 # ----------------------------------------------------------------------
1722
1724 """set colormap for window"""
1725 if screen==None:
1726 screen = window.get_screen()
1727 map = screen.get_rgba_colormap()
1728 if map:
1729 pass
1730 else:
1731 map = screen.get_rgb_colormap()
1732 window.set_colormap(map)
1733
1772
1781
1783 #this handle is called when composition changed
1784 self.remove_shape() # removing previous set shape , this is absolutly necessary
1785 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1786 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1787 self.keep_above= self.keep_above
1788 self.keep_below= self.keep_below
1789 self.window.show()
1790 #print 'Compositing method changed to %s' % str(self.window.is_composited())
1791 self.update_shape()
1792 self.redraw_canvas()
1793
1794 if not self.window.is_composited () :
1795 self.show_buttons = False
1796 self.disable_option("opacity")
1797 # print 'Warning - Buttons will not be shown until screenlet is restarted'
1798
1799 if self.window.is_composited () :
1800 self.enable_option("opacity")
1801
1802 self.is_sticky = self.is_sticky #and again ...
1803 self.keep_above= self.keep_above
1804 self.keep_below= self.keep_below
1805 self.window.set_keep_above(self.keep_above)
1806 self.window.set_keep_below(self.keep_below)
1807 self.on_composite_changed()
1808
1809 # NOTE: this should somehow handle the end of a move_drag-operation
1811 #print "onConfigure"
1812 #print event
1813 #if self.is_dragged == True:
1814 # set new position and cause a save of this Screenlet (not use
1815 # setattr to avoid conflicts with the window.move in __setattr__)
1816 if event.x != self.x:
1817 self.__dict__['x'] = event.x
1818 if self.session:
1819 self.session.backend.save_option(self.id, 'x', str(event.x))
1820 # self.is_dragged = False
1821 if event.y != self.y:
1822 self.__dict__['y'] = event.y
1823 if self.session:
1824 self.session.backend.save_option(self.id, 'y', str(event.y))
1825 # self.is_dragged = False
1826 return False
1827
1829 # cancel event?
1830 print "delete_event"
1831 if self.on_delete() == True:
1832 print "Cancel delete_event"
1833 return True
1834 else:
1835 self.close()
1836 return False
1837
1839 # call user-defined on_quit-handler
1840 self.on_quit()
1841 #print "destroy signal occurred"
1842 self.emit("screenlet_removed", self)
1843 # close gtk?
1844 if self.quit_on_close:
1845 if self.session: # if we have a session, flush current data
1846 self.session.backend.flush()
1847 gtk.main_quit()
1848 else:
1849 del self # ??? does this really work???
1850
1855 #return False
1856
1859
1864
1866 #print "Drag motion"
1867 if self.dragging_over == False:
1868 self.dragging_over = True
1869 self.on_drag_enter(drag_context, x, y, timestamp)
1870 return False
1871
1876
1878 #self.__mouse_inside = True
1879 self.__dict__['mouse_is_over'] = True
1880 self.on_mouse_enter(event)
1881
1882 #self.redraw_canvas()
1883
1885 ctx = widget.window.cairo_create()
1886 # clear context
1887 self.clear_cairo_context(ctx)
1888 # set a clip region for the expose event
1889 ctx.rectangle(event.area.x, event.area.y,
1890 event.area.width, event.area.height)
1891 ctx.clip()
1892
1893 # scale context
1894 #ctx.scale(self.scale, self.scale)
1895 # call drawing method
1896 self.on_draw(ctx)
1897 if self.show_buttons and self.draw_buttons and self.has_focus:
1898 self.create_buttons()
1899 # and delete context (needed?)
1900 del ctx
1901 return False
1902
1904 if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
1905 #Screenlet always gets focus after being dragged so this is a good method
1906 #to control the end of a move_drag operation!!!!!
1907 #This code happens on the end of a move_drag
1908 self.is_dragged=False
1909 self.has_focus = True
1910 self.on_focus(event)
1911 self.update_shape()
1912 self.redraw_canvas()
1913
1914
1915
1916
1918 if self.is_dragged==False:
1919 self.has_focus = False
1920 self.on_unfocus(event)
1921 self.update_shape()
1922 self.redraw_canvas()
1923
1924
1925
1927 """Handle keypress events, needed for in-place editing."""
1928 self.on_key_down(event.keyval, event.string, event)
1929
1931 #self.__mouse_inside = False
1932 #self.is_dragged = False
1933 self.__dict__['mouse_is_over'] = False
1934 self.on_mouse_leave(event)
1935
1936 #self.redraw_canvas()
1937
2010
2012 self.on_map()
2013
2015 self.on_unmap()
2016
2018 self.__dict__['mousex'] = event.x / self.scale
2019 self.__dict__['mousey'] = event.y / self.scale
2020
2021 self.on_mouse_move(event)
2022
2024 """called when window has been realized"""
2025 if self.window.window:
2026 self.window.window.set_back_pixmap(None, False) # needed?
2027
2028 self.on_realize()
2029
2031 if event.direction == gtk.gdk.SCROLL_UP:
2032 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
2033 self.on_scroll_up()
2034 elif event.direction == gtk.gdk.SCROLL_DOWN:
2035 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
2036 self.on_scroll_down()
2037 return False
2038
2039
2041 """Show notification window at current mouse position."""
2042 if self.notify == None:
2043 self.notify = Notify()
2044 self.notify.text = text
2045 self.notify.show()
2046
2048 """hide notification window"""
2049 if self.notify != None:
2050 self.notify.hide()
2051 self.notify = None
2052
2054 """Show tooltip window at current mouse position."""
2055 if self.tooltip == None:
2056 self.tooltip = Tooltip(300, 400)
2057 self.tooltip.text = text
2058 self.tooltip.x = tooltipx
2059 self.tooltip.y = tooltipy
2060 self.tooltip.show()
2061 else:
2062 #self.tooltip = Tooltip(300, 400)
2063 self.tooltip.text = text
2064 self.tooltip.x = tooltipx
2065 self.tooltip.y = tooltipy
2066 #self.tooltip.show()
2067
2073
2074 # TEST!!!
2076 """A simple base-class for creating owner-drawn gtk-widgets"""
2077
2078 __widget=None
2079
2080 mouse_inside = False
2081 width = 32
2082 height = 32
2083
2085 # call superclass
2086 super(ShapedWidget, self).__init__()
2087 # create/setup widget
2088 #self.__widget = gtk.Widget()
2089 self.set_app_paintable(True)
2090 self.set_size_request(width, height)
2091 # connect handlers
2092 self.set_events(gtk.gdk.ALL_EVENTS_MASK)
2093 self.connect("expose-event", self.expose_event)
2094 self.connect("button-press-event", self.button_press)
2095 self.connect("button-release-event", self.button_release)
2096 self.connect("enter-notify-event", self.enter_notify)
2097 self.connect("leave-notify-event", self.leave_notify)
2098
2099 # EXPERIMENTAL: TODO: cache bitmap until size changes
2101 """update widget's shape (only call this when shape has changed)"""
2102 data = ""
2103 for i in xrange(self.width*self.height):
2104 data += "0"
2105 bitmap = gtk.gdk.bitmap_create_from_data(None,
2106 data, self.width, self.height)
2107 ctx = bitmap.cairo_create()
2108 ctx.set_source_rgba(1, 1, 1, 0)
2109 ctx.set_operator (cairo.OPERATOR_SOURCE)
2110 ctx.paint()
2111 self.draw_shape(ctx)
2112 self.input_shape_combine_mask(bitmap, 0, 0)
2113 print "Updating shape."
2114
2119
2124
2128 #print "mouse enter"
2129
2133 #print "mouse leave"
2134
2137
2139 self.draw(ctx)
2140
2142 ctx = widget.window.cairo_create()
2143 # set a clip region for the expose event
2144 ctx.rectangle(event.area.x, event.area.y,
2145 event.area.width, event.area.height)
2146 ctx.clip()
2147 # clear context
2148 ctx.set_source_rgba(1, 1, 1, 0)
2149 ctx.set_operator (cairo.OPERATOR_SOURCE)
2150 ctx.paint()
2151 # call drawing method
2152 self.draw(ctx)
2153 # and delete context
2154 del ctx
2155 return False
2156
2158 """A window that displays a text and serves as Tooltip (very basic yet)."""
2159
2160 # internals
2161 __timeout = None
2162
2163 # attribs
2164 text = ''
2165 font_name = 'FreeSans 9'
2166 width = 100
2167 height = 20
2168 x = 0
2169 y = 0
2170
2172 object.__init__(self)
2173 # init
2174 self.__dict__['width'] = width
2175 self.__dict__['height'] = height
2176 self.window = gtk.Window()
2177 self.window.set_app_paintable(True)
2178 self.window.set_size_request(width, height)
2179 self.window.set_decorated(False)
2180 self.window.set_accept_focus(False)
2181 self.window.set_skip_pager_hint(True)
2182 self.window.set_skip_taskbar_hint(True)
2183 self.window.set_keep_above(True)
2184 self.screen_changed(self.window)
2185 self.window.connect("expose_event", self.expose)
2186 self.window.connect("screen-changed", self.screen_changed)
2187 #self.window.show()
2188 self.p_context = self.window.get_pango_context()
2189 self.p_layout = pango.Layout(self.p_context)
2190 self.p_layout.set_font_description(\
2191 pango.FontDescription(self.font_name))
2192 #self.p_layout.set_width(-1)
2193 self.p_layout.set_width(width * pango.SCALE - 6)
2194
2196 self.__dict__[name] = value
2197 if name in ('width', 'height', 'text'):
2198 if name== 'width':
2199 self.p_layout.set_width(width)
2200 elif name == 'text':
2201 self.p_layout.set_markup(value)
2202 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2203 self.height = min(max(logical_rect[3], 16), 400) + 6
2204 self.window.set_size_request(self.width, self.height)
2205 self.window.queue_draw()
2206 elif name == 'x':
2207 self.window.move(int(value), int(self.y))
2208 elif name == 'y':
2209 self.window.move(int(self.x), int(value))
2210
2212 """Show the Tooltip window."""
2213 self.cancel_show()
2214 self.window.show()
2215 self.window.set_keep_above(True)
2216
2218 """Show the Tooltip window after a given delay."""
2219 self.cancel_show()
2220 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2221
2226
2228 """Cancel showing of the Tooltip."""
2229 if self.__timeout:
2230 gobject.source_remove(self.__timeout)
2231 self.p_context = None
2232 self.p_layout = None
2233
2235 self.show()
2236
2238 if screen == None:
2239 screen = window.get_screen()
2240 map = screen.get_rgba_colormap()
2241 if not map:
2242 map = screen.get_rgb_colormap()
2243 window.set_colormap(map)
2244
2246 ctx = self.window.window.cairo_create()
2247 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2248 # set a clip region for the expose event
2249 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2250 ctx.clip()
2251 # clear context
2252 ctx.set_source_rgba(1, 1, 1, 0)
2253 ctx.set_operator (cairo.OPERATOR_SOURCE)
2254 ctx.paint()
2255 # draw rectangle
2256 ctx.set_source_rgba(1, 1, 0.5, 1)
2257 ctx.rectangle(0, 0, self.width, self.height)
2258 ctx.fill()
2259 # draw text
2260 ctx.save()
2261 ctx.translate(3, 3)
2262 ctx.set_source_rgba(0, 0, 0, 1)
2263 ctx.show_layout(self.p_layout)
2264 ctx.fill()
2265 ctx.restore()
2266 ctx.rectangle(0, 0, self.width, self.height)
2267 ctx.set_source_rgba(0, 0, 0, 0.7)
2268 ctx.stroke()
2269
2271 """A window that displays a text and serves as Notification (very basic yet)."""
2272
2273 # internals
2274 __timeout = None
2275
2276 # attribs
2277 text = ''
2278 font_name = 'FreeSans 9'
2279 width = 200
2280 height = 100
2281 x = 0
2282 y = 0
2283 gradient = cairo.LinearGradient(0, 100,0, 0)
2284
2286 object.__init__(self)
2287 # init
2288 self.window = gtk.Window()
2289 self.window.set_app_paintable(True)
2290 self.window.set_size_request(self.width, self.height)
2291 self.window.set_decorated(False)
2292 self.window.set_accept_focus(False)
2293 self.window.set_skip_pager_hint(True)
2294 self.window.set_skip_taskbar_hint(True)
2295 self.window.set_keep_above(True)
2296 self.screen_changed(self.window)
2297 self.window.connect("expose_event", self.expose)
2298 self.window.connect("screen-changed", self.screen_changed)
2299 #self.window.show()
2300 self.p_context = self.window.get_pango_context()
2301 self.p_layout = pango.Layout(self.p_context)
2302 self.p_layout.set_font_description(\
2303 pango.FontDescription(self.font_name))
2304 #self.p_layout.set_width(-1)
2305 self.p_layout.set_width(self.width * pango.SCALE - 6)
2306
2308 self.__dict__[name] = value
2309 if name in ('text'):
2310 if name == 'text':
2311 self.p_layout.set_markup(value)
2312 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2313 self.window.queue_draw()
2314
2316 """Show the Notify window."""
2317 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
2318 self.cancel_show()
2319 self.window.show()
2320 self.window.set_keep_above(True)
2321
2323 """Show the Notify window after a given delay."""
2324 self.cancel_show()
2325 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2326
2331
2333 """Cancel showing of the Notify."""
2334 if self.__timeout:
2335 gobject.source_remove(self.__timeout)
2336 self.p_context = None
2337 self.p_layout = None
2338
2340 self.show()
2341
2343 if screen == None:
2344 screen = window.get_screen()
2345 map = screen.get_rgba_colormap()
2346 if not map:
2347 map = screen.get_rgb_colormap()
2348 window.set_colormap(map)
2349
2351 ctx = self.window.window.cairo_create()
2352 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2353 # set a clip region for the expose event
2354 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2355 ctx.clip()
2356 # clear context
2357 ctx.set_source_rgba(1, 1, 1, 0)
2358 ctx.set_operator (cairo.OPERATOR_SOURCE)
2359 ctx.paint()
2360 # draw rectangle
2361 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2362 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2363 ctx.set_source(self.gradient)
2364 ctx.rectangle(0, 0, self.width, self.height)
2365 ctx.fill()
2366 # draw text
2367 ctx.save()
2368 ctx.translate(3, 3)
2369 ctx.set_source_rgba(1, 1, 1, 1)
2370 ctx.show_layout(self.p_layout)
2371 ctx.fill()
2372 ctx.restore()
2373 ctx.rectangle(0, 0, self.width, self.height)
2374 ctx.set_source_rgba(0, 0, 0, 0.7)
2375 ctx.stroke()
2376
2377 # TEST (as the name implies)
2378 """class TestWidget(ShapedWidget):
2379
2380 def __init__(self, width, height):
2381 #ShapedWidget.__init__(self, width, height)
2382 super(TestWidget, self).__init__(width, height)
2383
2384 def draw(self, ctx):
2385 if self.mouse_inside:
2386 ctx.set_source_rgba(1, 0, 0, 0.8)
2387 else:
2388 ctx.set_source_rgba(1, 1, 0, 0.8)
2389 ctx.rectangle(0, 0, 32, 32)
2390 ctx.fill()
2391 """
2392
2393
2394 # ------------------------------------------------------------------------------
2395 # MODULE-FUNCTIONS
2396 # ------------------------------------------------------------------------------
2397
2398 # the new recommended way of launching a screenlet from the "outside"
2400 """Launch a screenlet, either through its service or by launching a new
2401 process of the given screenlet. Name has to be the name of the Screenlet's
2402 class without trailing 'Screenlet'.
2403 NOTE: we could only launch the file here"""
2404 # check for service
2405 if services.service_is_running(name):
2406 # add screenlet through service, if running
2407 srvc = services.get_service_by_name(name)
2408 if srvc:
2409 try:
2410 srvc.add('') # empty string for auto-creating ID
2411 return True
2412 except Exception, ex:
2413 print "Error while adding instance by service: %s" % ex
2414 # service not running or error? launch screenlet's file
2415 path = utils.find_first_screenlet_path(name)
2416 if path:
2417 # get full path of screenlet's file
2418 slfile = path + '/' + name + 'Screenlet.py'
2419 # launch screenlet as separate process
2420 print "Launching Screenlet from: %s" % slfile
2421 if debug:
2422 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
2423 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
2424 else:
2425 out = '/dev/null'
2426 os.system('python -u %s > %s &' % (slfile, out))
2427 return True
2428 else:
2429 print "Screenlet '%s' could not be launched." % name
2430 return False
2431
2433 """Show a message for the given Screenlet (may contain Pango-Markup).
2434 If screenlet is None, this function can be used by other objects as well."""
2435 if screenlet == None:
2436 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2437 buttons=gtk.BUTTONS_OK)
2438 md.set_title(title)
2439 else:
2440 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2441 buttons=gtk.BUTTONS_OK)
2442 md.set_title(screenlet.__name__)
2443 md.set_markup(message)
2444 md.run()
2445 md.destroy()
2446
2448 """Show a question for the given Screenlet (may contain Pango-Markup)."""
2449 if screenlet == None:
2450 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2451 buttons=gtk.BUTTONS_YES_NO)
2452 md.set_title(title)
2453 else:
2454 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2455 buttons=gtk.BUTTONS_YES_NO)
2456 md.set_title(screenlet.__name__)
2457 md.set_markup(message)
2458 response = md.run()
2459 md.destroy()
2460 if response == gtk.RESPONSE_YES:
2461 return True
2462 return False
2463
2465 """Show an error for the given Screenlet (may contain Pango-Markup)."""
2466 if screenlet == None:
2467 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2468 buttons=gtk.BUTTONS_OK)
2469 md.set_title(title)
2470 else:
2471 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2472 buttons=gtk.BUTTONS_OK)
2473 md.set_title(screenlet.__name__)
2474 md.set_markup(message)
2475 md.run()
2476 md.destroy()
2477
2479 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
2480 import sys
2481 msg = 'FATAL ERROR: %s\n' % message
2482 sys.stdout.write(msg)
2483 sys.stderr.write(msg)
2484 sys.exit(1)
2485
2486 # LEGACY support: functions that are not used any longer (raise fatal error)
2487
2489 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2490
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Nov 28 02:55:27 2010 | http://epydoc.sourceforge.net |