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