Avoid unnecessary rendering.
[bertos.git] / gui / menu.c
index 8a08fe8df3afd608576490a0dd0ca3a0f928766d..4fcc07ccab01c7f9dc796305cfb7de96170d2c40 100755 (executable)
 
 /*#*
  *#* $Log$
+ *#* Revision 1.3  2006/05/28 15:03:31  bernie
+ *#* Avoid unnecessary rendering.
+ *#*
+ *#* Revision 1.2  2006/05/25 23:34:38  bernie
+ *#* Implement menu timeouts.
+ *#*
  *#* Revision 1.1  2006/05/15 07:20:54  bernie
  *#* Move menu to gui/.
  *#*
  *#*
  *#* Revision 1.2  2006/02/15 09:10:51  bernie
  *#* Make title bold; Fix height when we have no menubar.
- *#*
- *#* Revision 1.1  2006/02/10 12:29:36  bernie
- *#* Add menu system.
- *#*
- *#* Revision 1.48  2005/11/27 23:02:55  bernie
- *#* Move graphics modules from mware/ to gfx/.
- *#*
- *#* Revision 1.47  2005/11/16 18:10:19  bernie
- *#* Move top-level headers to cfg/ as in DevLib.
- *#*
- *#* Revision 1.46  2005/02/17 03:49:21  bernie
- *#* Update to new PGM api.
- *#*
- *#* Revision 1.45  2005/02/11 19:11:32  aleph
- *#* Move menu_displaymsg() in new displaymsg module
- *#*
- *#* Revision 1.44  2005/01/21 20:05:57  aleph
- *#* Fix build warning with debug off
- *#*
- *#* Revision 1.43  2005/01/13 16:56:36  aleph
- *#* Fix progmem includes.
- *#*
- *#* Revision 1.42  2004/10/31 11:02:15  aleph
- *#* Rename functions with correct codying conventions; Simplify version display
- *#*
- *#* Revision 1.41  2004/10/15 17:34:33  customer_pw
- *#* Fix menuitem max length
- *#*
- *#* Revision 1.40  2004/10/06 12:55:08  customer_pw
- *#* Declare unused (if !_DEBUG) menu_count()
- *#*
- *#* Revision 1.39  2004/10/01 14:04:59  customer_pw
- *#* Add accessor functions for menu flags
- *#*
- *#* Revision 1.38  2004/09/27 12:05:46  customer_pw
- *#* Use sel label for toggle menus and remove it
- *#*
- *#* Revision 1.37  2004/09/27 10:05:33  customer_pw
- *#* Menu cosmetic fixes
- *#*
- *#* Revision 1.36  2004/09/14 22:18:03  bernie
- *#* Adapt to stricter casting rules.
- *#*
- *#* Revision 1.35  2004/09/12 17:56:03  aleph
- *#* Include debug.h instead of drv/kdebug.h
  *#*/
 
 #include "menu.h"
 #include <drv/lcd_gfx.h>
 #endif
 
+#if (CONFIG_MENU_TIMEOUT != 0)
+#include <drv/timer.h>
+#endif
+
 #if CONFIG_MENU_MENUBAR
 #include "menubar.h"
 #endif
 
 
 /**
- * Count the items present in a menu.
+ * Return the total number of items in in a menu.
  */
 static int menu_count(const struct Menu *menu)
 {
@@ -189,34 +154,45 @@ static void menu_update_menubar(
 static void menu_layout(
                const struct Menu *menu,
                int first_item,
-               int items_per_page,
-               int selected)
+               int selected,
+               bool redraw)
 {
-       int ypos, cnt;
+       coord_t ypos;
+       int i;
        const char * PROGMEM title = PTRMSG(menu->title);
        Bitmap *bm = menu->bitmap;
 
        ypos = bm->cr.ymin;
 
+#if 0
+       if (redraw)
+       {
+               /* Clear screen */
+               text_clear(menu->bitmap);
+       }
+#endif
+
        if (title)
        {
-               text_xyprintf(bm, 0, ypos, STYLEF_UNDERLINE | STYLEF_BOLD | TEXT_CENTER | TEXT_FILL, title);
+               if (redraw)
+                       text_xyprintf(bm, 0, ypos, STYLEF_UNDERLINE | STYLEF_BOLD | TEXT_CENTER | TEXT_FILL, title);
                ypos += bm->font->height;
        }
 
 #if CONFIG_MENU_SMOOTH
        static coord_t yoffset = 0;
        static int old_first_item = 0;
-       static mtime_t old_time = 0; //UNUSED
        static int speed;
        coord_t old_ymin = bm->cr.ymin;
 
+       /* Clip drawing inside menu items area */
        gfx_setClipRect(bm,
                bm->cr.xmin, bm->cr.ymin + ypos,
                bm->cr.xmax, bm->cr.ymax);
 
        if (old_first_item != first_item)
        {
+               /* Speed proportional to distance */
                speed = ABS(old_first_item - first_item) * 3;
 
                if (old_first_item > first_item)
@@ -237,14 +213,16 @@ static void menu_layout(
                                        ++old_first_item;
                        }
                }
-               first_item = old_first_item;
+               first_item = MIN(old_first_item, menu_count(menu));
+
+               ypos += yoffset;
+               redraw = true;
        }
-       ypos += yoffset;
-#endif
+#endif /* CONFIG_MENU_SMOOTH */
 
-       for (cnt = 0; cnt < items_per_page; ++cnt)
+       if (redraw) for (i = first_item; /**/; ++i)
        {
-               const MenuItem *item = &menu->items[first_item + cnt];
+               const MenuItem *item = &menu->items[i];
 #if CPU_HARVARD
                MenuItem ram_item;
                if (menu->flags & MF_ROMITEMS)
@@ -254,6 +232,10 @@ static void menu_layout(
                }
 #endif
 
+               /* Check for end of room */
+               if (ypos > bm->cr.ymax)
+                       break;
+
                /* Check for end of menu */
                if (!(item->label || item->hook))
                        break;
@@ -268,7 +250,7 @@ static void menu_layout(
 #endif
                        (
                                bm, 0, ypos,
-                               (first_item + cnt == selected) ? (STYLEF_INVERT | TEXT_FILL) : TEXT_FILL,
+                               (i == selected) ? (STYLEF_INVERT | TEXT_FILL) : TEXT_FILL,
                                (item->flags & MIF_RAMLABEL) ? PSTR("%s%S") : PSTR("%S%S"),
                                PTRMSG(item->label),
                                (item->flags & MIF_TOGGLE) ?
@@ -280,12 +262,18 @@ static void menu_layout(
        }
 
 #if CONFIG_MENU_SMOOTH
+       if (redraw)
+       {
+               /* Clear rest of area */
+               gfx_rectClear(bm, bm->cr.xmin, ypos, bm->cr.xmax, bm->cr.ymax);
+
+               lcd_blitBitmap(&lcd_bitmap);
+       }
+
        /* Restore old cliprect */
        gfx_setClipRect(bm,
                        bm->cr.xmin, old_ymin,
                        bm->cr.xmax, bm->cr.ymax);
-
-       lcd_blitBitmap(&lcd_bitmap);
 #endif
 }
 
@@ -383,13 +371,19 @@ static int menu_prev_visible_item(const struct Menu *menu, int index)
 }
 
 
-/*!
+/**
  * Handle a menu and invoke hook functions for the selected menu items.
  */
 iptr_t menu_handle(const struct Menu *menu)
 {
        uint8_t items_per_page;
-       uint8_t first_item, selected;
+       uint8_t first_item = 0;
+       uint8_t selected;
+       bool redraw = true;
+
+#if (CONFIG_MENU_TIMEOUT != 0)
+       ticks_t now, menu_idle_time = timer_clock();
+#endif
 
 #if CONFIG_MENU_MENUBAR
        struct MenuBar mb;
@@ -419,10 +413,9 @@ iptr_t menu_handle(const struct Menu *menu)
                - (menu->title ? 1 : 0);
 
        /* Selected item should be a visible entry */
-       first_item = selected = menu_next_visible_item(menu, menu->selected - 1);
-
-       /* Clear screen */
-       text_clear(menu->bitmap);
+       //first_item = selected = menu_next_visible_item(menu, menu->selected - 1);
+       selected = menu->selected;
+       first_item = 0;
 
        for(;;)
        {
@@ -436,17 +429,25 @@ iptr_t menu_handle(const struct Menu *menu)
                while (selected >= first_item + items_per_page)
                        first_item = menu_next_visible_item(menu, first_item);
 
-               menu_layout(menu, first_item, items_per_page, selected);
+               menu_layout(menu, first_item, selected, redraw);
+               redraw = false;
 
                #if CONFIG_MENU_MENUBAR
                        menu_update_menubar(menu, &mb, selected);
                #endif
 
-#if CONFIG_MENU_SMOOTH
-               key = kbd_peek();
-#else
-               key = kbd_get();
-#endif
+               #if CONFIG_MENU_SMOOTH || (CONFIG_MENU_TIMEOUT != 0)
+                       key = kbd_peek();
+               #else
+                       key = kbd_get();
+               #endif
+
+               #if (CONFIG_MENU_TIMEOUT != 0)
+                       /* Reset idle timer on key press. */
+                       now = timer_clock();
+                       if (key)
+                               menu_idle_time = now;
+               #endif
 
                if (key & K_OK)
                {
@@ -460,6 +461,7 @@ iptr_t menu_handle(const struct Menu *menu)
                        }
 #endif
                        menu_doselect(menu, item);
+                       redraw = true;
 
                        /* Return userdata as result */
                        if (!menu->flags & MF_STICKY)
@@ -469,19 +471,23 @@ iptr_t menu_handle(const struct Menu *menu)
                                        CONST_CAST(struct Menu *, menu)->selected = selected;
                                return item->userdata;
                        }
-
-                       /* Clear screen */
-                       text_clear(menu->bitmap);
                }
                else if (key & K_UP)
                {
                        selected = menu_prev_visible_item(menu, selected);
+                       redraw = true;
                }
                else if (key & K_DOWN)
                {
                        selected = menu_next_visible_item(menu, selected);
+                       redraw = true;
                }
-               else if (key & K_CANCEL && !(menu->flags & MF_TOPLEVEL))
+               else if (((key & K_CANCEL)
+                       #if CONFIG_MENU_TIMEOUT != 0
+                               || (now - menu_idle_time > ms_to_ticks(CONFIG_MENU_TIMEOUT))
+                       #endif
+                               ) && !(menu->flags & MF_TOPLEVEL)
+               )
                {
                        /* Store currently selected item before leaving. */
                        if (menu->flags & MF_SAVESEL)