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