summaryrefslogtreecommitdiff
path: root/apps/gui/mask_select.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/mask_select.c')
-rw-r--r--apps/gui/mask_select.c287
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
41enum child_state {
42 EXPANDED,
43 SELECTED,
44 COLLAPSED,
45 DESELECTED,
46};
47
48/* Children of main categories */
49struct 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 */
57struct 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 */
67static 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 */
76static 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 }
89return mask;
90}
91
92static 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
107static 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 */
138static 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
184static 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
210static 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
231static 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*/
256int 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 = &empty;
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}