1 | /* |
---|
2 | menu.c |
---|
3 | |
---|
4 | Handles server-supplied menus defined by a table. Read menu.h for |
---|
5 | more information. |
---|
6 | |
---|
7 | Menus are similar to "pull-down" menus, but have some extra features. |
---|
8 | They can contain "normal" menu items, checkboxes, sliders, "movers", |
---|
9 | etc.. |
---|
10 | |
---|
11 | I should probably find a more elegant way of doing this in order |
---|
12 | to handle dynamically-changing menus such as the client list. Tcl/Tk |
---|
13 | has neat ways to do it. Hmm... |
---|
14 | |
---|
15 | */ |
---|
16 | |
---|
17 | #include <stdlib.h> |
---|
18 | #include <stdio.h> |
---|
19 | #include <string.h> |
---|
20 | #include <unistd.h> |
---|
21 | |
---|
22 | #include "parse.h" |
---|
23 | #include "sock.h" |
---|
24 | #include "render.h" |
---|
25 | #include "main.h" |
---|
26 | |
---|
27 | #include "drivers/lcd.h" |
---|
28 | #include "menu.h" |
---|
29 | |
---|
30 | // FIXME: Implement this where it is supposed to be... |
---|
31 | #include <time.h> |
---|
32 | void framedelay() |
---|
33 | { |
---|
34 | sock_poll_clients(); |
---|
35 | parse_all_client_messages(); |
---|
36 | |
---|
37 | usleep(TIME_UNIT); |
---|
38 | } |
---|
39 | |
---|
40 | static void draw_heartbeat() |
---|
41 | { |
---|
42 | static int timer = 0; |
---|
43 | |
---|
44 | if(heartbeat) |
---|
45 | { |
---|
46 | // Set this to pulsate like a real heart beat... |
---|
47 | // (binary is fun... :) |
---|
48 | lcd.icon(!((timer+4)&5), 0); |
---|
49 | lcd.chr(lcd.wid, 1, 0); |
---|
50 | } |
---|
51 | lcd.flush(); |
---|
52 | |
---|
53 | timer++; |
---|
54 | timer &= 0x0f; |
---|
55 | } |
---|
56 | |
---|
57 | static int PAD = 255; |
---|
58 | |
---|
59 | |
---|
60 | |
---|
61 | typedef struct menu_info |
---|
62 | { |
---|
63 | int selected; |
---|
64 | int length; |
---|
65 | } menu_info; |
---|
66 | |
---|
67 | |
---|
68 | |
---|
69 | static int draw_menu(Menu menu, menu_info *info); |
---|
70 | static int fill_menu_info(Menu menu, menu_info *info); |
---|
71 | static int menu_handle_action(menu_item *item); |
---|
72 | |
---|
73 | static int slid_func(menu_item *item); |
---|
74 | |
---|
75 | |
---|
76 | int do_menu(Menu menu) |
---|
77 | { |
---|
78 | menu_info info; |
---|
79 | int key=0; |
---|
80 | int status=MENU_OK; |
---|
81 | int done=0; |
---|
82 | |
---|
83 | int (*func)(); |
---|
84 | int (*readfunc)(int); |
---|
85 | |
---|
86 | |
---|
87 | if(!menu) return MENU_ERROR; |
---|
88 | |
---|
89 | fill_menu_info(menu, &info); |
---|
90 | |
---|
91 | |
---|
92 | while(!done) |
---|
93 | { |
---|
94 | // Keep the cursor off titles... (?) |
---|
95 | while(menu[info.selected].type == TYPE_TITL) |
---|
96 | { |
---|
97 | info.selected++; |
---|
98 | // If the title is the last thing in the menu... |
---|
99 | if(!menu[info.selected].text) info.selected -= 2; |
---|
100 | } |
---|
101 | |
---|
102 | |
---|
103 | draw_menu(menu, &info); |
---|
104 | |
---|
105 | // FIXME: This should use a better keypress interface, which |
---|
106 | // FIXME: handles things according to keybindings... |
---|
107 | |
---|
108 | for(key = lcd.getkey(); key==0; key = lcd.getkey()) |
---|
109 | { |
---|
110 | // sleep for 1/8th second... |
---|
111 | framedelay(); |
---|
112 | // do the heartbeat... |
---|
113 | draw_heartbeat(); |
---|
114 | // Check for client input... |
---|
115 | } |
---|
116 | |
---|
117 | |
---|
118 | // Handle the key according to the keybindings... |
---|
119 | switch(key) |
---|
120 | { |
---|
121 | case 'D': done=1; break; |
---|
122 | case 'B': if(info.selected > 0) info.selected--; |
---|
123 | while(menu[info.selected].type == TYPE_TITL) |
---|
124 | { |
---|
125 | if(info.selected > 0) |
---|
126 | info.selected--; |
---|
127 | else break; |
---|
128 | } |
---|
129 | break; |
---|
130 | case 'C': if(menu[info.selected+1].text) info.selected++; break; |
---|
131 | case 'A': |
---|
132 | switch(menu[info.selected].type) |
---|
133 | { |
---|
134 | case TYPE_MENU: status = do_menu(menu[info.selected].data); |
---|
135 | break; |
---|
136 | case TYPE_FUNC: |
---|
137 | func = menu[info.selected].data; |
---|
138 | if(func) |
---|
139 | status = func(); |
---|
140 | break; |
---|
141 | case TYPE_CHEK: |
---|
142 | readfunc = menu[info.selected].data; |
---|
143 | if(readfunc) |
---|
144 | status = readfunc(MENU_CHECK); |
---|
145 | status &= 0xffff0000; |
---|
146 | break; |
---|
147 | case TYPE_SLID: |
---|
148 | func = menu[info.selected].data; |
---|
149 | if(func) |
---|
150 | status = slid_func(&menu[info.selected]); |
---|
151 | break; |
---|
152 | default: |
---|
153 | break; |
---|
154 | } |
---|
155 | |
---|
156 | switch(status) |
---|
157 | { |
---|
158 | case MENU_OK: |
---|
159 | break; |
---|
160 | case MENU_CLOSE: |
---|
161 | return MENU_OK; |
---|
162 | case MENU_QUIT: |
---|
163 | return MENU_QUIT; |
---|
164 | // case MENU_KILL: |
---|
165 | // return MENU_KILL; |
---|
166 | case MENU_ERROR: |
---|
167 | return MENU_ERROR; |
---|
168 | } |
---|
169 | |
---|
170 | // status = menu_handle_action(&menu[info.selected]); |
---|
171 | // TODO: It should now do special stuff for "mover" widgets, |
---|
172 | // TODO: and handle the return code appropriately. |
---|
173 | break; |
---|
174 | default: break; |
---|
175 | } |
---|
176 | |
---|
177 | } |
---|
178 | |
---|
179 | return status; |
---|
180 | |
---|
181 | } |
---|
182 | |
---|
183 | static int draw_menu(Menu menu, menu_info *info) |
---|
184 | { |
---|
185 | int i; |
---|
186 | int x=1, y=1; |
---|
187 | int top=0, bottom=0; |
---|
188 | |
---|
189 | int (*readfunc)(int); |
---|
190 | |
---|
191 | // these should maybe be removed: |
---|
192 | int wid=lcd.wid, hgt=lcd.hgt; |
---|
193 | |
---|
194 | |
---|
195 | if(!menu) return MENU_ERROR; |
---|
196 | |
---|
197 | lcd.clear(); |
---|
198 | |
---|
199 | |
---|
200 | // Scroll down until the selected item is centered, if possible... |
---|
201 | top = info->selected - (hgt/2); |
---|
202 | if(top<0) top=0; |
---|
203 | bottom = top+hgt; |
---|
204 | if(bottom > info->length) bottom=info->length; |
---|
205 | top = bottom-hgt; |
---|
206 | if(top<0) top=0; |
---|
207 | |
---|
208 | |
---|
209 | |
---|
210 | // Draw all visible items... |
---|
211 | for(i=top; i<bottom; i++, y++) |
---|
212 | { |
---|
213 | if(i == info->selected) lcd.chr(2,y,'>'); |
---|
214 | |
---|
215 | switch(menu[i].type) |
---|
216 | { |
---|
217 | case TYPE_TITL: |
---|
218 | lcd.chr(1,y,PAD); |
---|
219 | lcd.chr(2,y,PAD); |
---|
220 | lcd.string(4,y,menu[i].text); |
---|
221 | for(x=strlen(menu[i].text)+5; x <= wid; x++) |
---|
222 | lcd.chr(x,y,PAD); |
---|
223 | break; |
---|
224 | case TYPE_MENU: |
---|
225 | lcd.string(3,y,menu[i].text); |
---|
226 | lcd.chr(wid,y,'>'); |
---|
227 | break; |
---|
228 | case TYPE_FUNC: |
---|
229 | lcd.string(3,y,menu[i].text); |
---|
230 | break; |
---|
231 | case TYPE_CHEK: |
---|
232 | if(menu[i].data) |
---|
233 | { |
---|
234 | readfunc = menu[i].data; |
---|
235 | if(readfunc(MENU_READ)) |
---|
236 | lcd.chr(wid,y,'Y'); |
---|
237 | else |
---|
238 | lcd.chr(wid,y,'N'); |
---|
239 | } |
---|
240 | lcd.string(3,y,menu[i].text); |
---|
241 | break; |
---|
242 | case TYPE_SLID: |
---|
243 | lcd.string(3,y,menu[i].text); |
---|
244 | break; |
---|
245 | case TYPE_MOVE: |
---|
246 | break; |
---|
247 | default: |
---|
248 | break; |
---|
249 | } |
---|
250 | } |
---|
251 | |
---|
252 | if(top != 0) |
---|
253 | lcd.chr(1,1,'^'); |
---|
254 | if(bottom < info->length) |
---|
255 | lcd.chr(1,hgt,'v'); |
---|
256 | |
---|
257 | |
---|
258 | draw_heartbeat(); |
---|
259 | //lcd.flush(); |
---|
260 | |
---|
261 | return 0; |
---|
262 | } |
---|
263 | |
---|
264 | |
---|
265 | static int fill_menu_info(Menu menu, menu_info *info) |
---|
266 | { |
---|
267 | int i; |
---|
268 | |
---|
269 | info->selected = 0; |
---|
270 | |
---|
271 | // count the entries in the menu |
---|
272 | for(i=0; menu[i].text; i++); |
---|
273 | |
---|
274 | info->length = i; |
---|
275 | |
---|
276 | return 0; |
---|
277 | |
---|
278 | } |
---|
279 | |
---|
280 | static int menu_handle_action(menu_item *item) |
---|
281 | { |
---|
282 | return MENU_OK; |
---|
283 | } |
---|
284 | |
---|
285 | |
---|
286 | static int slid_func(menu_item *item) |
---|
287 | { |
---|
288 | char str[16]; |
---|
289 | int key = 0; |
---|
290 | int value = 0; |
---|
291 | int x, y=1; |
---|
292 | int (*readfunc)(int); |
---|
293 | |
---|
294 | readfunc = item->data; |
---|
295 | |
---|
296 | lcd.init_hbar(); |
---|
297 | |
---|
298 | while(key != 'A' && key != 'D') |
---|
299 | { |
---|
300 | // Draw the title... |
---|
301 | lcd.clear(); |
---|
302 | lcd.chr(1,y,PAD); |
---|
303 | lcd.chr(2,y,PAD); |
---|
304 | lcd.string(4,y,item->text); |
---|
305 | for(x=strlen(item->text)+5; x <= lcd.wid; x++) |
---|
306 | lcd.chr(x,y,PAD); |
---|
307 | |
---|
308 | // Draw the slider now... |
---|
309 | value = readfunc(MENU_READ); |
---|
310 | if(value < 0 || value >= MENU_CLOSE) |
---|
311 | return value; |
---|
312 | sprintf(str, "%i", value); |
---|
313 | if(lcd.hgt >= 4) |
---|
314 | { |
---|
315 | lcd.string(8, 4, str); |
---|
316 | value = (lcd.wid * lcd.cellwid * value / 256); |
---|
317 | lcd.hbar(1, 3, value); |
---|
318 | } |
---|
319 | else |
---|
320 | { |
---|
321 | lcd.string(17, 2, str); |
---|
322 | value = ((lcd.wid-4) * lcd.cellwid * value / 256); |
---|
323 | lcd.hbar(1, 2, value); |
---|
324 | } |
---|
325 | //lcd.flush(); |
---|
326 | |
---|
327 | for(key = lcd.getkey(); key==0; key = lcd.getkey()) |
---|
328 | { |
---|
329 | // do the heartbeat... |
---|
330 | draw_heartbeat(); |
---|
331 | // sleep for 1/8th second... |
---|
332 | framedelay(); |
---|
333 | // Check for client input... |
---|
334 | } |
---|
335 | |
---|
336 | switch(key) |
---|
337 | { |
---|
338 | case 'B': |
---|
339 | value = readfunc(MENU_MINUS); |
---|
340 | break; |
---|
341 | case 'C': |
---|
342 | value = readfunc(MENU_PLUS); |
---|
343 | break; |
---|
344 | } |
---|
345 | |
---|
346 | if(value >= MENU_CLOSE |
---|
347 | || value < 0 |
---|
348 | || key == 'A' |
---|
349 | || key == 'D') |
---|
350 | return value; |
---|
351 | } |
---|
352 | |
---|
353 | return MENU_OK; |
---|
354 | } |
---|
355 | |
---|