Package screenlets
[hide private]
[frames] | no frames]

Source Code for Package screenlets

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