diff options
Diffstat (limited to 'apps/gui/mask_select.c')
-rw-r--r-- | apps/gui/mask_select.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/apps/gui/mask_select.c b/apps/gui/mask_select.c new file mode 100644 index 0000000000..e22ba7dd03 --- /dev/null +++ b/apps/gui/mask_select.c | |||
@@ -0,0 +1,287 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * Copyright (C) 2016 William Wilgus | ||
9 | * Derivative of folder_select.c by: | ||
10 | * Copyright (C) 2012 Jonathan Gordon | ||
11 | * Copyright (C) 2012 Thomas Martitz | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | /* TODO add language defines */ | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include "lang.h" | ||
28 | #include "language.h" | ||
29 | #include "list.h" | ||
30 | #include "plugin.h" | ||
31 | #include "mask_select.h" | ||
32 | #include "talk.h" | ||
33 | |||
34 | /* | ||
35 | * Order for changing child states: | ||
36 | * 1) expand folder | ||
37 | * 2) collapse and select | ||
38 | * 3) deselect (skip to 1) | ||
39 | */ | ||
40 | |||
41 | enum child_state { | ||
42 | EXPANDED, | ||
43 | SELECTED, | ||
44 | COLLAPSED, | ||
45 | DESELECTED, | ||
46 | }; | ||
47 | |||
48 | /* Children of main categories */ | ||
49 | struct child { | ||
50 | const char* name; | ||
51 | struct category *category; | ||
52 | enum child_state state; | ||
53 | int key_value; | ||
54 | }; | ||
55 | |||
56 | /* Main Categories in root */ | ||
57 | struct category { | ||
58 | const char *name; | ||
59 | struct child *children; | ||
60 | int children_count; | ||
61 | int depth; | ||
62 | struct category* previous; | ||
63 | int key_value; /*values of all children OR|D*/ | ||
64 | }; | ||
65 | |||
66 | /* empty category for children of root only one level needed */ | ||
67 | static struct category empty = { | ||
68 | .name = "", | ||
69 | .children = NULL, | ||
70 | .children_count = 0, | ||
71 | .depth = 1, | ||
72 | .previous = NULL, | ||
73 | }; | ||
74 | |||
75 | /* Or | all keyvalues that user selected */ | ||
76 | static int calculate_mask_r(struct category *root, int mask) | ||
77 | { | ||
78 | int i = 0; | ||
79 | while (i < root->children_count) | ||
80 | { | ||
81 | struct child *this = &root->children[i]; | ||
82 | if (this->state == SELECTED) | ||
83 | mask |= this->key_value; | ||
84 | |||
85 | else if (this->state == EXPANDED) | ||
86 | mask = calculate_mask_r(this->category, mask); | ||
87 | i++; | ||
88 | } | ||
89 | return mask; | ||
90 | } | ||
91 | |||
92 | static int count_items(struct category *start) | ||
93 | { | ||
94 | int count = 0; | ||
95 | int i; | ||
96 | |||
97 | for (i=0; i<start->children_count; i++) | ||
98 | { | ||
99 | struct child *foo = &start->children[i]; | ||
100 | if (foo->state == EXPANDED) | ||
101 | count += count_items(foo->category); | ||
102 | count++; | ||
103 | } | ||
104 | return count; | ||
105 | } | ||
106 | |||
107 | static struct child* find_index(struct category *start, | ||
108 | int index,struct category **parent) | ||
109 | { | ||
110 | int i = 0; | ||
111 | |||
112 | *parent = NULL; | ||
113 | |||
114 | while (i < start->children_count) | ||
115 | { | ||
116 | struct child *foo = &start->children[i]; | ||
117 | if (i == index) | ||
118 | { | ||
119 | *parent = start; | ||
120 | return foo; | ||
121 | } | ||
122 | i++; | ||
123 | if (foo->state == EXPANDED) | ||
124 | { | ||
125 | struct child *bar = find_index(foo->category, index - i, parent); | ||
126 | if (bar) | ||
127 | { | ||
128 | return bar; | ||
129 | } | ||
130 | index -= count_items(foo->category); | ||
131 | } | ||
132 | } | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | /* simplelist uses this callback to change | ||
137 | the states of the categories/children */ | ||
138 | static int item_action_callback(int action, struct gui_synclist *list) | ||
139 | { | ||
140 | struct category *root = (struct category*)list->data; | ||
141 | struct category *parent; | ||
142 | struct child *this = find_index(root, list->selected_item, &parent); | ||
143 | |||
144 | if (action == ACTION_STD_OK) | ||
145 | { | ||
146 | switch (this->state) | ||
147 | { | ||
148 | case EXPANDED: | ||
149 | this->state = SELECTED; | ||
150 | if (global_settings.talk_menu) | ||
151 | talk_id(LANG_ON, false); | ||
152 | break; | ||
153 | case SELECTED: | ||
154 | this->state = this->category->children_count == 0 ? | ||
155 | DESELECTED : COLLAPSED; | ||
156 | if (global_settings.talk_menu && this->category->children_count == 0) | ||
157 | talk_id(LANG_OFF, false); | ||
158 | break; | ||
159 | case COLLAPSED: | ||
160 | if (this->category == NULL) | ||
161 | this->category = root; | ||
162 | this->state = this->category->children_count == 0 ? | ||
163 | SELECTED : EXPANDED; | ||
164 | if (global_settings.talk_menu && this->category->children_count == 0) | ||
165 | talk_id(LANG_ON, false); | ||
166 | break; | ||
167 | case DESELECTED: | ||
168 | this->state = SELECTED; | ||
169 | if (global_settings.talk_menu) | ||
170 | talk_id(LANG_ON, false); | ||
171 | break; | ||
172 | |||
173 | default: | ||
174 | /* do nothing */ | ||
175 | return action; | ||
176 | } | ||
177 | list->nb_items = count_items(root); | ||
178 | return ACTION_REDRAW; | ||
179 | } | ||
180 | |||
181 | return action; | ||
182 | } | ||
183 | |||
184 | static const char * item_get_name(int selected_item, void * data, | ||
185 | char * buffer, size_t buffer_len) | ||
186 | { | ||
187 | struct category *root = (struct category*)data; | ||
188 | struct category *parent; | ||
189 | struct child *this = find_index(root, selected_item , &parent); | ||
190 | |||
191 | buffer[0] = '\0'; | ||
192 | |||
193 | if (parent->depth >= 0) | ||
194 | for(int i = 0; i <= parent->depth; i++) | ||
195 | strcat(buffer, "\t\0"); | ||
196 | |||
197 | /* state of selection needs icons so if icons are disabled use text*/ | ||
198 | if (!global_settings.show_icons) | ||
199 | { | ||
200 | if (this->state == SELECTED) | ||
201 | strcat(buffer, "+\0"); | ||
202 | else | ||
203 | strcat(buffer," \0"); | ||
204 | } | ||
205 | strlcat(buffer, P2STR((const unsigned char *)this->name), buffer_len); | ||
206 | |||
207 | return buffer; | ||
208 | } | ||
209 | |||
210 | static int item_get_talk(int selected_item, void *data) | ||
211 | { | ||
212 | struct category *root = (struct category*)data; | ||
213 | struct category *parent; | ||
214 | struct child *this = find_index(root, selected_item , &parent); | ||
215 | if (global_settings.talk_menu) | ||
216 | { | ||
217 | long id = P2ID((const unsigned char *)(this->name)); | ||
218 | if(id>=0) | ||
219 | talk_id(id, true); | ||
220 | else | ||
221 | talk_spell(this->name, true); | ||
222 | talk_id(VOICE_PAUSE,true); | ||
223 | if (this->state == SELECTED) | ||
224 | talk_id(LANG_ON, true); | ||
225 | else if (this->state == DESELECTED) | ||
226 | talk_id(LANG_OFF, true); | ||
227 | } | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static enum themable_icons item_get_icon(int selected_item, void * data) | ||
232 | { | ||
233 | struct category *root = (struct category*)data; | ||
234 | struct category *parent; | ||
235 | struct child *this = find_index(root, selected_item, &parent); | ||
236 | |||
237 | switch (this->state) | ||
238 | { | ||
239 | case SELECTED: | ||
240 | return Icon_Submenu; | ||
241 | case COLLAPSED: | ||
242 | return Icon_NOICON; | ||
243 | case EXPANDED: | ||
244 | return Icon_Submenu_Entered; | ||
245 | default: | ||
246 | return Icon_NOICON; | ||
247 | } | ||
248 | return Icon_NOICON; | ||
249 | } | ||
250 | |||
251 | /* supply your original mask,the page header (ie. User do this..), mask items | ||
252 | and count, they will be selected automatically if the mask includes | ||
253 | them. If user selects new items and chooses to save settings | ||
254 | new mask returned otherwise, original is returned | ||
255 | */ | ||
256 | int mask_select(int mask, const unsigned char* headermsg, | ||
257 | struct s_mask_items *mask_items, size_t items) | ||
258 | { | ||
259 | struct simplelist_info info; | ||
260 | |||
261 | struct child action_child[items]; | ||
262 | for (unsigned i = 0; i < items; i++) | ||
263 | { | ||
264 | action_child[i].name = mask_items[i].name; | ||
265 | action_child[i].category = ∅ | ||
266 | action_child[i].key_value = mask_items[i].bit_value; | ||
267 | action_child[i].state = mask_items[i].bit_value & mask ? | ||
268 | SELECTED : DESELECTED; | ||
269 | } | ||
270 | |||
271 | struct category root = { | ||
272 | .name = "", | ||
273 | .children = (struct child*) &action_child, | ||
274 | .children_count = items, | ||
275 | .depth = -1, | ||
276 | .previous = NULL, | ||
277 | }; | ||
278 | |||
279 | simplelist_info_init(&info, P2STR(headermsg), count_items(&root), &root); | ||
280 | info.get_name = item_get_name; | ||
281 | info.action_callback = item_action_callback; | ||
282 | info.get_icon = item_get_icon; | ||
283 | info.get_talk = item_get_talk; | ||
284 | simplelist_show_list(&info); | ||
285 | |||
286 | return calculate_mask_r(&root,0); | ||
287 | } | ||