Windows requires setting files to binary mode
[kugel-rb.git] / apps / menu.c
blobe483ad7136a522f0e67eab264f8d3126d67512ee
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Robert E. Hak
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdbool.h>
20 #include <stdlib.h>
22 #include "hwcompat.h"
23 #include "lcd.h"
24 #include "font.h"
25 #include "backlight.h"
26 #include "menu.h"
27 #include "button.h"
28 #include "kernel.h"
29 #include "debug.h"
30 #include "usb.h"
31 #include "panic.h"
32 #include "settings.h"
33 #include "status.h"
34 #include "screens.h"
35 #include "talk.h"
36 #include "lang.h"
37 #include "misc.h"
39 #ifdef HAVE_LCD_BITMAP
40 #include "icons.h"
41 #include "widgets.h"
42 #endif
44 struct menu {
45 int top;
46 int cursor;
47 struct menu_item* items;
48 int itemcount;
49 int (*callback)(int, int);
50 #if defined(HAVE_LCD_BITMAP) && (CONFIG_KEYPAD == RECORDER_PAD)
51 bool use_buttonbar; /* true if a buttonbar is defined */
52 const char *buttonbar[3];
53 #endif
56 #define MAX_MENUS 5
58 #ifdef HAVE_LCD_BITMAP
60 /* pixel margins */
61 #define MARGIN_X (global_settings.scrollbar && \
62 menu_lines < menus[m].itemcount ? SCROLLBAR_WIDTH : 0) +\
63 CURSOR_WIDTH
64 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
66 /* position the entry-list starts at */
67 #define LINE_X 0
68 #define LINE_Y (global_settings.statusbar ? 1 : 0)
70 #define CURSOR_X (global_settings.scrollbar && \
71 menu_lines < menus[m].itemcount ? 1 : 0)
72 #define CURSOR_Y 0 /* the cursor is not positioned in regard to
73 the margins, so this is the amount of lines
74 we add to the cursor Y position to position
75 it on a line */
76 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
78 #define SCROLLBAR_X 0
79 #define SCROLLBAR_Y lcd_getymargin()
80 #define SCROLLBAR_WIDTH 6
82 #else /* HAVE_LCD_BITMAP */
84 #define LINE_X 1 /* X position the entry-list starts at */
86 #define MENU_LINES 2
88 #define CURSOR_X 0
89 #define CURSOR_Y 0 /* not really used for players */
91 #endif /* HAVE_LCD_BITMAP */
93 #define CURSOR_CHAR 0x92
95 static struct menu menus[MAX_MENUS];
96 static bool inuse[MAX_MENUS] = { false };
98 /* count in letter positions, NOT pixels */
99 void put_cursorxy(int x, int y, bool on)
101 #ifdef HAVE_LCD_BITMAP
102 int fh, fw;
103 int xpos, ypos;
105 /* check here instead of at every call (ugly, but cheap) */
106 if (global_settings.invert_cursor)
107 return;
109 lcd_getstringsize("A", &fw, &fh);
110 xpos = x*6;
111 ypos = y*fh + lcd_getymargin();
112 if ( fh > 8 )
113 ypos += (fh - 8) / 2;
114 #endif
116 /* place the cursor */
117 if(on) {
118 #ifdef HAVE_LCD_BITMAP
119 lcd_bitmap ( bitmap_icons_6x8[Cursor],
120 xpos, ypos, 4, 8, true);
121 #else
122 lcd_putc(x, y, CURSOR_CHAR);
123 #endif
125 else {
126 #if defined(HAVE_LCD_BITMAP)
127 /* I use xy here since it needs to disregard the margins */
128 lcd_clearrect (xpos, ypos, 4, 8);
129 #else
130 lcd_putc(x, y, ' ');
131 #endif
135 void menu_draw(int m)
137 int i = 0;
138 #ifdef HAVE_LCD_BITMAP
139 int fw, fh;
140 int menu_lines;
141 int height = LCD_HEIGHT;
143 lcd_setfont(FONT_UI);
144 lcd_getstringsize("A", &fw, &fh);
145 if (global_settings.statusbar)
146 height -= STATUSBAR_HEIGHT;
148 #if CONFIG_KEYPAD == RECORDER_PAD
149 if(global_settings.buttonbar && menus[m].use_buttonbar) {
150 buttonbar_set(menus[m].buttonbar[0],
151 menus[m].buttonbar[1],
152 menus[m].buttonbar[2]);
153 height -= BUTTONBAR_HEIGHT;
155 #endif
157 menu_lines = height / fh;
159 #else
160 int menu_lines = MENU_LINES;
161 #endif
163 lcd_clear_display();
164 #ifdef HAVE_LCD_BITMAP
165 lcd_setmargins(MARGIN_X,MARGIN_Y); /* leave room for cursor and icon */
166 #endif
167 /* Adjust cursor pos if it's below the screen */
168 if (menus[m].cursor - menus[m].top >= menu_lines)
169 menus[m].top++;
171 /* Adjust cursor pos if it's above the screen */
172 if(menus[m].cursor < menus[m].top)
173 menus[m].top = menus[m].cursor;
175 for (i = menus[m].top;
176 (i < menus[m].itemcount) && (i<menus[m].top+menu_lines);
177 i++) {
179 /* We want to scroll the line where the cursor is */
180 if((menus[m].cursor - menus[m].top)==(i-menus[m].top))
181 #ifdef HAVE_LCD_BITMAP
182 if (global_settings.invert_cursor)
183 lcd_puts_scroll_style(LINE_X, i-menus[m].top,
184 P2STR(menus[m].items[i].desc), STYLE_INVERT);
185 else
186 #endif
187 lcd_puts_scroll(LINE_X, i-menus[m].top, P2STR(menus[m].items[i].desc));
188 else
189 lcd_puts(LINE_X, i-menus[m].top, P2STR(menus[m].items[i].desc));
192 /* place the cursor */
193 put_cursorxy(CURSOR_X, menus[m].cursor - menus[m].top, true);
195 #ifdef HAVE_LCD_BITMAP
196 if (global_settings.scrollbar && menus[m].itemcount > menu_lines)
197 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
198 height, menus[m].itemcount, menus[m].top,
199 menus[m].top + menu_lines, VERTICAL);
201 #if CONFIG_KEYPAD == RECORDER_PAD
202 if(global_settings.buttonbar && menus[m].use_buttonbar)
203 buttonbar_draw();
204 #endif /* CONFIG_KEYPAD == RECORDER_PAD */
205 #endif /* HAVE_LCD_BITMAP */
206 status_draw(true);
208 lcd_update();
212 * Move the cursor to a particular id,
213 * target: where you want it to be
215 static void put_cursor(int m, int target)
217 int voice_id;
219 menus[m].cursor = target;
220 menu_draw(m);
222 /* "say" the entry under the cursor */
223 if(global_settings.talk_menu)
225 voice_id = P2ID(menus[m].items[menus[m].cursor].desc);
226 if (voice_id >= 0) /* valid ID given? */
227 talk_id(voice_id, false); /* say it */
231 int menu_init(const struct menu_item* mitems, int count, int (*callback)(int, int),
232 const char *button1, const char *button2, const char *button3)
234 int i;
236 for ( i=0; i<MAX_MENUS; i++ ) {
237 if ( !inuse[i] ) {
238 inuse[i] = true;
239 break;
242 if ( i == MAX_MENUS ) {
243 DEBUGF("Out of menus!\n");
244 return -1;
246 menus[i].items = (struct menu_item*)mitems; /* de-const */
247 menus[i].itemcount = count;
248 menus[i].top = 0;
249 menus[i].cursor = 0;
250 menus[i].callback = callback;
251 #if defined(HAVE_LCD_BITMAP) && (CONFIG_KEYPAD == RECORDER_PAD)
252 menus[i].buttonbar[0] = button1;
253 menus[i].buttonbar[1] = button2;
254 menus[i].buttonbar[2] = button3;
256 if(button1 || button2 || button3)
257 menus[i].use_buttonbar = true;
258 else
259 menus[i].use_buttonbar = false;
260 #else
261 (void)button1;
262 (void)button2;
263 (void)button3;
264 #endif
265 return i;
268 void menu_exit(int m)
270 inuse[m] = false;
273 int menu_show(int m)
275 bool exit = false;
276 int key;
277 #ifdef HAVE_LCD_BITMAP
278 int fw, fh;
279 int menu_lines;
280 int height = LCD_HEIGHT;
282 lcd_setfont(FONT_UI);
283 lcd_getstringsize("A", &fw, &fh);
284 if (global_settings.statusbar)
285 height -= STATUSBAR_HEIGHT;
287 #if CONFIG_KEYPAD == RECORDER_PAD
288 if(global_settings.buttonbar && menus[m].use_buttonbar) {
289 buttonbar_set(menus[m].buttonbar[0],
290 menus[m].buttonbar[1],
291 menus[m].buttonbar[2]);
292 height -= BUTTONBAR_HEIGHT;
294 #endif
296 menu_lines = height / fh;
297 #else
298 int menu_lines = MENU_LINES;
299 #endif
301 /* Put the cursor on the first line and draw the menu */
302 put_cursor(m, menus[m].cursor);
304 /* wait until all keys are released */
305 while (button_get(false) != BUTTON_NONE)
306 yield();
308 while (!exit) {
309 key = button_get_w_tmo(HZ/2);
312 * "short-circuit" the default keypresses by running the
313 * callback function
314 * The callback may return a new key value, often this will be
315 * BUTTON_NONE or the same key value, but it's perfectly legal
316 * to "simulate" key presses by returning another value.
319 if( menus[m].callback != NULL )
320 key = menus[m].callback(key, m);
322 switch( key ) {
323 case MENU_PREV:
324 case MENU_PREV | BUTTON_REPEAT:
325 if (menus[m].cursor) {
326 /* move up */
327 put_cursor(m, menus[m].cursor-1);
329 else {
330 /* move to bottom */
331 menus[m].top = menus[m].itemcount-(menu_lines+1);
332 if (menus[m].top < 0)
333 menus[m].top = 0;
334 menus[m].cursor = menus[m].itemcount-1;
335 put_cursor(m, menus[m].itemcount-1);
337 break;
339 case MENU_NEXT:
340 case MENU_NEXT | BUTTON_REPEAT:
341 if (menus[m].cursor < menus[m].itemcount-1) {
342 /* move down */
343 put_cursor(m, menus[m].cursor+1);
345 else {
346 /* move to top */
347 menus[m].top = 0;
348 menus[m].cursor = 0;
349 put_cursor(m, 0);
351 break;
353 case MENU_ENTER:
354 #ifdef MENU_ENTER2
355 case MENU_ENTER2:
356 #endif
357 /* Erase current display state */
358 lcd_clear_display();
359 return menus[m].cursor;
361 case MENU_EXIT:
362 #ifdef MENU_EXIT2
363 case MENU_EXIT2:
364 #endif
365 #ifdef MENU_EXIT3
366 case MENU_EXIT3:
367 #endif
368 lcd_stop_scroll();
369 exit = true;
370 break;
372 default:
373 if(default_event_handler(key) == SYS_USB_CONNECTED)
374 return MENU_ATTACHED_USB;
375 break;
378 status_draw(false);
380 return MENU_SELECTED_EXIT;
384 bool menu_run(int m)
386 while (1) {
387 switch (menu_show(m))
389 case MENU_SELECTED_EXIT:
390 return false;
392 case MENU_ATTACHED_USB:
393 return true;
395 default:
396 if ((menus[m].items[menus[m].cursor].function) &&
397 (menus[m].items[menus[m].cursor].function()))
398 return true;
401 return false;
405 * Property function - return the current cursor for "menu"
408 int menu_cursor(int menu)
410 return menus[menu].cursor;
414 * Property function - return the "menu" description at "position"
417 char* menu_description(int menu, int position)
419 return P2STR(menus[menu].items[position].desc);
423 * Delete the element "position" from the menu items in "menu"
426 void menu_delete(int menu, int position)
428 int i;
430 /* copy the menu item from the one below */
431 for( i = position; i < (menus[menu].itemcount - 1); i++)
432 menus[menu].items[i] = menus[menu].items[i + 1];
434 /* reduce the count */
435 menus[menu].itemcount--;
437 /* adjust if this was the last menu item and the cursor was on it */
438 if(menus[menu].itemcount && menus[menu].itemcount <= menus[menu].cursor)
439 menus[menu].cursor = menus[menu].itemcount - 1;
442 void menu_insert(int menu, int position, char *desc, bool (*function) (void))
444 int i;
446 if(position < 0)
447 position = menus[menu].itemcount;
449 /* Move the items below one position forward */
450 for( i = menus[menu].itemcount; i > position; i--)
451 menus[menu].items[i] = menus[menu].items[i - 1];
453 /* Increase the count */
454 menus[menu].itemcount++;
456 /* Update the current item */
457 menus[menu].items[position].desc = desc;
458 menus[menu].items[position].function = function;
462 * Property function - return the "count" of menu items in "menu"
465 int menu_count(int menu)
467 return menus[menu].itemcount;
471 * Allows a menu item at the current cursor position in "menu" to be moved up the list
474 bool menu_moveup(int menu)
476 struct menu_item swap;
478 /* can't be the first item ! */
479 if( menus[menu].cursor == 0)
480 return false;
482 /* use a temporary variable to do the swap */
483 swap = menus[menu].items[menus[menu].cursor - 1];
484 menus[menu].items[menus[menu].cursor - 1] = menus[menu].items[menus[menu].cursor];
485 menus[menu].items[menus[menu].cursor] = swap;
486 menus[menu].cursor--;
488 return true;
492 * Allows a menu item at the current cursor position in "menu" to be moved down the list
495 bool menu_movedown(int menu)
497 struct menu_item swap;
499 /* can't be the last item ! */
500 if( menus[menu].cursor == menus[menu].itemcount - 1)
501 return false;
503 /* use a temporary variable to do the swap */
504 swap = menus[menu].items[menus[menu].cursor + 1];
505 menus[menu].items[menus[menu].cursor + 1] = menus[menu].items[menus[menu].cursor];
506 menus[menu].items[menus[menu].cursor] = swap;
507 menus[menu].cursor++;
509 return true;