| 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 try:
43 import rsvg
44 except ImportError: print 'No module RSVG , graphics will not be so good'
45 import os
46 import glob
47 import gettext
48 import math
49
50 # import screenlet-submodules
51 from options import *
52 import services
53 import utils
54 import sensors
55 # TEST
56 import menu
57 from menu import DefaultMenuItem, add_menuitem
58 from drawing import Drawing
59 # /TEST
60
61
62 #-------------------------------------------------------------------------------
63 # CONSTANTS
64 #-------------------------------------------------------------------------------
65
66 # the application name
67 APP_NAME = "Screenlets"
68
69 # the version of the Screenlets-baseclass in use
70 VERSION = "0.1.2"
71
72 # the application copyright
73 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>"
74
75 # the application authors
76 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"]
77
78 # the application comments
79 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"
80
81 DOCUMENTERS = ["Documentation generated by epydoc"]
82
83 ARTISTS = ["ODD radio screenlet theme by ODDie\nPasodoble mail theme by jEsuSdA\nSome themes by RYX\nSome themes by Whise\nMore to come..."]
84
85 TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/"
86
87 # the application website
88 WEBSITE = 'http://www.screenlets.org'
89
90 # the third party screenlets download site
91 THIRD_PARTY_DOWNLOAD = "http://screenlets.org/index.php/Category:UserScreenlets"
92
93
94 #-------------------------------------------------------------------------------
95 # PATHS
96 #-------------------------------------------------------------------------------
97 DIR_TMP = '/tmp/screenlets/'
98
99 TMP_DIR = DIR_TMP
100
101 TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running'
102
103 DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets'
104
105 DIR_USER = os.environ['HOME'] + '/.screenlets'
106
107 DIR_CONFIG = os.environ['HOME'] + '/.config/Screenlets'
108
109 SCREENLETS_PATH = [DIR_USER,DIR_USER_ROOT]
110
111
112 #-------------------------------------------------------------------------------
113 # DBUS
114 #-------------------------------------------------------------------------------
115
116 DAEMON_BUS = 'org.screenlets.ScreenletsDaemon'
117
118 DAEMON_PATH = '/org/screenlets/ScreenletsDaemon'
119
120 DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon'
121
122 #Other stuff
123
124 DEBUG_MODE = True
125
126 # translation stuff
127 gettext.textdomain('screenlets')
128 gettext.bindtextdomain('screenlets', INSTALL_PREFIX + '/share/locale')
129
132
133
134 #-------------------------------------------------------------------------------
135 # CLASSES
136 #-------------------------------------------------------------------------------
137
139 """A container with constants for the default menuitems"""
140
141 # default menuitem constants (is it right to increase like this?)
142 NONE = 0
143 DELETE = 1
144 THEMES = 2
145 INFO = 4
146 SIZE = 8
147 WINDOW_MENU = 16
148 PROPERTIES = 32
149 DELETE = 64
150 QUIT = 128
151 QUIT_ALL = 256
152 # EXPERIMENTAL!! If you use this, the file menu.xml in the
153 # Screenlet's data-dir is used for generating the menu ...
154 XML = 512
155 # the default items
156 STANDARD = 1|2|8|16|32|64|128|256
157
158
160 """ScreenletThemes are simple storages that allow loading files
161 as svg-handles within a theme-directory. Each Screenlet can have
162 its own theme-directory. It is up to the Screenlet-developer if he
163 wants to let his Screenlet support themes or not. Themes are
164 turned off by default - if your Screenlet uses Themes, just set the
165 attribute 'theme_name' to the name of the theme's dir you want to use.
166 TODO: remove dict-inheritance"""
167
168 # meta-info (set through theme.conf)
169 __name__ = ''
170 __author__ = ''
171 __version__ = ''
172 __info__ = ''
173
174 # attributes
175 path = ""
176 loaded = False
177 width = 0
178 height = 0
179 option_overrides = {}
180 p_fdesc = None
181 p_layout = None
182 tooltip = None
183 notify = None
184
185
187 # set theme-path and load all files in path
188 self.path = path
189 self.svgs = {}
190 self.pngs = {}
191 self.option_overrides = {}
192 self.loaded = self.__load_all()
193 if self.loaded == False:
194 raise Exception(_("Error while loading ScreenletTheme in: ") + path)
195
197 if name in ("width", "height"):
198 if self.loaded and len(self)>0:
199 size=self[0].get_dimension_data()
200 if name=="width":
201 return size[0]
202 else:
203 return size[1]
204 else:
205 return object.__getattr__(self, name)
206
208 """Apply this theme's overridden options to the given Screenlet."""
209 # disable the canvas-updates in the screenlet
210 screenlet.disable_updates = True
211 # theme_name needs special care (must be applied last)
212 theme_name = ''
213 # loop through overrides and appply them
214 for name in self.option_overrides:
215 print _("Override: ") + name
216 o = screenlet.get_option_by_name(name)
217 if o and not o.protected:
218 if name == 'theme_name':
219 # import/remember theme-name, but not apply yet
220 theme_name = o.on_import(self.option_overrides[name])
221 else:
222 # set option in screenlet
223 setattr(screenlet, name,
224 o.on_import(self.option_overrides[name]))
225 else:
226 print _("WARNING: Option '%s' not found or protected.") % name
227 # now apply theme
228 if theme_name != '':
229 screenlet.theme_name = theme_name
230 # re-enable updates and call redraw/reshape
231 screenlet.disable_updates = False
232 screenlet.redraw_canvas()
233 screenlet.update_shape()
234
236 """Checks if a file with filename is loaded in this theme."""
237 try:
238 if self[filename]:
239 return True
240 except:
241 #raise Exception
242 return False
243
245 """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
246 ctx.save()
247
248 if self.p_layout == None :
249
250 self.p_layout = ctx.create_layout()
251 else:
252
253 ctx.update_layout(self.p_layout)
254 self.p_fdesc = pango.FontDescription(font)
255 self.p_layout.set_font_description(self.p_fdesc)
256 self.p_layout.set_text(text)
257 extents, lextents = self.p_layout.get_pixel_extents()
258 ctx.restore()
259 return extents[2]
260
262 """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
263 ctx.save()
264
265 if self.p_layout == None :
266
267 self.p_layout = ctx.create_layout()
268 else:
269
270 ctx.update_layout(self.p_layout)
271 self.p_fdesc = pango.FontDescription(font)
272 self.p_layout.set_font_description(self.p_fdesc)
273 self.p_layout.set_text(text)
274 extents, lextents = self.p_layout.get_pixel_extents()
275 ctx.restore()
276 return extents
277
278 - def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
279 """@DEPRECATED Moved to Screenlets class: Draws text"""
280 ctx.save()
281 ctx.translate(x, y)
282 if self.p_layout == None :
283
284 self.p_layout = ctx.create_layout()
285 else:
286
287 ctx.update_layout(self.p_layout)
288 self.p_fdesc = pango.FontDescription()
289 self.p_fdesc.set_family_static(font)
290 self.p_fdesc.set_size(size * pango.SCALE)
291 self.p_fdesc.set_weight(weight)
292 self.p_layout.set_font_description(self.p_fdesc)
293 self.p_layout.set_width(width * pango.SCALE)
294 self.p_layout.set_alignment(allignment)
295 self.p_layout.set_ellipsize(ellipsize)
296 self.p_layout.set_markup(text)
297 ctx.show_layout(self.p_layout)
298 ctx.restore()
299
300
302 """@DEPRECATED Moved to Screenlets class: Draws a circule"""
303 ctx.save()
304 ctx.translate(x, y)
305 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
306 if fill:ctx.fill()
307 else: ctx.stroke()
308 ctx.restore()
309
310 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
311 """@DEPRECATED Moved to Screenlets class: Draws a line"""
312 ctx.save()
313 ctx.move_to(start_x, start_y)
314 ctx.set_line_width(line_width)
315 ctx.rel_line_to(end_x, end_y)
316 if close : ctx.close_path()
317 if preserve: ctx.stroke_preserve()
318 else: ctx.stroke()
319 ctx.restore()
320
322 """@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
323 ctx.save()
324 ctx.translate(x, y)
325 ctx.rectangle (0,0,width,height)
326 if fill:ctx.fill()
327 else: ctx.stroke()
328 ctx.restore()
329
331 """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
332 ctx.save()
333 ctx.translate(x, y)
334 padding=0 # Padding from the edges of the window
335 rounded=rounded_angle # How round to make the edges 20 is ok
336 w = width
337 h = height
338
339 # Move to top corner
340 ctx.move_to(0+padding+rounded, 0+padding)
341
342 # Top right corner and round the edge
343 ctx.line_to(w-padding-rounded, 0+padding)
344 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
345
346 # Bottom right corner and round the edge
347 ctx.line_to(w-padding, h-padding-rounded)
348 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
349
350 # Bottom left corner and round the edge.
351 ctx.line_to(0+padding+rounded, h-padding)
352 ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
353
354 # Top left corner and round the edge
355 ctx.line_to(0+padding, 0+padding+rounded)
356 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
357
358 # Fill in the shape.
359 if fill:ctx.fill()
360 else: ctx.stroke()
361 ctx.restore()
362
364 """@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
365
366 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
367 iw = pixbuf.get_width()
368 ih = pixbuf.get_height()
369 puxbuf = None
370 return iw,ih
371
373 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
374
375 ctx.save()
376 ctx.translate(x, y)
377 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
378 format = cairo.FORMAT_RGB24
379 if pixbuf.get_has_alpha():
380 format = cairo.FORMAT_ARGB32
381
382 iw = pixbuf.get_width()
383 ih = pixbuf.get_height()
384 image = cairo.ImageSurface(format, iw, ih)
385 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
386
387 ctx.paint()
388 puxbuf = None
389 image = None
390 ctx.restore()
391
392
393
395 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
396
397 ctx.save()
398 ctx.translate(x, y)
399 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
400 format = cairo.FORMAT_RGB24
401 if pixbuf.get_has_alpha():
402 format = cairo.FORMAT_ARGB32
403
404 iw = pixbuf.get_width()
405 ih = pixbuf.get_height()
406 image = cairo.ImageSurface(format, iw, ih)
407
408 matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
409 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
410 if image != None :image.set_matrix(matrix)
411 ctx.paint()
412 puxbuf = None
413 image = None
414 ctx.restore()
415
417 """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
418 if self.notify == None:
419 self.notify = Notify()
420 self.notify.text = text
421 self.notify.show()
422
424 """@DEPRECATED Moved to Screenlets class: hide notification window"""
425 if self.notify != None:
426 self.notify.hide()
427 self.notify = None
428
430 """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
431 if self.tooltip == None:
432 self.tooltip = Tooltip(300, 400)
433 self.tooltip.text = text
434 self.tooltip.x = tooltipx
435 self.tooltip.y = tooltipy
436 self.tooltip.show()
437
439 """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
440 if self.tooltip != None:
441 self.tooltip.hide()
442 self.tooltip = None
443
445 """Check if this theme contains overrides for options."""
446 return len(self.option_overrides) > 0
447
449 """Load a config-file from this theme's dir and save vars in list."""
450 ini = utils.IniReader()
451 if ini.load(filename):
452 if ini.has_section('Theme'):
453 self.__name__ = ini.get_option('name', section='Theme')
454 self.__author__ = ini.get_option('author', section='Theme')
455 self.__version__ = ini.get_option('version', section='Theme')
456 self.__info__ = ini.get_option('info', section='Theme')
457 if ini.has_section('Options'):
458 opts = ini.list_options(section='Options')
459 if opts:
460 for o in opts:
461 self.option_overrides[o[0]] = o[1]
462 print _("theme.conf loaded: ")
463 print _("Name: ") + str(self.__name__)
464 print _("Author: ") +str(self.__author__)
465 print _("Version: ") +str(self.__version__)
466 print _("Info: ") +str(self.__info__)
467 else:
468 print _("Failed to load theme.conf")
469
470
472 """Load an SVG-file into this theme and reference it as ref_name."""
473 if self.has_key(filename):
474 del self[filename]
475 try:
476 self[filename] = rsvg.Handle(self.path + "/" + filename)
477 self.svgs[filename[:-4]] = self[filename]
478 if self[filename] != None:
479 # set width/height
480 size=self[filename].get_dimension_data()
481 if size:
482 self.width = size[0]
483 self.height = size[1]
484 return True
485 except NameError, ex:
486 self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
487 self.svgs[filename[:-4]] = self[filename]
488 if self[filename] != None:
489 # set width/height
490 self.width = self[filename].get_width()
491 self.height = self[filename].get_height()
492 print str(ex)
493 return True
494
495 else:
496 return False
497 #self[filename] = None
498
500 """Load a PNG-file into this theme and reference it as ref_name."""
501 if self.has_key(filename):
502 del self[filename]
503 self[filename] = cairo.ImageSurface.create_from_png(self.path +
504 "/" + filename)
505 self.pngs[filename[:-4]] = self[filename]
506 if self[filename] != None:
507 return True
508 else:
509 return False
510 #self[filename] = None
511
513 """Load all files in the theme's path. Currently only loads SVGs and
514 PNGs."""
515 # clear overrides
516 #self.__option_overrides = {}
517 # read dir
518 dirlst = glob.glob(self.path + '/*')
519 if len(dirlst)==0:
520 return False
521 plen = len(self.path) + 1
522 for file in dirlst:
523 fname = file[plen:]
524 if fname.endswith('.svg'):
525 # svg file
526 if self.load_svg(fname) == False:
527 return False
528 elif fname.endswith('.png'):
529 # svg file
530 if self.load_png(fname) == False:
531 return False
532 elif fname == "theme.conf":
533 print _("theme.conf found! Loading option-overrides.")
534 # theme.conf
535 if self.load_conf(file) == False:
536 return False
537 return True
538
543
544 # TODO: fix function, rsvg handles are not freed properly
546 """Deletes the Theme's contents and frees all rsvg-handles.
547 TODO: freeing rsvg-handles does NOT work for some reason"""
548 self.option_overrides.clear()
549 for filename in self:
550 try:
551 self[filename].free()
552 except AttributeError:pass
553 #self[filename].close()
554 del filename
555 self.clear()
556
557 # TEST: render-function
558 # should be used like "theme.render(context, 'notes-bg')" and then use
559 # either an svg or png image
561 """Render an image from within this theme to the given context. This
562 function can EITHER use png OR svg images, so it is possible to
563 create themes using both image-formats when a Screenlet uses this
564 function for drawing its images. The image name has to be defined
565 without the extension and the function will automatically select
566 the available one (SVG is prefered over PNG)."""
567
568 ### Render Graphics even if rsvg is not available###
569 if os.path.isfile (self.path + '/' + name + '.svg'):
570
571 try:
572 self.svgs[name].render_cairo(ctx)
573 except:
574 try:
575 ctx.set_source_pixbuf(self.svgs[name], 0, 0)
576
577 ctx.paint()
578 pixbuf = None
579 except TypeError:
580 ctx.set_source_surface(self.pngs[name], 0, 0)
581 ctx.paint()
582
583 elif os.path.isfile (self.path + '/' + name + '.png'):
584 ctx.set_source_surface(self.pngs[name], 0, 0)
585 ctx.paint()
586
587
588
590 # Scale the pixmap
591 ctx.set_source_rgba(color[0], color[1], color[2], color[3])
592 ctx.set_source_surface(self.pngs[name], 0, 0)
593 ctx.mask_surface(image, 0, 0)
594 ctx.stroke()
595
596
597
599 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
600 fully invisible by default. Subclasses of Screenlet can render
601 their owner-drawn graphics on fully transparent background."""
602
603 # default meta-info for Screenlets
604 __name__ = _('No name set for this Screenlet')
605 __version__ = '0.0'
606 __author__ = _('No author defined for this Screenlet')
607 __desc__ = _('No info set for this Screenlet')
608 __requires__ = [] # still unused
609 #__target_version__ = '0.0.0'
610 #__backend_version__ = '0.0.1'
611
612 # attributes (TODO: remove them here and add them to the constructor,
613 # because they only should exist per instance)
614 id = '' # id-attribute for handling instances
615 window = None # the gtk.Window behind the scenes
616 theme = None # the assigned ScreenletTheme
617 uses_theme = True # flag indicating whether Screenlet uses themes
618 draw_buttons = True
619 show_buttons = True
620 menu = None # the right-click gtk.Menu
621 is_dragged = False # TODO: make this work
622 quit_on_close = True # if True, closing this instance quits gtk
623 saving_enabled = True # if False, saving is disabled
624 dragging_over = False # true if something is dragged over
625 disable_updates = False # to temporarily avoid refresh/reshape
626 p_context = None # PangoContext
627 p_layout = None # PangoLayout
628
629 # default editable options, available for all Screenlets
630 x = 0
631 y = 0
632 mousex = 0
633 mousey = 0
634 mouse_is_over = False
635 width = 100
636 height = 100
637 scale = 1.0
638 opacity = 1.0
639 theme_name = ""
640 is_visible = True
641 is_sticky = False
642 is_widget = False
643 keep_above = True
644 keep_below = False
645 skip_pager = True
646 first_run = False
647 skip_taskbar = True
648 lock_position = False
649 allow_option_override = True # if False, overrides are ignored
650 ask_on_option_override = True # if True, overrides need confirmation
651 resize_on_scroll = True
652 has_started = False
653 has_focus = False
654 # internals (deprecated? we still don't get the end of a begin_move_drag)
655 gtk_icon_theme = None
656 __lastx = 0
657 __lasty = 0
658 p_fdesc = None
659 p_layout = None
660 tooltip = None
661 notify = None
662 # some menuitems (needed for checking/unchecking)
663 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
664 __mi_keep_above = None
665 __mi_keep_below = None
666 __mi_widget = None
667 __mi_sticky = None
668 __mi_lock = None
669 # for custom signals (which aren't acutally used ... yet)
670 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
671 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
672
673 - def __init__ (self, id='', width=100, height=100, parent_window=None,
674 show_window=True, is_widget=False, is_sticky=False,
675 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
676 enable_saving=True, service_class=services.ScreenletService,
677 uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
678 """Constructor - should only be subclassed"""
679
680 # call gobject and EditableOptions superclasses
681 super(Screenlet, self).__init__()
682 EditableOptions.__init__(self)
683 # init properties
684 self.id = id
685 self.session = session
686 self.service = None
687 # if we have an id and a service-class, register our service
688 if self.id and service_class:
689 self.register_service(service_class)
690 # notify service about adding this instance
691 self.service.instance_added(self.id)
692 self.width = width
693 self.height = height
694 self.is_dragged = False
695 self.__path__ = path
696 self.saving_enabled = enable_saving # used by session
697 # set some attributes without calling __setattr__
698 self.__dict__['theme_name'] = ""
699 self.__dict__['is_widget'] = is_widget
700 self.__dict__['is_sticky'] = is_sticky
701 self.__dict__['draw_buttons'] = draw_buttons
702 self.resize_on_scroll = resize_on_scroll
703 self.__dict__['x'] = 0
704 self.__dict__['y'] = 0
705 # TEST: set scale relative to theme size (NOT WORKING)
706 #self.__dict__['scale'] = width/100.0
707 # /TEST
708 # shape bitmap
709 self.__shape_bitmap = None
710 self.__shape_bitmap_width = 0
711 self.__shape_bitmap_height = 0
712 # "editable" options, first create a group
713 self.add_options_group('Screenlet',
714 _('The basic settings for this Screenlet-instance.'))
715 # if this Screenlet uses themes, add theme-specific options
716 # (NOTE: this option became hidden with 0.0.9 and doesn't use
717 # get_available_themes anymore for showing the choices)
718 self.gtk_icon_theme = gtk.icon_theme_get_default()
719 self.load_buttons(None)
720 self.gtk_icon_theme.connect("changed", self.load_buttons)
721 if draw_buttons: self.draw_buttons = True
722 else: self.draw_buttons = False
723 if uses_theme:
724 self.uses_theme = True
725 self.add_option(StringOption('Screenlet', 'theme_name',
726 'default', '', '', hidden=True))
727 # create/add options
728 self.add_option(IntOption('Screenlet', 'x',
729 0, _('X-Position'), _('The X-position of this Screenlet ...'),
730 min=0, max=gtk.gdk.screen_width()))
731 self.add_option(IntOption('Screenlet', 'y',
732 0, _('Y-Position'), _('The Y-position of this Screenlet ...'),
733 min=0, max=gtk.gdk.screen_height()))
734 self.add_option(IntOption('Screenlet', 'width',
735 width, _('Width'), _('The width of this Screenlet ...'),
736 min=16, max=1000, hidden=True))
737 self.add_option(IntOption('Screenlet', 'height',
738 height, _('Height'), _('The height of this Screenlet ...'),
739 min=16, max=1000, hidden=True))
740 self.add_option(FloatOption('Screenlet', 'scale',
741 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'),
742 min=0.1, max=10.0, digits=2, increment=0.1))
743 self.add_option(FloatOption('Screenlet', 'opacity',
744 self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'),
745 min=0.1, max=1.0, digits=2, increment=0.1))
746 self.add_option(BoolOption('Screenlet', 'is_sticky',
747 is_sticky, _('Stick to Desktop'),
748 _('Show this Screenlet on all workspaces ...')))
749 self.add_option(BoolOption('Screenlet', 'is_widget',
750 is_widget, _('Treat as Widget'),
751 _('Treat this Screenlet as a "Widget" ...')))
752 self.add_option(BoolOption('Screenlet', 'is_dragged',
753 self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True))
754 self.add_option(BoolOption('Screenlet', 'is_sizable',
755 is_sizable, "Can the screenlet be resized","is_sizable", hidden=True))
756 self.add_option(BoolOption('Screenlet', 'is_visible',
757 self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True))
758 self.add_option(BoolOption('Screenlet', 'lock_position',
759 self.lock_position, _('Lock position'),
760 _('Stop the screenlet from being moved...')))
761 self.add_option(BoolOption('Screenlet', 'keep_above',
762 self.keep_above, _('Keep above'),
763 _('Keep this Screenlet above other windows ...')))
764 self.add_option(BoolOption('Screenlet', 'keep_below',
765 self.keep_below, _('Keep below'),
766 _('Keep this Screenlet below other windows ...')))
767 self.add_option(BoolOption('Screenlet', 'draw_buttons',
768 self.draw_buttons, _('Draw button controls'),
769 _('Draw buttons in top right corner')))
770 self.add_option(BoolOption('Screenlet', 'skip_pager',
771 self.skip_pager, _('Skip Pager'),
772 _('Set this Screenlet to show/hide in pagers ...')))
773 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
774 self.skip_pager, _('Skip Taskbar'),
775 _('Set this Screenlet to show/hide in taskbars ...')))
776 self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
777 self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll"))
778 if uses_theme:
779 self.ask_on_option_override = ask_on_option_override
780 self.add_option(BoolOption('Screenlet', 'allow_option_override',
781 self.allow_option_override, _('Allow overriding Options'),
782 _('Allow themes to override options in this screenlet ...')))
783 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
784 self.ask_on_option_override, _('Ask on Override'),
785 _('Show a confirmation-dialog when a theme wants to override ')+\
786 _('the current options of this Screenlet ...')))
787 # disable width/height
788 self.disable_option('width')
789 self.disable_option('height')
790 # create window
791 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
792 if parent_window:
793 self.window.set_parent_window(parent_window)
794 self.window.set_transient_for(parent_window)
795 self.window.set_destroy_with_parent(True)
796 self.window.resize(width, height)
797 self.window.set_decorated(False)
798 self.window.set_app_paintable(True)
799 # create pango layout, if active
800 if uses_pango:
801 self.p_context = self.window.get_pango_context()
802 if self.p_context:
803 self.p_layout = pango.Layout(self.p_context)
804 self.p_layout.set_font_description(\
805 pango.FontDescription("Sans 12"))
806 # set type hint
807
808 if str(sensors.sys_get_window_manager()).lower() == 'kwin':
809 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
810 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
811 elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
812 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
813 else:
814 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
815 self.window.set_keep_above(True)
816 self.window.set_skip_taskbar_hint(True)
817 self.window.set_skip_pager_hint(True)
818 if is_sticky:
819 self.window.stick()
820 self.alpha_screen_changed(self.window)
821 self.update_shape()
822 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
823 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
824 self.window.connect("composited-changed", self.composite_changed)
825 self.window.connect("delete_event", self.delete_event)
826 self.window.connect("destroy", self.destroy)
827 self.window.connect("expose_event", self.expose)
828 self.window.connect("button-press-event", self.button_press)
829 self.window.connect("button-release-event", self.button_release)
830 self.window.connect("configure-event", self.configure_event)
831 self.window.connect("screen-changed", self.alpha_screen_changed)
832 self.window.connect("realize", self.realize_event)
833 self.window.connect("enter-notify-event", self.enter_notify_event)
834 self.window.connect("leave-notify-event", self.leave_notify_event)
835 self.window.connect("focus-in-event", self.focus_in_event)
836 self.window.connect("focus-out-event", self.focus_out_event)
837 self.window.connect("scroll-event", self.scroll_event)
838 self.window.connect("motion-notify-event",self.motion_notify_event)
839 self.window.connect("map-event", self.map_event)
840 self.window.connect("unmap-event", self.unmap_event)
841 # add key-handlers (TODO: use keyword-attrib to activate?)
842 self.window.connect("key-press-event", self.key_press)
843 # drag/drop support (NOTE: still experimental and incomplete)
844 if drag_drop:
845 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
846 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
847 [("text/plain", 0, 0),
848 ("image", 0, 1),
849 ("text/uri-list", 0, 2)],
850 gtk.gdk.ACTION_COPY)
851 self.window.connect("drag_data_received", self.drag_data_received)
852 self.window.connect("drag-begin", self.drag_begin)
853 self.window.connect("drag-end", self.drag_end)
854 self.window.connect("drag-motion", self.drag_motion)
855 self.window.connect("drag-leave", self.drag_leave)
856 # create menu
857 self.menu = gtk.Menu()
858 # 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
859
860
861 if show_window:
862 self.window.show()
863 print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id
864 if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'):
865 self.first_run = True
866 self.window.hide()
867
869 # set the value in GObject (ESSENTIAL!!!!)
870 self.on_before_set_atribute(name, value)
871 gobject.GObject.__setattr__(self, name, value)
872 # And do other actions
873 if name=="x" or name=="y":
874 if self.has_started:
875 self.window.move(self.x, self.y)
876 elif name == 'opacity':
877 self.window.set_opacity(value)
878 elif name == 'scale':
879 self.window.resize(int(self.width * self.scale),
880 int(self.height * self.scale))
881 # TODO: call on_resize-handler here !!!!
882 self.on_scale()
883 self.redraw_canvas()
884 self.update_shape()
885
886
887 elif name == "theme_name":
888 #self.__dict__ ['theme_name'] = value
889 print _("LOAD NEW THEME: ") + value
890 print _("FOUND: ") + str(self.find_theme(value))
891 #self.load_theme(self.get_theme_dir() + value)
892 # load theme
893 path = self.find_theme(value)
894 if path:
895 self.load_theme(path)
896 #self.load_first_theme(value)
897 self.redraw_canvas()
898 self.update_shape()
899 elif name in ("width", "height"):
900 #self.__dict__ [name] = value
901 if self.window:
902 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
903 #self.redraw_canvas()
904 self.update_shape()
905 elif name == "is_widget":
906 if self.has_started:
907 self.set_is_widget(value)
908 elif name == "is_visible":
909 if self.has_started:
910 if value == True:
911 self.reshow()
912 else:
913 self.window.hide()
914 elif name == "is_sticky":
915 if value == True:
916 self.window.stick()
917 else:
918 self.window.unstick()
919 #if self.__mi_sticky:
920 # self.__mi_sticky.set_active(value)
921 elif name == "keep_above":
922 if self.has_started == True:
923 self.window.set_keep_above(bool(value))
924 #self.__mi_keep_above.set_active(value)
925 elif name == "keep_below":
926 if self.has_started == True:
927 self.window.set_keep_below(bool(value))
928 #self.__mi_keep_below.set_active(value)
929 elif name == "skip_pager":
930 if self.window.window:
931 self.window.window.set_skip_pager_hint(bool(value))
932 elif name == "skip_taskbar":
933 if self.window.window:
934 self.window.window.set_skip_taskbar_hint(bool(value))
935 # NOTE: This is the new recommended way of storing options in real-time
936 # (we access the backend through the session here)
937 if self.saving_enabled:
938 o = self.get_option_by_name(name)
939 if o != None:
940 self.session.backend.save_option(self.id, o.name,
941 o.on_export(value))
942 self.on_after_set_atribute(name, value)
943 # /TEST
944
945 #-----------------------------------------------------------------------
946 # Screenlet's public functions
947 #-----------------------------------------------------------------------
948
1060
1071
1091
1092
1093
1097
1118
1120 """Fills the given cairo.Context with fully transparent white."""
1121 ctx.save()
1122 ctx.set_source_rgba(1, 1, 1, 0)
1123 ctx.set_operator (cairo.OPERATOR_SOURCE)
1124 ctx.paint()
1125 ctx.restore()
1126
1128 """Close this Screenlet
1129 TODO: send close-notify instead of destroying window?"""
1130 #self.save_settings()
1131 self.window.unmap()
1132 self.window.destroy()
1133 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
1134
1136 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1137 with the icon and the mask. To supply your own icon you can use the
1138 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1139 w = self.width
1140 h = self.height
1141 icon, mask = self.on_create_drag_icon()
1142 if icon == None:
1143 # create icon
1144 icon = gtk.gdk.Pixmap(self.window.window, w, h)
1145 ctx = icon.cairo_create()
1146 self.clear_cairo_context(ctx)
1147 self.on_draw(ctx)
1148 if mask == None:
1149 # create mask
1150 mask = gtk.gdk.Pixmap(self.window.window, w, h)
1151 ctx = mask.cairo_create()
1152 self.clear_cairo_context(ctx)
1153 self.on_draw_shape(ctx)
1154 return (icon, mask)
1155
1159
1161 """Find the first occurence of a theme and return its global path."""
1162 sn = self.get_short_name()
1163 for p in SCREENLETS_PATH:
1164 fpath = p + '/' + sn + '/themes/' + name
1165 if os.path.isdir(fpath):
1166 return fpath
1167 return None
1168
1170 """Return the short name of this screenlet. This returns the classname
1171 of the screenlet without trailing "Screenlet". Please always use
1172 this function if you want to retrieve the short name of a Screenlet."""
1173 return self.__class__.__name__[:-9]
1174
1176 """Return the name of this screenlet's personal directory."""
1177 p = utils.find_first_screenlet_path(self.get_short_name())
1178 if p:
1179 return p
1180 else:
1181 if self.__path__ != '':
1182 return self.__path__
1183 else:
1184 return os.getcwd()
1185
1187 """Return the name of this screenlet's personal theme-dir.
1188 (Only returns the dir under the screenlet's location"""
1189 return self.get_screenlet_dir() + "/themes/"
1190
1192 """Returns a list with the names of all available themes in this
1193 Screenlet's theme-directory."""
1194 lst = []
1195 for p in SCREENLETS_PATH:
1196 d = p + '/' + self.get_short_name() + '/themes/'
1197 if os.path.isdir(d):
1198 #dirname = self.get_theme_dir()
1199 dirlst = glob.glob(d + '*')
1200 dirlst.sort()
1201 tdlen = len(d)
1202 for fname in dirlst:
1203 dname = fname[tdlen:]
1204 # TODO: check if it's a dir
1205 lst.append(dname)
1206 return lst
1207
1209 self.window.present()
1210 self.has_started = True
1211 self.is_dragged = False
1212 self.keep_above= self.keep_above
1213 self.keep_below= self.keep_below
1214 self.skip_taskbar = self.skip_taskbar
1215 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1216 self.window.set_keep_above(self.keep_above)
1217 self.window.set_keep_below(self.keep_below)
1218 if self.is_widget:
1219 self.set_is_widget(True)
1220 self.has_focus = False
1221
1223 """Called when screenlet finishes loading"""
1224
1225
1226 self.window.present()
1227
1228
1229 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary
1230 self.window.hide()
1231 self.window.move(self.x, self.y)
1232 self.window.show()
1233 self.has_started = True
1234 self.is_dragged = False
1235 self.keep_above= self.keep_above
1236 self.keep_below= self.keep_below
1237 self.is_sticky = self.is_sticky
1238 self.skip_taskbar = self.skip_taskbar
1239 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1240 self.window.set_keep_above(self.keep_above)
1241 self.window.set_keep_below(self.keep_below)
1242
1243 self.on_init()
1244 if self.is_widget:
1245 self.set_is_widget(True)
1246 self.has_focus = False
1247 ini = utils.IniReader()
1248 if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run:
1249
1250 if ini.get_option('Lock', section='Options') == 'True':
1251 self.lock_position = True
1252 elif ini.get_option('Lock', section='Options') == 'False':
1253 self.lock_position = False
1254 if ini.get_option('Sticky', section='Options') == 'True':
1255 self.is_sticky = True
1256 elif ini.get_option('Sticky', section='Options') == 'False':
1257 self.is_sticky = False
1258 if ini.get_option('Widget', section='Options') == 'True':
1259 self.is_widget = True
1260 elif ini.get_option('Widget', section='Options') == 'False':
1261 self.is_widget = False
1262 if ini.get_option('Keep_above', section='Options') == 'True':
1263 self.keep_above = True
1264 elif ini.get_option('Keep_above', section='Options') == 'False':
1265 self.keep_above = False
1266 if ini.get_option('Keep_below', section='Options') == 'True':
1267 self.keep_below = True
1268 elif ini.get_option('Keep_below', section='Options') == 'False':
1269 self.keep_below = False
1270 if ini.get_option('draw_buttons', section='Options') == 'True':
1271 self.draw_buttons = True
1272 elif ini.get_option('draw_buttons', section='Options') == 'False':
1273 self.draw_buttons = False
1274
1279
1280 # EXPERIMENTAL:
1281 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
1282 # To do all in one, set attribute self.theme_name instead
1284 """Load a theme for this Screenlet from the given path. NOTE:
1285 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
1286 in one call, set the attribute self.theme_name instead."""
1287 if self.theme:
1288 self.theme.free()
1289 del self.theme
1290 self.theme = ScreenletTheme(path)
1291 # check for errors
1292 if self.theme.loaded == False:
1293 print _("Error while loading theme: ") + path
1294 self.theme = None
1295 else:
1296 # call user-defined handler
1297 self.on_load_theme()
1298 # if override options is allowed, apply them
1299 if self.allow_option_override:
1300 if self.theme.has_overrides():
1301 if self.ask_on_option_override==True and \
1302 show_question(self,
1303 _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
1304 return
1305 self.theme.apply_option_overrides(self)
1306 # /EXPERIMENTAL
1307
1311
1313 """Register or create the given ScreenletService-(sub)class as the new
1314 service for this Screenlet. If self is not the first instance in the
1315 current session, the service from the first instance will be used
1316 instead and no new service is created."""
1317 if self.session:
1318 if len(self.session.instances) == 0:
1319 # if it is the basic service, add name to call
1320 if service_classobj==services.ScreenletService:#BUG
1321 self.service = service_classobj(self, self.get_short_name())
1322 else:
1323 # else only pass this screenlet
1324 self.service = service_classobj(self)
1325 else:
1326 self.service = self.session.instances[0].service
1327 # TODO: throw exception??
1328 return True
1329 return False
1330
1332 """Set this window to be treated as a Widget (only supported by
1333 compiz using the widget-plugin yet)"""
1334 if value==True:
1335 # set window type to utility
1336 #self.window.window.set_type_hint(
1337 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1338 # set _compiz_widget-property on window
1339 self.window.window.property_change("_COMPIZ_WIDGET",
1340 gtk.gdk.SELECTION_TYPE_WINDOW,
1341 32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1342 else:
1343 # set window type to normal
1344 #self.window.window.set_type_hint(
1345 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1346 # set _compiz_widget-property
1347 self.window.window.property_delete("_COMPIZ_WIDGET")
1348 # notify handler
1349 self.on_switch_widget_state(value)
1350
1352 """Show this Screenlet's underlying gtk.Window"""
1353 self.window.show()
1354 self.window.move(self.x, self.y)
1355 self.on_show()
1356
1358 """Show the EditableSettingsDialog for this Screenlet."""
1359 se = OptionsDialog(490, 450)
1360 img = gtk.Image()
1361 try:
1362 d = self.get_screenlet_dir()
1363 if os.path.isfile(d + '/icon.svg'):
1364 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1365 elif os.path.isfile(d + '/icon.png'):
1366 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1367 img.set_from_pixbuf(icn)
1368 except:
1369 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1370 se.set_title(self.__name__)
1371 se.set_info(self.__name__, self.__desc__, '(c) ' + self.__author__,
1372 version='v' + self.__version__, icon=img)
1373 se.show_options_for_object(self)
1374 resp = se.run()
1375 if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1376 se.reset_to_defaults()
1377 else:
1378 self.update_shape()
1379 se.destroy()
1380
1382 """Redraw the entire Screenlet's window area.
1383 TODO: store window alloaction in class and change when size changes."""
1384 # if updates are disabled, just exit
1385 if self.disable_updates:
1386 return
1387 if self.window:
1388 x, y, w, h = self.window.get_allocation()
1389 rect = gtk.gdk.Rectangle(x, y, w, h)
1390 if self.window.window:
1391 self.window.window.invalidate_rect(rect, True)
1392 self.window.window.process_updates(True)
1393 # if self.has_focus and self.draw_buttons and self.show_buttons:
1394 # self.create_buttons()
1395
1396
1398 """Redraw the given Rectangle (x, y, width, height) within the
1399 current Screenlet's window."""
1400 # if updates are disabled, just exit
1401 if self.disable_updates:
1402 return
1403 if self.window:
1404 rect = gtk.gdk.Rectangle(x, y, width, height)
1405 if self.window.window:
1406 self.window.window.invalidate_rect(rect, True)
1407 self.window.window.process_updates(True)
1408
1410 """Removed shaped window , in case the nom composited shape has been set"""
1411 if self.window.window:
1412 self.window.window.shape_combine_mask(None,0,0)
1413
1415 """Update window shape (only call this when shape has changed
1416 because it is very ressource intense if ran too often)."""
1417 # if updates are disabled, just exit
1418 if self.disable_updates:
1419 return
1420 #print _("UPDATING SHAPE")
1421 # TODO:
1422 #if not self.window.is_composited():
1423 # self.update_shape_non_composited()
1424 # calculate new width/height of shape bitmap
1425 w = int(self.width * self.scale)
1426 h = int(self.height * self.scale)
1427 # if 0 set it to 100 to avoid crashes and stay interactive
1428 if w==0: w = 100
1429 if h==0: h = 100
1430 # if size changed, recreate shape bitmap
1431 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1432 data = ''.zfill(w*h)
1433 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1434 w, h)
1435 self.__shape_bitmap_width = w
1436 self.__shape_bitmap_height = h
1437 # create context and draw shape
1438 ctx = self.__shape_bitmap.cairo_create()
1439 self.clear_cairo_context(ctx) #TEST
1440 if self.has_focus and self.draw_buttons and self.show_buttons:
1441 ctx.save()
1442 #theme1 = gtk.icon_theme_get_default()
1443 #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1444 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1445 #close = theme1.load_icon ("gtk-close", 16, 0)
1446 #prop = theme1.load_icon ("gtk-properties", 16, 0)
1447 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1448 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1449 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1450 ctx.translate((self.width*self.scale)-16,0)
1451 ctx.set_source_pixbuf(self.closeb, 0, 0)
1452 ctx.paint()
1453 ctx.restore()
1454 ctx.save()
1455 ctx.translate((self.width*self.scale)-32,0)
1456 ctx.set_source_pixbuf(self.prop, 0, 0)
1457 ctx.paint()
1458 ctx.restore()
1459 # shape the window acording if the window is composited or not
1460
1461 if self.window.is_composited():
1462
1463 self.on_draw_shape(ctx)
1464 # and cut window with mask
1465 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1466 else:
1467 try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1468 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1469 # and cut window with mask
1470 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1471 self.on_update_shape()
1472
1474 """TEST: This function is intended to shape the window whenever no
1475 composited environment can be found. (NOT WORKING YET!!!!)"""
1476 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1477 # calculate new width/height of shape bitmap
1478 w = int(self.width * self.scale)
1479 h = int(self.height * self.scale)
1480 # if 0 set it to 100 to avoid crashes and stay interactive
1481 if w==0: w = 100
1482 if h==0: h = 100
1483 # if size changed, recreate shape bitmap
1484 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1485 data = ''.zfill(w*h)
1486 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1487 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1488 self.__shape_bitmap_width = w
1489 self.__shape_bitmap_height = h
1490 # and render window contents to it
1491 # TOOD!!
1492 if self.__shape_bitmap:
1493 # create new mask
1494 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1495 # apply new mask to window
1496 self.window.shape_combine_mask(mask)
1497
1498 # ----------------------------------------------------------------------
1499 # Screenlet's event-handler dummies
1500 # ----------------------------------------------------------------------
1501
1503 """Called when the Screenlet gets deleted. Return True to cancel.
1504 TODO: sometimes not properly called"""
1505 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1506 _('Really delete this %s and its settings?') % self.get_short_name())
1507 """return not show_question(self, 'Deleting this instance of the '+\
1508 self.__name__ + ' will also delete all your personal '+\
1509 'changes you made to it!! If you just want to close the '+\
1510 'application, use "Quit" instead. Are you sure you want to '+\
1511 'delete this instance?')
1512 return False"""
1513
1514 # TODO: on_drag
1515 # TODO: on_drag_end
1516
1520
1524
1525
1527 """Called when the screenlet's drag-icon is created. You can supply
1528 your own icon and mask by returning them as a 2-tuple."""
1529 return (None, None)
1530
1534
1538
1542
1543
1547
1551
1555
1557 """Callback for drawing the Screenlet's window - override
1558 in subclasses to implement your own drawing."""
1559 pass
1560
1562 """Callback for drawing the Screenlet's shape - override
1563 in subclasses to draw the window's input-shape-mask."""
1564 pass
1565
1569
1573
1577
1579 """Called when the Screenlet's options have been applied and the
1580 screenlet finished its initialization. If you want to have your
1581 Screenlet do things on startup you should use this handler."""
1582 pass
1583
1587
1591
1595
1597 """Called when a buttonpress-event occured in Screenlet's window.
1598 Returning True causes the event to be not further propagated."""
1599 return False
1600
1604
1608
1612
1614 """Called when a buttonrelease-event occured in Screenlet's window.
1615 Returning True causes the event to be not further propagated."""
1616 return False
1617
1621
1624
1628
1632
1636
1640
1644
1648
1652 # ----------------------------------------------------------------------
1653 # Screenlet's event-handlers for GTK-events
1654 # ----------------------------------------------------------------------
1655
1657 """set colormap for window"""
1658 if screen==None:
1659 screen = window.get_screen()
1660 map = screen.get_rgba_colormap()
1661 if map:
1662 pass
1663 else:
1664 map = screen.get_rgb_colormap()
1665 window.set_colormap(map)
1666
1705
1714
1716 #this handle is called when composition changed
1717 self.remove_shape() # removing previous set shape , this is absolutly necessary
1718 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1719 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1720 self.keep_above= self.keep_above
1721 self.keep_below= self.keep_below
1722 self.window.show()
1723 #print _('Compositing method changed to %s') % str(self.window.is_composited())
1724 self.update_shape()
1725 self.redraw_canvas()
1726
1727 if not self.window.is_composited () :
1728 self.show_buttons = False
1729 # print _('Warning - Buttons will not be shown until screenlet is restarted')
1730
1731 self.is_sticky = self.is_sticky #and again ...
1732 self.keep_above= self.keep_above
1733 self.keep_below= self.keep_below
1734 self.window.set_keep_above(self.keep_above)
1735 self.window.set_keep_below(self.keep_below)
1736 self.on_composite_changed()
1737
1738 # NOTE: this should somehow handle the end of a move_drag-operation
1740 #print "onConfigure"
1741 #print event
1742 #if self.is_dragged == True:
1743 # set new position and cause a save of this Screenlet (not use
1744 # setattr to avoid conflicts with the window.move in __setattr__)
1745 if event.x != self.x:
1746 self.__dict__['x'] = event.x
1747 if self.session:
1748 self.session.backend.save_option(self.id, 'x', str(event.x))
1749 # self.is_dragged = False
1750 if event.y != self.y:
1751 self.__dict__['y'] = event.y
1752 if self.session:
1753 self.session.backend.save_option(self.id, 'y', str(event.y))
1754 # self.is_dragged = False
1755 return False
1756
1758 # cancel event?
1759 print "delete_event"
1760 if self.on_delete() == True:
1761 print _("Cancel delete_event")
1762 return True
1763 else:
1764 self.close()
1765 return False
1766
1768 # call user-defined on_quit-handler
1769 self.on_quit()
1770 #print "destroy signal occurred"
1771 self.emit("screenlet_removed", self)
1772 # close gtk?
1773 if self.quit_on_close:
1774 if self.session: # if we have a session, flush current data
1775 self.session.backend.flush()
1776 gtk.main_quit()
1777 else:
1778 del self # ??? does this really work???
1779
1784 #return False
1785
1788
1793
1795 #print "Drag motion"
1796 if self.dragging_over == False:
1797 self.dragging_over = True
1798 self.on_drag_enter(drag_context, x, y, timestamp)
1799 return False
1800
1805
1807 #self.__mouse_inside = True
1808 self.__dict__['mouse_is_over'] = True
1809 self.on_mouse_enter(event)
1810
1811 #self.redraw_canvas()
1812
1814 ctx = widget.window.cairo_create()
1815 # clear context
1816 self.clear_cairo_context(ctx)
1817 # set a clip region for the expose event
1818 ctx.rectangle(event.area.x, event.area.y,
1819 event.area.width, event.area.height)
1820 ctx.clip()
1821
1822 # scale context
1823 #ctx.scale(self.scale, self.scale)
1824 # call drawing method
1825 self.on_draw(ctx)
1826 if self.show_buttons and self.draw_buttons and self.has_focus:
1827 self.create_buttons()
1828 # and delete context (needed?)
1829 del ctx
1830 return False
1831
1833 if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
1834 #Screenlet always gets focus after being dragged so this is a good method
1835 #to control the end of a move_drag operation!!!!!
1836 #This code happens on the end of a move_drag
1837 self.is_dragged=False
1838 self.has_focus = True
1839 self.on_focus(event)
1840 self.update_shape()
1841 self.redraw_canvas()
1842
1843
1844
1845
1847 if self.is_dragged==False:
1848 self.has_focus = False
1849 self.on_unfocus(event)
1850 self.update_shape()
1851 self.redraw_canvas()
1852
1853
1854
1856 """Handle keypress events, needed for in-place editing."""
1857 self.on_key_down(event.keyval, event.string, event)
1858
1860 #self.__mouse_inside = False
1861 #self.is_dragged = False
1862 self.__dict__['mouse_is_over'] = False
1863 self.on_mouse_leave(event)
1864
1865 #self.redraw_canvas()
1866
1937
1939 self.on_map()
1940
1942 self.on_unmap()
1943
1945 self.__dict__['mousex'] = event.x / self.scale
1946 self.__dict__['mousey'] = event.y / self.scale
1947
1948 self.on_mouse_move(event)
1949
1951 """called when window has been realized"""
1952 if self.window.window:
1953 self.window.window.set_back_pixmap(None, False) # needed?
1954
1955 self.on_realize()
1956
1958 if event.direction == gtk.gdk.SCROLL_UP:
1959 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
1960 self.on_scroll_up()
1961 elif event.direction == gtk.gdk.SCROLL_DOWN:
1962 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
1963 self.on_scroll_down()
1964 return False
1965
1966
1968 """Show notification window at current mouse position."""
1969 if self.notify == None:
1970 self.notify = Notify()
1971 self.notify.text = text
1972 self.notify.show()
1973
1975 """hide notification window"""
1976 if self.notify != None:
1977 self.notify.hide()
1978 self.notify = None
1979
1981 """Show tooltip window at current mouse position."""
1982 if self.tooltip == None:
1983 self.tooltip = Tooltip(300, 400)
1984 self.tooltip.text = text
1985 self.tooltip.x = tooltipx
1986 self.tooltip.y = tooltipy
1987 self.tooltip.show()
1988 else:
1989 #self.tooltip = Tooltip(300, 400)
1990 self.tooltip.text = text
1991 self.tooltip.x = tooltipx
1992 self.tooltip.y = tooltipy
1993 #self.tooltip.show()
1994
2000
2001 # TEST!!!
2003 """A simple base-class for creating owner-drawn gtk-widgets"""
2004
2005 __widget=None
2006
2007 mouse_inside = False
2008 width = 32
2009 height = 32
2010
2012 # call superclass
2013 super(ShapedWidget, self).__init__()
2014 # create/setup widget
2015 #self.__widget = gtk.Widget()
2016 self.set_app_paintable(True)
2017 self.set_size_request(width, height)
2018 # connect handlers
2019 self.set_events(gtk.gdk.ALL_EVENTS_MASK)
2020 self.connect("expose-event", self.expose_event)
2021 self.connect("button-press-event", self.button_press)
2022 self.connect("button-release-event", self.button_release)
2023 self.connect("enter-notify-event", self.enter_notify)
2024 self.connect("leave-notify-event", self.leave_notify)
2025
2026 # EXPERIMENTAL: TODO: cache bitmap until size changes
2028 """update widget's shape (only call this when shape has changed)"""
2029 data = ""
2030 for i in xrange(self.width*self.height):
2031 data += "0"
2032 bitmap = gtk.gdk.bitmap_create_from_data(None,
2033 data, self.width, self.height)
2034 ctx = bitmap.cairo_create()
2035 ctx.set_source_rgba(1, 1, 1, 0)
2036 ctx.set_operator (cairo.OPERATOR_SOURCE)
2037 ctx.paint()
2038 self.draw_shape(ctx)
2039 self.input_shape_combine_mask(bitmap, 0, 0)
2040 print "Updating shape."
2041
2046
2051
2055 #print "mouse enter"
2056
2060 #print "mouse leave"
2061
2064
2066 self.draw(ctx)
2067
2069 ctx = widget.window.cairo_create()
2070 # set a clip region for the expose event
2071 ctx.rectangle(event.area.x, event.area.y,
2072 event.area.width, event.area.height)
2073 ctx.clip()
2074 # clear context
2075 ctx.set_source_rgba(1, 1, 1, 0)
2076 ctx.set_operator (cairo.OPERATOR_SOURCE)
2077 ctx.paint()
2078 # call drawing method
2079 self.draw(ctx)
2080 # and delete context
2081 del ctx
2082 return False
2083
2085 """A window that displays a text and serves as Tooltip (very basic yet)."""
2086
2087 # internals
2088 __timeout = None
2089
2090 # attribs
2091 text = ''
2092 font_name = 'FreeSans 9'
2093 width = 100
2094 height = 20
2095 x = 0
2096 y = 0
2097
2099 object.__init__(self)
2100 # init
2101 self.__dict__['width'] = width
2102 self.__dict__['height'] = height
2103 self.window = gtk.Window()
2104 self.window.set_app_paintable(True)
2105 self.window.set_size_request(width, height)
2106 self.window.set_decorated(False)
2107 self.window.set_accept_focus(False)
2108 self.window.set_skip_pager_hint(True)
2109 self.window.set_skip_taskbar_hint(True)
2110 self.window.set_keep_above(True)
2111 self.screen_changed(self.window)
2112 self.window.connect("expose_event", self.expose)
2113 self.window.connect("screen-changed", self.screen_changed)
2114 #self.window.show()
2115 self.p_context = self.window.get_pango_context()
2116 self.p_layout = pango.Layout(self.p_context)
2117 self.p_layout.set_font_description(\
2118 pango.FontDescription(self.font_name))
2119 #self.p_layout.set_width(-1)
2120 self.p_layout.set_width(width * pango.SCALE - 6)
2121
2123 self.__dict__[name] = value
2124 if name in ('width', 'height', 'text'):
2125 if name== 'width':
2126 self.p_layout.set_width(width)
2127 elif name == 'text':
2128 self.p_layout.set_markup(value)
2129 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2130 self.height = min(max(logical_rect[3], 16), 400) + 6
2131 self.window.set_size_request(self.width, self.height)
2132 self.window.queue_draw()
2133 elif name == 'x':
2134 self.window.move(int(value), int(self.y))
2135 elif name == 'y':
2136 self.window.move(int(self.x), int(value))
2137
2139 """Show the Tooltip window."""
2140 self.cancel_show()
2141 self.window.show()
2142 self.window.set_keep_above(True)
2143
2145 """Show the Tooltip window after a given delay."""
2146 self.cancel_show()
2147 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2148
2153
2155 """Cancel showing of the Tooltip."""
2156 if self.__timeout:
2157 gobject.source_remove(self.__timeout)
2158 self.p_context = None
2159 self.p_layout = None
2160
2162 self.show()
2163
2165 if screen == None:
2166 screen = window.get_screen()
2167 map = screen.get_rgba_colormap()
2168 if not map:
2169 map = screen.get_rgb_colormap()
2170 window.set_colormap(map)
2171
2173 ctx = self.window.window.cairo_create()
2174 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2175 # set a clip region for the expose event
2176 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2177 ctx.clip()
2178 # clear context
2179 ctx.set_source_rgba(1, 1, 1, 0)
2180 ctx.set_operator (cairo.OPERATOR_SOURCE)
2181 ctx.paint()
2182 # draw rectangle
2183 ctx.set_source_rgba(1, 1, 0.5, 1)
2184 ctx.rectangle(0, 0, self.width, self.height)
2185 ctx.fill()
2186 # draw text
2187 ctx.save()
2188 ctx.translate(3, 3)
2189 ctx.set_source_rgba(0, 0, 0, 1)
2190 ctx.show_layout(self.p_layout)
2191 ctx.fill()
2192 ctx.restore()
2193 ctx.rectangle(0, 0, self.width, self.height)
2194 ctx.set_source_rgba(0, 0, 0, 0.7)
2195 ctx.stroke()
2196
2198 """A window that displays a text and serves as Notification (very basic yet)."""
2199
2200 # internals
2201 __timeout = None
2202
2203 # attribs
2204 text = ''
2205 font_name = 'FreeSans 9'
2206 width = 200
2207 height = 100
2208 x = 0
2209 y = 0
2210 gradient = cairo.LinearGradient(0, 100,0, 0)
2211
2213 object.__init__(self)
2214 # init
2215 self.window = gtk.Window()
2216 self.window.set_app_paintable(True)
2217 self.window.set_size_request(self.width, self.height)
2218 self.window.set_decorated(False)
2219 self.window.set_accept_focus(False)
2220 self.window.set_skip_pager_hint(True)
2221 self.window.set_skip_taskbar_hint(True)
2222 self.window.set_keep_above(True)
2223 self.screen_changed(self.window)
2224 self.window.connect("expose_event", self.expose)
2225 self.window.connect("screen-changed", self.screen_changed)
2226 #self.window.show()
2227 self.p_context = self.window.get_pango_context()
2228 self.p_layout = pango.Layout(self.p_context)
2229 self.p_layout.set_font_description(\
2230 pango.FontDescription(self.font_name))
2231 #self.p_layout.set_width(-1)
2232 self.p_layout.set_width(self.width * pango.SCALE - 6)
2233
2235 self.__dict__[name] = value
2236 if name in ('text'):
2237 if name == 'text':
2238 self.p_layout.set_markup(value)
2239 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2240 self.window.queue_draw()
2241
2243 """Show the Notify window."""
2244 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
2245 self.cancel_show()
2246 self.window.show()
2247 self.window.set_keep_above(True)
2248
2250 """Show the Notify window after a given delay."""
2251 self.cancel_show()
2252 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2253
2258
2260 """Cancel showing of the Notify."""
2261 if self.__timeout:
2262 gobject.source_remove(self.__timeout)
2263 self.p_context = None
2264 self.p_layout = None
2265
2267 self.show()
2268
2270 if screen == None:
2271 screen = window.get_screen()
2272 map = screen.get_rgba_colormap()
2273 if not map:
2274 map = screen.get_rgb_colormap()
2275 window.set_colormap(map)
2276
2278 ctx = self.window.window.cairo_create()
2279 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2280 # set a clip region for the expose event
2281 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2282 ctx.clip()
2283 # clear context
2284 ctx.set_source_rgba(1, 1, 1, 0)
2285 ctx.set_operator (cairo.OPERATOR_SOURCE)
2286 ctx.paint()
2287 # draw rectangle
2288 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2289 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2290 ctx.set_source(self.gradient)
2291 ctx.rectangle(0, 0, self.width, self.height)
2292 ctx.fill()
2293 # draw text
2294 ctx.save()
2295 ctx.translate(3, 3)
2296 ctx.set_source_rgba(1, 1, 1, 1)
2297 ctx.show_layout(self.p_layout)
2298 ctx.fill()
2299 ctx.restore()
2300 ctx.rectangle(0, 0, self.width, self.height)
2301 ctx.set_source_rgba(0, 0, 0, 0.7)
2302 ctx.stroke()
2303
2304 # TEST (as the name implies)
2305 """class TestWidget(ShapedWidget):
2306
2307 def __init__(self, width, height):
2308 #ShapedWidget.__init__(self, width, height)
2309 super(TestWidget, self).__init__(width, height)
2310
2311 def draw(self, ctx):
2312 if self.mouse_inside:
2313 ctx.set_source_rgba(1, 0, 0, 0.8)
2314 else:
2315 ctx.set_source_rgba(1, 1, 0, 0.8)
2316 ctx.rectangle(0, 0, 32, 32)
2317 ctx.fill()
2318 """
2319
2320
2321 # ------------------------------------------------------------------------------
2322 # MODULE-FUNCTIONS
2323 # ------------------------------------------------------------------------------
2324
2325 # the new recommended way of launching a screenlet from the "outside"
2327 """Launch a screenlet, either through its service or by launching a new
2328 process of the given screenlet. Name has to be the name of the Screenlet's
2329 class without trailing 'Screenlet'.
2330 NOTE: we could only launch the file here"""
2331 # check for service
2332 if services.service_is_running(name):
2333 # add screenlet through service, if running
2334 srvc = services.get_service_by_name(name)
2335 if srvc:
2336 try:
2337 srvc.add('') # empty string for auto-creating ID
2338 return True
2339 except Exception, ex:
2340 print "Error while adding instance by service: %s" % ex
2341 # service not running or error? launch screenlet's file
2342 path = utils.find_first_screenlet_path(name)
2343 if path:
2344 # get full path of screenlet's file
2345 slfile = path + '/' + name + 'Screenlet.py'
2346 # launch screenlet as separate process
2347 print "Launching Screenlet from: %s" % slfile
2348 if debug:
2349 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
2350 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
2351 else:
2352 out = '/dev/null'
2353 os.system('python -u %s > %s &' % (slfile, out))
2354 return True
2355 else:
2356 print "Screenlet '%s' could not be launched." % name
2357 return False
2358
2360 """Show a message for the given Screenlet (may contain Pango-Markup).
2361 If screenlet is None, this function can be used by other objects as well."""
2362 if screenlet == None:
2363 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2364 buttons=gtk.BUTTONS_OK)
2365 md.set_title(title)
2366 else:
2367 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2368 buttons=gtk.BUTTONS_OK)
2369 md.set_title(screenlet.__name__)
2370 md.set_markup(message)
2371 md.run()
2372 md.destroy()
2373
2375 """Show a question for the given Screenlet (may contain Pango-Markup)."""
2376 if screenlet == None:
2377 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2378 buttons=gtk.BUTTONS_YES_NO)
2379 md.set_title(title)
2380 else:
2381 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2382 buttons=gtk.BUTTONS_YES_NO)
2383 md.set_title(screenlet.__name__)
2384 md.set_markup(message)
2385 response = md.run()
2386 md.destroy()
2387 if response == gtk.RESPONSE_YES:
2388 return True
2389 return False
2390
2392 """Show an error for the given Screenlet (may contain Pango-Markup)."""
2393 if screenlet == None:
2394 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2395 buttons=gtk.BUTTONS_OK)
2396 md.set_title(title)
2397 else:
2398 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2399 buttons=gtk.BUTTONS_OK)
2400 md.set_title(screenlet.__name__)
2401 md.set_markup(message)
2402 md.run()
2403 md.destroy()
2404
2406 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
2407 import sys
2408 msg = 'FATAL ERROR: %s\n' % message
2409 sys.stdout.write(msg)
2410 sys.stderr.write(msg)
2411 sys.exit(1)
2412
2413 # LEGACY support: functions that are not used any longer (raise fatal error)
2414
2416 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2417
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Wed Jun 4 18:53:03 2008 | http://epydoc.sourceforge.net |