Пример #1
0
    def __init__(self, canvas, swapfile, mode_get_cb=None):
        Manager.__init__(self)

        self._canvas = canvas
        self._swapfile = swapfile
        # FIXME: this is a temporary hack to let deeper objects know the
        # state we're at. Find a better place for it
        self._mode_get_cb = mode_get_cb

        self._group = ""
        self.__edje = None

        self.part = EditablePart(self)
        self.animation = EditableAnimation(self)
        self.signal = EditableProgram(self)

        self._size = None
        self._size_init()
        self._modification_init()
        self._parts_init()
        self._programs_init()
        self._animations_init()
        self._signals_init()

        self._error_msg = None
Пример #2
0
    def program_get(self, program):
        if not program in self.programs:
            return None

        prg = EditableProgram(self)
        prg.name = program

        return prg
Пример #3
0
    def program_get(self, program):
        if not program in self.programs:
            return None

        prg = EditableProgram(self)
        prg.name = program

        return prg
Пример #4
0
    def __init__(self, editable):
        Manager.__init__(self)

        self._edit_grp = editable

        self._name = None
        self._parts_init()
        self._states_init()

        self.program = EditableProgram(self._edit_grp)

        self._edit_grp.callback_add("group.changed", self._group_changed_cb)
        self._edit_grp.callback_add("animation.removed",
                                    self._animation_removed_cb)
Пример #5
0
    def __init__(self, canvas, swapfile, mode_get_cb=None):
        Manager.__init__(self)

        self._canvas = canvas
        self._swapfile = swapfile
        # FIXME: this is a temporary hack to let deeper objects know the
        # state we're at. Find a better place for it
        self._mode_get_cb = mode_get_cb

        self._group = ""
        self.__edje = None

        self.part = EditablePart(self)
        self.animation = EditableAnimation(self)
        self.signal = EditableProgram(self)

        self._size = None
        self._size_init()
        self._modification_init()
        self._parts_init()
        self._programs_init()
        self._animations_init()
        self._signals_init()

        self._error_msg = None
Пример #6
0
    def __init__(self, editable):
        Manager.__init__(self)

        self._edit_grp = editable

        self._name = None
        self._parts_init()
        self._states_init()

        self.program = EditableProgram(self._edit_grp)

        self._edit_grp.callback_add("group.changed", self._group_changed_cb)
        self._edit_grp.callback_add(
            "animation.removed", self._animation_removed_cb)
Пример #7
0
class Editable(Manager):
    default_display_size = (500, 500)
    pref_size_key = "pref_size"

    def __init__(self, canvas, swapfile, mode_get_cb=None):
        Manager.__init__(self)

        self._canvas = canvas
        self._swapfile = swapfile
        # FIXME: this is a temporary hack to let deeper objects know the
        # state we're at. Find a better place for it
        self._mode_get_cb = mode_get_cb

        self._group = ""
        self.__edje = None

        self.part = EditablePart(self)
        self.animation = EditableAnimation(self)
        self.signal = EditableProgram(self)

        self._size = None
        self._size_init()
        self._modification_init()
        self._parts_init()
        self._programs_init()
        self._animations_init()
        self._signals_init()

        self._error_msg = None

    def _mode_get(self):
        return self._mode_get_cb()

    mode = property(fget=_mode_get)

    # Edje
    def _edje_get(self):
        return self.__edje

    edje = property(_edje_get)

    # Filename
    def _filename_get(self):
        return self._swapfile.file

    filename = property(_filename_get)

    def _workfile_get(self):
        return self._swapfile.workfile

    workfile = property(_workfile_get)

    # Group Name
    def _group_get(self):
        return self._group

    def _group_set(self, value):
        if not value:
            value = ""
            self.event_emit("group.changed", value)
            self.__edje and self.__edje.delete()
            self.__edje = None
            self._edje_group = None
        else:
            old_group = self.__edje
            self.__edje = EdjeEdit(self._canvas,
                                   file=self._swapfile.workfile,
                                   group=value)
            self._edje_group = self.__edje.current_group
            self.event_emit("group.changed", value)
            old_group and old_group.delete()

        self._group = value

    group = property(_group_get, _group_set)

    def group_add(self, grp_name):
        if not self.__edje:
            self.__edje = EdjeEdit(self._canvas,
                                   file=self._swapfile.workfile,
                                   group=edje.file_collection_list(
                                       self._swapfile.workfile)[0])
            self.event_emit("group.changed", self.group)

        return self.__edje.group_add(grp_name)

    def group_exists(self, grp_name):
        if not self.__edje:
            self.__edje = EdjeEdit(self._canvas,
                                   file=self._swapfile.workfile,
                                   group=edje.file_collection_list(
                                       self._swapfile.workfile)[0])
            self.event_emit("group.changed", self.group)

        return self.__edje.group_exist(grp_name)

    def group_del(self, grp_name):
        dummy_grp = None
        all_grps = edje.file_collection_list(self._swapfile.workfile)
        for g in all_grps:
            if g != grp_name:
                dummy_grp = g
                break

        if not dummy_grp:
            self.error = "Can not delete the only group in file"
            return False

        if not self.__edje or self._group == grp_name:
            self.group = ""

            dummy_edje = EdjeEdit(self._canvas,
                                  file=self._swapfile.workfile,
                                  group=dummy_grp)
            r = dummy_edje.group_del(grp_name)
            self.error = dummy_edje.error
            dummy_edje.delete()

            return r

        r = self.__edje.group_del(grp_name)
        self.error = self.__edje.error
        return r

    def _error_set(self, msg=None):
        self._error_msg = msg

    def _error_get(self):
        return self._error_msg

    error = property(_error_get, _error_set)

    def group_rename(self, name):
        if not self._group:
            return False
        if not name:
            raise InvalidGroupNameError()
        if re.match(r'[\s].*|.*[\s]$', name):
            raise InvalidGroupNameError()
        if self.__edje.group_exist(name):
            return False

        self._edje_group.rename(name)
        self._group = name
        return True

    # GROUP Min/Max

    def _size_init(self):
        self._group_size_update(self, None)
        self.callback_add("group.changed", self._group_size_update)

    def _group_size_update(self, emissor, grp_name):
        self._size = None

        if not grp_name:
            self._max = None
            self._min = None
            return

        self._max = (self._edje_group.w_max, self._edje_group.h_max)
        self.event_emit("group.max.changed", self._max)
        self._min = (self._edje_group.w_min, self._edje_group.h_min)
        self.event_emit("group.min.changed", self._min)

        data = self.__edje.group_data_get(self.pref_size_key)

        if not data:
            w, h = self.default_display_size
        else:
            w, h = data.split("x")
            w = int(w)
            h = int(h)

        self.group_size = (w, h)

    def _verify_max_w(self, w, group_w, group_h, min_w, group):
        if w <= 0:
            return 0
        if w and w < min_w:
            w = min_w
        if w < group_w:
            group.resize(w, group_h)

        return w

    def _verify_max_h(self, h, group_w, group_h, min_h, group):
        if h <= 0:
            return 0
        if h and h < min_h:
            h = min_h
        if h < group_h:
            group.resize(group_w, h)

        return h

    def _max_get(self):
        return self._max

    def _max_set(self, value):
        if self._max == value:
            return

        w, h = value
        group_w, group_h = self.group_size
        min_w, min_h = self._min

        w = self._verify_max_w(w, group_w, group_h, min_w, self.__edje)
        h = self._verify_max_h(h, group_w, group_h, min_h, self.__edje)
        self._edje_group.w_max = w
        self._edje_group.h_max = h
        self._max = (w, h)
        self.event_emit("group.max.changed", self._max)

    group_max = property(_max_get, _max_set)

    def _verify_min_w(self, w, group_w, group_h, max_w, group):
        if w < 0:
            w = 0
        if max_w and w > max_w:
            w = max_w
        if w > group_w:
            group.resize(w, group_h)

        return w

    def _verify_min_h(self, h, group_w, group_h, max_h, group):
        if h < 0:
            h = 0
        if max_h and h > max_h:
            h = max_h
        if h > group_h:
            group.resize(group_w, h)

        return h

    def _min_get(self):
        return self._min

    def _min_set(self, value):
        if self._min == value:
            return

        w, h = value
        group_w, group_h = self.group_size
        max_w, max_h = self._max

        w = self._verify_min_w(w, group_w, group_h, max_w, self.__edje)
        h = self._verify_min_h(h, group_w, group_h, max_h, self.__edje)
        self._edje_group.w_min = w
        self._edje_group.h_min = h
        self._min = (w, h)
        self.event_emit("group.min.changed", self._min)

    group_min = property(_min_get, _min_set)

    def _size_get(self):
        return self._size

    def _size_set(self, value):
        if self._size == value:
            return

        w, h = value
        max_w, max_h = self._max
        min_w, min_h = self._min

        if min_w and w < min_w:
            w = min_w
        elif max_w and w > max_w:
            w = max_w

        if min_h and h < min_h:
            h = min_h
        elif max_h and h > max_h:
            h = max_h

        self._size = (w, h)
        self.__edje.size = (w, h)

        value = self.__edje.group_data_get(self.pref_size_key)
        if not value:
            self.__edje.group_data_add(self.pref_size_key, "0x0")
        self.__edje.group_data_set(self.pref_size_key, "%dx%d" % self._size)

        self.event_emit("group.size.changed", self._size)

    group_size = property(_size_get, _size_set)

    #Images
    def images_get(self):
        return self.__edje.images

    def image_id_get(self, name):
        return self.__edje.image_id_get(name)

    def image_add(self, img):
        if os.path.basename(img) not in self.images_get():
            return self.__edje.image_add(img)

    #Fonts
    def fonts_get(self):
        return self.__edje.fonts

    def font_add(self, fnt):
        if os.path.basename(fnt) not in self.fonts_get():
            self.__edje.font_add(fnt)

    # Modifications
    def _modification_init(self):
        self._modification_clear_cb(self, None)
        self.callback_add("saved", self._modification_clear_cb)
        self.callback_add("group.changed", self._modification_clear_cb)

    def _modification_clear_cb(self, emissor, data):
        self._modified = False

    def close(self):
        self._swapfile.close()

    def _empty_animations_clear(self):
        anim_del_lst = []
        for anim in self.animations:
            prog = self.program_get("@%[email protected]" % anim)
            if not prog.targets:
                anim_del_lst.append(anim)

        for a in anim_del_lst:
            self.animation_del(a)

    # Save

    def save(self, success_cb=None, fail_cb=None):
        def save_ok(sf):
            if success_cb:
                success_cb(sf)
            self.event_emit("saved")

        def save_error(err):
            if fail_cb:
                fail_cb(err)
            self.event_emit("save.error")

        self._empty_animations_clear()

        if self.__edje.save_all():
            self._swapfile.save(save_ok, save_error)
        else:
            self.event_emit("save.error")

    def save_as(self, path, success_cb=None, fail_cb=None, mode=None):
        def save_as_ok(sf):
            if success_cb:
                success_cb(sf)
            self.event_emit("filename.changed", sf.file)
            self.event_emit("saved")

        def save_as_error(err):
            if fail_cb:
                fail_cb(err)
            self.event_emit("save.error")

        self._empty_animations_clear()

        if self.__edje.save_all():
            self._swapfile.save(save_as_ok,
                                save_as_error,
                                filepath=path,
                                mode=mode)
        else:
            self.event_emit("save.error")

    # Parts
    def _parts_get(self):
        return self.__edje.parts

    parts = property(_parts_get)

    def _parts_init(self):
        self.callback_add("group.changed", self._parts_reload_cb)
        self.callback_add("part.added", self._parts_reload_cb)
        self.callback_add("part.removed", self._parts_reload_cb)
        self.part.callback_add("name.changed", self._parts_reload_cb)

    def _parts_reload_cb(self, emissor, data):
        if data:
            self.event_emit("parts.changed", self.parts)
        else:
            self.event_emit("parts.changed", [])

    # TODO: externals API may change in near future
    # besides being totally annoying this use (part_add + external_add, when
    # when type is external), there is no external_del (not even indirectly
    # called
    def external_add(self, module):
        return self.__edje.external_add(module)

    def _part_add(self, name, edje_type, source):
        if edje_type == edje.EDJE_PART_TYPE_EXTERNAL:
            external = edje.external_type_get(source)
            if external:
                self.__edje.external_add(external.module)
        return self.__edje.part_add(name, edje_type, source)

    def part_add(self, name, edje_type, source="", init=None):
        if not self._part_add(name, edje_type, source):
            return False

        part = self._part_init(name)

        if init:
            init(part)

        self._modified = True
        self.event_emit("part.added", name)
        return True

    def part_add_bydata(self, part_data, relatives=None, name=None):
        source = part_data["source"]
        if source is None:
            source = ''

        if not name:
            name = part_data.name

        if not self._part_add(name, part_data.type, source):
            return False

        part = self.__edje.part_get(name)
        part_data.apply_to(part)

        if relatives:
            for p, st in relatives.iteritems():
                part = self.part_get(p)
                for (st_name, st_value), rels in st.iteritems():
                    state = part.state_get(st_name, st_value)
                    if rels[0]:
                        state.rel1_to_x_set(name)
                    if rels[1]:
                        state.rel1_to_y_set(name)
                    if rels[2]:
                        state.rel2_to_x_set(name)
                    if rels[3]:
                        state.rel2_to_y_set(name)

        self.event_emit("part.added", name)

        return True

    def _part_init(self, name):
        part = self.__edje.part_get(name)
        edje_type = part.type
        state = part.state_get(*part.state_selected_get())

        w, h = self.__edje.size

        state.rel1_to = (None, None)
        state.rel1_relative = (0.0, 0.0)
        state.rel1_offset = (w / 4, h / 4)

        state.rel2_to = (None, None)
        state.rel2_relative = (0.0, 0.0)
        state.rel2_offset = (w * 3 / 4, h * 3 / 4)

        if edje_type == edje.EDJE_PART_TYPE_RECTANGLE:
            part.mouse_events = False

        elif edje_type == edje.EDJE_PART_TYPE_TEXT:
            part.mouse_events = False
            state.color = (0, 0, 0, 255)
            state.text = "YOUR TEXT HERE"
            state.font = "Sans"
            state.text_size = 16

        return part

    def part_get(self, part_name):
        return self.__edje.part_get(part_name)

    def part_object_get(self, part_name):
        return self.__edje.part_object_get(part_name)

    def part_del(self, name):
        if self.__edje.part_del(name):
            self._modified = True
            self.event_emit("part.removed", name)
            return True
        return False

    # Programs
    def _programs_get(self):
        if not self.__edje:
            return []
        return self.__edje.programs

    programs = property(_programs_get)

    def _programs_init(self):
        self.callback_add("group.changed", self._programs_reload_cb)
        self.signal.callback_add("program.name.changed",
                                 self._programs_reload_cb)

    def _programs_reload_cb(self, emissor, data):
        if data:
            self.event_emit("programs.changed", self.programs)
        else:
            self.event_emit("programs.changed", [])

    def program_add(self, name):
        if not self.__edje.program_add(name):
            return False

        self._modified = True
        self.event_emit("program.added", name)

        return True

    def program_del(self, name):
        if not self.__edje.program_del(name):
            return False

        self._modified = True
        self.event_emit("program.removed", name)

        return True

    def program_get(self, program):
        if not program in self.programs:
            return None

        prg = EditableProgram(self)
        prg.name = program

        return prg

    # Animations
    def _animation_get(self):
        return self._animations

    animations = property(_animation_get)

    def _animations_init(self):
        self._animations = None
        self.callback_add("programs.changed", self._animations_reload_cb)

    def _animations_reload_cb(self, emissor, data):
        self._animations = \
            map(lambda x: x[1:x.rindex("@")],
                filter(lambda x: x.startswith("@") and x.endswith("@end"),
                       self.programs))
        self.event_emit("animations.changed", self.animations)

    def animation_add(self, name, parts=None):
        if name in self._animations:
            return False

        self._modified = True

        # END
        endname = "@%s@end" % name
        self.program_add(endname)
        prog = self.program_get(endname)
        prog.signal_emit_action_set(("animation,end", name))

        # START
        startname = "@%[email protected]" % name
        self.program_add(startname)
        prog = self.program_get(startname)
        prog.state_set_action_set(startname)
        prog.signal = "animation,play"
        prog.source = name
        prog.after_add("@%s@end" % name)

        prevstatename = "default"
        statename = startname
        if not parts:
            parts = self.parts
        for p in parts:
            prog.target_add(p)
            part = self.__edje.part_get(p)
            if not part.state_add(startname):
                return False
            state = part.state_get(statename)
            state.copy_from(prevstatename)

        # STOP
        stopname = "@%s@stop" % name
        self.program_add(stopname)
        prog = self.program_get(stopname)
        prog.action = edje.EDJE_ACTION_TYPE_ACTION_STOP
        prog.signal = "animation,stop"
        prog.source = name
        prog.target_add(startname)
        prog.target_add(endname)

        self._animations.append(name)
        self.event_emit("animation.added", name)
        return True

    def animation_del(self, name):
        stopname = "@%s@stop" % name
        stopprog = self.program_get(stopname)

        if not stopprog:
            return False
        for p in stopprog.targets:
            prog = self.program_get(p)
            for pp in prog.targets:
                part = self.__edje.part_get(pp)
                if part:
                    part.state_selected_set("default")
                    if not part.state_del(p):
                        return False
            self.program_del(p)
        self.program_del(stopname)
        self.event_emit("animation.removed", name)
        self._animations.pop(self._animations.index(name))
        self._modified = True

        return True

    # Signals
    def _signal_get(self):
        return self._signals

    signals = property(_signal_get)

    def _signals_init(self):
        self._signals = None
        self.callback_add("programs.changed", self._signals_reload_cb)
        self.callback_add("signal.added", self._signals_reload_cb)
        self.callback_add("signal.removed", self._signals_reload_cb)

    def _signals_reload_cb(self, emissor, data):
        self._signals = [e for e in self.programs if not e.startswith("@")]
        self.event_emit("signals.changed", self._signals)

    def _signal_add(self, name):
        if not name or name.startswith("@"):
            return False

        if not self.program_add(name):
            return False

        return True

    def signal_add(self, name, action_type):
        if not self._signal_add(name):
            return False

        program = self.__edje.program_get(name)
        program.action_set(action_type)

        self.event_emit("signal.added", name)

        return True

    def signal_add_bydata(self, sig_data):
        name = sig_data.name
        if not self._signal_add(name):
            return False

        program = self.__edje.program_get(name)
        sig_data.apply_to(program)

        self.event_emit("signal.added", name)

        return True

    def signal_del(self, name):
        if not name in self._signals:
            return False

        if not self.program_del(name):
            return False

        self.event_emit("signal.removed", name)

        return True

    def relative_parts_get(self, part_name):
        relatives = dict()
        for p in self.parts:
            saved_states = dict()
            part = self.__edje.part_get(p)
            for st in part.states:
                st_name, value = st.split()
                st_value = float(value)
                state = part.state_get(st_name, st_value)
                if part_name in (state.rel1_to_get()[0],
                                 state.rel1_to_get()[1],
                                 state.rel2_to_get()[0],
                                 state.rel2_to_get()[1]):
                    saved_states[st_name, st_value] = (
                        part_name == state.rel1_to_get()[0],
                        part_name == state.rel1_to_get()[1],
                        part_name == state.rel2_to_get()[0],
                        part_name == state.rel2_to_get()[1])

            if saved_states:
                relatives[p] = saved_states
        return relatives

    def _groups_get(self):
        return edje.file_collection_list(self._swapfile.workfile)

    groups = property(_groups_get)
Пример #8
0
class EditableAnimation(Manager, object):
    def __init__(self, editable):
        Manager.__init__(self)

        self._edit_grp = editable

        self._name = None
        self._parts_init()
        self._states_init()

        self.program = EditableProgram(self._edit_grp)

        self._edit_grp.callback_add("group.changed", self._group_changed_cb)
        self._edit_grp.callback_add(
            "animation.removed", self._animation_removed_cb)

    def _group_changed_cb(self, emissor, data):
        self.name = None

    def _animation_removed_cb(self, emissor, data):
        if self._name == data:
            self.name = None
        for p in self._edit_grp.parts:
            part = self._edit_grp.part_get(p)
            part.state_selected_set("default", 0.00)

    # Name
    def _name_set(self, value):
        if not self._edit_grp.edje:
            return

        if not value:
            self._name = None
            self.event_emit("animation.unselected")
        elif self._name != value:
            if value in self._edit_grp.animations:
                self._name = value
                self.event_emit("animation.changed", self._name)
            else:
                self._name = None
                for p in self._edit_grp.parts:
                    part = self._edit_grp.part_get(p)
                    #FIXME: is this really desired?
                    part.state_selected_set("default")
                self.event_emit("animation.unselected")

    def _name_get(self):
        return self._name

    name = property(_name_get, _name_set)

    def name_set(self, name):
        if not name:
            return

        # stop program
        stopname = "@%s@stop" % self._name
        stopprog = self._edit_grp.edje.program_get(stopname)
        if not stopprog or not stopprog.rename("@%s@stop" % name):
            return
        stopprog.source_set(name)

        # others programs
        for p in stopprog.targets_get():
            prog = self._edit_grp.edje.program_get(p)
            time = re_anim_program.match(p).group(2)
            p2 = "@%s@%s" % (name, time)
            if time == "end":
                prog.state2_set(name)
            else:
                for pp in prog.targets_get():
                    part = self._edit_grp.part_get(pp)
                    if not part:
                        #prog.target_del(pp) TODO: binding
                        continue
                    state = part.state_get(p)
                    if not state:
                        continue
                    state.name_set(p2)

                if time == "0.00":
                    prog.source_set(name)

                prog.state_set(p2)

            prog.rename(p2)
        # Hack to force reload animation list
        # self._name = name
        state = self.state
        self._edit_grp._programs_reload_cb(self, True)
        self.name = name
        self.state = state
        return

    # Play
    def play(self):
        if self.name:
            self._edit_grp.edje.signal_callback_add(
                "animation,end", self._name, self._play_end)
            self._edit_grp.edje.program_get(self.program.afters[0]).run()

    def _play_end(self, obj, emission, source):
        self._edit_grp.edje.signal_callback_del(
            "animation,end", self._name, self._play_end)
        self._playback_state_update()
        self.event_emit("animation.play.end")

    def stop(self):
        self._edit_grp.edje.signal_emit("animation,stop", self._name)
        self._playback_state_update()

    def _playback_state_update(self):
        if not self.parts:
            return

        # got to fetch the exact state we're at
        edje.message_signal_process()

        p = self.parts.keys()[0]
        st = self._edit_grp.part_get(p).state_selected_get()[0]
        time = re_anim_program_time.match(st).group(2)
        if time:
            self.state = float(time)

    # Parts
    def _parts_init(self):
        self.parts = {}
        self._edit_grp.part.callback_add("name.changed", self._part_rename_cb)
        self.callback_add("animation.changed", self._parts_reload_cb)
        self.callback_add("animation.unselected", self._parts_reload_cb)

    def _parts_reload_cb(self, emissor, data):
        self.parts = {}
        if not data:
            return

        prog = self._edit_grp.program_get("@%[email protected]" % self._name)
        for t in prog.targets:
            self.parts[t] = True

    def _part_rename_cb(self, emissor, data):
        old_name, new_name = data
        p = self.parts.get(old_name)
        if not p:
            return
        self.parts[new_name] = True
        del self.parts[old_name]

    def part_add(self, part):
        if part in self.parts:
            return

        progname = "@%[email protected]" % self._name
        prog = self._edit_grp.program_get(progname)
        prog.target_add(part)
        p = self._edit_grp.part_get(part)
        p.state_copy("default", 0.0, progname, 0.0)
        self.parts[part] = True

        # Re-set current state to make sure everything is consistent
        curr = self._current
        self._current = None
        self._state_set(curr)

        self.event_emit("part.added", part)

    def part_remove(self, part):
        if part not in self.parts:
            return

        p = self._edit_grp.part_get(part)
        p.state_selected_set("default", 0.00)
        if p.name == self._edit_grp.part.name:
            self._edit_grp.part.state.name = None
        for t in self.timestops:
            progname = "@%s@%.2f" % (self._name, t)
            st = progname
            prog = self._edit_grp.program_get(progname)
            prog.target_del(part)
            if p.state_exist(st):
                if not p.state_del(st):
                        return

        del self.parts[part]
        self.event_emit("part.removed", part)

    def part_belongs(self, part):
        return part in self.parts

    # States
    def _states_init(self):
        self.timestops = []
        self.callback_add("animation.changed", self._states_reload_cb)
        self.callback_add("animation.unselected", self._states_reload_cb)
        self.callback_add("animation.changed", self._state_reload_cb)
        self.callback_add("animation.unselected", self._state_reload_cb)

    def _states_reload_cb(self, emissor, data):
        self.timestops = []

        if not data:
            return

        p = self._edit_grp.program_get("@%[email protected]" % self._name)
        t = p.name[-4:]
        while t != "@end":
            self.timestops.append(float(t))
            p = self._edit_grp.program_get(p.afters[0])
            t = p.name[-4:]

        self.event_emit("states.changed", self.timestops)

    def state_add(self, time):
        if not self._name:
            return
        if time < 0.0:
            return

        # Search
        idx = 0
        for t in self.timestops:
            if time == t:
                return idx
            if t > time:
                break
            idx += 1

        # Defines
        prev = self.timestops[idx - 1]
        prevname = "@%s@%.2f" % (self._name, prev)
        name = "@%s@%.2f" % (self._name, time)

        # States
        prevstatename = prevname
        statename = name

        # Create
        self._edit_grp.program_add(name)
        prog = self._edit_grp.program_get(name)
        prog.state_set_action_set(name)
        prog.transition = edje.EDJE_TWEEN_MODE_LINEAR
        prog.transition_time = time - prev
        for p in self.parts.iterkeys():
            prog.target_add(p)
            part = self._edit_grp.part_get(p)
            if not part.state_add(name):
                return
            state = part.state_get(statename)
            state.copy_from(prevstatename)

        # Link Prev
        prevprog = self._edit_grp.program_get(prevname)
        nextname = prevprog.afters[0]
        prog.after_add(nextname)
        prevprog.afters_clear()
        prevprog.after_add(name)

        # Link Next
        next = nextname[-4:]
        if not next == "@end":
            next = float(next)
            nextprog = self._edit_grp.program_get(nextname)
            nextprog.transition_time = next - time

        # Stop
        stopname = "@%s@stop" % self._name
        self._edit_grp.program_add(stopname)
        prog = self._edit_grp.program_get(stopname)
        prog.action = edje.EDJE_ACTION_TYPE_ACTION_STOP
        prog.signal = "animation,stop"
        prog.target_add(name)

        self.timestops.insert(idx, time)
        self.event_emit("state.added", time)

        return idx

    def state_del(self, time):
        if time == 0.0 or time == "end":
            return

        # Search
        idx = self.timestops.index(time)

        progname = "@%s@%.2f" % (self._name, time)
        prog = self._edit_grp.program_get(progname)

        # Unlink
        prev = self.timestops[idx - 1]
        prevname = "@%s@%.2f" % (self._name, prev)
        prevprog = self._edit_grp.program_get(prevname)
        nextname = prog.afters[0]
        prevprog.afters_clear()
        prevprog.after_add(nextname)

        # Fix Next
        next = nextname[-4:]
        if not next == "@end":
            next = float(next)
            nextprog = self._edit_grp.program_get(nextname)
            nextprog.transition_time = next - prev

        # Delete states from parts
        statename = progname
        for p in self.parts.iterkeys():
            part = self._edit_grp.part_get(p)
            if not part.state_del(statename, 0.0):
                return

        self.timestops.pop(idx)
        self.event_emit("state.removed", time)

    def _part_state_create(self, part):
        statename = self.program.name
        orig_state = "default"
        time_idx = 0
        while time_idx <= self._current_idx:
            time = self.timestops[time_idx]
            name = "@%s@%.2f" % (self._name, time)
            if part.state_exist(name):
                orig_state = name
            else:
                part.state_copy(orig_state, 0.0, statename, 0.0)
            time_idx += 1

        self.program.target_add(part.name)

    def _state_set(self, time):
        if not self._name or time == self._current:
            return

        self._current_idx = self.timestops.index(time)
        self._current = time
        self.program.name = "@%s@%.2f" % (self._name, time)
        statename = self.program.name
        for p in self.parts.iterkeys():
            part = self._edit_grp.part_get(p)
            if part.state_exist(statename):
                part.state_selected_set(statename)
            else:
                self._part_state_create(part)
                part.state_selected_set(statename)

        if self._edit_grp.part.name in self.parts:
            self._edit_grp.part.state.name = statename

        self.event_emit("frame.changed", self._edit_grp.part.state.name)

    def _state_get(self):
        return self._current

    state = property(_state_get, _state_set)

    def state_next(self):
        if not self._name:
            return None
        if self._current_idx == len(self.timestops) - 1:
            return None
        return self.timestops[self._current_idx + 1]

    def state_next_goto(self):
        state = self.state_next()
        if state is not None:
            self.state = state

    def state_prev(self):
        if not self._name:
            return None
        if self._current_idx == 0:
            return None
        return self.timestops[self._current_idx - 1]

    def state_prev_goto(self):
        state = self.state_prev()
        if state is not None:
            self.state = state

    def _state_reload_cb(self, emissor, data):
        self._current = None
        if data and self.timestops:
            self.state = 0.0

    # Info
    def _length_get(self):
        if self.timestops:
            return self.timestops[-1]
        return 0.0

    length = property(_length_get)
Пример #9
0
class Editable(Manager):
    default_display_size = (500, 500)
    pref_size_key = "pref_size"

    def __init__(self, canvas, swapfile, mode_get_cb=None):
        Manager.__init__(self)

        self._canvas = canvas
        self._swapfile = swapfile
        # FIXME: this is a temporary hack to let deeper objects know the
        # state we're at. Find a better place for it
        self._mode_get_cb = mode_get_cb

        self._group = ""
        self.__edje = None

        self.part = EditablePart(self)
        self.animation = EditableAnimation(self)
        self.signal = EditableProgram(self)

        self._size = None
        self._size_init()
        self._modification_init()
        self._parts_init()
        self._programs_init()
        self._animations_init()
        self._signals_init()

        self._error_msg = None

    def _mode_get(self):
        return self._mode_get_cb()

    mode = property(fget=_mode_get)

    # Edje
    def _edje_get(self):
        return self.__edje

    edje = property(_edje_get)

    # Filename
    def _filename_get(self):
        return self._swapfile.file

    filename = property(_filename_get)

    def _workfile_get(self):
        return self._swapfile.workfile

    workfile = property(_workfile_get)

    # Group Name
    def _group_get(self):
        return self._group

    def _group_set(self, value):
        if not value:
            value = ""
            self.event_emit("group.changed", value)
            self.__edje and self.__edje.delete()
            self.__edje = None
            self._edje_group = None
        else:
            old_group = self.__edje
            self.__edje = EdjeEdit(
                self._canvas, file=self._swapfile.workfile, group=value)
            self._edje_group = self.__edje.current_group
            self.event_emit("group.changed", value)
            old_group and old_group.delete()

        self._group = value

    group = property(_group_get, _group_set)

    def group_add(self, grp_name):
        if not self.__edje:
            self.__edje = EdjeEdit(
                self._canvas, file=self._swapfile.workfile,
                group=edje.file_collection_list(self._swapfile.workfile)[0])
            self.event_emit("group.changed", self.group)

        return self.__edje.group_add(grp_name)

    def group_exists(self, grp_name):
        if not self.__edje:
            self.__edje = EdjeEdit(
                self._canvas, file=self._swapfile.workfile,
                group=edje.file_collection_list(self._swapfile.workfile)[0])
            self.event_emit("group.changed", self.group)

        return self.__edje.group_exist(grp_name)

    def group_del(self, grp_name):
        dummy_grp = None
        all_grps = edje.file_collection_list(self._swapfile.workfile)
        for g in all_grps:
            if g != grp_name:
                dummy_grp = g
                break

        if not dummy_grp:
            self.error = "Can not delete the only group in file"
            return False

        if not self.__edje or self._group == grp_name:
            self.group = ""

            dummy_edje = EdjeEdit(
                self._canvas, file=self._swapfile.workfile, group=dummy_grp)
            r = dummy_edje.group_del(grp_name)
            self.error = dummy_edje.error
            dummy_edje.delete()

            return r

        r = self.__edje.group_del(grp_name)
        self.error = self.__edje.error
        return r

    def _error_set(self, msg=None):
            self._error_msg = msg

    def _error_get(self):
        return self._error_msg

    error = property(_error_get, _error_set)

    def group_rename(self, name):
        if not self._group:
            return False
        if not name:
            raise InvalidGroupNameError()
        if re.match(r'[\s].*|.*[\s]$', name):
            raise InvalidGroupNameError()
        if self.__edje.group_exist(name):
            return False

        self._edje_group.rename(name)
        self._group = name
        return True

    # GROUP Min/Max

    def _size_init(self):
        self._group_size_update(self, None)
        self.callback_add("group.changed", self._group_size_update)

    def _group_size_update(self, emissor, grp_name):
        self._size = None

        if not grp_name:
            self._max = None
            self._min = None
            return

        self._max = (self._edje_group.w_max, self._edje_group.h_max)
        self.event_emit("group.max.changed", self._max)
        self._min = (self._edje_group.w_min, self._edje_group.h_min)
        self.event_emit("group.min.changed", self._min)

        data = self.__edje.group_data_get(self.pref_size_key)

        if not data:
            w, h = self.default_display_size
        else:
            w, h = data.split("x")
            w = int(w)
            h = int(h)

        self.group_size = (w, h)

    def _verify_max_w(self, w, group_w, group_h, min_w, group):
        if w <= 0:
            return 0
        if w and w < min_w:
            w = min_w
        if w < group_w:
            group.resize(w, group_h)

        return w

    def _verify_max_h(self, h, group_w, group_h, min_h, group):
        if h <= 0:
            return 0
        if h and h < min_h:
            h = min_h
        if h < group_h:
            group.resize(group_w, h)

        return h

    def _max_get(self):
        return self._max

    def _max_set(self, value):
        if self._max == value:
            return

        w, h = value
        group_w, group_h = self.group_size
        min_w, min_h = self._min

        w = self._verify_max_w(w, group_w, group_h, min_w, self.__edje)
        h = self._verify_max_h(h, group_w, group_h, min_h, self.__edje)
        self._edje_group.w_max = w
        self._edje_group.h_max = h
        self._max = (w, h)
        self.event_emit("group.max.changed", self._max)

    group_max = property(_max_get, _max_set)

    def _verify_min_w(self, w, group_w, group_h, max_w, group):
        if w < 0:
            w = 0
        if max_w and w > max_w:
            w = max_w
        if w > group_w:
            group.resize(w, group_h)

        return w

    def _verify_min_h(self, h, group_w, group_h, max_h, group):
        if h < 0:
            h = 0
        if max_h and h > max_h:
            h = max_h
        if h > group_h:
            group.resize(group_w, h)

        return h

    def _min_get(self):
        return self._min

    def _min_set(self, value):
        if self._min == value:
            return

        w, h = value
        group_w, group_h = self.group_size
        max_w, max_h = self._max

        w = self._verify_min_w(w, group_w, group_h, max_w, self.__edje)
        h = self._verify_min_h(h, group_w, group_h, max_h, self.__edje)
        self._edje_group.w_min = w
        self._edje_group.h_min = h
        self._min = (w, h)
        self.event_emit("group.min.changed", self._min)

    group_min = property(_min_get, _min_set)

    def _size_get(self):
        return self._size

    def _size_set(self, value):
        if self._size == value:
            return

        w, h = value
        max_w, max_h = self._max
        min_w, min_h = self._min

        if min_w and w < min_w:
            w = min_w
        elif max_w and w > max_w:
            w = max_w

        if min_h and h < min_h:
            h = min_h
        elif max_h and h > max_h:
            h = max_h

        self._size = (w, h)
        self.__edje.size = (w, h)

        value = self.__edje.group_data_get(self.pref_size_key)
        if not value:
            self.__edje.group_data_add(self.pref_size_key, "0x0")
        self.__edje.group_data_set(self.pref_size_key, "%dx%d" % self._size)

        self.event_emit("group.size.changed", self._size)

    group_size = property(_size_get, _size_set)

    #Images
    def images_get(self):
        return self.__edje.images

    def image_id_get(self, name):
        return self.__edje.image_id_get(name)

    def image_add(self, img):
        if os.path.basename(img) not in self.images_get():
            return self.__edje.image_add(img)

    #Fonts
    def fonts_get(self):
        return self.__edje.fonts

    def font_add(self, fnt):
        if os.path.basename(fnt) not in self.fonts_get():
            self.__edje.font_add(fnt)

    # Modifications
    def _modification_init(self):
        self._modification_clear_cb(self, None)
        self.callback_add("saved", self._modification_clear_cb)
        self.callback_add("group.changed", self._modification_clear_cb)

    def _modification_clear_cb(self, emissor, data):
        self._modified = False

    def close(self):
        self._swapfile.close()

    def _empty_animations_clear(self):
        anim_del_lst = []
        for anim in self.animations:
            prog = self.program_get("@%[email protected]" % anim)
            if not prog.targets:
                anim_del_lst.append(anim)

        for a in anim_del_lst:
            self.animation_del(a)

    # Save

    def save(self, success_cb=None, fail_cb=None):

        def save_ok(sf):
            if success_cb:
                success_cb(sf)
            self.event_emit("saved")

        def save_error(err):
            if fail_cb:
                fail_cb(err)
            self.event_emit("save.error")

        self._empty_animations_clear()

        if self.__edje.save_all():
            self._swapfile.save(save_ok, save_error)
        else:
            self.event_emit("save.error")

    def save_as(self, path, success_cb=None, fail_cb=None,  mode=None):

        def save_as_ok(sf):
            if success_cb:
                success_cb(sf)
            self.event_emit("filename.changed", sf.file)
            self.event_emit("saved")

        def save_as_error(err):
            if fail_cb:
                fail_cb(err)
            self.event_emit("save.error")

        self._empty_animations_clear()

        if self.__edje.save_all():
            self._swapfile.save(save_as_ok, save_as_error, filepath=path,
                                mode=mode)
        else:
            self.event_emit("save.error")

    # Parts
    def _parts_get(self):
        return self.__edje.parts

    parts = property(_parts_get)

    def _parts_init(self):
        self.callback_add("group.changed", self._parts_reload_cb)
        self.callback_add("part.added", self._parts_reload_cb)
        self.callback_add("part.removed", self._parts_reload_cb)
        self.part.callback_add("name.changed", self._parts_reload_cb)

    def _parts_reload_cb(self, emissor, data):
        if data:
            self.event_emit("parts.changed", self.parts)
        else:
            self.event_emit("parts.changed", [])

    # TODO: externals API may change in near future
    # besides being totally annoying this use (part_add + external_add, when
    # when type is external), there is no external_del (not even indirectly
    # called
    def external_add(self, module):
        return self.__edje.external_add(module)

    def _part_add(self, name, edje_type, source):
        if edje_type == edje.EDJE_PART_TYPE_EXTERNAL:
            external = edje.external_type_get(source)
            if external:
                self.__edje.external_add(external.module)
        return self.__edje.part_add(name, edje_type, source)

    def part_add(self, name, edje_type, source="", init=None):
        if not self._part_add(name, edje_type, source):
            return False

        part = self._part_init(name)

        if init:
            init(part)

        self._modified = True
        self.event_emit("part.added", name)
        return True

    def part_add_bydata(self, part_data, relatives=None, name=None):
        source = part_data["source"]
        if source is None:
            source = ''

        if not name:
            name = part_data.name

        if not self._part_add(name, part_data.type, source):
            return False

        part = self.__edje.part_get(name)
        part_data.apply_to(part)

        if relatives:
            for p, st in relatives.iteritems():
                part = self.part_get(p)
                for (st_name, st_value), rels in st.iteritems():
                    state = part.state_get(st_name, st_value)
                    if rels[0]:
                        state.rel1_to_x_set(name)
                    if rels[1]:
                        state.rel1_to_y_set(name)
                    if rels[2]:
                        state.rel2_to_x_set(name)
                    if rels[3]:
                        state.rel2_to_y_set(name)

        self.event_emit("part.added", name)

        return True

    def _part_init(self, name):
        part = self.__edje.part_get(name)
        edje_type = part.type
        state = part.state_get(*part.state_selected_get())

        w, h = self.__edje.size

        state.rel1_to = (None, None)
        state.rel1_relative = (0.0, 0.0)
        state.rel1_offset = (w / 4, h / 4)

        state.rel2_to = (None, None)
        state.rel2_relative = (0.0, 0.0)
        state.rel2_offset = (w * 3 / 4, h * 3 / 4)

        if edje_type == edje.EDJE_PART_TYPE_RECTANGLE:
            part.mouse_events = False

        elif edje_type == edje.EDJE_PART_TYPE_TEXT:
            part.mouse_events = False
            state.color = (0, 0, 0, 255)
            state.text = "YOUR TEXT HERE"
            state.font = "Sans"
            state.text_size = 16

        return part

    def part_get(self, part_name):
        return self.__edje.part_get(part_name)

    def part_object_get(self, part_name):
        return self.__edje.part_object_get(part_name)

    def part_del(self, name):
        if self.__edje.part_del(name):
            self._modified = True
            self.event_emit("part.removed", name)
            return True
        return False

    # Programs
    def _programs_get(self):
        if not self.__edje:
            return []
        return self.__edje.programs

    programs = property(_programs_get)

    def _programs_init(self):
        self.callback_add("group.changed", self._programs_reload_cb)
        self.signal.callback_add(
            "program.name.changed", self._programs_reload_cb)

    def _programs_reload_cb(self, emissor, data):
        if data:
            self.event_emit("programs.changed", self.programs)
        else:
            self.event_emit("programs.changed", [])

    def program_add(self, name):
        if not self.__edje.program_add(name):
            return False

        self._modified = True
        self.event_emit("program.added", name)

        return True

    def program_del(self, name):
        if not self.__edje.program_del(name):
            return False

        self._modified = True
        self.event_emit("program.removed", name)

        return True

    def program_get(self, program):
        if not program in self.programs:
            return None

        prg = EditableProgram(self)
        prg.name = program

        return prg

    # Animations
    def _animation_get(self):
        return self._animations

    animations = property(_animation_get)

    def _animations_init(self):
        self._animations = None
        self.callback_add("programs.changed", self._animations_reload_cb)

    def _animations_reload_cb(self, emissor, data):
        self._animations = \
            map(lambda x: x[1:x.rindex("@")],
                filter(lambda x: x.startswith("@") and x.endswith("@end"),
                       self.programs))
        self.event_emit("animations.changed", self.animations)

    def animation_add(self, name, parts=None):
        if name in self._animations:
            return False

        self._modified = True

        # END
        endname = "@%s@end" % name
        self.program_add(endname)
        prog = self.program_get(endname)
        prog.signal_emit_action_set(("animation,end", name))

        # START
        startname = "@%[email protected]" % name
        self.program_add(startname)
        prog = self.program_get(startname)
        prog.state_set_action_set(startname)
        prog.signal = "animation,play"
        prog.source = name
        prog.after_add("@%s@end" % name)

        prevstatename = "default"
        statename = startname
        if not parts:
            parts = self.parts
        for p in parts:
            prog.target_add(p)
            part = self.__edje.part_get(p)
            if not part.state_add(startname):
                return False
            state = part.state_get(statename)
            state.copy_from(prevstatename)

        # STOP
        stopname = "@%s@stop" % name
        self.program_add(stopname)
        prog = self.program_get(stopname)
        prog.action = edje.EDJE_ACTION_TYPE_ACTION_STOP
        prog.signal = "animation,stop"
        prog.source = name
        prog.target_add(startname)
        prog.target_add(endname)

        self._animations.append(name)
        self.event_emit("animation.added", name)
        return True

    def animation_del(self, name):
        stopname = "@%s@stop" % name
        stopprog = self.program_get(stopname)

        if not stopprog:
            return False
        for p in stopprog.targets:
            prog = self.program_get(p)
            for pp in prog.targets:
                part = self.__edje.part_get(pp)
                if part:
                    part.state_selected_set("default")
                    if not part.state_del(p):
                        return False
            self.program_del(p)
        self.program_del(stopname)
        self.event_emit("animation.removed", name)
        self._animations.pop(self._animations.index(name))
        self._modified = True

        return True

    # Signals
    def _signal_get(self):
        return self._signals

    signals = property(_signal_get)

    def _signals_init(self):
        self._signals = None
        self.callback_add("programs.changed", self._signals_reload_cb)
        self.callback_add("signal.added", self._signals_reload_cb)
        self.callback_add("signal.removed", self._signals_reload_cb)

    def _signals_reload_cb(self, emissor, data):
        self._signals = [e for e in self.programs if not e.startswith("@")]
        self.event_emit("signals.changed", self._signals)

    def _signal_add(self, name):
        if not name or name.startswith("@"):
            return False

        if not self.program_add(name):
            return False

        return True

    def signal_add(self, name, action_type):
        if not self._signal_add(name):
            return False

        program = self.__edje.program_get(name)
        program.action_set(action_type)

        self.event_emit("signal.added", name)

        return True

    def signal_add_bydata(self, sig_data):
        name = sig_data.name
        if not self._signal_add(name):
            return False

        program = self.__edje.program_get(name)
        sig_data.apply_to(program)

        self.event_emit("signal.added", name)

        return True

    def signal_del(self, name):
        if not name in self._signals:
            return False

        if not self.program_del(name):
            return False

        self.event_emit("signal.removed", name)

        return True

    def relative_parts_get(self, part_name):
        relatives = dict()
        for p in self.parts:
            saved_states = dict()
            part = self.__edje.part_get(p)
            for st in part.states:
                st_name, value = st.split()
                st_value = float(value)
                state = part.state_get(st_name, st_value)
                if part_name in (state.rel1_to_get()[0],
                                 state.rel1_to_get()[1],
                                 state.rel2_to_get()[0],
                                 state.rel2_to_get()[1]):
                    saved_states[st_name, st_value] = (
                        part_name == state.rel1_to_get()[0],
                        part_name == state.rel1_to_get()[1],
                        part_name == state.rel2_to_get()[0],
                        part_name == state.rel2_to_get()[1])

            if saved_states:
                relatives[p] = saved_states
        return relatives

    def _groups_get(self):
        return edje.file_collection_list(self._swapfile.workfile)

    groups = property(_groups_get)
Пример #10
0
class EditableAnimation(Manager, object):
    def __init__(self, editable):
        Manager.__init__(self)

        self._edit_grp = editable

        self._name = None
        self._parts_init()
        self._states_init()

        self.program = EditableProgram(self._edit_grp)

        self._edit_grp.callback_add("group.changed", self._group_changed_cb)
        self._edit_grp.callback_add("animation.removed",
                                    self._animation_removed_cb)

    def _group_changed_cb(self, emissor, data):
        self.name = None

    def _animation_removed_cb(self, emissor, data):
        if self._name == data:
            self.name = None
        for p in self._edit_grp.parts:
            part = self._edit_grp.part_get(p)
            part.state_selected_set("default", 0.00)

    # Name
    def _name_set(self, value):
        if not self._edit_grp.edje:
            return

        if not value:
            self._name = None
            self.event_emit("animation.unselected")
        elif self._name != value:
            if value in self._edit_grp.animations:
                self._name = value
                self.event_emit("animation.changed", self._name)
            else:
                self._name = None
                for p in self._edit_grp.parts:
                    part = self._edit_grp.part_get(p)
                    #FIXME: is this really desired?
                    part.state_selected_set("default")
                self.event_emit("animation.unselected")

    def _name_get(self):
        return self._name

    name = property(_name_get, _name_set)

    def name_set(self, name):
        if not name:
            return

        # stop program
        stopname = "@%s@stop" % self._name
        stopprog = self._edit_grp.edje.program_get(stopname)
        if not stopprog or not stopprog.rename("@%s@stop" % name):
            return
        stopprog.source_set(name)

        # others programs
        for p in stopprog.targets_get():
            prog = self._edit_grp.edje.program_get(p)
            time = re_anim_program.match(p).group(2)
            p2 = "@%s@%s" % (name, time)
            if time == "end":
                prog.state2_set(name)
            else:
                for pp in prog.targets_get():
                    part = self._edit_grp.part_get(pp)
                    if not part:
                        #prog.target_del(pp) TODO: binding
                        continue
                    state = part.state_get(p)
                    if not state:
                        continue
                    state.name_set(p2)

                if time == "0.00":
                    prog.source_set(name)

                prog.state_set(p2)

            prog.rename(p2)
        # Hack to force reload animation list
        # self._name = name
        state = self.state
        self._edit_grp._programs_reload_cb(self, True)
        self.name = name
        self.state = state
        return

    # Play
    def play(self):
        if self.name:
            self._edit_grp.edje.signal_callback_add("animation,end",
                                                    self._name, self._play_end)
            self._edit_grp.edje.program_get(self.program.afters[0]).run()

    def _play_end(self, obj, emission, source):
        self._edit_grp.edje.signal_callback_del("animation,end", self._name,
                                                self._play_end)
        self._playback_state_update()
        self.event_emit("animation.play.end")

    def stop(self):
        self._edit_grp.edje.signal_emit("animation,stop", self._name)
        self._playback_state_update()

    def _playback_state_update(self):
        if not self.parts:
            return

        # got to fetch the exact state we're at
        edje.message_signal_process()

        p = self.parts.keys()[0]
        st = self._edit_grp.part_get(p).state_selected_get()[0]
        time = re_anim_program_time.match(st).group(2)
        if time:
            self.state = float(time)

    # Parts
    def _parts_init(self):
        self.parts = {}
        self._edit_grp.part.callback_add("name.changed", self._part_rename_cb)
        self.callback_add("animation.changed", self._parts_reload_cb)
        self.callback_add("animation.unselected", self._parts_reload_cb)

    def _parts_reload_cb(self, emissor, data):
        self.parts = {}
        if not data:
            return

        prog = self._edit_grp.program_get("@%[email protected]" % self._name)
        for t in prog.targets:
            self.parts[t] = True

    def _part_rename_cb(self, emissor, data):
        old_name, new_name = data
        p = self.parts.get(old_name)
        if not p:
            return
        self.parts[new_name] = True
        del self.parts[old_name]

    def part_add(self, part):
        if part in self.parts:
            return

        progname = "@%[email protected]" % self._name
        prog = self._edit_grp.program_get(progname)
        prog.target_add(part)
        p = self._edit_grp.part_get(part)
        p.state_copy("default", 0.0, progname, 0.0)
        self.parts[part] = True

        # Re-set current state to make sure everything is consistent
        curr = self._current
        self._current = None
        self._state_set(curr)

        self.event_emit("part.added", part)

    def part_remove(self, part):
        if part not in self.parts:
            return

        p = self._edit_grp.part_get(part)
        p.state_selected_set("default", 0.00)
        if p.name == self._edit_grp.part.name:
            self._edit_grp.part.state.name = None
        for t in self.timestops:
            progname = "@%s@%.2f" % (self._name, t)
            st = progname
            prog = self._edit_grp.program_get(progname)
            prog.target_del(part)
            if p.state_exist(st):
                if not p.state_del(st):
                    return

        del self.parts[part]
        self.event_emit("part.removed", part)

    def part_belongs(self, part):
        return part in self.parts

    # States
    def _states_init(self):
        self.timestops = []
        self.callback_add("animation.changed", self._states_reload_cb)
        self.callback_add("animation.unselected", self._states_reload_cb)
        self.callback_add("animation.changed", self._state_reload_cb)
        self.callback_add("animation.unselected", self._state_reload_cb)

    def _states_reload_cb(self, emissor, data):
        self.timestops = []

        if not data:
            return

        p = self._edit_grp.program_get("@%[email protected]" % self._name)
        t = p.name[-4:]
        while t != "@end":
            self.timestops.append(float(t))
            p = self._edit_grp.program_get(p.afters[0])
            t = p.name[-4:]

        self.event_emit("states.changed", self.timestops)

    def state_add(self, time):
        if not self._name:
            return
        if time < 0.0:
            return

        # Search
        idx = 0
        for t in self.timestops:
            if time == t:
                return idx
            if t > time:
                break
            idx += 1

        # Defines
        prev = self.timestops[idx - 1]
        prevname = "@%s@%.2f" % (self._name, prev)
        name = "@%s@%.2f" % (self._name, time)

        # States
        prevstatename = prevname
        statename = name

        # Create
        self._edit_grp.program_add(name)
        prog = self._edit_grp.program_get(name)
        prog.state_set_action_set(name)
        prog.transition = edje.EDJE_TWEEN_MODE_LINEAR
        prog.transition_time = time - prev
        for p in self.parts.iterkeys():
            prog.target_add(p)
            part = self._edit_grp.part_get(p)
            if not part.state_add(name):
                return
            state = part.state_get(statename)
            state.copy_from(prevstatename)

        # Link Prev
        prevprog = self._edit_grp.program_get(prevname)
        nextname = prevprog.afters[0]
        prog.after_add(nextname)
        prevprog.afters_clear()
        prevprog.after_add(name)

        # Link Next
        next = nextname[-4:]
        if not next == "@end":
            next = float(next)
            nextprog = self._edit_grp.program_get(nextname)
            nextprog.transition_time = next - time

        # Stop
        stopname = "@%s@stop" % self._name
        self._edit_grp.program_add(stopname)
        prog = self._edit_grp.program_get(stopname)
        prog.action = edje.EDJE_ACTION_TYPE_ACTION_STOP
        prog.signal = "animation,stop"
        prog.target_add(name)

        self.timestops.insert(idx, time)
        self.event_emit("state.added", time)

        return idx

    def state_del(self, time):
        if time == 0.0 or time == "end":
            return

        # Search
        idx = self.timestops.index(time)

        progname = "@%s@%.2f" % (self._name, time)
        prog = self._edit_grp.program_get(progname)

        # Unlink
        prev = self.timestops[idx - 1]
        prevname = "@%s@%.2f" % (self._name, prev)
        prevprog = self._edit_grp.program_get(prevname)
        nextname = prog.afters[0]
        prevprog.afters_clear()
        prevprog.after_add(nextname)

        # Fix Next
        next = nextname[-4:]
        if not next == "@end":
            next = float(next)
            nextprog = self._edit_grp.program_get(nextname)
            nextprog.transition_time = next - prev

        # Delete states from parts
        statename = progname
        for p in self.parts.iterkeys():
            part = self._edit_grp.part_get(p)
            if not part.state_del(statename, 0.0):
                return

        self.timestops.pop(idx)
        self.event_emit("state.removed", time)

    def _part_state_create(self, part):
        statename = self.program.name
        orig_state = "default"
        time_idx = 0
        while time_idx <= self._current_idx:
            time = self.timestops[time_idx]
            name = "@%s@%.2f" % (self._name, time)
            if part.state_exist(name):
                orig_state = name
            else:
                part.state_copy(orig_state, 0.0, statename, 0.0)
            time_idx += 1

        self.program.target_add(part.name)

    def _state_set(self, time):
        if not self._name or time == self._current:
            return

        self._current_idx = self.timestops.index(time)
        self._current = time
        self.program.name = "@%s@%.2f" % (self._name, time)
        statename = self.program.name
        for p in self.parts.iterkeys():
            part = self._edit_grp.part_get(p)
            if part.state_exist(statename):
                part.state_selected_set(statename)
            else:
                self._part_state_create(part)
                part.state_selected_set(statename)

        if self._edit_grp.part.name in self.parts:
            self._edit_grp.part.state.name = statename

        self.event_emit("frame.changed", self._edit_grp.part.state.name)

    def _state_get(self):
        return self._current

    state = property(_state_get, _state_set)

    def state_next(self):
        if not self._name:
            return None
        if self._current_idx == len(self.timestops) - 1:
            return None
        return self.timestops[self._current_idx + 1]

    def state_next_goto(self):
        state = self.state_next()
        if state is not None:
            self.state = state

    def state_prev(self):
        if not self._name:
            return None
        if self._current_idx == 0:
            return None
        return self.timestops[self._current_idx - 1]

    def state_prev_goto(self):
        state = self.state_prev()
        if state is not None:
            self.state = state

    def _state_reload_cb(self, emissor, data):
        self._current = None
        if data and self.timestops:
            self.state = 0.0

    # Info
    def _length_get(self):
        if self.timestops:
            return self.timestops[-1]
        return 0.0

    length = property(_length_get)