예제 #1
0
    def __init__(self, ident, rep):

        self.__action_stamp = 1

        # the display menu
        self.__DISPLAY_MENU = [
            MenuItem("/__cfg", _("_Configure desklet"),
                     callback = self.__handle_configure,
                     icon = gtk.STOCK_PREFERENCES),
            MenuItem("/__move", _("_Move desklet"),
                     callback = self.__handle_move),
            MenuItem("/__sep1"),
            MenuItem("/__src", _("_View Source"),
                     callback = self.__handle_source,
                     icon = gtk.STOCK_EDIT),
            MenuItem("/__sep2"),
            MenuItem("/__restart", _("Re_start desklet"),
                     callback = self.__handle_restart,
                     icon = gtk.STOCK_REDO),
            MenuItem("/__remove", _("_Remove desklet"),
                     callback = self.__handle_remove,
                     icon = gtk.STOCK_DELETE),
            MenuItem("/__remove", _("_Disable desklet"),
                     callback = self.__handle_disable,
                     icon = gtk.STOCK_CLOSE),
            MenuItem("/__sep3"),
            MenuItem("/__about", _("_About"),
                     callback = self.__handle_about,
                     icon = gtk.STOCK_ABOUT)
            ]

        # the layout object
        self.__layout_object = LayoutObject(None)

        # the stack of current mouse cursors
        self.__cursor_stack = []

        # the controls handling the deprecated sensor stuff: id -> control
        self.__sensor_controls = {}

        # mapping of the elements to their parent arrays: child_id -> array_id
        self.__arrays = {}

        # content of this display (valid until display has been initialized)
        self.__content = None

        # scriptlets of this display (valid until display has been initialized)
        self.__scriptlets = []

        # the root TargetGroup
        self.__group = None

        # the scripting environment
        self.__script = Script(ident, rep)

        # the path of the .display file
        self.__path = os.path.dirname(rep)
        self.__display_file = rep

        # the configurator object
        self.__configurator = DisplayConfigger(ident, self.__path)
        self.__configurator.set_scripting_environment(self.__script)

        # the about window
        self.__about = gtk.AboutDialog()
        self.__about.set_wrap_license(True)
        self.__about.connect("response", self.__about_response_callback)
        self.__about.connect("close", self.__about_close_and_delete_callback)
        self.__about.connect("delete_event",
                              self.__about_close_and_delete_callback)

        # the readme window
        self.__readme = HIGDialog(buttons = (gtk.STOCK_CLOSE,
                                             gtk.RESPONSE_CLOSE), self_destroy = False)
        self.__readme.set_title("%s - README" % os.path.basename(rep))

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        sw.set_shadow_type(gtk.SHADOW_IN)
        textview = gtk.TextView()
        textview.set_editable(False)
        textview.set_overwrite(False)
        textview.set_left_margin(8)
        textview.set_right_margin(8)
        textview.set_wrap_mode(gtk.WRAP_WORD)
        textview.set_cursor_visible(False)
        textview.set_size_request(500, 300)
        self.__readme_buffer = textview.get_buffer()
        sw.add(textview)
        sw.show()
        textview.show()
        self.__readme.vbox.add(sw)
        self.__readme.connect("response", self.__readme_response_callback)
        self.__readme.connect("close", self.__readme_close_and_delete_callback)
        self.__readme.connect("delete_event",
                              self.__readme_close_and_delete_callback)

        # the unique ID of this display
        self.__id = ident

        # the last position of the mouse pointer (used for filtering out
        # unwanted events)
        self.__last_pointer_pos = (-1, -1)

        # mapping between sensors and targets; which target watches
        # which sensor?
        # (sensor, port) -> (target, property)
        self.__mapping = {}

        # temporary data for remembering the position of the last mouse click
        self.__pointer_pos = (0, 0)

        # whether the display reacts on events
        self.__is_sensitive = True

        gtk.HBox.__init__(self)
예제 #2
0
class Display(gtk.HBox, Observable):

    __slots__ = ('__sensor_controls', '__arrays',
                 '__group', '__script',
                 '__configurator', '__path', '__display_file', '__id',
                 '__last_pointer_pos', '__pointer_pos', '__is_sensitive',
                 '__cursor_stack')

    # observer commands
    OBS_CLOSE = 0
    OBS_RESTART = 1
    OBS_GEOMETRY = 2
    OBS_SHAPE = 3
    OBS_TITLE = 4
    OBS_ICON = 5
    OBS_FLAGS = 6
    OBS_BORDERS = 7
    OBS_CURSOR = 8
    OBS_CLOSED = 9
    OBS_DISABLE = 10


    # event types
    EVENT_MOTION = 0
    EVENT_PRESS = 1
    EVENT_RELEASE = 2
    EVENT_KEY_PRESS = 3
    EVENT_KEY_RELEASE = 4
    EVENT_LEAVE = 5
    EVENT_SCROLL = 6


    # regular expression to test whether a path is absolute
    __IS_ABSOLUTE_PATH_RE = re.compile("[a-zA-Z]+://.+")

    def __init__(self, ident, rep):

        self.__action_stamp = 1

        # the display menu
        self.__DISPLAY_MENU = [
            MenuItem("/__cfg", _("_Configure desklet"),
                     callback = self.__handle_configure,
                     icon = gtk.STOCK_PREFERENCES),
            MenuItem("/__move", _("_Move desklet"),
                     callback = self.__handle_move),
            MenuItem("/__sep1"),
            MenuItem("/__src", _("_View Source"),
                     callback = self.__handle_source,
                     icon = gtk.STOCK_EDIT),
            MenuItem("/__sep2"),
            MenuItem("/__restart", _("Re_start desklet"),
                     callback = self.__handle_restart,
                     icon = gtk.STOCK_REDO),
            MenuItem("/__remove", _("_Remove desklet"),
                     callback = self.__handle_remove,
                     icon = gtk.STOCK_DELETE),
            MenuItem("/__remove", _("_Disable desklet"),
                     callback = self.__handle_disable,
                     icon = gtk.STOCK_CLOSE),
            MenuItem("/__sep3"),
            MenuItem("/__about", _("_About"),
                     callback = self.__handle_about,
                     icon = gtk.STOCK_ABOUT)
            ]

        # the layout object
        self.__layout_object = LayoutObject(None)

        # the stack of current mouse cursors
        self.__cursor_stack = []

        # the controls handling the deprecated sensor stuff: id -> control
        self.__sensor_controls = {}

        # mapping of the elements to their parent arrays: child_id -> array_id
        self.__arrays = {}

        # content of this display (valid until display has been initialized)
        self.__content = None

        # scriptlets of this display (valid until display has been initialized)
        self.__scriptlets = []

        # the root TargetGroup
        self.__group = None

        # the scripting environment
        self.__script = Script(ident, rep)

        # the path of the .display file
        self.__path = os.path.dirname(rep)
        self.__display_file = rep

        # the configurator object
        self.__configurator = DisplayConfigger(ident, self.__path)
        self.__configurator.set_scripting_environment(self.__script)

        # the about window
        self.__about = gtk.AboutDialog()
        self.__about.set_wrap_license(True)
        self.__about.connect("response", self.__about_response_callback)
        self.__about.connect("close", self.__about_close_and_delete_callback)
        self.__about.connect("delete_event",
                              self.__about_close_and_delete_callback)

        # the readme window
        self.__readme = HIGDialog(buttons = (gtk.STOCK_CLOSE,
                                             gtk.RESPONSE_CLOSE), self_destroy = False)
        self.__readme.set_title("%s - README" % os.path.basename(rep))

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        sw.set_shadow_type(gtk.SHADOW_IN)
        textview = gtk.TextView()
        textview.set_editable(False)
        textview.set_overwrite(False)
        textview.set_left_margin(8)
        textview.set_right_margin(8)
        textview.set_wrap_mode(gtk.WRAP_WORD)
        textview.set_cursor_visible(False)
        textview.set_size_request(500, 300)
        self.__readme_buffer = textview.get_buffer()
        sw.add(textview)
        sw.show()
        textview.show()
        self.__readme.vbox.add(sw)
        self.__readme.connect("response", self.__readme_response_callback)
        self.__readme.connect("close", self.__readme_close_and_delete_callback)
        self.__readme.connect("delete_event",
                              self.__readme_close_and_delete_callback)

        # the unique ID of this display
        self.__id = ident

        # the last position of the mouse pointer (used for filtering out
        # unwanted events)
        self.__last_pointer_pos = (-1, -1)

        # mapping between sensors and targets; which target watches
        # which sensor?
        # (sensor, port) -> (target, property)
        self.__mapping = {}

        # temporary data for remembering the position of the last mouse click
        self.__pointer_pos = (0, 0)

        # whether the display reacts on events
        self.__is_sensitive = True

        gtk.HBox.__init__(self)



    #
    # Returns the unique ID of this display.
    #
    def get_id(self): return self.__id

    def get_next_child_index(self): return -1

    def get_index_path(self): return []



    #
    # Initializes the display. Call this after you have finished constructing
    # it.
    #
    def initialize(self):
        assert self.__content

        childtype, settings, children = self.__content

        # load saved positions
        positions = DefaultStateSaver().get_key("positions", {})
        lx, ly = positions.get(self.get_id(), (UNSET_COORD, UNSET_COORD))
        x = settings.get("x", lx)
        y = settings.get("y", ly)
        if (x != UNSET_COORD): settings["x"] = x
        if (y != UNSET_COORD): settings["y"] = y

        # setup the root layout object (representing the root window)
        self.__layout_object.set_geometry(Unit.ZERO, Unit.ZERO,
                              Unit.Unit(gtk.gdk.screen_width(), Unit.UNIT_PX),
                              Unit.Unit(gtk.gdk.screen_height(), Unit.UNIT_PX))

        # build widget hierarchy
        self.new_child(childtype, settings, children)
        self.add(self.__group.get_widget())

        # run initial scripts
        for s in self.__scriptlets:
            self.execute_script(s)

        # load stored configuration
        self.__configurator.load_config()

        # unlock the geometry engine
        gobject.idle_add(self.__group.unlock_geometry)



    #
    # Sets the content of the display.
    #
    def set_content(self, childtype, settings, children):

        self.__content = (childtype, settings, children)



    #
    # Sets the callbacks for the responses (about and readme dialogs).
    #
    def __about_response_callback(self, about, response_id):
        if (response_id < 0):
            self.__about.hide()
            self.__about.emit_stop_by_name("response")
        elif (response_id == 2003):
            self.__open_readme()
        elif (response_id == gtk.RESPONSE_CLOSE):
            self.__about.hide()



    def __about_close_and_delete_callback(self, about, event=None):

        self.__about.hide()
        return True



    def __readme_response_callback(self, readme, response_id):

        if (response_id == gtk.RESPONSE_CLOSE):
            self.__readme.hide()



    def __readme_close_and_delete_callback(self, readme, event=None):

        self.__readme.hide()
        return True



    #
    # Sets metadata.
    #
    def set_metadata(self, metadata):

        pbuf = None
        fbuf = None
        rbuf = None

        preview = metadata.get("preview", "")
        name = metadata.get("name", "")
        version = metadata.get("version", "")
        copyright = metadata.get("copyright", "")
        comments = metadata.get("comments", "")
        license = metadata.get("license", "")
        website = metadata.get("website", "")
        author = metadata.get("author", "")
        category = metadata.get("category", "")
        dependency = metadata.get("dependency", "")
        description = metadata.get("description", "") + "\n"

        # a banner without the display's name would look crappy
        if (name):
            if (preview):
                preview = self.get_full_path(preview)
                data = vfs.read_entire_file(preview)
                loader = gtk.gdk.PixbufLoader()
                loader.write(data, len(data))
                loader.close()
                pbuf = loader.get_pixbuf()
            self.__configurator.set_banner(preview,
                                "<big>%s</big> %s\n"
                                "<small>%s</small>" % (name, version, author))

            # include the LICENSE file if available
            for elem in ("COPYING", "LICENSE"):
                try:
                    fbuf = open(self.get_full_path(elem), "r")
                except:
                    pass

            if (fbuf):
                license = fbuf.read()
            else:
                log("Warning: COPYING or LICENSE not included in desklet \"%s %s\".\nPlease contact the author!" % (name, version))

            # include the README file if available
            try:
                rbuf = open(self.get_full_path("README"), "r")
            except:
                log("README file not included in desklet!")

            if (rbuf):
                self.__readme_button = \
                    self.__about.add_button(_("_Readme"), 2003)
                self.__about.action_area.reorder_child(self.__readme_button,
                                                       gtk.PACK_START)
                self.__readme_buffer.set_text(rbuf.read())

            # add comments to the description
            if (comments): description = "%s\n%s\n" % (description, comments)

            # feed the About Window with the Metadata
            self.__about.set_name(name)
            self.__about.set_version(version)
            self.__about.set_copyright(copyright)
            self.__about.set_comments(description)
            if (license): self.__about.set_license(license)
            self.__about.set_website(website)
            self.__about.set_authors([author])
            self.__about.set_logo(pbuf)



    #
    # Adds the given scriptlet.
    #
    def add_scriptlet(self, code, filename):

        scriptlet = Scriptlet(code, filename)
        self.__scriptlets.append(scriptlet)



    #
    # Executes the given script.
    #
    def execute_script(self, code):

        self.__script.execute(code)



    #
    # Executes the given callback script.
    #
    def execute_callback_script(self, code, this):

        scriptlet = Scriptlet(code, self.__display_file)
        self.__script.add_element(None, "self", this)
        self.__script.execute(scriptlet)



    #
    # Adds the given target to the scripting environment.
    #
    def add_target_to_script(self, name, target):

        index_path = target.get_index_path()
        length = len(index_path)

        if ("#" in name): name = name[:name.find("#")]
        if (length > 0):
            self.__script.add_element_with_path(_ROOT, name, target, index_path)
        else:
            self.__script.add_element(_ROOT, name, target)



    #
    # Builds the configurator.
    #
    def build_configurator(self, items):

        self.__configurator.build(items)
        self.__configurator.set_path(self.__path)



    #
    # Sends an event to be executed to the display.
    #
    def send_event(self, etype, *args):

        if (not self.__is_sensitive): return
        w, h = self.__group.get_widget().size_request()
        lx, ly = self.__pointer_pos

        self.__action_stamp += 1
        actions = []

        if (etype == self.EVENT_MOTION):
            x, y = args
            utils.request_idle_call(self.__queue_motion, x, y, w, h, False)
            return

        elif (etype == self.EVENT_LEAVE):
            utils.request_idle_call(self.__queue_motion, 0, 0, w, h, True)
            return

        elif (etype == self.EVENT_PRESS):
            button, x, y, counter = args
            self.__pointer_pos = (x, y)
            if (counter == 1):
                action = DisplayTarget.ACTION_PRESS
            else:
                action = DisplayTarget.ACTION_DOUBLECLICK

            event = Struct(button = button, _args = [button])
            actions.append((action, event))

        elif (etype == self.EVENT_RELEASE):
            button, x, y = args
            if (button == 1):
                if (abs(lx - x) < 10 and abs(ly - y) < 10):
                    action = DisplayTarget.ACTION_CLICK
                    event = Struct(button = button, _args = [button])
                    actions.append((action, event))

                action = DisplayTarget.ACTION_RELEASE

            elif (button == 2):
                return
            elif (button == 3):
                action = DisplayTarget.ACTION_MENU
            else:
                return
            event = Struct(button = button, _args = [button])
            actions.append((action, event))

        elif (etype == self.EVENT_SCROLL):
            direction, x, y = args
            if (direction == gtk.gdk.SCROLL_UP):
                direction = 0
            elif (direction == gtk.gdk.SCROLL_DOWN):
                direction = 1
            else:
                direction = -1
            action = DisplayTarget.ACTION_SCROLL
            event = Struct(direction = direction, _args = [direction])
            actions.append((action, event))

        elif (etype == self.EVENT_KEY_PRESS):
            key, x, y = args
            action = DisplayTarget.ACTION_KEY_PRESS
            event = Struct(key = key)
            actions.append((action, event))

        elif (etype == self.EVENT_KEY_RELEASE):
            key, x, y = args
            action = DisplayTarget.ACTION_KEY_RELEASE
            event = Struct(key = key)
            actions.append((action, event))

        else:
            # what kind of event did we get there?
            import traceback; traceback.print_exc()

        for action, event in actions:
            self.__group.get_layout_object().send_action(
                Unit.Unit(x, Unit.UNIT_PX),
                Unit.Unit(y, Unit.UNIT_PX),
                self.__action_stamp,
                action, event)

        # extend the menu or create one if there's none
        if (action == DisplayTarget.ACTION_MENU):
            utils.request_idle_call(self.open_menu, [])



    #
    # Returns the path of the .display file.
    #
    def get_path(self):

        return self.__path



    #
    # Returns the full path of the given path which may be relative to the
    # .display file.
    #
    def get_full_path(self, path):

        # a path is absolute iff it starts with "/" or with a protocol name
        # such as "http://", otherwise it's relative
        if (path.startswith("/") or self.__IS_ABSOLUTE_PATH_RE.match(path)):
            return path
        else:
            return os.path.join(self.__path, path)



    #
    # Returns the display.
    #
    def _get_display(self):

        return self



    #
    # Creates and returns a new layout object as a child of this element's
    # layout object.
    #
    def new_layout_object(self):

        return self.__layout_object.new_child()



    def new_child(self, childtype, settings, children):

        # we don't catch the KeyError here, but in the DisplayFactory
        try:
            self.__group = targetregistry.create(childtype, self)
        except KeyError, exc:
            log("Error: %s\n" % `exc`)
        self.__group.get_widget().show()
        cid = settings["id"]
        self.add_target_to_script(cid, self.__group)

        for t, s, c in children:
            self.__group.new_child(t, s, c)

        for key, value in settings.items():
            self.__group.set_xml_prop(key, value)