~gdesklets-desklet-team/gdesklets/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
from dom.ArrayBehavior import ArrayBehavior
from dom.ControlBehavior import ControlBehavior
from dom.GroupBehavior import GroupBehavior
from dom.ImageBehavior import ImageBehavior
from dom.LabelBehavior import LabelBehavior
from dom.LanguageBehavior import LanguageBehavior
from dom.GaugeBehavior import GaugeBehavior
from dom.EntryBehavior import EntryBehavior
from dom.FrameBehavior import FrameBehavior
from dom.CanvasBehavior import CanvasBehavior
from dom.ScriptBehavior import ScriptBehavior
from dom.PlotterBehavior import PlotterBehavior
from layout.LayoutObject import LayoutObject
from datatypes import Unit
from dom import coreexc
from dom import events
import datatypes


# behavior types
_TYPES = {"array"    : ArrayBehavior,
          "control"  : ControlBehavior,
          "display"  : GroupBehavior,
          "group"    : GroupBehavior,
          "image"    : ImageBehavior,
          "label"    : LabelBehavior,
          "language" : LanguageBehavior,
          "gauge"    : GaugeBehavior, 
          "entry"    : EntryBehavior, 
          "frame"    : FrameBehavior,
          "canvas"   : CanvasBehavior, 
          "script"   : ScriptBehavior, 
          "plotter"  : PlotterBehavior}


class Element(object):
    """
        The Element class is the central class of the core. Every element is of
        this type. The different behavior of different elements is handled by the
        behavior member (see BaseBehavior).
    
        An element consists of a set of typed properties and a set of child
        elements. Furthermore, it provides a factory method 'new_child' for
        spawning new child elements.
    """

    def __init__(self, intermediate, desklet, layout_object = None):

        etype, settings, children = intermediate

        # element type (e.g. group, label, ...)
        self.__etype = etype
        # desklet as DOM representation
        self.__desklet = desklet
        self.__layout_object = layout_object or LayoutObject()
        # some element types have children
        self.__children = []
        
        self.__properties = {}
        self.__proptypes = {}
        self.__propaccessors = {}

        self.__actions = {}

        # intermediate representation of the element and its children
        self.__intermediate = intermediate

        # the event stamp of the last event; this is needed to detect ENTER
        # and LEAVE events
        self.__last_event_stamp = 0

        # initialize the element type's behavior
        behavior = _TYPES[etype]
        behavior.init(self)

        # set callback for the (given) layout object
        self.__layout_object.set_action_callback(self.__on_action)

        # set properties
        [ self.set_property_from_string(key, value) \
          for key, value in settings.items() ]


    def destroy(self):
        """
        Destroys the element and removes all of its child elements.
        """

        map(self.remove_child, self.__children)
            

    def __on_action(self, src, x, y, estamp, ev_name, ev_value):
        """
        Reacts on user interaction.
        src: the source where the action took place (not used)
        x: x coordinate
        y: y coordinate
        estamp: event timestamp
        ev_name: event name (e.g. on-motion, on-button-press, ...)
        ev_value: event value
        """

        self.detect_enter(estamp)
        # get the handler, defined in BaseBehavior (e.g. _on_button, on_motion)
        handler = self.__actions.get(ev_name)
        if (handler):
            handler(self, x, y, ev_name, ev_value)


    def detect_enter(self, estamp):
        """
        Detects enter event.
        """

        if (self.__last_event_stamp == 0):
            # trigger ENTER event
            ev_name = events.ENTER
            handler = self.__actions.get(ev_name)
            if (handler):
                handler(self, Unit.ZERO, Unit.ZERO, ev_name, None)

        self.__last_event_stamp = estamp


    def detect_leave(self, estamp):
        """
        Detects leave event.
        """

        if (self.__last_event_stamp == 0):
            # if the element has not been entered, don't bother checking it or
            # its children
            return

        elif (estamp != self.__last_event_stamp):
            self.__last_event_stamp = 0
            # trigger LEAVE event
            ev_name = events.LEAVE
            handler = self.__actions.get(ev_name)
            if (handler):
                handler(self, Unit.ZERO, Unit.ZERO, ev_name, None)

        [ child.detect_leave(estamp) for child in self.__children ]



    def get_type(self):
        """
        Returns the element's type.
        """

        return self.__etype


    def register_property(self, name, ptype, default,
                          setter = None, getter = None):
        """
        Registers the given property with the given type.
        """

        self.__properties[name] = default
        self.__proptypes[name] = ptype
        self.__propaccessors[name] = (setter, getter)


    def register_event(self, eventname, handler):
        """
        Registers an event and its event handler.
        """
        
        self.register_property(eventname, datatypes.SCRIPT, "")
        self.__actions[eventname] = handler
        

    def introduce(self):
        """
        Introduces a new child, i.e. notifies observers about all the
        properties and children. This is done after creating an element.
        """

        ident = self._getp("id")
        [ self.__desklet.notify_change(ident, key, self.get_property(key)) \
          for key in self.__properties ]

        for child in self.__children:
            c_ident = child._getp("id")
            self.__desklet.notify_create(ident, c_ident, child.get_type())
            child.introduce()


    def _setp(self, key, value):
        """
        Sets a property.
        key: the attribute's name of an element (e.g. x, y, width, height)
        value: the attribute's value of an element
        """

        self.__properties[key] = value
        # get the ident to notify the desklet
        ident = self._getp("id")
        self.__desklet.notify_change(ident, key, value)
        

    def _getp(self, key):
        """
        Returns an attribute's value for a given key.
        """

        return self.__properties[key]


    def _get_layout_object(self):
        """
        Returns the layout object to get some information of the LayoutEngine.
        """

        return self.__layout_object


    def _get_element(self, ident):
        """
        Returns the element with the given ID.
        """

        return self.__desklet._get_element(ident)


    def get_intermediate(self):
        """
        Returns a list of the element's intermediate representations.
        """

        return self.__intermediate


    def new_child(self, data, index_path):
        """
        Creates a child element out of the given intermediate representation
        and handles all the necessary registering stuff.
        """

        etype, settings, children = data

        # create layout object
        layout = self.__layout_object.new_child()

        # create child
        child = Element(data, self.__desklet, layout)
        child._setp("index-path", index_path)

        ident = child._getp("id")
        if (index_path):
            # index paths are written in the # notation
            ident += "#" + "#".join(( `s` for s in index_path ))
        child._setp("id", ident)

        # append to children
        self.__children.append(child)

        # register
        self.__desklet.register_element(ident, child)

        # notify
        self.__desklet.notify_create(self._getp("id"), ident, etype)
        child.introduce()

        # The <array> element is handled specially because it does
        # create its children later dynamically at runtime.
        [ child.new_child(childs, index_path) for childs in children \
          if (etype not in ("array",)) ]


    def remove_child(self, child):
        """
        Removes the given child element and handles all the necessary
        unregistering stuff.
        """

        # remove layout object
        child_layout = child._get_layout_object()
        layout = self._get_layout_object()
        layout.remove_child(child_layout)

        # unregister child
        ident = child._getp("id")
        self.__desklet.unregister_element(ident)
        
        # remove child
        self.__children.remove(child)

        child.destroy()

        # notify
        self.__desklet.notify_remove(self._getp("id"), ident)


    def get_children(self):
        """
        Returns a list of an element's children.
        """

        return self.__children


    def set_property(self, key, value):
        """
        Sets the given property.
        Raises NoSuchPropertyError if the property does not exist.
        """

        try:
            setter, getter = self.__propaccessors[key]
        except:
            raise coreexc.NoSuchPropertyError(key)
        
        if (setter):
            setter(self, key, value)
        else:
            self._setp(key, value)            


    def get_property(self, key):
        """
        Returns the value of the given property.
        Raises NoSuchPropertyError if the property does not exist.
        """

        try:
            setter, getter = self.__propaccessors[key]
        except:
            raise coreexc.NoSuchPropertyError(key)
        
        if (getter):
            return getter(self, key)
        else:
            return self._getp(key)


    def set_property_from_string(self, key, strvalue):
        """
        Sets the given property from a string-encoded value.
        Raises NoSuchPropertyError if the property does not exist.
        """

        try:
            typename = self.__proptypes[key]
        except:
            raise coreexc.NoSuchPropertyError(key)

        value = datatypes.str_to_type(strvalue, typename)
        self.set_property(key, value)


    def send_event(self, x, y, estamp, ev_name, ev_value):
        """
        Sends an event to the LayoutEngine.
        """

        self.__layout_object.send_action(Unit.Unit(x, Unit.UNIT_PX),
                                         Unit.Unit(y, Unit.UNIT_PX),
                                         estamp, ev_name, ev_value)


    def do_callback(self, event_name, event_obj):
        """
        Runs the given callback.
        """

        try:
            code = self._getp(event_name)
        except KeyError:
            return
        
        language = "python"
        self.set_property("event", event_obj)
        self.__desklet.notify_script(self._getp("id"), code, language)
        self.set_property("event", None)