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