Pvote 1.0b: Navigator.py

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 

[OP_ADD, OP_REMOVE, OP_APPEND, OP_POP, OP_CLEAR] = range(5)
[SG_CLIP, SG_OPTION, SG_LIST_SELS, SG_COUNT_SELS, SG_MAX_SELS] = range(5)
[PR_GROUP_EMPTY, PR_GROUP_FULL, PR_OPTION_SELECTED] = range(3)

class Navigator:
    def __init__(self, model, audio, video, printer):
        self.model = model
        [self.audio, self.video, self.printer] = [audio, video, printer]
        self.selections = [[] for group in model.groups]
        self.page_i = None
        self.goto(0, 0)

    def goto(self, page_i, state_i):
        if page_i != None and self.page_i != len(self.model.pages) - 1:
            if page_i == len(self.model.pages) - 1:
                self.printer.write(self.selections)
            [self.page_i, self.page] = [page_i, self.model.pages[page_i]]
            [self.state_i, self.state] = [state_i, self.page.states[state_i]]
            self.play(self.state.segments)
        self.update()

    def update(self):
        self.video.goto(self.page_i)
        self.video.paste(self.state.sprite_i, self.state_i)

        slot_i = len(self.page.states) 
        for area in self.page.option_areas:
            unselected = area.option_i not in self.selections[area.group_i]
            group = self.model.groups[area.group_i]
            option = group.options[area.option_i]
            self.video.paste(option.sprite_i + unselected, slot_i)
            slot_i = slot_i + 1

        for area in self.page.counter_areas:
            count = len(self.selections[area.group_i])
            self.video.paste(area.sprite_i + count, slot_i)
            slot_i = slot_i + 1

        for area in self.page.review_areas:
            slot_i = self.review(area.group_i, slot_i, area.cursor_sprite_i)

    def review(self, group_i, slot_i, cursor_sprite_i):
        group = self.model.groups[group_i]
        selections = self.selections[group_i]
        for i in range(group.max_sels):
            if i < len(selections):
                option = group.options[selections[i]]
                self.video.paste(option.sprite_i, slot_i)
                if option.writein_group_i != None:
                    self.review(option.writein_group_i, slot_i + 1, None)
            if i == len(selections) and cursor_sprite_i != None:
                self.video.paste(cursor_sprite_i, slot_i)
            slot_i = slot_i + 1 + group.max_chars
        return slot_i

    def press(self, key):
        for binding in self.state.bindings + self.page.bindings:
            if key == binding.key and self.test(binding.conditions):
                return self.invoke(binding)

    def touch(self, target_i):
        for binding in self.state.bindings + self.page.bindings:
            if target_i == binding.target_i and self.test(binding.conditions):
                return self.invoke(binding)

    def test(self, conditions):
        for cond in conditions:
            [group_i, option_i] = self.get_option(cond)
            if cond.predicate == PR_GROUP_EMPTY:
                result = len(self.selections[group_i]) == 0
            if cond.predicate == PR_GROUP_FULL:
                max = self.model.groups[group_i].max_sels
                result = len(self.selections[group_i]) == max
            if cond.predicate == PR_OPTION_SELECTED:
                result = option_i in self.selections[group_i]
            if cond.invert == result:
                return 0
        return 1

    def invoke(self, binding):
        for step in binding.steps:
            self.execute(step)
        self.audio.stop()
        self.play(binding.segments)
        self.goto(binding.next_page_i, binding.next_state_i)

    def execute(self, step):
        [group_i, option_i] = self.get_option(step)
        group = self.model.groups[group_i]
        selections = self.selections[group_i]
        selected = option_i in selections

        if step.op == OP_ADD and not selected or step.op == OP_APPEND:
            if len(selections) < group.max_sels:
                selections.append(option_i)
        if step.op == OP_REMOVE and selected:
            selections.remove(option_i)

        if step.op == OP_POP and len(selections) > 0:
            selections.pop()
        if step.op == OP_CLEAR:
            self.selections[group_i] = []

    def timeout(self):
        self.play(self.state.timeout_segments)
        self.goto(self.state.timeout_page_i, self.state.timeout_state_i)

    def play(self, segments):
        for segment in segments:
            if self.test(segment.conditions):
                if segment.type == SG_CLIP:
                    self.audio.play(segment.clip_i)
                else:
                    [group_i, option_i] = self.get_option(segment)
                    group = self.model.groups[group_i]
                    selections = self.selections[group_i]

                if segment.type == SG_OPTION:
                    self.play_option(group.options[option_i], segment.clip_i)
                if segment.type == SG_LIST_SELS:
                    for option_i in selections:
                        self.play_option(group.options[option_i], segment.clip_i)
                if segment.type == SG_COUNT_SELS:
                    self.audio.play(segment.clip_i + len(selections))
                if segment.type == SG_MAX_SELS:
                    self.audio.play(segment.clip_i + group.max_sels)

    def play_option(self, option, offset):
        self.audio.play(option.clip_i + offset)
        if option.writein_group_i != None:
            writein_group = self.model.groups[option.writein_group_i]
            for option_i in self.selections[option.writein_group_i]:
                self.audio.play(writein_group.options[option_i].clip_i)

    def get_option(self, object):
        if object.group_i == None:
            area = self.page.option_areas[object.option_i]
            return [area.group_i, area.option_i]
        return [object.group_i, object.option_i]