| Home | Trees | Indices | Help |
|
|---|
|
|
1 # Screenlets main module
2 # (c) RYX aka Rico Pfaus <ryx@ryxperience.com> 2007
3 # Whise aka Helder Fraga <helder.fraga@hotmail.com> 2008
4 # Natan Yellin (Aantn) <aantny@gmail.com> 2008
5 # Guido Tabbernuk <boamaod@gmail.com> 2010-2011
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 #
21 ##@mainpage
22 #
23 ##@section intro_sec General Information
24 #
25 # INFO:
26 # - Screenlets are small owner-drawn applications that can be described as
27 # " the virtual representation of things lying/standing around on your desk".
28 # Sticknotes, clocks, rulers, ... the possibilities are endless. The goal of
29 # the Screenlets is to simplify the creation of fully themeable mini-apps that
30 # each solve basic desktop-work-related needs and generally improve the
31 # usability and eye-candy of the modern Linux-desktop.
32 #
33 # TODO: (possible improvements, not essential)
34 # - still more error-handling and maybe custom exceptions!!!
35 # - improve xml-based menu (is implemented, but I'm not happy with it)
36 # - switching themes slowly increases the memory usage (possible leak)
37 # - maybe attributes for dependancies/requirements (e.g. special
38 # python-libs or certain Screenlets)
39 # -
40 #
41
42 #-------------------------------------------------------------------------------
43 # Imports!
44 #-------------------------------------------------------------------------------
45
46 import os
47 import sys
48 from optparse import OptionParser
49
50 import gtk
51 import gettext
52 from xdg.BaseDirectory import *
53 #-------------------------------------------------------------------------------
54 # Find Install Prefix
55 # Note: It's important to do this before we import the Screenlets submodules
56 #-------------------------------------------------------------------------------
57 try:
58 INSTALL_PREFIX = open("/etc/screenlets/prefix").read()[:-1]
59 except:
60 INSTALL_PREFIX = '/usr'
61
62 # translation stuff
63 gettext.textdomain('screenlets')
64 gettext.bindtextdomain('screenlets', INSTALL_PREFIX + '/share/locale')
65
66 #-------------------------------------------------------------------------------
67 # Parse command line options
68 # Note: This must be done before we import any submodules
69 # TODO: Fix up command line parsing. Right now, the same options are parsed for
70 # both screenlets, the manager, the daemon, and melange.
71 #-------------------------------------------------------------------------------
72 # Define command line related constants.
73 # These are used as the default values for command line options that aren't overriden
74 LOG_DISABLED = False # Disable log
75 LOG_LEVEL = 4 # TODO: change to 3 for stable version to show only warning, error and critical messages
76 LOG_OUTPUT = "FILE" # default output, allowed FILE, STDERR, STDOUT
77 LOG_NAME = "screenlets" # Log name
78 LOG_FILE = "/tmp/%s.log" % os.path.basename(sys.argv[0]) # full path to log file; only used if LOG_OUTPUT is FILE
79
80 SESSION_NAME = "default"
81 SESSION_REPLACE = "False"
82 SERVER_NAME = "none"
83
84 # Create the parser
85 parser = OptionParser()
86 # Add options used by all of the various modules
87 parser.add_option("-l", "--logging-level", dest = "LOG_LEVEL", default = LOG_LEVEL,
88 help = ("set logging level.\n0 - log disabled, 1 - only critical errors, 2 - all errors,\
89 3 - all errors and warnings, 4 - all errors, warnings, and info messages, 5 - all messages \
90 including debug output.\ndefault: %s") %(LOG_LEVEL))
91 parser.add_option("-f", "--logging-file", dest = "LOG_FILE", default = LOG_FILE,
92 help = ("write log to LOG_FILE. Default: %s") %(LOG_FILE))
93 parser.add_option("-s", "--server", dest = "SERVER_NAME", default = SERVER_NAME,
94 help = "server name. default options are Melange and Sidebar")
95 parser.add_option("-o", "--output", dest = "LOG_OUTPUT", default = LOG_OUTPUT,
96 help = ("set output. allowed outputs: FILE, STDOUT and STDERR. Default: %s") %(LOG_OUTPUT))
97 parser.add_option("-q", "--quiet", action="store_true", dest = "LOG_DISABLED",
98 default = LOG_DISABLED, help = "Disable log. The same as log level 0")
99 parser.add_option("-r", "--replace", action="store_true", dest = "SESSION_REPLACE",
100 default = SESSION_REPLACE, help = "Replace old session. Default: %s" %(SESSION_REPLACE))
101 parser.add_option("-e", "--session", action="store_true", dest = "SESSION_NAME",
102 default = SESSION_NAME, help = "Name of session. Default: %s" %(SESSION_NAME))
103 # Parse the options and store them in global module variables
104 COMMAND_LINE_OPTIONS, COMMAND_LINE_ARGS = parser.parse_args()
105
106 #-------------------------------------------------------------------------------
107 # Finish with the imports and import all of the submodules
108 #-------------------------------------------------------------------------------
109
110 import pygtk
111 pygtk.require('2.0')
112 import cairo, pango
113 import gobject
114 import glib
115 try:
116 import rsvg
117 except ImportError: print 'No module RSVG , graphics will not be so good'
118 import subprocess
119 import glob
120 import math
121 import time
122
123 # import screenlet-submodules
124 from options import *
125 import services
126 import utils
127 import sensors
128 # TEST
129 import menu
130 from menu import DefaultMenuItem, add_menuitem
131 from drawing import Drawing
132 # /TEST
133
134 import logger
135
138
139 #-------------------------------------------------------------------------------
140 # CONSTANTS
141 #-------------------------------------------------------------------------------
142
143 # the application name
144 APP_NAME = "Screenlets"
145
146 # the version of the Screenlets-baseclass in use
147 VERSION = "0.1.6"
148
149 # the application copyright
150 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>\nNatan Yellin (Aantn) <aantny@gmail.com>\nGuido Tabbernuk <boamaod@gmail.com>"
151
152 # the application authors
153 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga) <helder.fraga@hotmail.com>", "Sorcerer (Hendrik Kaju)", "Natan Yellin (Aantn) <aantny@gmail.com>", "Guido Tabbernuk <boamaod@gmail.com>" ]
154
155 # the application comments
156 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"
157
158 DOCUMENTERS = ["Documentation generated by epydoc"]
159
160 ARTISTS = ["Some themes by RYX\nSome themes by Whise\nMonochrome icons by Tabbernuk"]
161
162 TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/"
163
164 # the application website
165 WEBSITE = 'http://www.screenlets.org'
166
167 # 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!)
168 THIRD_PARTY_DOWNLOAD = _("http://www.screenlets.org/index.php/Get_more_screenlets")
169
170
171 #-------------------------------------------------------------------------------
172 # PATHS
173 #-------------------------------------------------------------------------------
174 DIR_TMP = '/tmp/screenlets/'
175
176 TMP_DIR = DIR_TMP
177
178 TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running'
179
180 DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets'
181
182 DIR_USER = os.path.join(os.environ['HOME'], '.screenlets')
183
184 DIR_CONFIG = os.path.join(xdg_config_home,'screenlets')
185 OLD_DIR_CONFIG = os.path.join(xdg_config_home,'Screenlets')
186 # workaround for XDG compliance update, see https://bugs.launchpad.net/screenlets/+bug/827369
187 try:
188 if not os.path.exists(DIR_CONFIG) and os.path.exists(OLD_DIR_CONFIG):
189 os.rename(OLD_DIR_CONFIG, DIR_CONFIG)
190 except:
191 pass
192
193 try:
194 if not os.path.isdir(DIR_CONFIG):
195 os.system('mkdir %s' % DIR_CONFIG)
196 except:
197 pass
198 try:
199 if not os.path.isdir(DIR_USER):
200 os.system('mkdir %s' % DIR_USER)
201 except:
202 pass
203
204 CONFIG_FILE = os.path.join(DIR_CONFIG, "config.ini")
205 OLD_CONFIG_FILE = os.path.join(DIR_USER, "config.ini")
206 # workaround for XDG compliance update, see https://bugs.launchpad.net/screenlets/+bug/827369
207 try:
208 if not os.path.exists(CONFIG_FILE) and os.path.exists(OLD_CONFIG_FILE):
209 os.rename(OLD_CONFIG_FILE, CONFIG_FILE)
210 except:
211 pass
212
213 # note that this is the order how themes are preferred to each other
214 # don't change the order just like that
215 SCREENLETS_PATH = [DIR_USER, DIR_USER_ROOT]
216
217 SCREENLETS_PACK_PREFIX = "screenlets-pack-"
218
219 #-------------------------------------------------------------------------------
220 # DBUS
221 #-------------------------------------------------------------------------------
222
223 DAEMON_BUS = 'org.screenlets.ScreenletsDaemon'
224
225 DAEMON_PATH = '/org/screenlets/ScreenletsDaemon'
226
227 DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon'
228
229 #Other stuff
230
231 DEBUG_MODE = True
232
233 DEBIAN = True
234 try:
235 subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
236 except OSError:
237 DEBIAN = False
238
239 UBUNTU = True
240 try:
241 subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
242 except OSError:
243 UBUNTU = False
244
245 #-------------------------------------------------------------------------------
246 # CLASSES
247 #-------------------------------------------------------------------------------
248
250 """A container with constants for the default menuitems"""
251
252 # default menuitem constants (is it right to increase like this?)
253 NONE = 0
254 DELETE = 1
255 THEMES = 2
256 INFO = 4
257 SIZE = 8
258 WINDOW_MENU = 16
259 PROPERTIES = 32
260 DELETE = 64
261 QUIT = 128
262 QUIT_ALL = 256
263 # EXPERIMENTAL!! If you use this, the file menu.xml in the
264 # Screenlet's data-dir is used for generating the menu ...
265 XML = 512
266 ADD = 1024
267 # the default items
268 STANDARD = 1|2|8|16|32|64|128|256|1024
269
270
272 """ScreenletThemes are simple storages that allow loading files
273 as svg-handles within a theme-directory. Each Screenlet can have
274 its own theme-directory. It is up to the Screenlet-developer if he
275 wants to let his Screenlet support themes or not. Themes are
276 turned off by default - if your Screenlet uses Themes, just set the
277 attribute 'theme_name' to the name of the theme's dir you want to use.
278 TODO: remove dict-inheritance"""
279
280 # meta-info (set through theme.conf)
281 __name__ = ''
282 __author__ = ''
283 __version__ = ''
284 __info__ = ''
285
286 # attributes
287 path = ""
288 loaded = False
289 width = 0
290 height = 0
291 option_overrides = {}
292 p_fdesc = None
293 p_layout = None
294 tooltip = None
295 notify = None
296
297
299 # set theme-path and load all files in path
300 self.path = path
301 self.svgs = {}
302 self.pngs = {}
303 self.option_overrides = {}
304 self.loaded = self.__load_all()
305 if self.loaded == False:
306 raise Exception("Error while loading ScreenletTheme in: " + path)
307
309 if name in ("width", "height"):
310 if self.loaded and len(self)>0:
311 size=self[0].get_dimension_data()
312 if name=="width":
313 return size[0]
314 else:
315 return size[1]
316 else:
317 return object.__getattr__(self, name)
318
320 """Apply this theme's overridden options to the given Screenlet."""
321 # disable the canvas-updates in the screenlet
322 screenlet.disable_updates = True
323 # theme_name needs special care (must be applied last)
324 theme_name = ''
325 # loop through overrides and appply them
326 for name in self.option_overrides:
327 print "Override: " + name
328 o = screenlet.get_option_by_name(name)
329 if o and not o.protected:
330 if name == 'theme_name':
331 # import/remember theme-name, but not apply yet
332 theme_name = o.on_import(self.option_overrides[name])
333 else:
334 # set option in screenlet
335 setattr(screenlet, name,
336 o.on_import(self.option_overrides[name]))
337 else:
338 print "WARNING: Option '%s' not found or protected." % name
339 # now apply theme
340 if theme_name != '':
341 screenlet.theme_name = theme_name
342 # re-enable updates and call redraw/reshape
343 screenlet.disable_updates = False
344 screenlet.redraw_canvas()
345 screenlet.update_shape()
346
348 """Checks if a file with filename is loaded in this theme."""
349 try:
350 if self[filename]:
351 return True
352 except:
353 #raise Exception
354 return False
355
357 """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
358 ctx.save()
359
360 if self.p_layout == None :
361
362 self.p_layout = ctx.create_layout()
363 else:
364
365 ctx.update_layout(self.p_layout)
366 self.p_fdesc = pango.FontDescription(font)
367 self.p_layout.set_font_description(self.p_fdesc)
368 self.p_layout.set_text(text)
369 extents, lextents = self.p_layout.get_pixel_extents()
370 ctx.restore()
371 return extents[2]
372
374 """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
375 ctx.save()
376
377 if self.p_layout == None :
378
379 self.p_layout = ctx.create_layout()
380 else:
381
382 ctx.update_layout(self.p_layout)
383 self.p_fdesc = pango.FontDescription(font)
384 self.p_layout.set_font_description(self.p_fdesc)
385 self.p_layout.set_text(text)
386 extents, lextents = self.p_layout.get_pixel_extents()
387 ctx.restore()
388 return extents
389
390 - def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
391 """@DEPRECATED Moved to Screenlets class: Draws text"""
392 ctx.save()
393 ctx.translate(x, y)
394 if self.p_layout == None :
395
396 self.p_layout = ctx.create_layout()
397 else:
398
399 ctx.update_layout(self.p_layout)
400 self.p_fdesc = pango.FontDescription()
401 self.p_fdesc.set_family_static(font)
402 self.p_fdesc.set_size(size * pango.SCALE)
403 self.p_fdesc.set_weight(weight)
404 self.p_layout.set_font_description(self.p_fdesc)
405 self.p_layout.set_width(width * pango.SCALE)
406 self.p_layout.set_alignment(allignment)
407 self.p_layout.set_ellipsize(ellipsize)
408 self.p_layout.set_markup(text)
409 ctx.show_layout(self.p_layout)
410 ctx.restore()
411
412
414 """@DEPRECATED Moved to Screenlets class: Draws a circule"""
415 ctx.save()
416 ctx.translate(x, y)
417 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
418 if fill: ctx.fill()
419 else: ctx.stroke()
420 ctx.restore()
421
422 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
423 """@DEPRECATED Moved to Screenlets class: Draws a line"""
424 ctx.save()
425 ctx.move_to(start_x, start_y)
426 ctx.set_line_width(line_width)
427 ctx.rel_line_to(end_x, end_y)
428 if close : ctx.close_path()
429 if preserve: ctx.stroke_preserve()
430 else: ctx.stroke()
431 ctx.restore()
432
434 """@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
435 ctx.save()
436 ctx.translate(x, y)
437 ctx.rectangle (0,0,width,height)
438 if fill:ctx.fill()
439 else: ctx.stroke()
440 ctx.restore()
441
443 """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
444 ctx.save()
445 ctx.translate(x, y)
446 padding=0 # Padding from the edges of the window
447 rounded=rounded_angle # How round to make the edges 20 is ok
448 w = width
449 h = height
450
451 # Move to top corner
452 ctx.move_to(0+padding+rounded, 0+padding)
453
454 # Top right corner and round the edge
455 ctx.line_to(w-padding-rounded, 0+padding)
456 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
457
458 # Bottom right corner and round the edge
459 ctx.line_to(w-padding, h-padding-rounded)
460 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
461
462 # Bottom left corner and round the edge.
463 ctx.line_to(0+padding+rounded, h-padding)
464 ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
465
466 # Top left corner and round the edge
467 ctx.line_to(0+padding, 0+padding+rounded)
468 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
469
470 # Fill in the shape.
471 if fill:ctx.fill()
472 else: ctx.stroke()
473 ctx.restore()
474
476 """@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
477
478 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
479 iw = pixbuf.get_width()
480 ih = pixbuf.get_height()
481 puxbuf = None
482 return iw,ih
483
485 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
486
487 ctx.save()
488 ctx.translate(x, y)
489 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
490 format = cairo.FORMAT_RGB24
491 if pixbuf.get_has_alpha():
492 format = cairo.FORMAT_ARGB32
493
494 iw = pixbuf.get_width()
495 ih = pixbuf.get_height()
496 image = cairo.ImageSurface(format, iw, ih)
497 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
498
499 ctx.paint()
500 puxbuf = None
501 image = None
502 ctx.restore()
503
504
505
507 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
508
509 ctx.save()
510 ctx.translate(x, y)
511 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
512 format = cairo.FORMAT_RGB24
513 if pixbuf.get_has_alpha():
514 format = cairo.FORMAT_ARGB32
515
516 iw = pixbuf.get_width()
517 ih = pixbuf.get_height()
518 image = cairo.ImageSurface(format, iw, ih)
519
520 matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
521 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
522 if image != None :image.set_matrix(matrix)
523 ctx.paint()
524 puxbuf = None
525 image = None
526 ctx.restore()
527
529 """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
530 if self.notify == None:
531 self.notify = Notify()
532 self.notify.text = text
533 self.notify.show()
534
536 """@DEPRECATED Moved to Screenlets class: hide notification window"""
537 if self.notify != None:
538 self.notify.hide()
539 self.notify = None
540
542 """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
543 if self.tooltip == None:
544 self.tooltip = Tooltip(300, 400)
545 self.tooltip.text = text
546 self.tooltip.x = tooltipx
547 self.tooltip.y = tooltipy
548 self.tooltip.show()
549
551 """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
552 if self.tooltip != None:
553 self.tooltip.hide()
554 self.tooltip = None
555
557 """Check if this theme contains overrides for options."""
558 return len(self.option_overrides) > 0
559
561 """Load a config-file from this theme's dir and save vars in list."""
562 ini = utils.IniReader()
563 if ini.load(filename):
564 if ini.has_section('Theme'):
565 self.__name__ = ini.get_option('name', section='Theme')
566 self.__author__ = ini.get_option('author', section='Theme')
567 self.__version__ = ini.get_option('version', section='Theme')
568 self.__info__ = ini.get_option('info', section='Theme')
569 if ini.has_section('Options'):
570 opts = ini.list_options(section='Options')
571 if opts:
572 for o in opts:
573 self.option_overrides[o[0]] = o[1]
574 print "Loaded theme config from:", filename
575 print "\tName: " + str(self.__name__)
576 print "\tAuthor: " +str(self.__author__)
577 print "\tVersion: " +str(self.__version__)
578 print "\tInfo: " +str(self.__info__)
579 else:
580 print "Failed to theme config from", filename
581
582
584 """Load an SVG-file into this theme and reference it as ref_name."""
585 if self.has_key(filename):
586 del self[filename]
587 try:
588 self[filename] = rsvg.Handle(self.path + "/" + filename)
589 self.svgs[filename[:-4]] = self[filename]
590 if self[filename] != None:
591 # set width/height
592 size=self[filename].get_dimension_data()
593 if size:
594 self.width = size[0]
595 self.height = size[1]
596 return True
597 except NameError, ex:
598 self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
599 self.svgs[filename[:-4]] = self[filename]
600 if self[filename] != None:
601 # set width/height
602 self.width = self[filename].get_width()
603 self.height = self[filename].get_height()
604 print str(ex)
605 return True
606
607 else:
608 return False
609 #self[filename] = None
610
612 """Load a PNG-file into this theme and reference it as ref_name."""
613 if self.has_key(filename):
614 del self[filename]
615 self[filename] = cairo.ImageSurface.create_from_png(self.path +
616 "/" + filename)
617 self.pngs[filename[:-4]] = self[filename]
618 if self[filename] != None:
619 return True
620 else:
621 return False
622 #self[filename] = None
623
625 """Load all files in the theme's path. Currently only loads SVGs and
626 PNGs."""
627 # clear overrides
628 #self.__option_overrides = {}
629 # read dir
630 dirlst = glob.glob(self.path + '/*')
631 if len(dirlst)==0:
632 return False
633 plen = len(self.path) + 1
634 for file in dirlst:
635 fname = file[plen:]
636 if fname.endswith('.svg'):
637 # svg file
638 if self.load_svg(fname) == False:
639 return False
640 elif fname.endswith('.png'):
641 # svg file
642 if self.load_png(fname) == False:
643 return False
644 elif fname == "theme.conf":
645 print "theme.conf found! Loading option-overrides."
646 # theme.conf
647 if self.load_conf(file) == False:
648 return False
649 # print "Theme %s loaded from %s" % (self.__name__, self.path)
650 return True
651
656
657 # TODO: fix function, rsvg handles are not freed properly
659 """Deletes the Theme's contents and frees all rsvg-handles.
660 TODO: freeing rsvg-handles does NOT work for some reason"""
661 self.option_overrides.clear()
662 for filename in self:
663 try:
664 self[filename].free()
665 except AttributeError:pass
666 #self[filename].close()
667 del filename
668 self.clear()
669
670 # TEST: render-function
671 # should be used like "theme.render(context, 'notes-bg')" and then use
672 # either an svg or png image
674 """Render an image from within this theme to the given context. This
675 function can EITHER use png OR svg images, so it is possible to
676 create themes using both image-formats when a Screenlet uses this
677 function for drawing its images. The image name has to be defined
678 without the extension and the function will automatically select
679 the available one (SVG is prefered over PNG)."""
680
681 ### Render Graphics even if rsvg is not available###
682 if os.path.isfile (self.path + '/' + name + '.svg'):
683
684 try:
685 self.svgs[name].render_cairo(ctx)
686 except:
687 try:
688 ctx.set_source_pixbuf(self.svgs[name], 0, 0)
689
690 ctx.paint()
691 pixbuf = None
692 except TypeError:
693 ctx.set_source_surface(self.pngs[name], 0, 0)
694 ctx.paint()
695
696 elif os.path.isfile (self.path + '/' + name + '.png'):
697 ctx.set_source_surface(self.pngs[name], 0, 0)
698 ctx.paint()
699
700
701
708
709
710
712 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
713 fully invisible by default. Subclasses of Screenlet can render
714 their owner-drawn graphics on fully transparent background."""
715
716 # default meta-info for Screenlets
717 __name__ = _('No name set for this Screenlet')
718 __version__ = '0.0'
719 __author__ = _('No author defined for this Screenlet')
720 __desc__ = _('No info set for this Screenlet')
721 __requires__ = []
722 #__target_version__ = '0.0.0'
723 #__backend_version__ = '0.0.1'
724
725 # attributes (TODO: remove them here and add them to the constructor,
726 # because they only should exist per instance)
727 id = '' # id-attribute for handling instances
728 window = None # the gtk.Window behind the scenes
729 theme = None # the assigned ScreenletTheme
730 uses_theme = True # flag indicating whether Screenlet uses themes
731 draw_buttons = True
732 show_buttons = True
733 menu = None # the right-click gtk.Menu
734 is_dragged = False # TODO: make this work
735 quit_on_close = True # if True, closing this instance quits gtk
736 saving_enabled = True # if False, saving is disabled
737 dragging_over = False # true if something is dragged over
738 disable_updates = False # to temporarily avoid refresh/reshape
739 p_context = None # PangoContext
740 p_layout = None # PangoLayout
741
742 # default editable options, available for all Screenlets
743 x = None
744 y = None
745 mousex = 0
746 mousey = 0
747 mouse_is_over = False
748 width = 100
749 height = 100
750 scale = 1.0
751 opacity = 1.0
752 theme_name = ""
753 is_visible = True
754 is_sticky = False
755 is_widget = False
756 keep_above = False
757 keep_below = True
758 skip_pager = True
759 first_run = False
760 skip_taskbar = True
761 lock_position = False
762 allow_option_override = True # if False, overrides are ignored
763 ask_on_option_override = True # if True, overrides need confirmation
764 ignore_requirements = False # if True, DEB requirements are ignored
765 resize_on_scroll = True
766 has_started = False
767 has_focus = False
768 # internals (deprecated? we still don't get the end of a begin_move_drag)
769 gtk_icon_theme = None
770 __lastx = 0
771 __lasty = 0
772 p_fdesc = None
773 p_layout = None
774 tooltip = None
775 notify = None
776 # some menuitems (needed for checking/unchecking)
777 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
778 __mi_keep_above = None
779 __mi_keep_below = None
780 __mi_widget = None
781 __mi_sticky = None
782 __mi_lock = None
783 # for custom signals (which aren't acutally used ... yet)
784 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
785 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
786
787 - def __init__ (self, id='', width=100, height=100, parent_window=None,
788 show_window=True, is_widget=False, is_sticky=False,
789 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
790 enable_saving=True, service_class=services.ScreenletService,
791 uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
792 """Constructor - should only be subclassed"""
793
794 # call gobject and EditableOptions superclasses
795 super(Screenlet, self).__init__()
796 EditableOptions.__init__(self)
797 # init properties
798 self.id = id
799 self.session = session
800 self.service = None
801 self.__desc__ = self.__doc__
802
803 # if we have an id and a service-class, register our service
804 if self.id and service_class:
805 self.register_service(service_class)
806 # notify service about adding this instance
807 self.service.instance_added(self.id)
808 self.width = width
809 self.height = height
810 self.is_dragged = False
811 self.__path__ = path
812 self.saving_enabled = enable_saving # used by session
813 # set some attributes without calling __setattr__
814 self.__dict__['theme_name'] = ""
815 self.__dict__['is_widget'] = is_widget
816 self.__dict__['is_sticky'] = is_sticky
817 self.__dict__['draw_buttons'] = draw_buttons
818 self.resize_on_scroll = resize_on_scroll
819 self.__dict__['x'] = gtk.gdk.screen_width()/2 - self.width/2
820 self.__dict__['y'] = gtk.gdk.screen_height()/2 - self.height/2
821 # TEST: set scale relative to theme size (NOT WORKING)
822 #self.__dict__['scale'] = width/100.0
823 # /TEST
824 # shape bitmap
825 self.__shape_bitmap = None
826 self.__shape_bitmap_width = 0
827 self.__shape_bitmap_height = 0
828 # "editable" options, first create a group
829 self.add_options_group('Screenlet',
830 _('The basic settings for this Screenlet-instance.'))
831 # if this Screenlet uses themes, add theme-specific options
832 # (NOTE: this option became hidden with 0.0.9 and doesn't use
833 # get_available_themes anymore for showing the choices)
834 self.gtk_icon_theme = gtk.icon_theme_get_default()
835 self.load_buttons(None)
836 self.gtk_icon_theme.connect("changed", self.load_buttons)
837 if draw_buttons: self.draw_buttons = True
838 else: self.draw_buttons = False
839 if uses_theme:
840 self.uses_theme = True
841 self.add_option(StringOption('Screenlet', 'theme_name',
842 default='default', hidden=True))
843 # create/add options
844 self.add_option(IntOption('Screenlet', 'x',
845 default=self.x, label=_('X-Position'),
846 desc=_('The X-position of this Screenlet ...'),
847 min=0, max=gtk.gdk.screen_width()))
848 self.add_option(IntOption('Screenlet', 'y',
849 default=self.y, label=_('Y-Position'),
850 desc=_('The Y-position of this Screenlet ...'),
851 min=0, max=gtk.gdk.screen_height()))
852 self.add_option(IntOption('Screenlet', 'width',
853 default=width, label=_('Width'),
854 desc=_('The width of this Screenlet ...'),
855 min=16, max=1000, hidden=True))
856 self.add_option(IntOption('Screenlet', 'height',
857 default=height, label=_('Height'),
858 desc=_('The height of this Screenlet ...'),
859 min=16, max=1000, hidden=True))
860 self.add_option(FloatOption('Screenlet', 'scale',
861 default=self.scale, label=_('Scale'),
862 desc=_('The scale-factor of this Screenlet ...'),
863 min=0.1, max=10.0, digits=2, increment=0.1))
864 self.add_option(FloatOption('Screenlet', 'opacity',
865 default=self.opacity, label=_('Opacity'),
866 desc=_('The opacity of the Screenlet window ...'),
867 min=0.1, max=1.0, digits=2, increment=0.1))
868 self.add_option(BoolOption('Screenlet', 'is_sticky',
869 default=is_sticky, label=_('Stick to Desktop'),
870 desc=_('Show this Screenlet on all workspaces ...')))
871 self.add_option(BoolOption('Screenlet', 'is_widget',
872 default=is_widget, label=_('Treat as Widget'),
873 desc=_('Treat this Screenlet as a "Widget" ...')))
874 self.add_option(BoolOption('Screenlet', 'is_dragged',
875 default=self.is_dragged, label="Is the screenlet dragged",
876 desc="Is the screenlet dragged", hidden=True))
877 self.add_option(BoolOption('Screenlet', 'is_sizable',
878 default=is_sizable, label="Can the screenlet be resized",
879 desc="is_sizable", hidden=True))
880 self.add_option(BoolOption('Screenlet', 'is_visible',
881 default=self.is_visible, label="Usefull to use screenlets as gnome panel applets",
882 desc="is_visible", hidden=True))
883 self.add_option(BoolOption('Screenlet', 'lock_position',
884 default=self.lock_position, label=_('Lock position'),
885 desc=_('Stop the screenlet from being moved...')))
886 self.add_option(BoolOption('Screenlet', 'keep_above',
887 default=self.keep_above, label=_('Keep above'),
888 desc=_('Keep this Screenlet above other windows ...')))
889 self.add_option(BoolOption('Screenlet', 'keep_below',
890 default=self.keep_below, label=_('Keep below'),
891 desc=_('Keep this Screenlet below other windows ...')))
892 self.add_option(BoolOption('Screenlet', 'draw_buttons',
893 default=self.draw_buttons, label=_('Draw button controls'),
894 desc=_('Draw buttons in top right corner')))
895 self.add_option(BoolOption('Screenlet', 'skip_pager',
896 default=self.skip_pager, label=_('Skip Pager'),
897 desc=_('Set this Screenlet to show/hide in pagers ...')))
898 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
899 default=self.skip_pager, label=_('Skip Taskbar'),
900 desc=_('Set this Screenlet to show/hide in taskbars ...')))
901 self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
902 default=self.resize_on_scroll, label=_("Resize on mouse scroll"),
903 desc="resize_on_scroll"))
904 self.add_option(BoolOption('Screenlet', 'ignore_requirements',
905 self.ignore_requirements, _('Ignore requirements'),
906 _('Set this Screenlet to ignore/demand DEB requirements ...')))
907 if uses_theme:
908 self.ask_on_option_override = ask_on_option_override
909 self.add_option(BoolOption('Screenlet', 'allow_option_override',
910 default=self.allow_option_override, label=_('Allow overriding Options'),
911 desc=_('Allow themes to override options in this screenlet ...')))
912 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
913 default=self.ask_on_option_override, label=_('Ask on Override'),
914 desc=_('Show a confirmation-dialog when a theme wants to override ')+\
915 _('the current options of this Screenlet ...')))
916 # disable width/height
917 self.disable_option('width')
918 self.disable_option('height')
919 # create window
920 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
921 self.window.set_position(gtk.WIN_POS_CENTER)
922 self.window.set_accept_focus(True)
923 if parent_window:
924 self.window.set_parent_window(parent_window)
925 self.window.set_transient_for(parent_window)
926 self.window.set_destroy_with_parent(True)
927 self.window.resize(width, height)
928 self.window.set_decorated(False)
929 # Set the window role so we can manipulate it with a window manager (such as Compiz)
930 self.window.set_role(self.id)
931 try: # Workaround for Ubuntu Natty
932 self.window.set_property('has-resize-grip', False)
933 except TypeError:
934 pass
935 self.window.set_app_paintable(True)
936 # create pango layout, if active
937 if uses_pango:
938 self.p_context = self.window.get_pango_context()
939 if self.p_context:
940 self.p_layout = pango.Layout(self.p_context)
941 self.p_layout.set_font_description(\
942 pango.FontDescription("Sans 12"))
943 # set type hint
944
945 if str(sensors.sys_get_window_manager()).lower() == 'kwin':
946 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
947 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
948 elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
949 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
950 else:
951 # Dock seems to be best for Unity, because it is seen after "Show desktop"
952 # Toolbar seems to be almost like Dock, focus is fine under GNOME3, but not Drag'n'drop
953 # Utility works also under GNOME3 (focus + DnD), however "Hide desktop" hides
954 # the widgets under Unity (most neutral default choice)
955 if os.environ.get('DESKTOP_SESSION') == "ubuntu":
956 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
957 # elif os.environ.get('DESKTOP_SESSION') == "ubuntu-2d":
958 # self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
959 # elif os.environ.get('DESKTOP_SESSION') == "gnome":
960 # self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
961 else:
962 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
963 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
964 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
965 self.window.set_keep_above(self.keep_above)
966 self.window.set_keep_below(self.keep_below)
967 self.window.set_skip_taskbar_hint(True)
968 self.window.set_skip_pager_hint(True)
969 if is_sticky:
970 self.window.stick()
971 self.alpha_screen_changed(self.window)
972 self.update_shape()
973 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
974 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
975 self.window.connect("composited-changed", self.composite_changed)
976 self.window.connect("delete_event", self.delete_event)
977 self.window.connect("destroy", self.destroy)
978 self.window.connect("expose_event", self.expose)
979 self.window.connect("button-press-event", self.button_press)
980 self.window.connect("button-release-event", self.button_release)
981 self.window.connect("configure-event", self.configure_event)
982 self.window.connect("screen-changed", self.alpha_screen_changed)
983 self.window.connect("realize", self.realize_event)
984 self.window.connect("enter-notify-event", self.enter_notify_event)
985 self.window.connect("leave-notify-event", self.leave_notify_event)
986 self.window.connect("focus-in-event", self.focus_in_event)
987 self.window.connect("focus-out-event", self.focus_out_event)
988 self.window.connect("scroll-event", self.scroll_event)
989 self.window.connect("motion-notify-event",self.motion_notify_event)
990 self.window.connect("map-event", self.map_event)
991 self.window.connect("unmap-event", self.unmap_event)
992 # add key-handlers (TODO: use keyword-attrib to activate?)
993 self.window.connect("key-press-event", self.key_press)
994 # drag/drop support (NOTE: still experimental and incomplete)
995 if drag_drop:
996 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
997 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
998 [("text/plain", 0, 0),
999 ("image", 0, 1),
1000 ("text/uri-list", 0, 2)],
1001 gtk.gdk.ACTION_COPY)
1002 self.window.connect("drag_data_received", self.drag_data_received)
1003 self.window.connect("drag-begin", self.drag_begin)
1004 self.window.connect("drag-end", self.drag_end)
1005 self.window.connect("drag-motion", self.drag_motion)
1006 self.window.connect("drag-leave", self.drag_leave)
1007 # create menu
1008 self.menu = gtk.Menu()
1009 # 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
1010
1011
1012 if show_window:
1013 self.window.show()
1014 if self.__name__.endswith("Screenlet"):
1015 short_name = self.__name__[:-9]
1016 else:
1017 short_name = self.__name__
1018 if not os.path.exists(DIR_CONFIG + '/' + short_name + '/default/'+ self.id + '.ini'):
1019 self.first_run = True
1020 self.window.hide()
1021
1022 #Make opacity available only when composite is enabled
1023 if not self.window.is_composited () :
1024 self.disable_option('opacity')
1025
1027 # set the value in GObject (ESSENTIAL!!!!)
1028 self.on_before_set_atribute(name, value)
1029 gobject.GObject.__setattr__(self, name, value)
1030 # And do other actions
1031 if name=="x" or name=="y":
1032 if self.has_started:
1033 self.window.move(self.x, self.y)
1034 elif name == 'opacity':
1035 self.window.set_opacity(value)
1036 elif name == 'scale':
1037 self.window.resize(int(self.width * self.scale),
1038 int(self.height * self.scale))
1039 # TODO: call on_resize-handler here !!!!
1040 self.on_scale()
1041 self.redraw_canvas()
1042 self.update_shape()
1043
1044
1045 elif name == "theme_name":
1046 #self.__dict__ ['theme_name'] = value
1047 #self.load_theme(self.get_theme_dir() + value)
1048 # load theme
1049 print "Theme set to: '%s'" % value
1050 path = self.find_theme(value)
1051 if path:
1052 self.load_theme(path)
1053 #self.load_first_theme(value)
1054 self.redraw_canvas()
1055 self.update_shape()
1056 elif name in ("width", "height"):
1057 #self.__dict__ [name] = value
1058 if self.window:
1059 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
1060 #self.redraw_canvas()
1061 self.update_shape()
1062 elif name == "is_widget":
1063 if self.has_started:
1064 self.set_is_widget(value)
1065 elif name == "is_visible":
1066 if self.has_started:
1067 if value == True:
1068 self.reshow()
1069 else:
1070 self.window.hide()
1071 elif name == "is_sticky":
1072 if value == True:
1073 self.window.stick()
1074 else:
1075 self.window.unstick()
1076 #if self.__mi_sticky:
1077 # self.__mi_sticky.set_active(value)
1078 elif name == "keep_above":
1079 if self.has_started == True:
1080 self.window.set_keep_above(bool(value))
1081 #self.__mi_keep_above.set_active(value)
1082 elif name == "keep_below":
1083 if self.has_started == True:
1084 self.window.set_keep_below(bool(value))
1085 #self.__mi_keep_below.set_active(value)
1086 elif name == "skip_pager":
1087 if self.window.window:
1088 self.window.window.set_skip_pager_hint(bool(value))
1089 elif name == "skip_taskbar":
1090 if self.window.window:
1091 self.window.window.set_skip_taskbar_hint(bool(value))
1092 # NOTE: This is the new recommended way of storing options in real-time
1093 # (we access the backend through the session here)
1094 if self.saving_enabled:
1095 o = self.get_option_by_name(name)
1096 if o != None:
1097 self.session.backend.save_option(self.id, o.name,
1098 o.on_export(value))
1099 self.on_after_set_atribute(name, value)
1100 # /TEST
1101
1102 #-----------------------------------------------------------------------
1103 # Screenlet's public functions
1104 #-----------------------------------------------------------------------
1105
1107 '''Checks if required DEB packages are installed'''
1108
1109 req_feedback = ""
1110 fail = False
1111
1112 # operators=['>', '=', '<']
1113
1114 commandstr = 'LANG=C 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/"'
1115 for req in self.__requires__:
1116 operator = None
1117 # req = req.replace(' ', '')
1118 if req.find('(') != -1:
1119 # package version is specified with an operator (no logical operators supported yet!)
1120 pos = req.find('(')
1121 package = req[:pos].strip()
1122 version_str = req[pos+1:]
1123 version_str = version_str[:version_str.find(')')]
1124 while version_str.find(' ') != -1:
1125 version_str = req.replace(' ', ' ')
1126 res = version_str.split(' ')
1127 version = res[1]
1128 operator = res[0]
1129 else:
1130 # when only package name is specified
1131 package = req
1132 # version of the deb package if unspecified
1133 version = _("?")
1134
1135 installed_version = os.popen(commandstr % package).readline().replace('\n', '')
1136
1137 if len(installed_version) < 1:
1138 req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version}
1139 fail = True
1140 else:
1141 req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version}
1142 # will fail only if dpkg says that version is too old
1143 # otherwise it's responsibility of developer to provide
1144 # correct version id and operator (won't detect problems with these)
1145 if operator is not None:
1146 comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\""
1147 # print comp_command
1148 if subprocess.call(comp_command, shell=True) != 0:
1149 fail = True
1150 if fail:
1151 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")
1152
1265
1276
1296
1297
1298
1302
1323
1325 """Fills the given cairo.Context with fully transparent white."""
1326 ctx.save()
1327 ctx.set_source_rgba(1, 1, 1, 0)
1328 ctx.set_operator (cairo.OPERATOR_SOURCE)
1329 ctx.paint()
1330 ctx.restore()
1331
1333 """Close this Screenlet
1334 TODO: send close-notify instead of destroying window?"""
1335 #self.save_settings()
1336 self.window.unmap()
1337 self.window.destroy()
1338 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
1339
1341 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1342 with the icon and the mask. To supply your own icon you can use the
1343 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1344 w = self.width
1345 h = self.height
1346 icon, mask = self.on_create_drag_icon()
1347 if icon == None:
1348 # create icon
1349 icon = gtk.gdk.Pixmap(self.window.window, w, h)
1350 ctx = icon.cairo_create()
1351 self.clear_cairo_context(ctx)
1352 self.on_draw(ctx)
1353 if mask == None:
1354 # create mask
1355 mask = gtk.gdk.Pixmap(self.window.window, w, h)
1356 ctx = mask.cairo_create()
1357 self.clear_cairo_context(ctx)
1358 self.on_draw_shape(ctx)
1359 return (icon, mask)
1360
1364
1366 """Find the best occurence of a theme and return its global path."""
1367 sn = self.get_short_name()
1368 utils.refresh_available_screenlet_paths()
1369 for p in SCREENLETS_PATH:
1370 fpath = p + '/' + sn + '/themes/' + name
1371 if os.path.isdir(fpath):
1372 return fpath
1373 return None
1374
1376 """Return the short name of this screenlet. This returns the classname
1377 of the screenlet without trailing "Screenlet". Please always use
1378 this function if you want to retrieve the short name of a Screenlet."""
1379 return self.__class__.__name__[:-9]
1380
1382 """Return the name of this screenlet's personal directory."""
1383 p = utils.find_first_screenlet_path(self.get_short_name())
1384 if p:
1385 return p
1386 else:
1387 if self.__path__ != '':
1388 return self.__path__
1389 else:
1390 return os.getcwd()
1391
1393 """Return the name of this screenlet's personal theme-dir.
1394 (Only returns the dir under the screenlet's location"""
1395 return self.get_screenlet_dir() + "/themes/"
1396
1398 """Returns a list with the names of all available themes in this
1399 Screenlet's theme-directories."""
1400 lst = []
1401 utils.refresh_available_screenlet_paths()
1402 for p in SCREENLETS_PATH:
1403 d = p + '/' + self.get_short_name() + '/themes/'
1404 if os.path.isdir(d):
1405 #dirname = self.get_theme_dir()
1406 dirlst = glob.glob(d + '*')
1407 dirlst.sort()
1408 tdlen = len(d)
1409 for fname in dirlst:
1410 if os.path.isdir(fname):
1411 dname = fname[tdlen:]
1412 if not dname in lst:
1413 lst.append(dname)
1414 return lst
1415
1417 self.window.present()
1418 self.has_started = True
1419 self.is_dragged = False
1420 self.keep_above= self.keep_above
1421 self.keep_below= self.keep_below
1422 self.skip_taskbar = self.skip_taskbar
1423 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1424 self.window.set_keep_above(self.keep_above)
1425 self.window.set_keep_below(self.keep_below)
1426 if self.is_widget:
1427 self.set_is_widget(True)
1428 self.has_focus = False
1429
1431 """Called when screenlet finishes loading"""
1432
1433
1434
1435 if DEBIAN and not self.ignore_requirements:
1436 self.check_requirements()
1437
1438 self.window.present()
1439 self.show()
1440
1441 self.has_started = True
1442 self.is_dragged = False
1443 self.keep_above= self.keep_above
1444 self.keep_below= self.keep_below
1445 self.is_sticky = self.is_sticky
1446 self.skip_taskbar = self.skip_taskbar
1447 self.window.set_skip_taskbar_hint(self.skip_taskbar)
1448 self.window.set_keep_above(self.keep_above)
1449 self.window.set_keep_below(self.keep_below)
1450
1451 self.on_init()
1452 if self.is_widget:
1453 self.set_is_widget(True)
1454 self.has_focus = False
1455 ini = utils.IniReader()
1456 if ini.load (DIR_CONFIG + '/config.ini') and self.first_run:
1457
1458 if ini.get_option('Lock', section='Options') == 'True':
1459 self.lock_position = True
1460 elif ini.get_option('Lock', section='Options') == 'False':
1461 self.lock_position = False
1462 if ini.get_option('Sticky', section='Options') == 'True':
1463 self.is_sticky = True
1464 elif ini.get_option('Sticky', section='Options') == 'False':
1465 self.is_sticky = False
1466 if ini.get_option('Widget', section='Options') == 'True':
1467 self.is_widget = True
1468 elif ini.get_option('Widget', section='Options') == 'False':
1469 self.is_widget = False
1470 if ini.get_option('Keep_above', section='Options') == 'True':
1471 self.keep_above = True
1472 elif ini.get_option('Keep_above', section='Options') == 'False':
1473 self.keep_above = False
1474 if ini.get_option('Keep_below', section='Options') == 'True':
1475 self.keep_below = True
1476 elif ini.get_option('Keep_below', section='Options') == 'False':
1477 self.keep_below = False
1478 if ini.get_option('draw_buttons', section='Options') == 'True':
1479 self.draw_buttons = True
1480 elif ini.get_option('draw_buttons', section='Options') == 'False':
1481 self.draw_buttons = False
1482
1487
1488 # EXPERIMENTAL:
1489 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
1490 # To do all in one, set attribute self.theme_name instead
1492 """Load a theme for this Screenlet from the given path. NOTE:
1493 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
1494 in one call, set the attribute self.theme_name instead."""
1495 if self.theme:
1496 self.theme.free()
1497 del self.theme
1498 self.theme = ScreenletTheme(path)
1499 # check for errors
1500 if self.theme.loaded == False:
1501 print "Error while loading theme: " + path
1502 self.theme = None
1503 else:
1504 # call user-defined handler
1505 self.on_load_theme()
1506 # if override options is allowed, apply them
1507 if self.allow_option_override:
1508 if self.theme.has_overrides():
1509 if self.ask_on_option_override==True and \
1510 show_question(self,
1511 _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
1512 return
1513 self.theme.apply_option_overrides(self)
1514 # /EXPERIMENTAL
1515
1519
1521 """Register or create the given ScreenletService-(sub)class as the new
1522 service for this Screenlet. If self is not the first instance in the
1523 current session, the service from the first instance will be used
1524 instead and no new service is created."""
1525 if self.session:
1526 if len(self.session.instances) == 0:
1527 # if it is the basic service, add name to call
1528 if service_classobj==services.ScreenletService:#BUG
1529 self.service = service_classobj(self, self.get_short_name())
1530 else:
1531 # else only pass this screenlet
1532 self.service = service_classobj(self)
1533 else:
1534 self.service = self.session.instances[0].service
1535 # TODO: throw exception??
1536 return True
1537 return False
1538
1540 """Set this window to be treated as a Widget (only supported by
1541 compiz using the widget-plugin yet)"""
1542 if value==True:
1543 # set window type to utility
1544 #self.window.window.set_type_hint(
1545 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1546 # set _compiz_widget-property on window
1547 self.window.window.property_change("_COMPIZ_WIDGET",
1548 gtk.gdk.SELECTION_TYPE_WINDOW,
1549 32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1550 else:
1551 # set window type to normal
1552 #self.window.window.set_type_hint(
1553 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1554 # set _compiz_widget-property
1555 self.window.window.property_delete("_COMPIZ_WIDGET")
1556 # notify handler
1557 self.on_switch_widget_state(value)
1558
1560 """Show this Screenlet's underlying gtk.Window"""
1561 # Hack for Ubuntu Oneiric, see https://bugs.launchpad.net/screenlets/+bug/885322 and https://bugs.launchpad.net/unity/+bug/904040
1562 while True and os.environ.get('DESKTOP_SESSION') == "ubuntu":
1563 if os.system("pgrep -fl unity-panel-service | grep -v pgrep") == 0:
1564 print "PANEL-SERVICE OK"
1565 break
1566 print "WAITING..."
1567 time.sleep(1)
1568 self.window.move(self.x, self.y)
1569 self.window.show()
1570 self.on_show()
1571
1573 """Show the EditableSettingsDialog for this Screenlet."""
1574 se = OptionsDialog(490, 450)
1575 img = gtk.Image()
1576 try:
1577 d = self.get_screenlet_dir()
1578 if os.path.isfile(d + '/icon.svg'):
1579 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1580 elif os.path.isfile(d + '/icon.png'):
1581 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1582 img.set_from_pixbuf(icn)
1583 except:
1584 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1585 se.set_title(self.__name__)
1586 se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__),
1587 version='v' + self.__version__, icon=img)
1588 se.show_options_for_object(self)
1589 resp = se.run()
1590 if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1591 se.reset_to_defaults()
1592 else:
1593 self.update_shape()
1594 se.destroy()
1595
1597 """Redraw the entire Screenlet's window area.
1598 TODO: store window alloaction in class and change when size changes."""
1599 # if updates are disabled, just exit
1600 if self.disable_updates:
1601 return
1602 if self.window:
1603 x, y, w, h = self.window.get_allocation()
1604 rect = gtk.gdk.Rectangle(x, y, w, h)
1605 if self.window.window:
1606 self.window.window.invalidate_rect(rect, True)
1607 self.window.window.process_updates(True)
1608 # if self.has_focus and self.draw_buttons and self.show_buttons:
1609 # self.create_buttons()
1610
1611
1613 """Redraw the given Rectangle (x, y, width, height) within the
1614 current Screenlet's window."""
1615 # if updates are disabled, just exit
1616 if self.disable_updates:
1617 return
1618 if self.window:
1619 rect = gtk.gdk.Rectangle(x, y, width, height)
1620 if self.window.window:
1621 self.window.window.invalidate_rect(rect, True)
1622 self.window.window.process_updates(True)
1623
1625 """Removed shaped window , in case the nom composited shape has been set"""
1626 if self.window.window:
1627 self.window.window.shape_combine_mask(None,0,0)
1628
1629 w = self.window.allocation.width
1630 h = self.window.allocation.height
1631
1632 # if 0 return to avoid crashing
1633 if w==0 or h==0: return False
1634 # if size changed, recreate shape bitmap
1635 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1636 self.__shape_bitmap = screenlets.create_empty_bitmap(w, h)
1637 self.__shape_bitmap_width = w
1638 self.__shape_bitmap_height = h
1639
1640 # create context
1641 ctx = self.__shape_bitmap.cairo_create()
1642 self.clear_cairo_context(ctx)
1643
1644 # shape the window acording if the window is composited or not
1645 if self.window.is_composited():
1646 # log.debug(_("Updating input shape"))
1647 self.on_draw_shape(ctx)
1648 # self.main_view.set_shape(self.__shape_bitmap, True)
1649 else:
1650 try:
1651 self.on_draw_shape(ctx)
1652 except:
1653 self.on_draw(ctx)
1654 # log.debug(_("Updating window shape"))
1655 # self.main_view.set_shape(self.__shape_bitmap, False)
1656
1658 """Update window shape (only call this when shape has changed
1659 because it is very ressource intense if ran too often)."""
1660 # if updates are disabled, just exit
1661 if self.disable_updates:
1662 return
1663 #print "UPDATING SHAPE"
1664 # TODO:
1665 #if not self.window.is_composited():
1666 # self.update_shape_non_composited()
1667 # calculate new width/height of shape bitmap
1668 w = int(self.width * self.scale)
1669 h = int(self.height * self.scale)
1670 # if 0 set it to 100 to avoid crashes and stay interactive
1671 if w==0: w = 100
1672 if h==0: h = 100
1673 # if size changed, recreate shape bitmap
1674 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1675 data = ''.zfill(w*h)
1676 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1677 w, h)
1678 self.__shape_bitmap_width = w
1679 self.__shape_bitmap_height = h
1680 # create context and draw shape
1681 ctx = self.__shape_bitmap.cairo_create()
1682 self.clear_cairo_context(ctx) #TEST
1683 if self.has_focus and self.draw_buttons and self.show_buttons:
1684 ctx.save()
1685 #theme1 = gtk.icon_theme_get_default()
1686 #ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1687 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1688 #close = theme1.load_icon ("gtk-close", 16, 0)
1689 #prop = theme1.load_icon ("gtk-properties", 16, 0)
1690 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1691 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1692 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1693 ctx.translate((self.width*self.scale)-16,0)
1694 ctx.set_source_pixbuf(self.closeb, 0, 0)
1695 ctx.paint()
1696 ctx.restore()
1697 ctx.save()
1698 ctx.translate((self.width*self.scale)-32,0)
1699 ctx.set_source_pixbuf(self.prop, 0, 0)
1700 ctx.paint()
1701 ctx.restore()
1702 # shape the window acording if the window is composited or not
1703
1704 if self.window.is_composited():
1705
1706 self.on_draw_shape(ctx)
1707 # and cut window with mask
1708 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1709 else:
1710 try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1711 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1712 # and cut window with mask
1713 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1714 self.on_update_shape()
1715
1717 """TEST: This function is intended to shape the window whenever no
1718 composited environment can be found. (NOT WORKING YET!!!!)"""
1719 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1720 # calculate new width/height of shape bitmap
1721 w = int(self.width * self.scale)
1722 h = int(self.height * self.scale)
1723 # if 0 set it to 100 to avoid crashes and stay interactive
1724 if w==0: w = 100
1725 if h==0: h = 100
1726 # if size changed, recreate shape bitmap
1727 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1728 data = ''.zfill(w*h)
1729 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1730 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1731 self.__shape_bitmap_width = w
1732 self.__shape_bitmap_height = h
1733 # and render window contents to it
1734 # TOOD!!
1735 if self.__shape_bitmap:
1736 # create new mask
1737 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1738 # apply new mask to window
1739 self.window.shape_combine_mask(mask)
1740
1744
1745 # ----------------------------------------------------------------------
1746 # Screenlet's event-handler dummies
1747 # ----------------------------------------------------------------------
1748
1750 """Called when the Screenlet gets deleted. Return True to cancel.
1751 TODO: sometimes not properly called"""
1752 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1753 _('Really delete this %s and its settings?') % self.get_short_name())
1754 """return not show_question(self, 'Deleting this instance of the '+\
1755 self.__name__ + ' will also delete all your personal '+\
1756 'changes you made to it!! If you just want to close the '+\
1757 'application, use "Quit" instead. Are you sure you want to '+\
1758 'delete this instance?')
1759 return False"""
1760
1761 # TODO: on_drag
1762 # TODO: on_drag_end
1763
1767
1771
1772
1774 """Called when the screenlet's drag-icon is created. You can supply
1775 your own icon and mask by returning them as a 2-tuple."""
1776 return (None, None)
1777
1781
1785
1789
1790
1794
1798
1802
1804 """Callback for drawing the Screenlet's window - override
1805 in subclasses to implement your own drawing."""
1806 pass
1807
1809 """Callback for drawing the Screenlet's shape - override
1810 in subclasses to draw the window's input-shape-mask."""
1811 pass
1812
1816
1820
1824
1826 """Called when the Screenlet's options have been applied and the
1827 screenlet finished its initialization. If you want to have your
1828 Screenlet do things on startup you should use this handler."""
1829 pass
1830
1834
1838
1842
1844 """Called when a buttonpress-event occured in Screenlet's window.
1845 Returning True causes the event to be not further propagated."""
1846 return False
1847
1851
1855
1859
1861 """Called when a buttonrelease-event occured in Screenlet's window.
1862 Returning True causes the event to be not further propagated."""
1863 return False
1864
1868
1871
1875
1879
1883
1887
1891
1895
1899 # ----------------------------------------------------------------------
1900 # Screenlet's event-handlers for GTK-events
1901 # ----------------------------------------------------------------------
1902
1904 """set colormap for window"""
1905 if screen==None:
1906 screen = window.get_screen()
1907 map = screen.get_rgba_colormap()
1908 if map:
1909 pass
1910 else:
1911 map = screen.get_rgb_colormap()
1912 window.set_colormap(map)
1913
1952
1961
1963 #this handle is called when composition changed
1964 self.remove_shape() # removing previous set shape , this is absolutly necessary
1965 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1966 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1967 self.keep_above= self.keep_above
1968 self.keep_below= self.keep_below
1969 self.window.show()
1970 #print 'Compositing method changed to %s' % str(self.window.is_composited())
1971 self.update_shape()
1972 self.redraw_canvas()
1973
1974 if not self.window.is_composited () :
1975 self.show_buttons = False
1976 self.disable_option("opacity")
1977 # print 'Warning - Buttons will not be shown until screenlet is restarted'
1978
1979 if self.window.is_composited () :
1980 self.enable_option("opacity")
1981
1982 self.is_sticky = self.is_sticky #and again ...
1983 self.keep_above= self.keep_above
1984 self.keep_below= self.keep_below
1985 self.window.set_keep_above(self.keep_above)
1986 self.window.set_keep_below(self.keep_below)
1987 self.on_composite_changed()
1988
1989 # NOTE: this should somehow handle the end of a move_drag-operation
1991 #print "onConfigure"
1992 #print event
1993 #if self.is_dragged == True:
1994 # set new position and cause a save of this Screenlet (not use
1995 # setattr to avoid conflicts with the window.move in __setattr__)
1996 if event.x != self.x:
1997 self.__dict__['x'] = event.x
1998 if self.session:
1999 self.session.backend.save_option(self.id, 'x', str(event.x))
2000 # self.is_dragged = False
2001 if event.y != self.y:
2002 self.__dict__['y'] = event.y
2003 if self.session:
2004 self.session.backend.save_option(self.id, 'y', str(event.y))
2005 # self.is_dragged = False
2006 return False
2007
2009 # cancel event?
2010 print "delete_event"
2011 if self.on_delete() == True:
2012 print "Cancel delete_event"
2013 return True
2014 else:
2015 self.close()
2016 return False
2017
2019 # call user-defined on_quit-handler
2020 self.on_quit()
2021 #print "destroy signal occurred"
2022 self.emit("screenlet_removed", self)
2023 # close gtk?
2024 if self.quit_on_close:
2025 if self.session: # if we have a session, flush current data
2026 self.session.backend.flush()
2027 gtk.main_quit()
2028 else:
2029 del self # ??? does this really work???
2030
2035 #return False
2036
2039
2044
2046 #print "Drag motion"
2047 if self.dragging_over == False:
2048 self.dragging_over = True
2049 self.on_drag_enter(drag_context, x, y, timestamp)
2050 return False
2051
2056
2058 #self.__mouse_inside = True
2059 self.__dict__['mouse_is_over'] = True
2060 self.on_mouse_enter(event)
2061
2062 #self.redraw_canvas()
2063
2065 ctx = widget.window.cairo_create()
2066 # set a clip region for the expose event
2067 ctx.rectangle(event.area.x, event.area.y,
2068 event.area.width, event.area.height)
2069 ctx.clip()
2070 # clear context
2071 self.clear_cairo_context(ctx)
2072
2073 # scale context
2074 #ctx.scale(self.scale, self.scale)
2075 # call drawing method
2076 self.on_draw(ctx)
2077 if self.show_buttons and self.draw_buttons and self.has_focus:
2078 self.create_buttons()
2079 # and delete context (needed?)
2080 del ctx
2081 return False
2082
2084 if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
2085 #Screenlet always gets focus after being dragged so this is a good method
2086 #to control the end of a move_drag operation!!!!!
2087 #This code happens on the end of a move_drag
2088 self.is_dragged=False
2089 self.has_focus = True
2090 self.on_focus(event)
2091 self.update_shape()
2092 self.redraw_canvas()
2093
2094
2095
2096
2098 if self.is_dragged==False:
2099 self.has_focus = False
2100 self.on_unfocus(event)
2101 self.update_shape()
2102 self.redraw_canvas()
2103
2104
2105
2107 """Handle keypress events, needed for in-place editing."""
2108 self.on_key_down(event.keyval, event.string, event)
2109
2111 #self.__mouse_inside = False
2112 #self.is_dragged = False
2113 self.__dict__['mouse_is_over'] = False
2114 self.on_mouse_leave(event)
2115
2116 #self.redraw_canvas()
2117
2190
2192 self.on_map()
2193
2195 self.on_unmap()
2196
2198 self.__dict__['mousex'] = event.x / self.scale
2199 self.__dict__['mousey'] = event.y / self.scale
2200
2201 self.on_mouse_move(event)
2202
2204 """called when window has been realized"""
2205 if self.window.window:
2206 self.window.window.set_back_pixmap(None, False) # needed?
2207
2208 self.on_realize()
2209
2211 if event.direction == gtk.gdk.SCROLL_UP:
2212 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
2213 self.on_scroll_up()
2214 elif event.direction == gtk.gdk.SCROLL_DOWN:
2215 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
2216 self.on_scroll_down()
2217 return False
2218
2219
2221 """Show notification window at current mouse position."""
2222 if self.notify == None:
2223 self.notify = Notify()
2224 self.notify.text = text
2225 self.notify.show()
2226
2228 """hide notification window"""
2229 if self.notify != None:
2230 self.notify.hide()
2231 self.notify = None
2232
2234 """Show tooltip window at current mouse position."""
2235 if self.tooltip == None:
2236 self.tooltip = Tooltip(300, 400)
2237 self.tooltip.text = text
2238 self.tooltip.x = tooltipx
2239 self.tooltip.y = tooltipy
2240 self.tooltip.show()
2241 else:
2242 #self.tooltip = Tooltip(300, 400)
2243 self.tooltip.text = text
2244 self.tooltip.x = tooltipx
2245 self.tooltip.y = tooltipy
2246 #self.tooltip.show()
2247
2253
2254 # TEST!!!
2256 """A simple base-class for creating owner-drawn gtk-widgets"""
2257
2258 __widget=None
2259
2260 mouse_inside = False
2261 width = 32
2262 height = 32
2263
2265 # call superclass
2266 super(ShapedWidget, self).__init__()
2267 # create/setup widget
2268 #self.__widget = gtk.Widget()
2269 self.set_app_paintable(True)
2270 self.set_size_request(width, height)
2271 # connect handlers
2272 self.set_events(gtk.gdk.ALL_EVENTS_MASK)
2273 self.connect("expose-event", self.expose_event)
2274 self.connect("button-press-event", self.button_press)
2275 self.connect("button-release-event", self.button_release)
2276 self.connect("enter-notify-event", self.enter_notify)
2277 self.connect("leave-notify-event", self.leave_notify)
2278
2279 # EXPERIMENTAL: TODO: cache bitmap until size changes
2281 """update widget's shape (only call this when shape has changed)"""
2282 data = ""
2283 for i in xrange(self.width*self.height):
2284 data += "0"
2285 bitmap = gtk.gdk.bitmap_create_from_data(None,
2286 data, self.width, self.height)
2287 ctx = bitmap.cairo_create()
2288 ctx.set_source_rgba(1, 1, 1, 0)
2289 ctx.set_operator (cairo.OPERATOR_SOURCE)
2290 ctx.paint()
2291 self.draw_shape(ctx)
2292 self.input_shape_combine_mask(bitmap, 0, 0)
2293 print "Updating shape."
2294
2299
2304
2308 #print "mouse enter"
2309
2313 #print "mouse leave"
2314
2317
2319 self.draw(ctx)
2320
2322 ctx = widget.window.cairo_create()
2323 # set a clip region for the expose event
2324 ctx.rectangle(event.area.x, event.area.y,
2325 event.area.width, event.area.height)
2326 ctx.clip()
2327 # clear context
2328 ctx.set_source_rgba(1, 1, 1, 0)
2329 ctx.set_operator (cairo.OPERATOR_SOURCE)
2330 ctx.paint()
2331 # call drawing method
2332 self.draw(ctx)
2333 # and delete context
2334 del ctx
2335 return False
2336
2338 __gtype_name__ = 'WrapLabel'
2339
2341 gtk.Label.__init__(self)
2342
2343 self.__wrap_width = 0
2344 self.layout = self.get_layout()
2345 self.layout.set_wrap(pango.WRAP_WORD_CHAR)
2346
2347 if str != None:
2348 self.set_text(str)
2349
2350 self.set_alignment(0.0, 0.0)
2351
2353 layout = self.get_layout()
2354 width, height = layout.get_pixel_size()
2355 requisition.width = 0
2356 requisition.height = height
2357
2361
2365
2369
2378
2380 """A window that displays a text and serves as Tooltip (very basic yet)."""
2381
2382 # internals
2383 __timeout = None
2384
2385 # attribs
2386 text = ''
2387 font_name = 'FreeSans 9'
2388 width = 100
2389 height = 20
2390 x = 0
2391 y = 0
2392
2394 object.__init__(self)
2395 # init
2396 self.__dict__['width'] = width
2397 self.__dict__['height'] = height
2398 self.window = gtk.Window()
2399 self.window.set_app_paintable(True)
2400 self.window.set_size_request(width, height)
2401 self.window.set_decorated(False)
2402 self.window.set_accept_focus(False)
2403 self.window.set_skip_pager_hint(True)
2404 self.window.set_skip_taskbar_hint(True)
2405 self.window.set_keep_above(True)
2406 self.screen_changed(self.window)
2407 self.window.connect("screen-changed", self.screen_changed)
2408 #self.window.show()
2409
2410 try: # Workaround for Ubuntu Natty
2411 self.window.set_property('has-resize-grip', False)
2412 except TypeError:
2413 pass
2414 self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(65535, 65535, 32767))
2415 self.label = WrapLabel()
2416 self.label.modify_font(pango.FontDescription(self.font_name))
2417 self.label.set_line_wrap(True)
2418 self.label.show()
2419 self.window.add(self.label)
2420
2421 self.p_layout = self.label.get_layout()
2422 self.p_layout.set_width(width * pango.SCALE - 6)
2423
2425 self.__dict__[name] = value
2426 if name in ('width', 'height', 'text'):
2427 if name== 'width':
2428 self.p_layout.set_width(width)
2429 elif name == 'text':
2430 value = utils.html_to_pango(value)
2431 self.p_layout.set_markup(value)
2432 self.label.set_markup(value)
2433 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2434 self.height = min(max(logical_rect[3], 16), 400) + 6
2435 self.window.set_size_request(self.width, self.height)
2436 self.window.queue_draw()
2437 elif name == 'x':
2438 self.window.move(int(value), int(self.y))
2439 elif name == 'y':
2440 self.window.move(int(self.x), int(value))
2441
2443 """Show the Tooltip window."""
2444 self.cancel_show()
2445 self.window.show()
2446 self.window.set_keep_above(True)
2447
2449 """Show the Tooltip window after a given delay."""
2450 self.cancel_show()
2451 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2452
2457
2459 """Cancel showing of the Tooltip."""
2460 if self.__timeout:
2461 gobject.source_remove(self.__timeout)
2462 self.p_context = None
2463 self.p_layout = None
2464
2466 self.show()
2467
2475
2477 """A window that displays a text and serves as Notification (very basic yet)."""
2478
2479 # internals
2480 __timeout = None
2481
2482 # attribs
2483 text = ''
2484 font_name = 'FreeSans 9'
2485 width = 200
2486 height = 100
2487 x = 0
2488 y = 0
2489 gradient = cairo.LinearGradient(0, 100,0, 0)
2490
2492 object.__init__(self)
2493 # init
2494 self.window = gtk.Window()
2495 self.window.set_app_paintable(True)
2496 self.window.set_size_request(self.width, self.height)
2497 self.window.set_decorated(False)
2498 self.window.set_accept_focus(False)
2499 self.window.set_skip_pager_hint(True)
2500 self.window.set_skip_taskbar_hint(True)
2501 self.window.set_keep_above(True)
2502 self.screen_changed(self.window)
2503 self.window.connect("expose_event", self.expose)
2504 self.window.connect("screen-changed", self.screen_changed)
2505 #self.window.show()
2506 self.p_context = self.window.get_pango_context()
2507 self.p_layout = pango.Layout(self.p_context)
2508 self.p_layout.set_font_description(\
2509 pango.FontDescription(self.font_name))
2510 #self.p_layout.set_width(-1)
2511 self.p_layout.set_width(self.width * pango.SCALE - 6)
2512
2514 self.__dict__[name] = value
2515 if name in ('text'):
2516 if name == 'text':
2517 self.p_layout.set_markup(value)
2518 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2519 self.window.queue_draw()
2520
2522 """Show the Notify window."""
2523 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
2524 self.cancel_show()
2525 self.window.show()
2526 self.window.set_keep_above(True)
2527
2529 """Show the Notify window after a given delay."""
2530 self.cancel_show()
2531 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2532
2537
2539 """Cancel showing of the Notify."""
2540 if self.__timeout:
2541 gobject.source_remove(self.__timeout)
2542 self.p_context = None
2543 self.p_layout = None
2544
2546 self.show()
2547
2549 if screen == None:
2550 screen = window.get_screen()
2551 map = screen.get_rgba_colormap()
2552 if not map:
2553 map = screen.get_rgb_colormap()
2554 window.set_colormap(map)
2555
2557 ctx = self.window.window.cairo_create()
2558 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2559 # set a clip region for the expose event
2560 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2561 ctx.clip()
2562 # clear context
2563 ctx.set_source_rgba(1, 1, 1, 0)
2564 ctx.set_operator (cairo.OPERATOR_SOURCE)
2565 ctx.paint()
2566 # draw rectangle
2567 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2568 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2569 ctx.set_source(self.gradient)
2570 ctx.rectangle(0, 0, self.width, self.height)
2571 ctx.fill()
2572 # draw text
2573 ctx.save()
2574 ctx.translate(3, 3)
2575 ctx.set_source_rgba(1, 1, 1, 1)
2576 ctx.show_layout(self.p_layout)
2577 ctx.fill()
2578 ctx.restore()
2579 ctx.rectangle(0, 0, self.width, self.height)
2580 ctx.set_source_rgba(0, 0, 0, 0.7)
2581 ctx.stroke()
2582
2583 # TEST (as the name implies)
2584 """class TestWidget(ShapedWidget):
2585
2586 def __init__(self, width, height):
2587 #ShapedWidget.__init__(self, width, height)
2588 super(TestWidget, self).__init__(width, height)
2589
2590 def draw(self, ctx):
2591 if self.mouse_inside:
2592 ctx.set_source_rgba(1, 0, 0, 0.8)
2593 else:
2594 ctx.set_source_rgba(1, 1, 0, 0.8)
2595 ctx.rectangle(0, 0, 32, 32)
2596 ctx.fill()
2597 """
2598
2599
2600 # ------------------------------------------------------------------------------
2601 # MODULE-FUNCTIONS
2602 # ------------------------------------------------------------------------------
2603
2604 # the new recommended way of launching a screenlet from the "outside"
2606 """Launch a screenlet, either through its service or by launching a new
2607 process of the given screenlet. Name has to be the name of the Screenlet's
2608 class without trailing 'Screenlet'.
2609 NOTE: we could only launch the file here"""
2610 # check for service
2611 if services.service_is_running(name):
2612 # add screenlet through service, if running
2613 srvc = services.get_service_by_name(name)
2614 if srvc:
2615 try:
2616 srvc.add('') # empty string for auto-creating ID
2617 return True
2618 except Exception, ex:
2619 print "Error while adding instance by service: %s" % ex
2620 # service not running or error? launch screenlet's file
2621 path = utils.find_first_screenlet_path(name)
2622 if path:
2623 # get full path of screenlet's file
2624 slfile = path + '/' + name + 'Screenlet.py'
2625 # launch screenlet as separate process
2626 print "Launching Screenlet from: %s" % slfile
2627 if debug:
2628 print "Logging output goes to: "+DIR_CONFIG+"/%sScreenlet.log" % name
2629 out = DIR_CONFIG+'/%sScreenlet.log' % name
2630 else:
2631 out = '/dev/null'
2632 os.system('python -u %s > %s &' % (slfile, out))
2633 return True
2634 else:
2635 print "Screenlet '%s' could not be launched." % name
2636 return False
2637
2639 """Show a message for the given Screenlet (may contain Pango-Markup).
2640 If screenlet is None, this function can be used by other objects as well."""
2641 if screenlet == None:
2642 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2643 buttons=gtk.BUTTONS_OK)
2644 md.set_title(title)
2645 else:
2646 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2647 buttons=gtk.BUTTONS_OK)
2648 md.set_title(screenlet.__name__)
2649 md.set_markup(message)
2650 md.run()
2651 md.destroy()
2652
2654 """Show a question for the given Screenlet (may contain Pango-Markup)."""
2655 if screenlet == None:
2656 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2657 buttons=gtk.BUTTONS_YES_NO)
2658 md.set_title(title)
2659 else:
2660 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2661 buttons=gtk.BUTTONS_YES_NO)
2662 md.set_title(screenlet.__name__)
2663 md.set_markup(message)
2664 response = md.run()
2665 md.destroy()
2666 if response == gtk.RESPONSE_YES:
2667 return True
2668 return False
2669
2671 """Show an error for the given Screenlet (may contain Pango-Markup)."""
2672 if screenlet == None:
2673 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2674 buttons=gtk.BUTTONS_OK)
2675 md.set_title(title)
2676 else:
2677 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2678 buttons=gtk.BUTTONS_OK)
2679 md.set_title(screenlet.__name__)
2680 md.set_markup(message)
2681 md.run()
2682 md.destroy()
2683
2685 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
2686 import sys
2687 msg = 'FATAL ERROR: %s\n' % message
2688 sys.stdout.write(msg)
2689 sys.stderr.write(msg)
2690 sys.exit(1)
2691
2692 # LEGACY support: functions that are not used any longer (raise fatal error)
2693
2695 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2696
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed Jan 4 16:58:26 2012 | http://epydoc.sourceforge.net |