menu.c
Go to the documentation of this file.
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 RenderHook renderhook = (item->flags & MIF_RENDERHOOK) ? CONST_CAST(RenderHook, item->label) : menu_defaultRenderHook; 00275 00276 /* Render menuitem */ 00277 renderhook(menu->bitmap, ypos++, (i == selected), item); 00278 00279 ypos += bm->font->height; 00280 } 00281 } 00282 00283 #if CONFIG_MENU_SMOOTH 00284 if (redraw) 00285 { 00286 /* Clear rest of area */ 00287 gfx_rectClear(bm, bm->cr.xmin, ypos, bm->cr.xmax, bm->cr.ymax); 00288 00289 menu->lcd_blitBitmap(bm); 00290 } 00291 00292 /* Restore old cliprect */ 00293 gfx_setClipRect(bm, 00294 bm->cr.xmin, old_ymin, 00295 bm->cr.xmax, bm->cr.ymax); 00296 00297 #endif /* CONFIG_MENU_SMOOTH */ 00298 } 00299 00300 00304 static iptr_t menu_doselect(const struct Menu *menu, struct MenuItem *item) 00305 { 00306 iptr_t result = 0; 00307 00308 /* Exclude other items */ 00309 int mask, i; 00310 for (mask = item->flags & MIF_EXCLUDE_MASK, i = 0; mask; mask >>= 1, ++i) 00311 { 00312 if (mask & 1) 00313 menu->items[i].flags &= ~MIF_CHECKED; 00314 } 00315 00316 if (item->flags & MIF_DISABLED) 00317 return MENU_DISABLED; 00318 00319 /* Handle checkable items */ 00320 if (item->flags & MIF_TOGGLE) 00321 item->flags ^= MIF_CHECKED; 00322 else if (item->flags & MIF_CHECKIT) 00323 item->flags |= MIF_CHECKED; 00324 00325 /* Handle items with callback hooks */ 00326 if (item->hook) 00327 { 00328 /* Push a jmp buffer to abort the operation with the STOP/CANCEL key */ 00329 if (!PUSH_ABORT) 00330 { 00331 result = item->hook(item->userdata); 00332 POP_ABORT; 00333 } 00334 } 00335 else 00336 result = item->userdata; 00337 00338 return result; 00339 } 00340 00341 00345 static int menu_next_visible_item(const struct Menu *menu, int index) 00346 { 00347 int total = menu_count(menu); 00348 int item_flags; 00349 00350 do 00351 { 00352 if (++index >= total) 00353 index = 0; 00354 00355 #if CPU_HARVARD 00356 if (menu->flags & MF_ROMITEMS) 00357 { 00358 ASSERT(sizeof(menu->items[index].flags) == sizeof(int)); 00359 item_flags = pgm_read_int(&menu->items[index].flags); 00360 } 00361 else 00362 #endif 00363 item_flags = menu->items[index].flags; 00364 } 00365 while (item_flags & MIF_HIDDEN); 00366 00367 return index; 00368 } 00369 00370 00374 static int menu_prev_visible_item(const struct Menu *menu, int index) 00375 { 00376 int total = menu_count(menu); 00377 int item_flags; 00378 00379 do 00380 { 00381 if (--index < 0) 00382 index = total - 1; 00383 00384 #if CPU_HARVARD 00385 if (menu->flags & MF_ROMITEMS) 00386 { 00387 ASSERT(sizeof(menu->items[index].flags) == sizeof(int)); 00388 item_flags = pgm_read_int(&menu->items[index].flags); 00389 } 00390 else 00391 #endif 00392 item_flags = menu->items[index].flags; 00393 } 00394 while (item_flags & MIF_HIDDEN); 00395 00396 return index; 00397 } 00398 00399 00403 iptr_t menu_handle(const struct Menu *menu) 00404 { 00405 uint8_t items_per_page; 00406 uint8_t first_item = 0; 00407 uint8_t selected; 00408 iptr_t result = 0; 00409 bool redraw = true; 00410 00411 #if (CONFIG_MENU_TIMEOUT != 0) 00412 ticks_t now, menu_idle_time = timer_clock(); 00413 #endif 00414 00415 #if CONFIG_MENU_MENUBAR 00416 struct MenuBar mb; 00417 const_iptr_t labels[] = 00418 { 00419 (const_iptr_t)LABEL_BACK, 00420 (const_iptr_t)LABEL_UPARROW, 00421 (const_iptr_t)LABEL_DOWNARROW, 00422 (const_iptr_t)0 00423 }; 00424 00425 /* 00426 * Initialize menu bar 00427 */ 00428 if (menu->flags & MF_TOPLEVEL) 00429 labels[0] = (const_iptr_t)LABEL_EMPTY; 00430 00431 mbar_init(&mb, menu->bitmap, labels, countof(labels)); 00432 #endif /* CONFIG_MENU_MENUBAR */ 00433 00434 00435 items_per_page = 00436 (menu->bitmap->height / menu->bitmap->font->height - 1) 00437 #if CONFIG_MENU_MENUBAR 00438 - 1 /* menu bar labels */ 00439 #endif 00440 - (menu->title ? 1 : 0); 00441 00442 /* Selected item should be a visible entry */ 00443 //first_item = selected = menu_next_visible_item(menu, menu->selected - 1); 00444 selected = menu->selected; 00445 first_item = 0; 00446 00447 for(;;) 00448 { 00449 keymask_t key; 00450 00451 /* 00452 * Keep selected item visible 00453 */ 00454 while (selected < first_item) 00455 first_item = menu_prev_visible_item(menu, first_item); 00456 while (selected >= first_item + items_per_page) 00457 first_item = menu_next_visible_item(menu, first_item); 00458 00459 menu_layout(menu, first_item, selected, redraw); 00460 redraw = false; 00461 00462 #if CONFIG_MENU_MENUBAR 00463 menu_update_menubar(menu, &mb, selected); 00464 #endif 00465 00466 #if CONFIG_MENU_SMOOTH || (CONFIG_MENU_TIMEOUT != 0) 00467 key = kbd_peek(); 00468 cpu_relax(); 00469 #else 00470 key = kbd_get(); 00471 #endif 00472 00473 #if (CONFIG_MENU_TIMEOUT != 0) 00474 /* Reset idle timer on key press. */ 00475 now = timer_clock(); 00476 if (key) 00477 menu_idle_time = now; 00478 #endif 00479 00480 if (key & K_OK) 00481 { 00482 struct MenuItem *item = &(menu->items[selected]); 00483 #if CPU_HARVARD 00484 MenuItem ram_item; 00485 if (menu->flags & MF_ROMITEMS) 00486 { 00487 memcpy_P(&ram_item, item, sizeof(ram_item)); 00488 item = &ram_item; 00489 } 00490 #endif 00491 result = menu_doselect(menu, item); 00492 redraw = true; 00493 00494 /* Return immediately */ 00495 if (!(menu->flags & MF_STICKY)) 00496 break; 00497 00498 #if (CONFIG_MENU_TIMEOUT != 0) 00499 /* Chain timeout */ 00500 if ((result == MENU_TIMEOUT) && !(menu->flags & MF_TOPLEVEL)) 00501 break; 00502 00503 /* Reset timeout */ 00504 menu_idle_time = timer_clock(); 00505 #endif 00506 } 00507 else if (key & K_UP) 00508 { 00509 selected = menu_prev_visible_item(menu, selected); 00510 redraw = true; 00511 } 00512 else if (key & K_DOWN) 00513 { 00514 selected = menu_next_visible_item(menu, selected); 00515 redraw = true; 00516 } 00517 else if (!(menu->flags & MF_TOPLEVEL)) 00518 { 00519 if (key & K_CANCEL) 00520 { 00521 result = MENU_CANCEL; 00522 break; 00523 } 00524 00525 #if CONFIG_MENU_TIMEOUT != 0 00526 if (now - menu_idle_time > ms_to_ticks(CONFIG_MENU_TIMEOUT)) 00527 { 00528 result = MENU_TIMEOUT; 00529 break; 00530 } 00531 #endif 00532 } 00533 } 00534 00535 /* Store currently selected item before leaving. */ 00536 if (menu->flags & MF_SAVESEL) 00537 CONST_CAST(struct Menu *, menu)->selected = selected; 00538 00539 return result; 00540 } 00541 00542 00552 int menu_setFlags(struct Menu *menu, int idx, int flags) 00553 { 00554 ASSERT(idx < menu_count(menu)); 00555 ASSERT(!(menu->flags & MF_ROMITEMS)); 00556 00557 int old = menu->items[idx].flags; 00558 menu->items[idx].flags |= flags; 00559 return old; 00560 } 00561 00562 00572 int menu_clearFlags(struct Menu *menu, int idx, int flags) 00573 { 00574 ASSERT(idx < menu_count(menu)); 00575 ASSERT(!(menu->flags & MF_ROMITEMS)); 00576 00577 int old = menu->items[idx].flags; 00578 menu->items[idx].flags &= ~flags; 00579 return old; 00580 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/bertos_logo.png)