BeRTOS
|
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> /* strcpy() */ 00057 00058 #if CPU_HARVARD 00059 #include <avr/pgmspace.h> /* strncpy_P() */ 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 /* Temporary fake defines for ABORT stuff... */ 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; /*NOP*/; ++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 /* CONFIG_MENU_MENUBAR */ 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 /* Clear screen */ 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 /* Clip drawing inside menu items area */ 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 /* Speed proportional to distance */ 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 /* CONFIG_MENU_SMOOTH */ 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 /* CPU_HARVARD */ 00262 00263 /* Check for end of room */ 00264 if (ypos > bm->cr.ymax) 00265 break; 00266 00267 /* Check for end of menu */ 00268 if (!(item->label || item->hook)) 00269 break; 00270 00271 /* Only print visible items */ 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 /* Render menuitem */ 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 /* Clear rest of area */ 00288 gfx_rectClear(bm, bm->cr.xmin, ypos, bm->cr.xmax, bm->cr.ymax); 00289 00290 menu->lcd_blitBitmap(bm); 00291 } 00292 00293 /* Restore old cliprect */ 00294 gfx_setClipRect(bm, 00295 bm->cr.xmin, old_ymin, 00296 bm->cr.xmax, bm->cr.ymax); 00297 00298 #endif /* CONFIG_MENU_SMOOTH */ 00299 } 00300 00301 00305 static iptr_t menu_doselect(const struct Menu *menu, struct MenuItem *item) 00306 { 00307 iptr_t result = 0; 00308 00309 /* Exclude other items */ 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 /* Handle checkable items */ 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 /* Handle items with callback hooks */ 00327 if (item->hook) 00328 { 00329 /* Push a jmp buffer to abort the operation with the STOP/CANCEL key */ 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 * Initialize menu bar 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 /* CONFIG_MENU_MENUBAR */ 00434 00435 00436 items_per_page = 00437 (menu->bitmap->height / menu->bitmap->font->height - 1) 00438 #if CONFIG_MENU_MENUBAR 00439 - 1 /* menu bar labels */ 00440 #endif 00441 - (menu->title ? 1 : 0); 00442 00443 /* Selected item should be a visible entry */ 00444 //first_item = selected = menu_next_visible_item(menu, menu->selected - 1); 00445 selected = menu->selected; 00446 first_item = 0; 00447 00448 for(;;) 00449 { 00450 keymask_t key; 00451 00452 /* 00453 * Keep selected item visible 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 /* Reset idle timer on key press. */ 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 /* Return immediately */ 00496 if (!(menu->flags & MF_STICKY)) 00497 break; 00498 00499 #if (CONFIG_MENU_TIMEOUT != 0) 00500 /* Chain timeout */ 00501 if ((result == MENU_TIMEOUT) && !(menu->flags & MF_TOPLEVEL)) 00502 break; 00503 00504 /* Reset timeout */ 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 /* Store currently selected item before leaving. */ 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 }