Package screenlets

[hide private]
[frames] | no frames]

# Source Code for Package screenlets

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