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  # 
  10  ##@mainpage 
  11  # 
  12  ##@section intro_sec General Information 
  13  # 
  14  # INFO: 
  15  # - Screenlets are small owner-drawn applications that can be described as 
  16  #  " the virtual representation of things lying/standing around on your desk". 
  17  #   Sticknotes, clocks, rulers, ... the possibilities are endless. The goal of  
  18  #   the Screenlets is to simplify the creation of fully themeable mini-apps that 
  19  #   each solve basic desktop-work-related needs and generally improve the  
  20  #   usability and eye-candy of the modern Linux-desktop. 
  21  # 
  22  # TODO: (possible improvements, not essential) 
  23  # - still more error-handling and maybe custom exceptions!!! 
  24  # - improve xml-based menu (is implemented, but I'm not happy with it) 
  25  # - switching themes slowly increases the memory usage (possible leak) 
  26  # - maybe attributes for dependancies/requirements (e.g. special  
  27  #   python-libs or certain Screenlets) 
  28  # - 
  29  # 
  30   
  31  import pygtk 
  32  pygtk.require('2.0') 
  33  import gtk 
  34  import cairo, pango 
  35  import gobject 
  36  import rsvg 
  37  import os 
  38  import glob 
  39  import gettext 
  40  import math 
  41   
  42  # import screenlet-submodules 
  43  from options import * 
  44  import services 
  45  import utils 
  46   
  47  # TEST 
  48  import XmlMenu 
  49  # /TEST 
  50   
  51   
  52  #------------------------------------------------------------------------------- 
  53  # CONSTANTS 
  54  #------------------------------------------------------------------------------- 
  55   
  56  # the application name 
  57  APP_NAME = "Screenlets" 
  58   
  59  # the version of the Screenlets-baseclass in use 
  60  VERSION = "0.0.12" 
  61   
  62  # the application copyright 
  63  COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>" 
  64   
  65  # the application authors 
  66  AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Hendrik Kaju (sorcerer)"] 
  67   
  68  # the application comments 
  69  COMMENTS = "Screenlets are 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." 
  70   
  71  # the application website 
  72  WEBSITE = 'http://www.screenlets.org' 
  73   
  74  # the third party screenlets download site 
  75  THIRD_PARTY_DOWNLOAD = "http://screenlets.org/index.php/Category:UserScreenlets" 
  76   
  77  # install prefix (/usr or /usr/local) DO NOT CHANGE YET, WILL CHANGE WITH v0.1.0 
  78  INSTALL_PREFIX = '/usr' 
  79   
  80  # the global PATH where the screenlets are installed  
  81  PATH = INSTALL_PREFIX + '/share/screenlets' 
  82   
  83  # A list containing all the paths to search for screenlet-"packages" 
  84  # (these paths get searched when a new screenlet-instance shall be 
  85  # loaded through the module-loader function or a screenlet needs data 
  86  # from its personal dir) 
  87  SCREENLETS_PATH = [os.environ['HOME'] + '/.screenlets', PATH] 
  88   
  89  # translation stuff 
  90  gettext.textdomain('screenlets') 
  91  gettext.bindtextdomain('screenlets', '/usr/share/locale') 
  92   
93 -def _(s):
94 return gettext.gettext(s)
95 96 97 #------------------------------------------------------------------------------- 98 # CLASSES 99 #------------------------------------------------------------------------------- 100
101 -class DefaultMenuItem:
102 """A container with constants for the default menuitems""" 103 104 # default menuitem constants (is it right to increase like this?) 105 NONE = 0 106 DELETE = 1 107 THEMES = 2 108 INFO = 4 109 SIZE = 8 110 WINDOW_MENU = 16 111 PROPERTIES = 32 112 # EXPERIMENTAL!! If you use this, the file menu.xml in the 113 # Screenlet's data-dir is used for generating the menu ... 114 XML = 512 115 # the default items 116 STANDARD = 1|2|8|16|32
117 118
119 -class ScreenletTheme (dict):
120 """ScreenletThemes are simple storages that allow loading files 121 as svg-handles within a theme-directory. Each Screenlet can have 122 its own theme-directory. It is up to the Screenlet-developer if he 123 wants to let his Screenlet support themes or not. Themes are 124 turned off by default - if your Screenlet uses Themes, just set the 125 attribute 'theme_name' to the name of the theme's dir you want to use. 126 TODO: remove dict-inheritance""" 127 128 # meta-info (set through theme.conf) 129 __name__ = '' 130 __author__ = '' 131 __version__ = '' 132 __info__ = '' 133 134 # attributes 135 path = "" 136 loaded = False 137 width = 0 138 height = 0 139 option_overrides = {} 140 p_fdesc = None 141 p_layout = None 142 tooltip = None 143 notify = None 144
145 - def __init__ (self, path):
146 # set theme-path and load all files in path 147 self.path = path 148 self.svgs = {} 149 self.pngs = {} 150 self.option_overrides = {} 151 self.loaded = self.__load_all() 152 if self.loaded == False: 153 raise Exception(_("Error while loading ScreenletTheme in: ") + path)
154
155 - def __getattr__ (self, name):
156 if name in ("width", "height"): 157 if self.loaded and len(self)>0: 158 size=self[0].get_dimension_data() 159 if name=="width": 160 return size[0] 161 else: 162 return size[1] 163 else: 164 return object.__getattr__(self, name)
165
166 - def apply_option_overrides (self, screenlet):
167 """Apply this theme's overridden options to the given Screenlet.""" 168 # disable the canvas-updates in the screenlet 169 screenlet.disable_updates = True 170 # theme_name needs special care (must be applied last) 171 theme_name = '' 172 # loop through overrides and appply them 173 for name in self.option_overrides: 174 print _("Override: ") + name 175 o = screenlet.get_option_by_name(name) 176 if o and not o.protected: 177 if name == 'theme_name': 178 # import/remember theme-name, but not apply yet 179 theme_name = o.on_import(self.option_overrides[name]) 180 else: 181 # set option in screenlet 182 setattr(screenlet, name, 183 o.on_import(self.option_overrides[name])) 184 else: 185 print _("WARNING: Option '%s' not found or protected.") % name 186 # now apply theme 187 if theme_name != '': 188 screenlet.theme_name = theme_name 189 # re-enable updates and call redraw/reshape 190 screenlet.disable_updates = False 191 screenlet.redraw_canvas() 192 screenlet.update_shape()
193
194 - def check_entry (self, filename):
195 """Checks if a file with filename is loaded in this theme.""" 196 try: 197 if self[filename]: 198 return True 199 except: 200 #raise Exception 201 return False
202
203 - def draw_text(self, ctx, text, x, y, font, size, width, allignment):
204 """Draws text""" 205 ctx.save() 206 ctx.translate(x, y) 207 if self.p_layout == None : 208 209 self.p_layout = ctx.create_layout() 210 else: 211 212 ctx.update_layout(self.p_layout) 213 self.p_fdesc = pango.FontDescription() 214 self.p_fdesc.set_family_static(font) 215 self.p_fdesc.set_size(size * pango.SCALE) 216 self.p_layout.set_font_description(self.p_fdesc) 217 self.p_layout.set_width(width * pango.SCALE) 218 self.p_layout.set_alignment(allignment) 219 self.p_layout.set_markup(text) 220 ctx.show_layout(self.p_layout) 221 ctx.restore()
222 223
224 - def draw_circle(self,ctx,x,y,width,height,fill=True):
225 """Draws a circule""" 226 ctx.save() 227 ctx.translate(x, y) 228 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi) 229 if fill:ctx.fill() 230 else: ctx.stroke() 231 ctx.restore()
232
233 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
234 """Draws a line""" 235 ctx.save() 236 ctx.move_to(start_x, start_y) 237 ctx.set_line_width(line_width) 238 ctx.rel_line_to(end_x, end_y) 239 if close : ctx.close_path() 240 if preserve: ctx.stroke_preserve() 241 else: ctx.stroke() 242 ctx.restore()
243
244 - def draw_rectangle(self,ctx,x,y,width,height,fill=True):
245 """Draws a rectangle""" 246 ctx.save() 247 ctx.translate(x, y) 248 ctx.rectangle (0,0,width,height) 249 if fill:ctx.fill() 250 else: ctx.stroke() 251 ctx.restore()
252
253 - def draw_rounded_rectangle(self,ctx,x,y,rounded_angle,width,height,fill=True):
254 """Draws a rounded rectangle""" 255 ctx.save() 256 ctx.translate(x, y) 257 padding=0 # Padding from the edges of the window 258 rounded=rounded_angle # How round to make the edges 20 is ok 259 w = width 260 h = height 261 262 # Move to top corner 263 ctx.move_to(0+padding+rounded, 0+padding) 264 265 # Top right corner and round the edge 266 ctx.line_to(w-padding-rounded, 0+padding) 267 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, math.pi/2, 0) 268 269 # Bottom right corner and round the edge 270 ctx.line_to(w-padding, h-padding-rounded) 271 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2) 272 273 # Bottom left corner and round the edge. 274 ctx.line_to(0+padding+rounded, h-padding) 275 ctx.arc(0+padding+rounded, h-padding-rounded, rounded, math.pi+math.pi/2, math.pi) 276 277 # Top left corner and round the edge 278 ctx.line_to(0+padding, 0+padding+rounded) 279 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi/2, 0) 280 281 # Fill in the shape. 282 if fill:ctx.fill() 283 else: ctx.stroke() 284 ctx.restore()
285
286 - def show_notification (self,text):
287 """Show notification window at current mouse position.""" 288 if self.notify == None: 289 self.notify = Notify() 290 self.notify.text = text 291 self.notify.show()
292
293 - def hide_notification (self):
294 """hide notification window""" 295 if self.notify != None: 296 self.notify.hide() 297 self.notify = None
298
299 - def show_tooltip (self,text,tooltipx,tooltipy):
300 """Show tooltip window at current mouse position.""" 301 if self.tooltip == None: 302 self.tooltip = Tooltip(300, 400) 303 self.tooltip.text = text 304 self.tooltip.x = tooltipx 305 self.tooltip.y = tooltipy 306 self.tooltip.show()
307
308 - def hide_tooltip (self):
309 """hide tooltip window""" 310 if self.tooltip != None: 311 self.tooltip.hide() 312 self.tooltip = None
313
314 - def has_overrides (self):
315 """Check if this theme contains overrides for options.""" 316 return len(self.option_overrides) > 0
317
318 - def load_conf (self, filename):
319 """Load a config-file from this theme's dir and save vars in list.""" 320 ini = utils.IniReader() 321 if ini.load(filename): 322 if ini.has_section('Theme'): 323 self.__name__ = ini.get_option('name', section='Theme') 324 self.__author__ = ini.get_option('author', section='Theme') 325 self.__version__ = ini.get_option('version', section='Theme') 326 self.__info__ = ini.get_option('info', section='Theme') 327 if ini.has_section('Options'): 328 opts = ini.list_options(section='Options') 329 if opts: 330 for o in opts: 331 self.option_overrides[o[0]] = o[1] 332 print _("theme.conf loaded: ") 333 print _("Name: ") + str(self.__name__) 334 print _("Author: ") +str(self.__author__) 335 print _("Version: ") +str(self.__version__) 336 print _("Info: ") +str(self.__info__) 337 else: 338 print _("Failed to load theme.conf")
339 340
341 - def load_svg (self, filename):
342 """Load an SVG-file into this theme and reference it as ref_name.""" 343 if self.has_key(filename): 344 del self[filename] 345 self[filename] = rsvg.Handle(self.path + "/" + filename) 346 self.svgs[filename[:-4]] = self[filename] 347 if self[filename] != None: 348 # set width/height 349 size=self[filename].get_dimension_data() 350 if size: 351 self.width = size[0] 352 self.height = size[1] 353 return True 354 else: 355 return False
356
357 - def load_png (self, filename):
358 """Load a PNG-file into this theme and reference it as ref_name.""" 359 if self.has_key(filename): 360 del self[filename] 361 self[filename] = cairo.ImageSurface.create_from_png(self.path + 362 "/" + filename) 363 self.pngs[filename[:-4]] = self[filename] 364 if self[filename] != None: 365 return True 366 else: 367 return False
368
369 - def __load_all (self):
370 """Load all files in the theme's path. Currently only loads SVGs and 371 PNGs.""" 372 # clear overrides 373 #self.__option_overrides = {} 374 # read dir 375 dirlst = glob.glob(self.path + '/*') 376 if len(dirlst)==0: 377 return False 378 plen = len(self.path) + 1 379 for file in dirlst: 380 fname = file[plen:] 381 if fname.endswith('.svg'): 382 # svg file 383 if self.load_svg(fname) == False: 384 return False 385 elif fname.endswith('.png'): 386 # svg file 387 if self.load_png(fname) == False: 388 return False 389 elif fname == "theme.conf": 390 print _("theme.conf found! Loading option-overrides.") 391 # theme.conf 392 if self.load_conf(file) == False: 393 return False 394 return True
395
396 - def reload (self):
397 """Re-Load all files in the theme's path.""" 398 self.free() 399 self.__load_all()
400 401 # TODO: fix function, rsvg handles are not freed properly
402 - def free (self):
403 """Deletes the Theme's contents and frees all rsvg-handles. 404 TODO: freeing rsvg-handles does NOT work for some reason""" 405 self.option_overrides.clear() 406 for filename in self: 407 #self[filename].close() 408 del filename 409 self.clear()
410 411 # TEST: render-function 412 # should be used like "theme.render(context, 'notes-bg')" and then use 413 # either an svg or png image
414 - def render (self, ctx, name):
415 """Render an image from within this theme to the given context. This 416 function can EITHER use png OR svg images, so it is possible to 417 create themes using both image-formats when a Screenlet uses this 418 function for drawing its images. The image name has to be defined 419 without the extension and the function will automatically select 420 the available one (SVG is prefered over PNG).""" 421 """if self.has_key(name + '.svg'): 422 self[name + '.svg'].render_cairo(ctx) 423 else: 424 ctx.set_source_surface(self[name + '.png'], 0, 0) 425 ctx.paint()""" 426 try: 427 #self[name + '.svg'].render_cairo(ctx) 428 self.svgs[name].render_cairo(ctx) 429 except: 430 #ctx.set_source_surface(self[name + '.png'], 0, 0) 431 ctx.set_source_surface(self.pngs[name], 0, 0) 432 ctx.paint()
433 434 #else: 435 # ctx.set_source_pixbuf(self[name + '.png'], 0, 0) 436 # ctx.paint() 437 438
439 -class Screenlet (gobject.GObject, EditableOptions):
440 """A Screenlet is a (i.e. contains a) shaped gtk-window that is 441 fully invisible by default. Subclasses of Screenlet can render 442 their owner-drawn graphics on fully transparent background.""" 443 444 # default meta-info for Screenlets 445 __name__ = _('No name set for this Screenlet') 446 __version__ = '0.0' 447 __author__ = _('No author defined for this Screenlet') 448 __desc__ = _('No info set for this Screenlet') 449 __requires__ = [] # still unused 450 #__target_version__ = '0.0.0' 451 #__backend_version__ = '0.0.1' 452 453 # attributes (TODO: remove them here and add them to the constructor, 454 # because they only should exist per instance) 455 id = '' # id-attribute for handling instances 456 window = None # the gtk.Window behind the scenes 457 theme = None # the assigned ScreenletTheme 458 uses_theme = True # flag indicating whether Screenlet uses themes 459 menu = None # the right-click gtk.Menu 460 is_dragged = False # TODO: make this work 461 quit_on_close = True # if True, closing this instance quits gtk 462 saving_enabled = True # if False, saving is disabled 463 dragging_over = False # true if something is dragged over 464 disable_updates = False # to temporarily avoid refresh/reshape 465 p_context = None # PangoContext 466 p_layout = None # PangoLayout 467 468 # default editable options, available for all Screenlets 469 x = 0 470 y = 0 471 mousex = 0 472 mousey = 0 473 width = 100 474 height = 100 475 scale = 1.0 476 theme_name = "" 477 is_sticky = False 478 is_widget = False 479 keep_above = True 480 keep_below = False 481 skip_pager = True 482 skip_taskbar = True 483 lock_position = False 484 allow_option_override = True # if False, overrides are ignored 485 ask_on_option_override = True # if True, overrides need confirmation 486 has_started = False 487 488 # internals (deprecated? we still don't get the end of a begin_move_drag) 489 __lastx = 0 490 __lasty = 0 491 492 # some menuitems (needed for checking/unchecking) 493 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?) 494 __mi_keep_above = None 495 __mi_keep_below = None 496 __mi_widget = None 497 __mi_sticky = None 498 __mi_lock = None 499 # for custom signals (which aren't acutally used ... yet) 500 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST, 501 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))) 502
503 - def __init__ (self, id='', width=100, height=100, parent_window=None, 504 show_window=True, is_widget=False, is_sticky=False, 505 uses_theme=True, path=os.getcwd(), drag_drop=False, session=None, 506 enable_saving=True, service_class=services.ScreenletService, 507 uses_pango=False):
508 """Constructor - should only be subclassed""" 509 # call gobject and EditableOptions superclasses 510 super(Screenlet, self).__init__() 511 EditableOptions.__init__(self) 512 # init properties 513 self.id = id 514 self.session = session 515 self.service = None 516 # if we have an id and a service-class, register our service 517 if self.id and service_class: 518 self.register_service(service_class) 519 # notify service about adding this instance 520 self.service.instance_added(self.id) 521 self.width = width 522 self.height = height 523 self.is_dragged = False 524 self.__path__ = path 525 self.saving_enabled = enable_saving # used by session 526 # set some attributes without calling __setattr__ 527 self.__dict__['theme_name'] = "" 528 self.__dict__['is_widget'] = is_widget 529 self.__dict__['is_sticky'] = is_sticky 530 self.__dict__['x'] = 0 531 self.__dict__['y'] = 0 532 # TEST: set scale relative to theme size (NOT WORKING) 533 #self.__dict__['scale'] = width/100.0 534 # /TEST 535 # shape bitmap 536 self.__shape_bitmap = None 537 self.__shape_bitmap_width = 0 538 self.__shape_bitmap_height = 0 539 # "editable" options, first create a group 540 self.add_options_group('Screenlet', 541 _('The basic settings for this Screenlet-instance.')) 542 # if this Screenlet uses themes, add theme-specific options 543 # (NOTE: this option became hidden with 0.0.9 and doesn't use 544 # get_available_themes anymore for showing the choices) 545 if uses_theme: 546 self.uses_theme = True 547 self.add_option(StringOption('Screenlet', 'theme_name', 548 'default', '', '', hidden=True)) 549 # create/add options 550 self.add_option(IntOption('Screenlet', 'x', 551 0, _('X-Position'), _('The X-position of this Screenlet ...'), 552 min=0, max=gtk.gdk.screen_width())) 553 self.add_option(IntOption('Screenlet', 'y', 554 0, _('Y-Position'), _('The Y-position of this Screenlet ...'), 555 min=0, max=gtk.gdk.screen_height())) 556 self.add_option(IntOption('Screenlet', 'width', 557 width, _('Width'), _('The width of this Screenlet ...'), 558 min=16, max=1000, hidden=True)) 559 self.add_option(IntOption('Screenlet', 'height', 560 height, _('Height'), _('The height of this Screenlet ...'), 561 min=16, max=1000, hidden=True)) 562 self.add_option(FloatOption('Screenlet', 'scale', 563 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'), 564 min=0.1, max=10.0, digits=2, increment=0.1)) 565 self.add_option(BoolOption('Screenlet', 'is_sticky', 566 is_sticky, _('Stick to Desktop'), 567 _('Show this Screenlet on all workspaces ...'))) 568 self.add_option(BoolOption('Screenlet', 'is_widget', 569 is_widget, _('Treat as Widget'), 570 _('Treat this Screenlet as a "Widget" ...'))) 571 self.add_option(BoolOption('Screenlet', 'lock_position', 572 self.lock_position, _('Lock position'), 573 _('Stop the screenlet from being moved...'))) 574 self.add_option(BoolOption('Screenlet', 'keep_above', 575 self.keep_above, _('Keep above'), 576 _('Keep this Screenlet above other windows ...'))) 577 self.add_option(BoolOption('Screenlet', 'keep_below', 578 self.keep_below, _('Keep below'), 579 _('Keep this Screenlet below other windows ...'))) 580 self.add_option(BoolOption('Screenlet', 'skip_pager', 581 self.skip_pager, _('Skip Pager'), 582 _('Set this Screenlet to show/hide in pagers ...'))) 583 self.add_option(BoolOption('Screenlet', 'skip_taskbar', 584 self.skip_pager, _('Skip Taskbar'), 585 _('Set this Screenlet to show/hide in taskbars ...'))) 586 if uses_theme: 587 self.add_option(BoolOption('Screenlet', 'allow_option_override', 588 self.allow_option_override, _('Allow overriding Options'), 589 _('Allow themes to override options in this screenlet ...'))) 590 self.add_option(BoolOption('Screenlet', 'ask_on_option_override', 591 self.ask_on_option_override, _('Ask on Override'), 592 _('Show a confirmation-dialog when a theme wants to override ')+\ 593 _('the current options of this Screenlet ...'))) 594 # disable width/height 595 self.disable_option('width') 596 self.disable_option('height') 597 # create window 598 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 599 if parent_window: 600 self.window.set_parent_window(parent_window) 601 self.window.set_transient_for(parent_window) 602 self.window.set_destroy_with_parent(True) 603 self.window.resize(width, height) 604 self.window.set_decorated(False) 605 self.window.set_app_paintable(True) 606 # create pango layout, if active 607 if uses_pango: 608 self.p_context = self.window.get_pango_context() 609 if self.p_context: 610 self.p_layout = pango.Layout(self.p_context) 611 self.p_layout.set_font_description(\ 612 pango.FontDescription("Sans 12")) 613 # set type hint 614 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK) 615 self.window.set_keep_above(True) 616 self.window.set_skip_taskbar_hint(True) 617 self.window.set_skip_pager_hint(True) 618 if is_sticky: 619 self.window.stick() 620 self.alpha_screen_changed(self.window) 621 self.update_shape() 622 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK) 623 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK) 624 self.window.connect("composited-changed", self.composite_changed) 625 self.window.connect("delete_event", self.delete_event) 626 self.window.connect("destroy", self.destroy) 627 self.window.connect("expose_event", self.expose) 628 self.window.connect("button-press-event", self.button_press) 629 self.window.connect("button-release-event", self.button_release) 630 self.window.connect("configure-event", self.configure_event) 631 self.window.connect("screen-changed", self.alpha_screen_changed) 632 self.window.connect("realize", self.realize_event) 633 self.window.connect("enter-notify-event", self.enter_notify_event) 634 self.window.connect("leave-notify-event", self.leave_notify_event) 635 self.window.connect("focus-in-event", self.focus_in_event) 636 self.window.connect("focus-out-event", self.focus_out_event) 637 self.window.connect("scroll-event", self.scroll_event) 638 self.window.connect("motion-notify-event",self.motion_notify_event) 639 self.window.connect("map-event", self.map_event) 640 self.window.connect("unmap-event", self.unmap_event) 641 # add key-handlers (TODO: use keyword-attrib to activate?) 642 self.window.connect("key-press-event", self.key_press) 643 # drag/drop support (NOTE: still experimental and incomplete) 644 if drag_drop: 645 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION | 646 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL, 647 [("text/plain", 0, 0), 648 ("image", 0, 1), 649 ("text/uri-list", 0, 2)], 650 gtk.gdk.ACTION_COPY) 651 self.window.connect("drag_data_received", self.drag_data_received) 652 self.window.connect("drag-begin", self.drag_begin) 653 self.window.connect("drag-end", self.drag_end) 654 self.window.connect("drag-motion", self.drag_motion) 655 self.window.connect("drag-leave", self.drag_leave) 656 # create menu 657 self.menu = gtk.Menu() 658 # 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 659 if show_window: 660 self.window.show() 661 self.window.hide()
662
663 - def __setattr__ (self, name, value):
664 # set the value in GObject (ESSENTIAL!!!!) 665 self.on_before_set_atribute(name, value) 666 gobject.GObject.__setattr__(self, name, value) 667 # And do other actions 668 if name=="x" or name=="y": 669 self.window.move(self.x, self.y) 670 elif name == 'scale': 671 self.window.resize(int(self.width * self.scale), 672 int(self.height * self.scale)) 673 # TODO: call on_resize-handler here !!!! 674 self.on_scale() 675 self.redraw_canvas() 676 self.update_shape() 677 elif name == "theme_name": 678 #self.__dict__ ['theme_name'] = value 679 print _("LOAD NEW THEME: ") + value 680 print _("FOUND: ") + str(self.find_theme(value)) 681 #self.load_theme(self.get_theme_dir() + value) 682 # load theme 683 path = self.find_theme(value) 684 if path: 685 self.load_theme(path) 686 #self.load_first_theme(value) 687 self.redraw_canvas() 688 self.update_shape() 689 elif name in ("width", "height"): 690 #self.__dict__ [name] = value 691 if self.window: 692 self.window.resize(int(self.width*self.scale), int(self.height*self.scale)) 693 #self.redraw_canvas() 694 self.update_shape() 695 elif name == "is_widget": 696 if self.has_started: 697 self.set_is_widget(value) 698 elif name == "is_sticky": 699 if value == True: 700 self.window.stick() 701 else: 702 self.window.unstick() 703 #if self.__mi_sticky: 704 # self.__mi_sticky.set_active(value) 705 elif name == "keep_above": 706 self.window.set_keep_above(bool(value)) 707 #self.__mi_keep_above.set_active(value) 708 elif name == "keep_below": 709 self.window.set_keep_below(bool(value)) 710 #self.__mi_keep_below.set_active(value) 711 elif name == "skip_pager": 712 if self.window.window: 713 self.window.window.set_skip_pager_hint(bool(value)) 714 elif name == "skip_taskbar": 715 if self.window.window: 716 self.window.window.set_skip_taskbar_hint(bool(value)) 717 # NOTE: This is the new recommended way of storing options in real-time 718 # (we access the backend through the session here) 719 if self.saving_enabled: 720 o = self.get_option_by_name(name) 721 if o != None: 722 self.session.backend.save_option(self.id, o.name, 723 o.on_export(value)) 724 self.on_after_set_atribute(name, value)
725 # /TEST 726 727 #----------------------------------------------------------------------- 728 # Screenlet's public functions 729 #----------------------------------------------------------------------- 730 731 # NOTE: This function is deprecated and will get removed. The 732 # XML-based menus should be preferred
734 """Appends the default menu-items to self.menu. You can add on OR'ed 735 flag with DefaultMenuItems you want to add.""" 736 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' 737 # children already exist? add separator 738 if len(self.menu.get_children()) > 0: 739 self.add_menuitem("", "-") 740 # create menu (or submenu?) 741 #if flags & DefaultMenuItem.IS_SUBMENU : 742 # menu = gtk.Menu() 743 #else: 744 menu = self.menu 745 # EXPERIMENTAL: 746 if flags & DefaultMenuItem.XML: 747 # create XML-menu from screenletpath/menu.xml 748 xfile = self.get_screenlet_dir() + "/menu.xml" 749 xmlmenu = XmlMenu.create_menu_from_file(xfile, 750 self.menuitem_callback) 751 if xmlmenu: 752 self.menu = xmlmenu 753 pass 754 # add size-selection 755 if flags & DefaultMenuItem.SIZE: 756 size_item = gtk.MenuItem(_("Size")) 757 size_item.show() 758 size_menu = gtk.Menu() 759 menu.append(size_item) 760 size_item.set_submenu(size_menu) 761 #for i in xrange(10): 762 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): 763 s = str(int(i * 100)) 764 item = gtk.MenuItem(s + " %") 765 item.connect("activate", self.menuitem_callback, 766 "scale:"+str(i)) 767 item.show() 768 size_menu.append(item) 769 # create theme-selection menu 770 if flags & DefaultMenuItem.THEMES: 771 themes_item = gtk.MenuItem(_("Theme")) 772 themes_item.show() 773 themes_menu = gtk.Menu() 774 menu.append(themes_item) 775 themes_item.set_submenu(themes_menu) 776 # create theme-list from theme-directory 777 lst = self.get_available_themes() 778 for tname in lst: 779 item = gtk.MenuItem(tname) 780 item.connect("activate", self.menuitem_callback, 781 "theme:"+tname) 782 item.show() 783 themes_menu.append(item) 784 # add window-options menu 785 if flags & DefaultMenuItem.WINDOW_MENU: 786 winmenu_item = gtk.MenuItem(_("Window")) 787 winmenu_item.show() 788 winmenu_menu = gtk.Menu() 789 menu.append(winmenu_item) 790 winmenu_item.set_submenu(winmenu_menu) 791 # add "lock"-menuitem 792 self.__mi_lock = item = gtk.CheckMenuItem(_("Lock")) 793 item.set_active(self.lock_position) 794 item.connect("activate", self.menuitem_callback, 795 "option:lock") 796 item.show() 797 winmenu_menu.append(item) 798 # add "Sticky"-menuitem 799 self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky")) 800 item.set_active(self.is_sticky) 801 item.connect("activate", self.menuitem_callback, 802 "option:sticky") 803 item.show() 804 winmenu_menu.append(item) 805 # add "Widget"-menuitem 806 self.__mi_widget = item = gtk.CheckMenuItem(_("Widget")) 807 item.set_active(self.is_widget) 808 item.connect("activate", self.menuitem_callback, 809 "option:widget") 810 item.show() 811 winmenu_menu.append(item) 812 # add "Keep above"-menuitem 813 self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above")) 814 item.set_active(self.keep_above) 815 item.connect("activate", self.menuitem_callback, 816 "option:keep_above") 817 item.show() 818 winmenu_menu.append(item) 819 # add "Keep Below"-menuitem 820 self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below")) 821 item.set_active(self.keep_below) 822 item.connect("activate", self.menuitem_callback, 823 "option:keep_below") 824 item.show() 825 winmenu_menu.append(item) 826 # add Settings-item 827 if flags & DefaultMenuItem.PROPERTIES: 828 self.add_menuitem("", "-") 829 self.add_menuitem("options", _("Properties...")) 830 # add info-item 831 if flags & DefaultMenuItem.INFO: 832 self.add_menuitem("", "-") 833 self.add_menuitem("info", _("Info...")) 834 # add delete item 835 if flags & DefaultMenuItem.DELETE: 836 self.add_menuitem("", "-") 837 self.add_menuitem("delete", _("Delete Screenlet ...")) 838 # add Quit-item 839 self.add_menuitem("", "-") 840 self.add_menuitem("quit_instance", _("Quit this %s ...") % self.get_short_name()) 841 # add Quit-item 842 self.add_menuitem("", "-") 843 self.add_menuitem("quit", _("Quit all %ss ...") % self.get_short_name())
844
845 - def add_menuitem (self, id, label, callback=None):
846 """Simple way to add menuitems to the right-click menu.""" 847 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' 848 if callback == None: 849 callback = self.menuitem_callback 850 if label == "-": 851 menu_item = gtk.SeparatorMenuItem() 852 else: 853 menu_item = gtk.MenuItem(label) 854 menu_item.connect("activate", callback, id) 855 self.menu.append(menu_item) 856 menu_item.show() 857 return menu_item
858
859 - def clear_cairo_context (self, ctx):
860 """Fills the given cairo.Context with fully transparent white.""" 861 ctx.save() 862 ctx.set_source_rgba(1, 1, 1, 0) 863 ctx.set_operator (cairo.OPERATOR_SOURCE) 864 ctx.paint() 865 ctx.restore()
866
867 - def close (self):
868 """Close this Screenlet 869 TODO: send close-notify instead of destroying window?""" 870 #self.save_settings() 871 self.window.unmap() 872 self.window.destroy()
873 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE)) 874
875 - def create_drag_icon (self):
876 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple 877 with the icon and the mask. To supply your own icon you can use the 878 on_create_drag_icon-handler and return the icon/mask as 2-tuple.""" 879 w = self.width 880 h = self.height 881 icon, mask = self.on_create_drag_icon() 882 if icon == None: 883 # create icon 884 icon = gtk.gdk.Pixmap(self.window.window, w, h) 885 ctx = icon.cairo_create() 886 self.clear_cairo_context(ctx) 887 self.on_draw(ctx) 888 if mask == None: 889 # create mask 890 mask = gtk.gdk.Pixmap(self.window.window, w, h) 891 ctx = mask.cairo_create() 892 self.clear_cairo_context(ctx) 893 self.on_draw_shape(ctx) 894 return (icon, mask)
895
896 - def enable_saving (self, enabled=True):
897 """Enable/Disable realtime-saving of options.""" 898 self.saving_enabled = enabled
899
900 - def find_theme (self, name):
901 """Find the first occurence of a theme and return its global path.""" 902 sn = self.get_short_name() 903 for p in SCREENLETS_PATH: 904 fpath = p + '/' + sn + '/themes/' + name 905 if os.path.isdir(fpath): 906 return fpath 907 return None
908
909 - def get_short_name (self):
910 """Return the short name of this screenlet. This returns the classname 911 of the screenlet without trailing "Screenlet". Please always use 912 this function if you want to retrieve the short name of a Screenlet.""" 913 return self.__class__.__name__[:-9]
914
915 - def get_screenlet_dir (self):
916 """@DEPRECATED: Return the name of this screenlet's personal directory.""" 917 p = utils.find_first_screenlet_path(self.get_short_name()) 918 if p: 919 return p 920 else: 921 if self.__path__ != '': 922 return self.__path__ 923 else: 924 return os.getcwd()
925
926 - def get_theme_dir (self):
927 """@DEPRECATED: Return the name of this screenlet's personal theme-dir. 928 (Only returns the dir under the screenlet's location""" 929 return self.get_screenlet_dir() + "/themes/"
930
931 - def get_available_themes (self):
932 """Returns a list with the names of all available themes in this 933 Screenlet's theme-directory.""" 934 lst = [] 935 for p in SCREENLETS_PATH: 936 d = p + '/' + self.get_short_name() + '/themes/' 937 if os.path.isdir(d): 938 #dirname = self.get_theme_dir() 939 dirlst = glob.glob(d + '*') 940 dirlst.sort() 941 tdlen = len(d) 942 for fname in dirlst: 943 dname = fname[tdlen:] 944 # TODO: check if it's a dir 945 lst.append(dname) 946 return lst
947
948 - def finish_loading(self):
949 """Called when screenlet finishes loading""" 950 self.has_started = True 951 self.on_init() 952 try: self.window.show() 953 except: print 'unable to show window' 954 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary 955 self.keep_above= self.keep_above 956 self.keep_below= self.keep_below 957 if self.is_widget: 958 self.set_is_widget(True)
959 - def hide (self):
960 """Hides this Screenlet's underlying gtk.Window""" 961 self.window.hide() 962 self.on_hide()
963 964 # EXPERIMENTAL: 965 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!! 966 # To do all in one, set attribute self.theme_name instead
967 - def load_theme (self, path):
968 """Load a theme for this Screenlet from the given path. NOTE: 969 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all 970 in one call, set the attribute self.theme_name instead.""" 971 if self.theme: 972 self.theme.free() 973 del self.theme 974 self.theme = ScreenletTheme(path) 975 # check for errors 976 if self.theme.loaded == False: 977 print _("Error while loading theme: ") + path 978 self.theme = None 979 else: 980 # call user-defined handler 981 self.on_load_theme() 982 # if override options is allowed, apply them 983 if self.allow_option_override: 984 if self.theme.has_overrides(): 985 if self.ask_on_option_override==True and \ 986 show_question(self, 987 _('This theme wants to override your settings for ')+\ 988 _('this Screenlet. Do you want to allow that?')) == False: 989 return 990 self.theme.apply_option_overrides(self)
991 # /EXPERIMENTAL 992
993 - def main (self):
994 """If the Screenlet runs as stand-alone app, starts gtk.main()""" 995 gtk.main()
996
997 - def register_service (self, service_classobj):
998 """Register or create the given ScreenletService-(sub)class as the new 999 service for this Screenlet. If self is not the first instance in the 1000 current session, the service from the first instance will be used 1001 instead and no new service is created.""" 1002 if self.session: 1003 if len(self.session.instances) == 0: 1004 # if it is the basic service, add name to call 1005 if service_classobj==services.ScreenletService: 1006 self.service = service_classobj(self, self.get_short_name()) 1007 else: 1008 # else only pass this screenlet 1009 self.service = service_classobj(self) 1010 else: 1011 self.service = self.session.instances[0].service 1012 # TODO: throw exception?? 1013 return True 1014 return False
1015
1016 - def set_is_widget (self, value):
1017 """Set this window to be treated as a Widget (only supported by 1018 compiz using the widget-plugin yet)""" 1019 if value==True: 1020 # set window type to utility 1021 #self.window.window.set_type_hint( 1022 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY) 1023 # set _compiz_widget-property on window 1024 self.window.window.property_change("_COMPIZ_WIDGET", 1025 gtk.gdk.SELECTION_TYPE_WINDOW, 1026 32, gtk.gdk.PROP_MODE_REPLACE, (True,)) 1027 else: 1028 # set window type to normal 1029 #self.window.window.set_type_hint( 1030 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL) 1031 # set _compiz_widget-property 1032 self.window.window.property_delete("_COMPIZ_WIDGET") 1033 # notify handler 1034 self.on_switch_widget_state(value)
1035
1036 - def show (self):
1037 """Show this Screenlet's underlying gtk.Window""" 1038 self.window.show() 1039 self.window.move(self.x, self.y) 1040 self.on_show()
1041
1042 - def show_settings_dialog (self):
1043 """Show the EditableSettingsDialog for this Screenlet.""" 1044 se = OptionsDialog(490, 450) 1045 img = gtk.Image() 1046 try: 1047 d = self.get_screenlet_dir() 1048 if os.path.isfile(d + '/icon.svg'): 1049 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg') 1050 elif os.path.isfile(d + '/icon.png'): 1051 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png') 1052 img.set_from_pixbuf(icn) 1053 except: 1054 img.set_from_stock(gtk.STOCK_PROPERTIES, 5) 1055 se.set_title(self.__name__) 1056 se.set_info(self.__name__, self.__desc__, '(c) by ' + self.__author__, 1057 version='v' + self.__version__, icon=img) 1058 se.show_options_for_object(self) 1059 resp = se.run() 1060 if resp == gtk.RESPONSE_REJECT: # TODO!!!!! 1061 se.reset_to_defaults() 1062 else: 1063 self.update_shape() 1064 se.destroy()
1065
1066 - def redraw_canvas (self):
1067 """Redraw the entire Screenlet's window area. 1068 TODO: store window alloaction in class and change when size changes.""" 1069 # if updates are disabled, just exit 1070 if self.disable_updates: 1071 return 1072 if self.window: 1073 x, y, w, h = self.window.get_allocation() 1074 rect = gtk.gdk.Rectangle(x, y, w, h) 1075 if self.window.window: 1076 self.window.window.invalidate_rect(rect, True) 1077 self.window.window.process_updates(True)
1078
1079 - def redraw_canvas_area (self, x, y, width, height):
1080 """Redraw the given Rectangle (x, y, width, height) within the 1081 current Screenlet's window.""" 1082 # if updates are disabled, just exit 1083 if self.disable_updates: 1084 return 1085 if self.window: 1086 rect = gtk.gdk.Rectangle(x, y, width, height) 1087 if self.window.window: 1088 self.window.window.invalidate_rect(rect, True) 1089 self.window.window.process_updates(True)
1090
1091 - def remove_shape(self):
1092 """Removed shaped window , in case the nom composited shape has been set""" 1093 if self.window.window: 1094 self.window.window.shape_combine_mask(None,0,0)
1095
1096 - def update_shape (self):
1097 """Update window shape (only call this when shape has changed 1098 because it is very ressource intense if ran too often).""" 1099 # if updates are disabled, just exit 1100 if self.disable_updates: 1101 return 1102 print _("UPDATING SHAPE") 1103 # TODO: 1104 #if not self.window.is_composited(): 1105 # self.update_shape_non_composited() 1106 # calculate new width/height of shape bitmap 1107 w = int(self.width * self.scale) 1108 h = int(self.height * self.scale) 1109 # if 0 set it to 100 to avoid crashes and stay interactive 1110 if w==0: w = 100 1111 if h==0: h = 100 1112 # if size changed, recreate shape bitmap 1113 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height: 1114 data = ''.zfill(w*h) 1115 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data, 1116 w, h) 1117 self.__shape_bitmap_width = w 1118 self.__shape_bitmap_height = h 1119 # create context and draw shape 1120 ctx = self.__shape_bitmap.cairo_create() 1121 self.clear_cairo_context(ctx) #TEST 1122 1123 # shape the window acording if the window is composited or not 1124 1125 if self.window.is_composited(): 1126 1127 self.on_draw_shape(ctx) 1128 # and cut window with mask 1129 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0) 1130 else: 1131 try: self.on_draw(ctx) #Works better then the shape method on non composited windows 1132 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method 1133 # and cut window with mask 1134 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1135
1136 - def update_shape_non_composited (self):
1137 """TEST: This function is intended to shape the window whenever no 1138 composited environment can be found. (NOT WORKING YET!!!!)""" 1139 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file) 1140 # calculate new width/height of shape bitmap 1141 w = int(self.width * self.scale) 1142 h = int(self.height * self.scale) 1143 # if 0 set it to 100 to avoid crashes and stay interactive 1144 if w==0: w = 100 1145 if h==0: h = 100 1146 # if size changed, recreate shape bitmap 1147 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height: 1148 data = ''.zfill(w*h) 1149 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data, 1150 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w) 1151 self.__shape_bitmap_width = w 1152 self.__shape_bitmap_height = h 1153 # and render window contents to it 1154 # TOOD!! 1155 if self.__shape_bitmap: 1156 # create new mask 1157 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255) 1158 # apply new mask to window 1159 self.window.shape_combine_mask(mask)
1160 1161 # ---------------------------------------------------------------------- 1162 # Screenlet's event-handler dummies 1163 # ---------------------------------------------------------------------- 1164
1165 - def on_delete (self):
1166 """Called when the Screenlet gets deleted. Return True to cancel. 1167 TODO: sometimes not properly called""" 1168 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\ 1169 _('Really delete this %s and its settings?') % self.get_short_name()) 1170 """return not show_question(self, 'Deleting this instance of the '+\ 1171 self.__name__ + ' will also delete all your personal '+\ 1172 'changes you made to it!! If you just want to close the '+\ 1173 'application, use "Quit" instead. Are you sure you want to '+\ 1174 'delete this instance?') 1175 return False"""
1176 1177 # TODO: on_drag 1178 # TODO: on_drag_end 1179
1180 - def on_after_set_atribute(self,name, value):
1181 """Called after setting screenlet atributes""" 1182 pass
1183
1184 - def on_before_set_atribute(self,name, value):
1185 """Called before setting screenlet atributes""" 1186 pass
1187 1188
1189 - def on_create_drag_icon (self):
1190 """Called when the screenlet's drag-icon is created. You can supply 1191 your own icon and mask by returning them as a 2-tuple.""" 1192 return (None, None)
1193
1194 - def on_map(self):
1195 """Called when screenlet was mapped""" 1196 pass
1197
1198 - def on_unmap(self):
1199 """Called when screenlet was unmapped""" 1200 pass
1201
1202 - def on_composite_changed(self):
1203 """Called when composite state has changed""" 1204 pass
1205 1206
1207 - def on_drag_begin (self, drag_context):
1208 """Called when the Screenlet gets dragged.""" 1209 pass
1210
1211 - def on_drag_enter (self, drag_context, x, y, timestamp):
1212 """Called when something gets dragged into the Screenlets area.""" 1213 pass
1214
1215 - def on_drag_leave (self, drag_context, timestamp):
1216 """Called when something gets dragged out of the Screenlets area.""" 1217 pass
1218
1219 - def on_draw (self, ctx):
1220 """Callback for drawing the Screenlet's window - override 1221 in subclasses to implement your own drawing.""" 1222 pass
1223
1224 - def on_draw_shape (self, ctx):
1225 """Callback for drawing the Screenlet's shape - override 1226 in subclasses to draw the window's input-shape-mask.""" 1227 pass
1228
1229 - def on_drop (self, x, y, sel_data, timestamp):
1230 """Called when a selection is dropped on this Screenlet.""" 1231 return False
1232
1233 - def on_focus (self, event):
1234 """Called when the Screenlet's window receives focus.""" 1235 pass
1236
1237 - def on_hide (self):
1238 """Called when the Screenlet gets hidden.""" 1239 pass
1240
1241 - def on_init (self):
1242 """Called when the Screenlet's options have been applied and the 1243 screenlet finished its initialization. If you want to have your 1244 Screenlet do things on startup you should use this handler.""" 1245 pass
1246
1247 - def on_key_down (self, keycode, keyvalue, event=None):
1248 """Called when a key is pressed within the screenlet's window.""" 1249 pass
1250
1251 - def on_load_theme (self):
1252 """Called when the theme is reloaded (after loading, before redraw).""" 1253 pass
1254
1255 - def on_menuitem_select (self, id):
1256 """Called when a menuitem is selected.""" 1257 pass
1258
1259 - def on_mouse_down (self, event):
1260 """Called when a buttonpress-event occured in Screenlet's window. 1261 Returning True causes the event to be not further propagated.""" 1262 return False
1263
1264 - def on_mouse_enter (self, event):
1265 """Called when the mouse enters the Screenlet's window.""" 1266 pass
1267
1268 - def on_mouse_leave (self, event):
1269 """Called when the mouse leaves the Screenlet's window.""" 1270 pass
1271
1272 - def on_mouse_move(self, event):
1273 """Called when the mouse moves in the Screenlet's window.""" 1274 pass
1275
1276 - def on_mouse_up (self, event):
1277 """Called when a buttonrelease-event occured in Screenlet's window. 1278 Returning True causes the event to be not further propagated.""" 1279 return False
1280
1281 - def on_quit (self):
1282 """Callback for handling destroy-event. Perform your cleanup here!""" 1283 return True
1284
1285 - def on_realize (self):
1286 """"Callback for handling the realize-event."""
1287
1288 - def on_scale (self):
1289 """Called when Screenlet.scale is changed.""" 1290 pass
1291
1292 - def on_scroll_up (self):
1293 """Called when mousewheel is scrolled up (button4).""" 1294 pass
1295
1296 - def on_scroll_down (self):
1297 """Called when mousewheel is scrolled down (button5).""" 1298 pass
1299
1300 - def on_show (self):
1301 """Called when the Screenlet gets shown after being hidden.""" 1302 pass
1303
1304 - def on_switch_widget_state (self, state):
1305 """Called when the Screenlet enters/leaves "Widget"-state.""" 1306 pass
1307
1308 - def on_unfocus (self, event):
1309 """Called when the Screenlet's window loses focus.""" 1310 pass
1311 1312 # ---------------------------------------------------------------------- 1313 # Screenlet's event-handlers for GTK-events 1314 # ---------------------------------------------------------------------- 1315
1316 - def alpha_screen_changed (self, window, screen=None):
1317 """set colormap for window""" 1318 if screen==None: 1319 screen = window.get_screen() 1320 map = screen.get_rgba_colormap() 1321 if map: 1322 pass 1323 else: 1324 map = screen.get_rgb_colormap() 1325 window.set_colormap(map)
1326
1327 - def button_press (self, widget, event):
1328 #print "Button press" 1329 # set flags for user-handler 1330 if self.lock_position == False: 1331 if event.button == 1: 1332 self.is_dragged = True 1333 # call user-handler for onmousedown 1334 if self.on_mouse_down(event) == True: 1335 return True 1336 # unhandled? continue 1337 if self.lock_position == False: 1338 if event.button == 1: 1339 widget.begin_move_drag(event.button, int(event.x_root), 1340 int(event.y_root), event.time) 1341 1342 if event.button == 3: 1343 try: 1344 self.__mi_lock.set_active(self.lock_position) 1345 self.__mi_sticky.set_active(self.is_sticky) 1346 self.__mi_widget.set_active(self.is_widget) 1347 self.__mi_keep_above.set_active(self.keep_above) 1348 self.__mi_keep_below.set_active(self.keep_below) 1349 except : pass 1350 self.menu.popup(None, None, None, event.button, event.time) 1351 elif event.button == 4: 1352 print _("MOUSEWHEEL") 1353 self.scale -= 0.1 1354 elif event.button == 5: 1355 print _("MOUSEWHEEL") 1356 self.scale += 0.1 1357 return False
1358
1359 - def button_release (self, widget, event):
1360 print "Button release" 1361 self.is_dragged = False # doesn't work!!! we don't get an event when move_drag ends :( ... 1362 if self.on_mouse_up(event): 1363 return True 1364 return False
1365
1366 - def composite_changed(self,widget):
1367 #this handle is called when composition changed 1368 self.remove_shape() # removing previous set shape , this is absolutly necessary 1369 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state 1370 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that 1371 self.window.show() 1372 print 'composite change to ' + str(self.window.is_composited()) 1373 self.redraw_canvas() 1374 self.update_shape() 1375 self.is_sticky = self.is_sticky #and again ... 1376 self.on_composite_changed()
1377 1378 # NOTE: this should somehow handle the end of a move_drag-operation
1379 - def configure_event (self, widget, event):
1380 #print "onConfigure" 1381 #print event 1382 #if self.is_dragged == True: 1383 # set new position and cause a save of this Screenlet (not use 1384 # setattr to avoid conflicts with the window.move in __setattr__) 1385 if event.x != self.x: 1386 self.__dict__['x'] = event.x 1387 if self.session: 1388 self.session.backend.save_option(self.id, 'x', str(event.x)) 1389 if event.y != self.y: 1390 self.__dict__['y'] = event.y 1391 if self.session: 1392 self.session.backend.save_option(self.id, 'y', str(event.y)) 1393 return False
1394
1395 - def delete_event (self, widget, event, data=None):
1396 # cancel event? 1397 print "delete_event" 1398 if self.on_delete() == True: 1399 print _("Cancel delete_event") 1400 return True 1401 else: 1402 self.close() 1403 return False
1404
1405 - def destroy (self, widget, data=None):
1406 # call user-defined on_quit-handler 1407 self.on_quit() 1408 #print "destroy signal occurred" 1409 self.emit("screenlet_removed", self) 1410 # close gtk? 1411 if self.quit_on_close: 1412 if self.session: # if we have a session, flush current data 1413 self.session.backend.flush() 1414 gtk.main_quit() 1415 else: 1416 del self # ??? does this really work???
1417
1418 - def drag_begin (self, widget, drag_context):
1419 print _("Start drag") 1420 self.is_dragged = True 1421 self.on_drag_begin(drag_context)
1422 #return False 1423
1424 - def drag_data_received (self, widget, dc, x, y, sel_data, info, timestamp):
1425 return self.on_drop(x, y, sel_data, timestamp)
1426
1427 - def drag_end (self, widget, drag_context):
1428 print _("End drag") 1429 self.is_dragged = False 1430 return False
1431
1432 - def drag_motion (self, widget, drag_context, x, y, timestamp):
1433 #print "Drag motion" 1434 if self.dragging_over == False: 1435 self.dragging_over = True 1436 self.on_drag_enter(drag_context, x, y, timestamp) 1437 return False
1438
1439 - def drag_leave (self, widget, drag_context, timestamp):
1440 self.dragging_over = False 1441 self.on_drag_leave(drag_context, timestamp) 1442 return
1443
1444 - def enter_notify_event (self, widget, event):
1445 #self.__mouse_inside = True 1446 self.on_mouse_enter(event)
1447 #self.redraw_canvas() 1448
1449 - def expose (self, widget, event):
1450 ctx = widget.window.cairo_create() 1451 # clear context 1452 self.clear_cairo_context(ctx) 1453 # set a clip region for the expose event 1454 ctx.rectangle(event.area.x, event.area.y, 1455 event.area.width, event.area.height) 1456 ctx.clip() 1457 # scale context 1458 #ctx.scale(self.scale, self.scale) 1459 # call drawing method 1460 self.on_draw(ctx) 1461 # and delete context (needed?) 1462 del ctx 1463 return False
1464
1465 - def focus_in_event (self, widget, event):
1466 self.on_focus(event)
1467
1468 - def focus_out_event (self, widget, event):
1469 self.on_unfocus(event)
1470
1471 - def key_press (self, widget, event):
1472 """Handle keypress events, needed for in-place editing.""" 1473 self.on_key_down(event.keyval, event.string, event)
1474
1475 - def leave_notify_event (self, widget, event):
1476 #self.__mouse_inside = False 1477 self.on_mouse_leave(event)
1478
1479 - def menuitem_callback (self, widget, id):
1480 if id == "delete": 1481 if not self.on_delete(): 1482 # remove instance 1483 self.session.delete_instance (self.id) 1484 # notify about being rmeoved (does this get send???) 1485 self.service.instance_removed(self.id) 1486 elif id == "quit_instance": 1487 print 'quiting this one only' 1488 self.session.quit_instance (self.id) 1489 self.service.instance_removed(self.id) 1490 elif id == "quit": 1491 self.close() 1492 elif id in ("info", "about", "settings", "options", "properties"): 1493 # show settings dialog 1494 self.show_settings_dialog() 1495 elif id.startswith('scale:'): 1496 self.scale = float(id[6:]) 1497 elif id[:5] == "size:": # DEPRECATED?? 1498 # set size and update shape (redraw is done by setting height) 1499 #self.__dict__['width'] = int(id[5:]) 1500 self.width = int(id[5:]) 1501 self.height = int(id[5:]) 1502 self.update_shape() 1503 elif id[:6]=="theme:": 1504 print "Screenlet: Set theme " + id[6:] 1505 # set theme 1506 self.theme_name = id[6:] 1507 elif id[:8] == "setting:": 1508 # set a boolean option to the opposite state 1509 try: 1510 if type(self.__dict__[id[8:]]) == bool: 1511 self.__dict__[id[8:]] = not self.__dict__[id[8:]] # UNSAFE!! 1512 except: 1513 print _("Error: Cannot set missing or non-boolean value '")\ 1514 + id[8:] + "'" 1515 elif id[:7] == "option:": 1516 # NOTE: this part should be removed and XML-menus 1517 # should be used by default ... maybe 1518 # set option 1519 if id[7:]=="lock": 1520 if self.__mi_lock.get_active () != self.lock_position: 1521 self.lock_position = not self.lock_position 1522 elif id[7:]=="sticky": 1523 if self.__mi_sticky.get_active () != self.is_sticky: 1524 self.is_sticky = not self.is_sticky 1525 #widget.toggle() 1526 elif id[7:]=="widget": 1527 if self.__mi_widget.get_active () != self.is_widget: 1528 self.is_widget = not self.is_widget 1529 elif id[7:]=="keep_above": 1530 if self.__mi_keep_above.get_active () != self.keep_above: 1531 self.keep_above = not self.keep_above 1532 self.__mi_keep_above.set_active(self.keep_above) 1533 if self.keep_below and self.keep_above : 1534 self.keep_below = False 1535 self.__mi_keep_below.set_active(False) 1536 elif id[7:]=="keep_below": 1537 if self.__mi_keep_below.get_active () != self.keep_below: 1538 self.keep_below = not self.keep_below 1539 self.__mi_keep_below.set_active(self.keep_below) 1540 if self.keep_below and self.keep_above : 1541 self.keep_above = False 1542 self.__mi_keep_above.set_active(False) 1543 else: 1544 #print "Item: " + string 1545 pass 1546 # call user-handler 1547 self.on_menuitem_select(id) 1548 return False
1549
1550 - def map_event(self, widget, event):
1551 self.on_map()
1552
1553 - def unmap_event(self, widget, event):
1554 self.on_unmap()
1555
1556 - def motion_notify_event(self, widget, event):
1557 self.mousex = event.x / self.scale 1558 self.mousey = event.y / self.scale 1559 self.on_mouse_move(event)
1560
1561 - def realize_event (self, widget):
1562 """called when window has been realized""" 1563 if self.window.window: 1564 self.window.window.set_back_pixmap(None, False) # needed? 1565 1566 self.on_realize()
1567
1568 - def scroll_event (self, widget, event):
1569 if event.direction == gtk.gdk.SCROLL_UP: 1570 self.on_scroll_up() 1571 elif event.direction == gtk.gdk.SCROLL_DOWN: 1572 self.on_scroll_down() 1573 return False
1574 1575 1576 1577 # TEST!!!
1578 -class ShapedWidget (gtk.DrawingArea):
1579 """A simple base-class for creating owner-drawn gtk-widgets""" 1580 1581 __widget=None 1582 1583 mouse_inside = False 1584 width = 32 1585 height = 32 1586
1587 - def __init__ (self, width, height):
1588 # call superclass 1589 super(ShapedWidget, self).__init__() 1590 # create/setup widget 1591 #self.__widget = gtk.Widget() 1592 self.set_app_paintable(True) 1593 self.set_size_request(width, height) 1594 # connect handlers 1595 self.set_events(gtk.gdk.ALL_EVENTS_MASK) 1596 self.connect("expose-event", self.expose_event) 1597 self.connect("button-press-event", self.button_press) 1598 self.connect("button-release-event", self.button_release) 1599 self.connect("enter-notify-event", self.enter_notify) 1600 self.connect("leave-notify-event", self.leave_notify)
1601 1602 # EXPERIMENTAL: TODO: cache bitmap until size changes
1603 - def update_shape (self):
1604 """update widget's shape (only call this when shape has changed)""" 1605 data = "" 1606 for i in xrange(self.width*self.height): 1607 data += "0" 1608 bitmap = gtk.gdk.bitmap_create_from_data(None, 1609 data, self.width, self.height) 1610 ctx = bitmap.cairo_create() 1611 ctx.set_source_rgba(1, 1, 1, 0) 1612 ctx.set_operator (cairo.OPERATOR_SOURCE) 1613 ctx.paint() 1614 self.draw_shape(ctx) 1615 self.input_shape_combine_mask(bitmap, 0, 0) 1616 print "Updating shape."
1617
1618 - def button_press (self, widget, event):
1619 if event.button==1: 1620 print "left button pressed!" 1621 return False
1622
1623 - def button_release (self, widget, event):
1624 #if event.button==1: 1625 #print "left button release!" 1626 return False
1627
1628 - def enter_notify (self, widget, event):
1629 self.mouse_inside = True 1630 self.queue_draw()
1631 #print "mouse enter" 1632
1633 - def leave_notify (self, widget, event):
1634 self.mouse_inside = False 1635 self.queue_draw()
1636 #print "mouse leave" 1637
1638 - def draw (self, ctx):
1639 pass
1640
1641 - def draw_shape (self, ctx):
1642 self.draw(ctx)
1643
1644 - def expose_event (self, widget, event):
1645 ctx = widget.window.cairo_create() 1646 # set a clip region for the expose event 1647 ctx.rectangle(event.area.x, event.area.y, 1648 event.area.width, event.area.height) 1649 ctx.clip() 1650 # clear context 1651 ctx.set_source_rgba(1, 1, 1, 0) 1652 ctx.set_operator (cairo.OPERATOR_SOURCE) 1653 ctx.paint() 1654 # call drawing method 1655 self.draw(ctx) 1656 # and delete context 1657 del ctx 1658 return False
1659
1660 -class Tooltip:
1661 """A window that displays a text and serves as Tooltip (very basic yet).""" 1662 1663 # internals 1664 __timeout = None 1665 1666 # attribs 1667 text = '' 1668 font_name = 'FreeSans 9' 1669 width = 100 1670 height = 20 1671 x = 0 1672 y = 0 1673
1674 - def __init__ (self, width, height):
1675 object.__init__(self) 1676 # init 1677 self.__dict__['width'] = width 1678 self.__dict__['height'] = height 1679 self.window = gtk.Window() 1680 self.window.set_app_paintable(True) 1681 self.window.set_size_request(width, height) 1682 self.window.set_decorated(False) 1683 self.window.set_accept_focus(False) 1684 self.window.set_skip_pager_hint(True) 1685 self.window.set_skip_taskbar_hint(True) 1686 self.window.set_keep_above(True) 1687 self.screen_changed(self.window) 1688 self.window.connect("expose_event", self.expose) 1689 self.window.connect("screen-changed", self.screen_changed) 1690 #self.window.show() 1691 self.p_context = self.window.get_pango_context() 1692 self.p_layout = pango.Layout(self.p_context) 1693 self.p_layout.set_font_description(\ 1694 pango.FontDescription(self.font_name)) 1695 #self.p_layout.set_width(-1) 1696 self.p_layout.set_width(width * pango.SCALE - 6)
1697
1698 - def __setattr__ (self, name, value):
1699 self.__dict__[name] = value 1700 if name in ('width', 'height', 'text'): 1701 if name== 'width': 1702 self.p_layout.set_width(width) 1703 elif name == 'text': 1704 self.p_layout.set_markup(value) 1705 ink_rect, logical_rect = self.p_layout.get_pixel_extents() 1706 self.height = min(max(logical_rect[3], 16), 400) + 6 1707 self.window.set_size_request(self.width, self.height) 1708 self.window.queue_draw() 1709 elif name == 'x': 1710 self.window.move(int(value), int(self.y)) 1711 elif name == 'y': 1712 self.window.move(int(self.x), int(value))
1713
1714 - def show (self):
1715 """Show the Tooltip window.""" 1716 self.cancel_show() 1717 self.window.show() 1718 self.window.set_keep_above(True)
1719
1720 - def show_delayed (self, delay):
1721 """Show the Tooltip window after a given delay.""" 1722 self.cancel_show() 1723 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
1724
1725 - def hide (self):
1726 """Hide the Tooltip window.""" 1727 self.cancel_show() 1728 self.window.destroy()
1729
1730 - def cancel_show (self):
1731 """Cancel showing of the Tooltip.""" 1732 if self.__timeout: 1733 gobject.source_remove(self.__timeout) 1734 self.p_context = None 1735 self.p_layout = None
1736
1737 - def __show_timeout (self):
1738 self.show()
1739
1740 - def screen_changed (self, window, screen=None):
1741 if screen == None: 1742 screen = window.get_screen() 1743 map = screen.get_rgba_colormap() 1744 if not map: 1745 map = screen.get_rgb_colormap() 1746 window.set_colormap(map)
1747
1748 - def expose (self, widget, event):
1749 ctx = self.window.window.cairo_create() 1750 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? 1751 # set a clip region for the expose event 1752 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) 1753 ctx.clip() 1754 # clear context 1755 ctx.set_source_rgba(1, 1, 1, 0) 1756 ctx.set_operator (cairo.OPERATOR_SOURCE) 1757 ctx.paint() 1758 # draw rectangle 1759 ctx.set_source_rgba(1, 1, 0.5, 1) 1760 ctx.rectangle(0, 0, self.width, self.height) 1761 ctx.fill() 1762 # draw text 1763 ctx.save() 1764 ctx.translate(3, 3) 1765 ctx.set_source_rgba(0, 0, 0, 1) 1766 ctx.show_layout(self.p_layout) 1767 ctx.fill() 1768 ctx.restore() 1769 ctx.rectangle(0, 0, self.width, self.height) 1770 ctx.set_source_rgba(0, 0, 0, 0.7) 1771 ctx.stroke()
1772
1773 -class Notify:
1774 """A window that displays a text and serves as Notification (very basic yet).""" 1775 1776 # internals 1777 __timeout = None 1778 1779 # attribs 1780 text = '' 1781 font_name = 'FreeSans 9' 1782 width = 200 1783 height = 100 1784 x = 0 1785 y = 0 1786 gradient = cairo.LinearGradient(0, 100,0, 0) 1787
1788 - def __init__ (self):
1789 object.__init__(self) 1790 # init 1791 self.window = gtk.Window() 1792 self.window.set_app_paintable(True) 1793 self.window.set_size_request(self.width, self.height) 1794 self.window.set_decorated(False) 1795 self.window.set_accept_focus(False) 1796 self.window.set_skip_pager_hint(True) 1797 self.window.set_skip_taskbar_hint(True) 1798 self.window.set_keep_above(True) 1799 self.screen_changed(self.window) 1800 self.window.connect("expose_event", self.expose) 1801 self.window.connect("screen-changed", self.screen_changed) 1802 #self.window.show() 1803 self.p_context = self.window.get_pango_context() 1804 self.p_layout = pango.Layout(self.p_context) 1805 self.p_layout.set_font_description(\ 1806 pango.FontDescription(self.font_name)) 1807 #self.p_layout.set_width(-1) 1808 self.p_layout.set_width(self.width * pango.SCALE - 6)
1809
1810 - def __setattr__ (self, name, value):
1811 self.__dict__[name] = value 1812 if name in ('text'): 1813 if name == 'text': 1814 self.p_layout.set_markup(value) 1815 ink_rect, logical_rect = self.p_layout.get_pixel_extents() 1816 self.window.queue_draw()
1817
1818 - def show (self):
1819 """Show the Notify window.""" 1820 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height) 1821 self.cancel_show() 1822 self.window.show() 1823 self.window.set_keep_above(True)
1824
1825 - def show_delayed (self, delay):
1826 """Show the Notify window after a given delay.""" 1827 self.cancel_show() 1828 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
1829
1830 - def hide (self):
1831 """Hide the Notify window.""" 1832 self.cancel_show() 1833 self.window.destroy()
1834
1835 - def cancel_show (self):
1836 """Cancel showing of the Notify.""" 1837 if self.__timeout: 1838 gobject.source_remove(self.__timeout) 1839 self.p_context = None 1840 self.p_layout = None
1841
1842 - def __show_timeout (self):
1843 self.show()
1844
1845 - def screen_changed (self, window, screen=None):
1846 if screen == None: 1847 screen = window.get_screen() 1848 map = screen.get_rgba_colormap() 1849 if not map: 1850 map = screen.get_rgb_colormap() 1851 window.set_colormap(map)
1852
1853 - def expose (self, widget, event):
1854 ctx = self.window.window.cairo_create() 1855 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? 1856 # set a clip region for the expose event 1857 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) 1858 ctx.clip() 1859 # clear context 1860 ctx.set_source_rgba(1, 1, 1, 0) 1861 ctx.set_operator (cairo.OPERATOR_SOURCE) 1862 ctx.paint() 1863 # draw rectangle 1864 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9) 1865 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9) 1866 ctx.set_source(self.gradient) 1867 ctx.rectangle(0, 0, self.width, self.height) 1868 ctx.fill() 1869 # draw text 1870 ctx.save() 1871 ctx.translate(3, 3) 1872 ctx.set_source_rgba(1, 1, 1, 1) 1873 ctx.show_layout(self.p_layout) 1874 ctx.fill() 1875 ctx.restore() 1876 ctx.rectangle(0, 0, self.width, self.height) 1877 ctx.set_source_rgba(0, 0, 0, 0.7) 1878 ctx.stroke()
1879 1880 # TEST (as the name implies) 1881 """class TestWidget(ShapedWidget): 1882 1883 def __init__(self, width, height): 1884 #ShapedWidget.__init__(self, width, height) 1885 super(TestWidget, self).__init__(width, height) 1886 1887 def draw(self, ctx): 1888 if self.mouse_inside: 1889 ctx.set_source_rgba(1, 0, 0, 0.8) 1890 else: 1891 ctx.set_source_rgba(1, 1, 0, 0.8) 1892 ctx.rectangle(0, 0, 32, 32) 1893 ctx.fill() 1894 """ 1895 1896 1897 # ------------------------------------------------------------------------------ 1898 # MODULE-FUNCTIONS 1899 # ------------------------------------------------------------------------------ 1900 1901 # the new recommended way of launching a screenlet from the "outside"
1902 -def launch_screenlet (name, debug=False):
1903 """Launch a screenlet, either through its service or by launching a new 1904 process of the given screenlet. Name has to be the name of the Screenlet's 1905 class without trailing 'Screenlet'. 1906 NOTE: we could only launch the file here""" 1907 # check for service 1908 if services.service_is_running(name): 1909 # add screenlet through service, if running 1910 srvc = services.get_service_by_name(name) 1911 if srvc: 1912 try: 1913 srvc.add('') # empty string for auto-creating ID 1914 return True 1915 except Exception, ex: 1916 print "Error while adding instance by service: %s" % ex 1917 # service not running or error? launch screenlet's file 1918 path = utils.find_first_screenlet_path(name) 1919 if path: 1920 # get full path of screenlet's file 1921 slfile = path + '/' + name + 'Screenlet.py' 1922 # launch screenlet as separate process 1923 print "Launching Screenlet from: %s" % slfile 1924 if debug: 1925 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name 1926 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name 1927 else: 1928 out = '/dev/null' 1929 os.system('python -u %s > %s &' % (slfile, out)) 1930 return True 1931 else: 1932 print "Screenlet '%s' could not be launched." % name 1933 return False
1934
1935 -def show_message (screenlet, message, title=''):
1936 """Show a message for the given Screenlet (may contain Pango-Markup). 1937 If screenlet is None, this function can be used by other objects as well.""" 1938 if screenlet == None: 1939 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO, 1940 buttons=gtk.BUTTONS_OK) 1941 md.set_title(title) 1942 else: 1943 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO, 1944 buttons=gtk.BUTTONS_OK) 1945 md.set_title(screenlet.__name__) 1946 md.set_markup(message) 1947 md.run() 1948 md.destroy()
1949
1950 -def show_question (screenlet, message, title=''):
1951 """Show a question for the given Screenlet (may contain Pango-Markup).""" 1952 if screenlet == None: 1953 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION, 1954 buttons=gtk.BUTTONS_YES_NO) 1955 md.set_title(title) 1956 else: 1957 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION, 1958 buttons=gtk.BUTTONS_YES_NO) 1959 md.set_title(screenlet.__name__) 1960 md.set_markup(message) 1961 response = md.run() 1962 md.destroy() 1963 if response == gtk.RESPONSE_YES: 1964 return True 1965 return False
1966
1967 -def show_error (screenlet, message, title='Error'):
1968 """Show an error for the given Screenlet (may contain Pango-Markup).""" 1969 if screenlet == None: 1970 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR, 1971 buttons=gtk.BUTTONS_OK) 1972 md.set_title(title) 1973 else: 1974 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR, 1975 buttons=gtk.BUTTONS_OK) 1976 md.set_title(screenlet.__name__) 1977 md.set_markup(message) 1978 md.run() 1979 md.destroy()
1980
1981 -def fatal_error (message):
1982 """Raise a fatal error to stdout and stderr and exit with an errorcode.""" 1983 import sys 1984 msg = 'FATAL ERROR: %s\n' % message 1985 sys.stdout.write(msg) 1986 sys.stderr.write(msg) 1987 sys.exit(1)
1988 1989 # LEGACY support: functions that are not used any longer (raise fatal error) 1990
1991 -def create_new_instance (name):
1992 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
1993