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