00001
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 #include "menu.h"
00088
00089 #include <gfx/gfx.h>
00090 #include <gfx/font.h>
00091 #include <gfx/text.h>
00092 #include <drv/kbd.h>
00093 #include <cfg/compiler.h>
00094 #include <cfg/debug.h>
00095 #include <appconfig.h>
00096 #include <string.h>
00097
00098 #if CPU_HARVARD
00099 #include <avr/pgmspace.h>
00100 #endif
00101
00102 #if CONFIG_MENU_SMOOTH
00103 #include <drv/lcd_gfx.h>
00104 #endif
00105
00106 #if (CONFIG_MENU_TIMEOUT != 0)
00107 #include <drv/timer.h>
00108 #endif
00109
00110 #if CONFIG_MENU_MENUBAR
00111 #include "menubar.h"
00112 #endif
00113
00114 #if defined(CONFIG_LOCALE) && (CONFIG_LOCALE == 1)
00115 #include "msg.h"
00116 #else
00117 #define PTRMSG(x) ((const char *)x)
00118 #endif
00119
00120
00121
00122 #define abort_top 0
00123 #define PUSH_ABORT false
00124 #define POP_ABORT do {} while(0)
00125 #define DO_ABORT do {} while(0)
00126
00127
00131 static int menu_count(const struct Menu *menu)
00132 {
00133 int cnt = 0;
00134
00135 for (cnt = 0; ; ++cnt)
00136 {
00137 const MenuItem *item = &menu->items[cnt];
00138 #if CPU_HARVARD
00139 MenuItem ram_item;
00140 if (menu->flags & MF_ROMITEMS)
00141 {
00142 memcpy_P(&ram_item, item, sizeof(ram_item));
00143 item = &ram_item;
00144 }
00145 #endif
00146 if (!(item->label || item->hook))
00147 break;
00148 }
00149
00150 return cnt;
00151 }
00152
00153 #if CONFIG_MENU_MENUBAR
00154
00158 static void menu_update_menubar(
00159 const struct Menu *menu,
00160 struct MenuBar *mb,
00161 int selected)
00162 {
00163 int item_flags;
00164 #if CPU_HARVARD
00165 if (menu->flags & MF_ROMITEMS)
00166 {
00167 ASSERT(sizeof(menu->items[selected].flags) == sizeof(int));
00168 item_flags = pgm_read_int(&menu->items[selected].flags);
00169 }
00170 else
00171 #endif
00172 item_flags = menu->items[selected].flags;
00173
00174 const_iptr_t newlabel = (const_iptr_t)LABEL_OK;
00175
00176 if (item_flags & MIF_DISABLED)
00177 newlabel = (const_iptr_t)LABEL_EMPTY;
00178 else if (item_flags & MIF_TOGGLE)
00179 newlabel = (const_iptr_t)LABEL_SEL;
00180 else if (item_flags & MIF_CHECKIT)
00181 {
00182 newlabel = (item_flags & MIF_CHECKED) ?
00183 (const_iptr_t)LABEL_EMPTY : (const_iptr_t)LABEL_SEL;
00184 }
00185
00186 mb->labels[3] = newlabel;
00187 mbar_draw(mb);
00188 }
00189 #endif
00190
00191
00192 static void menu_defaultRenderHook(struct Bitmap *bm, int ypos, bool selected, const struct MenuItem *item)
00193 {
00194 if (item->flags & MIF_CHECKIT)
00195 {
00196 gfx_rectClear(bm, 0, ypos,
00197 bm->font->height, ypos + bm->font->height);
00198
00199 if (item->flags & MIF_TOGGLE)
00200 gfx_rectDraw(bm, 2, ypos + 2,
00201 bm->font->height - 2, ypos + bm->font->height - 2);
00202 if (item->flags & MIF_CHECKED)
00203 {
00204 gfx_line(bm,
00205 3, ypos + 3,
00206 bm->font->height - 3, ypos + bm->font->height - 3);
00207 gfx_line(bm,
00208 bm->font->height - 3, ypos + 3,
00209 3, ypos + bm->font->height - 3);
00210 }
00211 }
00212
00213 #if CPU_HARVARD
00214 ((item->flags & MIF_RAMLABEL) ? text_xyprintf : text_xyprintf_P)
00215 #else
00216 text_xyprintf
00217 #endif
00218 (
00219 bm, (item->flags & MIF_CHECKIT) ? bm->font->height : 0, ypos,
00220 selected ? (STYLEF_INVERT | TEXT_FILL) : TEXT_FILL,
00221 PTRMSG(item->label)
00222 );
00223 }
00224
00228 static void menu_layout(
00229 const struct Menu *menu,
00230 int first_item,
00231 int selected,
00232 bool redraw)
00233 {
00234 coord_t ypos;
00235 int i;
00236 const char * PROGMEM title = PTRMSG(menu->title);
00237 Bitmap *bm = menu->bitmap;
00238
00239 ypos = bm->cr.ymin;
00240
00241 #if 0
00242 if (redraw)
00243 {
00244
00245 text_clear(menu->bitmap);
00246 }
00247 #endif
00248
00249 if (title)
00250 {
00251 if (redraw)
00252 text_xyprintf(bm, 0, ypos, STYLEF_UNDERLINE | STYLEF_BOLD | TEXT_CENTER | TEXT_FILL, title);
00253 ypos += bm->font->height;
00254 }
00255
00256 #if CONFIG_MENU_SMOOTH
00257 static coord_t yoffset = 0;
00258 static int old_first_item = 0;
00259 static int speed;
00260 coord_t old_ymin = bm->cr.ymin;
00261
00262
00263 gfx_setClipRect(bm,
00264 bm->cr.xmin, bm->cr.ymin + ypos,
00265 bm->cr.xmax, bm->cr.ymax);
00266
00267 if (old_first_item != first_item)
00268 {
00269
00270 speed = ABS(old_first_item - first_item) * 3;
00271
00272 if (old_first_item > first_item)
00273 {
00274 yoffset += speed;
00275 if (yoffset > bm->font->height)
00276 {
00277 yoffset = 0;
00278 --old_first_item;
00279 }
00280 }
00281 else
00282 {
00283 yoffset -= speed;
00284 if (yoffset < -bm->font->height)
00285 {
00286 yoffset = 0;
00287 ++old_first_item;
00288 }
00289 }
00290 first_item = MIN(old_first_item, menu_count(menu));
00291
00292 ypos += yoffset;
00293 redraw = true;
00294 }
00295 #endif
00296
00297 if (redraw) for (i = first_item; ; ++i)
00298 {
00299 const MenuItem *item = &menu->items[i];
00300 #if CPU_HARVARD
00301 MenuItem ram_item;
00302 if (menu->flags & MF_ROMITEMS)
00303 {
00304 memcpy_P(&ram_item, item, sizeof(ram_item));
00305 item = &ram_item;
00306 }
00307 #endif
00308
00309
00310 if (ypos > bm->cr.ymax)
00311 break;
00312
00313
00314 if (!(item->label || item->hook))
00315 break;
00316
00317
00318 if (!(item->flags & MIF_HIDDEN))
00319 {
00320
00321 RenderHook renderhook = (item->flags & MIF_RENDERHOOK) ? (RenderHook)item->label : menu_defaultRenderHook;
00322
00323
00324 renderhook(menu->bitmap, ypos++, (i == selected), item);
00325
00326 ypos += bm->font->height;
00327 }
00328 }
00329
00330 #if CONFIG_MENU_SMOOTH
00331 if (redraw)
00332 {
00333
00334 gfx_rectClear(bm, bm->cr.xmin, ypos, bm->cr.xmax, bm->cr.ymax);
00335
00336 lcd_blitBitmap(&lcd_bitmap);
00337 }
00338
00339
00340 gfx_setClipRect(bm,
00341 bm->cr.xmin, old_ymin,
00342 bm->cr.xmax, bm->cr.ymax);
00343
00344 #endif
00345 }
00346
00347
00351 static iptr_t menu_doselect(const struct Menu *menu, struct MenuItem *item)
00352 {
00353 iptr_t result = 0;
00354
00355
00356 int mask, i;
00357 for (mask = item->flags & MIF_EXCLUDE_MASK, i = 0; mask; mask >>= 1, ++i)
00358 {
00359 if (mask & 1)
00360 menu->items[i].flags &= ~MIF_CHECKED;
00361 }
00362
00363 if (item->flags & MIF_DISABLED)
00364 return MENU_DISABLED;
00365
00366
00367 if (item->flags & MIF_TOGGLE)
00368 item->flags ^= MIF_CHECKED;
00369 else if (item->flags & MIF_CHECKIT)
00370 item->flags |= MIF_CHECKED;
00371
00372
00373 if (item->hook)
00374 {
00375
00376 if (!PUSH_ABORT)
00377 {
00378 result = item->hook(item->userdata);
00379 POP_ABORT;
00380 }
00381 }
00382 else
00383 result = item->userdata;
00384
00385 return result;
00386 }
00387
00388
00392 static int menu_next_visible_item(const struct Menu *menu, int index)
00393 {
00394 int total = menu_count(menu);
00395 int item_flags;
00396
00397 do
00398 {
00399 if (++index >= total)
00400 index = 0;
00401
00402 #if CPU_HARVARD
00403 if (menu->flags & MF_ROMITEMS)
00404 {
00405 ASSERT(sizeof(menu->items[index].flags) == sizeof(int));
00406 item_flags = pgm_read_int(&menu->items[index].flags);
00407 }
00408 else
00409 #endif
00410 item_flags = menu->items[index].flags;
00411 }
00412 while (item_flags & MIF_HIDDEN);
00413
00414 return index;
00415 }
00416
00417
00421 static int menu_prev_visible_item(const struct Menu *menu, int index)
00422 {
00423 int total = menu_count(menu);
00424 int item_flags;
00425
00426 do
00427 {
00428 if (--index < 0)
00429 index = total - 1;
00430
00431 #if CPU_HARVARD
00432 if (menu->flags & MF_ROMITEMS)
00433 {
00434 ASSERT(sizeof(menu->items[index].flags) == sizeof(int));
00435 item_flags = pgm_read_int(&menu->items[index].flags);
00436 }
00437 else
00438 #endif
00439 item_flags = menu->items[index].flags;
00440 }
00441 while (item_flags & MIF_HIDDEN);
00442
00443 return index;
00444 }
00445
00446
00450 iptr_t menu_handle(const struct Menu *menu)
00451 {
00452 uint8_t items_per_page;
00453 uint8_t first_item = 0;
00454 uint8_t selected;
00455 iptr_t result = 0;
00456 bool redraw = true;
00457
00458 #if (CONFIG_MENU_TIMEOUT != 0)
00459 ticks_t now, menu_idle_time = timer_clock();
00460 #endif
00461
00462 #if CONFIG_MENU_MENUBAR
00463 struct MenuBar mb;
00464 const_iptr_t labels[] =
00465 {
00466 (const_iptr_t)LABEL_BACK,
00467 (const_iptr_t)LABEL_UPARROW,
00468 (const_iptr_t)LABEL_DOWNARROW,
00469 (const_iptr_t)0
00470 };
00471
00472
00473
00474
00475 if (menu->flags & MF_TOPLEVEL)
00476 labels[0] = (const_iptr_t)LABEL_EMPTY;
00477
00478 mbar_init(&mb, menu->bitmap, labels, countof(labels));
00479 #endif
00480
00481
00482 items_per_page =
00483 (menu->bitmap->height / menu->bitmap->font->height)
00484 #if CONFIG_MENU_MENUBAR
00485 - 1
00486 #endif
00487 - (menu->title ? 1 : 0);
00488
00489
00490
00491 selected = menu->selected;
00492 first_item = 0;
00493
00494 for(;;)
00495 {
00496 keymask_t key;
00497
00498
00499
00500
00501 while (selected < first_item)
00502 first_item = menu_prev_visible_item(menu, first_item);
00503 while (selected >= first_item + items_per_page)
00504 first_item = menu_next_visible_item(menu, first_item);
00505
00506 menu_layout(menu, first_item, selected, redraw);
00507 redraw = false;
00508
00509 #if CONFIG_MENU_MENUBAR
00510 menu_update_menubar(menu, &mb, selected);
00511 #endif
00512
00513 #if CONFIG_MENU_SMOOTH || (CONFIG_MENU_TIMEOUT != 0)
00514 key = kbd_peek();
00515 #else
00516 key = kbd_get();
00517 #endif
00518
00519 #if (CONFIG_MENU_TIMEOUT != 0)
00520
00521 now = timer_clock();
00522 if (key)
00523 menu_idle_time = now;
00524 #endif
00525
00526 if (key & K_OK)
00527 {
00528 struct MenuItem *item = &(menu->items[selected]);
00529 #if CPU_HARVARD
00530 MenuItem ram_item;
00531 if (menu->flags & MF_ROMITEMS)
00532 {
00533 memcpy_P(&ram_item, item, sizeof(ram_item));
00534 item = &ram_item;
00535 }
00536 #endif
00537 result = menu_doselect(menu, item);
00538 redraw = true;
00539
00540
00541 if (!(menu->flags & MF_STICKY))
00542 break;
00543
00544 #if (CONFIG_MENU_TIMEOUT != 0)
00545
00546 if ((result == MENU_TIMEOUT) && !(menu->flags & MF_TOPLEVEL))
00547 break;
00548
00549
00550 menu_idle_time = timer_clock();
00551 #endif
00552 }
00553 else if (key & K_UP)
00554 {
00555 selected = menu_prev_visible_item(menu, selected);
00556 redraw = true;
00557 }
00558 else if (key & K_DOWN)
00559 {
00560 selected = menu_next_visible_item(menu, selected);
00561 redraw = true;
00562 }
00563 else if (!(menu->flags & MF_TOPLEVEL))
00564 {
00565 if (key & K_CANCEL)
00566 {
00567 result = MENU_CANCEL;
00568 break;
00569 }
00570
00571 #if CONFIG_MENU_TIMEOUT != 0
00572 if (now - menu_idle_time > ms_to_ticks(CONFIG_MENU_TIMEOUT))
00573 {
00574 result = MENU_TIMEOUT;
00575 break;
00576 }
00577 #endif
00578 }
00579 }
00580
00581
00582 if (menu->flags & MF_SAVESEL)
00583 CONST_CAST(struct Menu *, menu)->selected = selected;
00584
00585 return result;
00586 }
00587
00588
00598 int menu_setFlags(struct Menu *menu, int idx, int flags)
00599 {
00600 ASSERT(idx < menu_count(menu));
00601 ASSERT(!(menu->flags & MF_ROMITEMS));
00602
00603 int old = menu->items[idx].flags;
00604 menu->items[idx].flags |= flags;
00605 return old;
00606 }
00607
00608
00618 int menu_clearFlags(struct Menu *menu, int idx, int flags)
00619 {
00620 ASSERT(idx < menu_count(menu));
00621 ASSERT(!(menu->flags & MF_ROMITEMS));
00622
00623 int old = menu->items[idx].flags;
00624 menu->items[idx].flags &= ~flags;
00625 return old;
00626 }