Refactor BeRTOS to be in his own directory.
[bertos.git] / bertos / gui / leveledit.c
diff --git a/bertos/gui/leveledit.c b/bertos/gui/leveledit.c
new file mode 100644 (file)
index 0000000..902d715
--- /dev/null
@@ -0,0 +1,330 @@
+/**
+ * \file
+ * <!--
+ * This file is part of BeRTOS.
+ *
+ * Bertos is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction.  Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License.  This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
+ *
+ * -->
+ *
+ * \brief Generic editor for (volume/gain/contrast/...) setting.
+ *
+ * \version $Id$
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+
+#include "leveledit.h"
+
+#include <cfg/macros.h> /* MAX() */
+#include <drv/kbd.h>
+#include <drv/timer.h>
+#include <gui/levelbar.h>
+#include <mware/pgm.h>
+#include <gfx/text.h>
+#include <gfx/font.h>
+#include <appconfig.h>
+
+#if CONFIG_MENU_MENUBAR
+#include <gui/menubar.h>
+#endif
+
+// BEGIN project_grl LOCAL
+#include <drv/lcd_gfx.h>
+#include <gui/guiman.h>
+// END project_grl LOCAL
+
+#define LBAR_HEIGHT 16
+
+/**
+ * Allow user to change level.
+ */
+void level_edit(struct LevelEdit *lev)
+{
+#if CONFIG_MENU_MENUBAR
+       /* Labels for menubars */
+       enum LabelId ch_labels[] = { LABEL_C1PLUS2, LABEL_CH_1, LABEL_CH_2 };
+       const_iptr_t labels[] =
+       {
+               (const_iptr_t)LABEL_BACK,
+               (const_iptr_t)LABEL_MINUS,
+               (const_iptr_t)LABEL_PLUS,
+               (const_iptr_t)LABEL_EMPTY
+       };
+       struct MenuBar mb;
+#endif /* CONFIG_MENU_MENUBAR */
+
+       struct LevelBar bar1, bar2;
+       keymask_t keys, old_rpt_mask;
+       int step, rep_step;
+
+       rep_step = MAX(lev->step, ((lev->max - lev->min) / 200));
+       step = lev->step;
+
+       // Allow keys repetition.
+       old_rpt_mask = kbd_setRepeatMask(K_UP | K_DOWN);
+
+       text_clear(lev->bitmap);
+       //text_style(STYLEF_UNDERLINE, STYLEF_UNDERLINE);
+       text_puts(lev->title, lev->bitmap);
+       //text_style(0, STYLEF_UNDERLINE);
+
+       if (lev->type & LEVELEDIT_DOUBLE)
+       {
+               int chn = 0;  /* edit both channels */
+
+               /* Levelbars init */
+               lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL,
+                               lev->min, lev->max, *lev->ch1_val, 0, 16, lev->bitmap->width / 2 - 1, 23);
+               lbar_init(&bar2, lev->bitmap, LBAR_HORIZONTAL,
+                               lev->min, lev->max, *lev->ch2_val, lev->bitmap->width / 2 + 1, 16, lev->bitmap->width, 23);
+
+               #if CONFIG_MENU_MENUBAR
+                       labels[3] = (const_iptr_t)ch_labels[chn];
+                       mbar_init(&mb, lev->bitmap, labels, countof(labels));
+                       mbar_draw(&mb);
+               #endif /* CONFIG_MENU_MENUBAR */
+
+               /* Input loop for double level setting */
+               for (;;)
+               {
+                       #if CONFIG_LEVELEDIT_TIMEOUT != 0
+                               ticks_t idle_timeout = timer_clock();
+                       #endif
+                       do
+                       {
+                               if (lev->display_hook)
+                                       lev->display_hook(lev);
+                               else
+                               {
+                                       text_xprintf(lev->bitmap, 1, 0, TEXT_CENTER | TEXT_FILL, lev->unit);
+                                       PGM_FUNC(text_xprintf)(lev->bitmap, 1, 3, 0, PGM_STR("%d"), *lev->ch1_val);
+                                       PGM_FUNC(text_xprintf)(lev->bitmap, 1, 14, 0, PGM_STR("%d"), *lev->ch2_val);
+
+                                       lbar_setLevel(&bar1, *lev->ch1_val);
+                                       lbar_setLevel(&bar2, *lev->ch2_val);
+                                       lbar_draw(&bar1);
+                                       lbar_draw(&bar2);
+                               }
+
+                               #if CONFIG_LEVELEDIT_TIMEOUT != 0
+                                       if (timer_clock() - idle_timeout > ms_to_ticks(CONFIG_LEVELEDIT_TIMEOUT))
+                                       {
+                                               /* Accept input implicitly */
+                                               keys = K_OK;
+                                               break;
+                                       }
+                               #endif
+                       }
+                       while (!(keys = kbd_peek()));
+
+                       if (keys & K_CANCEL)
+                               break;
+
+                       if (keys & K_OK)
+                       {
+                               chn = (chn + 1) % 3;
+
+                               #if CONFIG_MENU_MENUBAR
+                                       labels[3] = (const_iptr_t)ch_labels[chn];
+                                       mbar_draw(&mb);
+                               #endif /* CONFIG_MENU_MENUBAR */
+                       }
+
+                       /* Increment step to achieve greater accelerations on larger values */
+                       if (keys & K_REPEAT)
+                               step = MIN(rep_step, step + 1);
+                       else
+                               step = lev->step;
+
+                       if (keys & (K_UP | K_DOWN))
+                       {
+                               if (keys & K_UP)
+                               {
+                                       /* If changing both channels (chn == 0), don't change
+                                        * level if one of two is at min or max */
+                                       if (chn != 0 ||
+                                                       (*lev->ch1_val + step <= lev->max
+                                                        && *lev->ch2_val + step <= lev->max))
+                                       {
+                                               /* If chn == 0 change both channels */
+                                               if (chn != 2)
+                                               {
+                                                       *lev->ch1_val += step;
+                                                       if (*lev->ch1_val > lev->max)
+                                                               *lev->ch1_val = lev->max;
+                                               }
+                                               if (chn != 1)
+                                               {
+                                                       *lev->ch2_val += step;
+                                                       if (*lev->ch2_val > lev->max)
+                                                               *lev->ch2_val = lev->max;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (chn != 0 ||
+                                                       (*lev->ch1_val - step >= lev->min
+                                                        && *lev->ch2_val - step >= lev->min))
+                                       {
+                                               if (chn != 2)
+                                               {
+                                                       *lev->ch1_val -= step;
+                                                       if (*lev->ch1_val < lev->min)
+                                                               *lev->ch1_val = lev->min;
+                                               }
+                                               if (chn != 1)
+                                               {
+                                                       *lev->ch2_val -= step;
+                                                       if (*lev->ch2_val < lev->min)
+                                                               *lev->ch2_val = lev->min;
+                                               }
+                                       }
+                               }
+
+                               if (lev->set_hook)
+                                       lev->set_hook();
+                       }
+               } // end for(;;)
+       }
+       else
+       {
+               const PGM_ATTR char *fmt = lev->unit ? PGM_STR("%d %s") : PGM_STR("%d");
+
+/*
+               const int textw = MAX(PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->max, lev->unit),
+                               PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->min, lev->unit));
+
+               const coord_t barlen = lev->bitmap->width - 6 - textw;
+*/
+               const coord_t barlen = lev->bitmap->width;
+               const coord_t barvtop = lev->bitmap->height / 2 - LBAR_HEIGHT/2 + lev->bitmap->font->height;
+               lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL,
+                         lev->min, lev->max, *lev->ch1_val,
+                         0, barvtop, barlen,  barvtop + LBAR_HEIGHT);
+
+               #if CONFIG_MENU_MENUBAR
+                       mbar_init(&mb, lev->bitmap, labels, countof(labels));
+                       mbar_draw(&mb);
+               #endif /* CONFIG_MENU_MENUBAR */
+
+               /* Input loop for single level setting */
+               for (;;)
+               {
+                       #if CONFIG_LEVELEDIT_TIMEOUT != 0
+                               ticks_t idle_timeout = timer_clock();
+                       #endif
+                       do
+                       {
+                               if (lev->display_hook)
+                                       lev->display_hook(lev);
+                               else
+                               {
+                                       if (lev->type != LEVELEDIT_NOBAR)
+                                       {
+                                               lbar_setLevel(&bar1, *lev->ch1_val);
+                                               lbar_draw(&bar1);
+                                       }
+                                       PGM_FUNC(text_xyprintf)(lev->bitmap, 0, bar1.y1 - lev->bitmap->font->height,
+                                               TEXT_CENTER | TEXT_FILL, fmt, *lev->ch1_val, lev->unit);
+                               }
+
+                               #if CONFIG_LEVELEDIT_TIMEOUT != 0
+                                       if (timer_clock() - idle_timeout > CONFIG_LEVELEDIT_TIMEOUT)
+                                       {
+                                               /* Accept input implicitly */
+                                               keys = K_CANCEL;
+                                               break;
+                                       }
+                               #endif
+
+// BEGIN project_grl LOCAL
+#if 1
+                               //FIXME: must to this only when needed
+                               lcd_blitBitmap(&lcd_bitmap);
+                       }
+                       while (!(keys = GuiMan_KbdPeek()));
+#else
+                       }
+                       while (!(keys = kbd_peek()));
+#endif
+// END project_grl LOCAL
+
+                       if (keys & K_CANCEL)
+                               break;
+
+                       /* Increment step to achieve greater accelerations on larger values */
+                       if (keys & K_REPEAT)
+                               step = MIN(rep_step, step + 1);
+                       else
+                               step = lev->step;
+
+                       if (keys & K_UP)
+                       {
+                               *lev->ch1_val += step;
+                               if (*lev->ch1_val > lev->max)
+                                       *lev->ch1_val = lev->max;
+                       }
+
+                       if (keys & K_DOWN)
+                       {
+                               *lev->ch1_val -= step;
+                               if (*lev->ch1_val < lev->min)
+                                       *lev->ch1_val = lev->min;
+                       }
+
+                       if (lev->set_hook)
+                               lev->set_hook();
+               }
+       }
+
+       kbd_setRepeatMask(old_rpt_mask);
+}
+
+/**
+ * LevelEdit structure initialization.
+ * Init data structure and init LevelEdit widgets.
+ */
+void level_init(struct LevelEdit *lev,
+               int type,
+               struct Bitmap *bmp, const char *title, const char *unit,
+               int min, int max, int step,
+               int *ch1_val, int *ch2_val,
+               level_set_callback *set_hook, display_callback *display_hook)
+{
+       lev->type   = type;
+       lev->bitmap = bmp;
+       lev->title  = title;
+       lev->unit   = unit;
+       lev->min    = min;
+       lev->max    = max;
+       lev->step   = step;
+
+       lev->ch1_val = ch1_val;
+       lev->ch2_val = ch2_val;
+       lev->set_hook     = set_hook;
+       lev->display_hook = display_hook;
+}