Import into DevLib.
authorbernie <bernie@38d2e660-2303-0410-9eaa-f027e97ec537>
Wed, 19 Jul 2006 13:00:01 +0000 (13:00 +0000)
committerbernie <bernie@38d2e660-2303-0410-9eaa-f027e97ec537>
Wed, 19 Jul 2006 13:00:01 +0000 (13:00 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@672 38d2e660-2303-0410-9eaa-f027e97ec537

gfx/fillpoly.cpp [new file with mode: 0755]
gui/levelbar.c [new file with mode: 0755]
gui/levelbar.h [new file with mode: 0755]
gui/leveledit.c [new file with mode: 0755]
gui/leveledit.h [new file with mode: 0755]

diff --git a/gfx/fillpoly.cpp b/gfx/fillpoly.cpp
new file mode 100755 (executable)
index 0000000..25be11a
--- /dev/null
@@ -0,0 +1,380 @@
+/**
+ * \file
+ * <!--
+ * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
+ * This file is part of DevLib - See README.devlib for information.
+ * -->
+ *
+ * \version $Id$
+ * \author Massimiliano Corsini <chad@develer.com>
+ *
+ *
+ * \brief Low-level drawing routines.
+ *
+ * This file contains the implementation of the low-level drawing routines
+ * to draw fill rectangle, fill triangle and so on.
+ *
+ */
+
+/*#*
+ *#* $Log$
+ *#* Revision 1.1  2006/07/19 13:00:01  bernie
+ *#* Import into DevLib.
+ *#*
+ *#* Revision 1.10  2005/10/15 15:03:43  rasky
+ *#* Remove per-pixel clipping from line().
+ *#* Use clipLine() also for a-scope.
+ *#*
+ *#* Revision 1.9  2005/10/14 15:21:32  eldes
+ *#* Implement the cohen-sutherland clipping on the buffer
+ *#*
+ *#* Revision 1.8  2005/09/27 13:28:10  rasky
+ *#* Add clipping capabilities to line()
+ *#* Fix off-by-one computation of rectangles of drawing.
+ *#*
+ *#* Revision 1.7  2005/09/27 10:41:35  rasky
+ *#* Import line-drawing routine from Devlib
+ *#*
+ *#* Revision 1.6  2005/09/19 16:36:05  chad
+ *#* Fix doxygen autobrief
+ *#*
+ *#* Revision 1.5  2005/07/06 12:51:47  chad
+ *#* Make the fillRectangle() independent of the order of the points of the rectangle
+ *#*
+ *#* Revision 1.4  2005/06/17 15:06:36  chad
+ *#* Remove conversion warning
+ *#*
+ *#* Revision 1.3  2005/06/17 15:04:47  chad
+ *#* Add line clipping capability
+ *#*
+ *#* Revision 1.2  2005/06/15 14:04:43  chad
+ *#* Add line routine
+ *#*
+ *#* Revision 1.1  2005/06/15 13:34:34  chad
+ *#* Low-level drawing routines
+ *#*
+ *#*/
+
+// Qt-specific headers
+#include <qpoint.h>
+
+
+/**
+ * Low-level routine to draw a line.
+ *
+ * This routine is based on the Bresenham Line-Drawing Algorithm.
+ *
+ * The \a stride represents the width of the image buffer.
+ * (\a x1, \a y1) are the coordinates of the starting point.
+ * (\a x2, \a y2) are the coordinates of the ending point.
+ *
+ * The line has no anti-alias, and clipping is not performed. The line
+ * must be fully contained in the buffer (use clipLine() if you need
+ * to clip it).
+ */
+void line(unsigned char *buf,
+                 unsigned long bufw, unsigned long bufh, unsigned long stride,
+                 int x1, int y1, int x2, int y2, unsigned char color)
+{
+       int x, y, e, len, adx, ady, signx, signy;
+
+       if (x2 > x1)
+       {
+               /* left to right */
+               signx = +1;
+               adx = x2 - x1;
+       }
+       else
+       {
+               /* right to left */
+               signx = -1;
+               adx = x1 - x2;
+       }
+
+       if (y2 > y1)
+       {
+               /* top to bottom */
+               signy = +1;
+               ady = y2 - y1;
+       }
+       else
+       {
+               /* bottom to top */
+               signy = -1;
+               ady = y1 - y2;
+       }
+
+       x = x1;
+       y = y1;
+
+       if (adx > ady)
+       {
+               /* X-major line (octants 1/4/5/8) */
+               len = adx;
+               e = -adx;
+               while (len--)
+               {
+                       /* Sanity check */
+                       assert(y >= 0 && y < static_cast<int>(bufh) &&
+                                  x >= 0 && x < static_cast<int>(bufw));
+                       buf[y * stride + x] = color;
+                       x += signx;
+                       e += ady;
+                       if (e >= 0)
+                       {
+                               y += signy;
+                               e -= adx;
+                       }
+               }
+       }
+       else
+       {
+               /* Y-major line (octants 2/3/6/7) */
+               len = ady;
+               e = -ady;
+               while (len--)
+               {
+                       /* Sanity check */
+                       assert(y >= 0 && y < static_cast<int>(bufh) &&
+                                  x >= 0 && x < static_cast<int>(bufw));
+                       buf[y * stride + x] = color;
+                       y += signy;
+                       e += adx;
+                       if (e >= 0)
+                       {
+                               x += signx;
+                               e -= ady;
+                       }
+               }
+       }
+}
+
+/// Helper routine for clipLine().
+static int region(int x, int y, int w, int h)
+{
+       int code = 0;
+
+       if (y >= h)
+               code |= 1; // top
+       else if (y < 0)
+               code |= 2; // bottom
+
+       if (x >= w)
+               code |= 4; // right
+       else if (x < 0)
+               code |= 8; // left
+
+       return code;
+}
+
+/**
+ * Low-level routine to draw a line, clipped to the buffer extents.
+ *
+ * This routine executes the clipping, and then invokes line().
+ * Parameters are the same of line(). The clipping is performed
+ * using the Cohen-Sutherland algorithm, which is very fast.
+ */
+void clipLine(unsigned char *buf,
+                 unsigned long w, unsigned long h, unsigned long stride,
+                 int x1, int y1, int x2, int y2, unsigned char color)
+{
+       int code1 = region(x1, y1, w, h);
+       int code2 = region(x2, y2, w, h);
+
+       // Loop while there is at least one point outside
+       while (code1 | code2)
+       {
+               // Check for line totally outside
+               if (code1 & code2)
+                       return;
+
+               int c = code1 ? code1 : code2;
+               int x, y;
+
+               if (c & 1) // top
+               {
+                       x = x1 + (x2 - x1) * (h - y1) / (y2 - y1);
+                       y = h - 1;
+               }
+               else if (c & 2) //bottom
+               {
+                       x = x1 + (x2 - x1) * -y1 / (y2 - y1);
+                       y = 0;
+               }
+               else if (c & 4) //right
+               {
+                       y = y1 + (y2 - y1) * (w - x1) / (x2 - x1);
+                       x = w - 1;
+               }
+               else //left
+               {
+                       y = y1 + (y2 - y1) * -x1 / (x2 - x1);
+                       x = 0;
+               }
+
+               if (c == code1) // first endpoint was clipped
+               {
+                       x1 = x; y1 = y;
+                       code1 = region(x1, y1, w, h);
+               }
+               else //second endpoint was clipped
+               {
+                       x2 = x; y2 = y;
+                       code2 = region(x2, y2, w, h);
+               }
+       }
+
+       line(buf, w, h, stride, x1, y1, x2, y2, color);
+}
+
+
+/**
+ * Low-level routine to draw a filled rectangle.
+ *
+ * The triangle is filled with the given color.
+ *
+ * The \a stride represents the width of the image buffer.
+ * The points \a p1 and \a p2 are two opposite corners of the
+ * rectangle.
+ */
+void fillRectangle(unsigned char *buf, unsigned long stride,
+                                  QPoint p1, QPoint p2, unsigned char color)
+{
+       QPoint ul;       // upper-left corner
+       QPoint lr;       // lower-right corner
+
+       if (p2.x() > p1.x())
+       {
+               ul.setX(p1.x());
+               lr.setX(p2.x());
+       }
+       else
+       {
+               ul.setX(p2.x());
+               lr.setX(p1.x());
+       }
+
+       if (p2.y() > p1.y())
+       {
+               ul.setY(p1.y());
+               lr.setY(p2.y());
+       }
+       else
+       {
+               ul.setY(p2.y());
+               lr.setY(p1.y());
+       }
+
+       int width = lr.x() - ul.x();
+       unsigned long offset = ul.x() + ul.y()*stride;
+
+       for (int h = ul.y(); h < lr.y(); h++)
+       {
+               memset(buf+offset, color, width);
+               offset += stride;
+       }
+}
+
+/**
+ * Low-level routines to draw a filled triangle.
+ *
+ * The triangle is filled with the given \a color.
+ * The \a stride represents the width of the image buffer (\a buf).
+ *
+ * The routine use fixed-point arithmetic.
+ */
+void fillTriangle(unsigned char* buf, unsigned long stride,
+                                 QPoint v1, QPoint v2, QPoint v3, unsigned char color)
+{
+       int altezza[3];
+
+       // Sort by vertical coordinate
+       if (v1.y() > v2.y())
+               std::swap(v1, v2);
+       if (v1.y() > v3.y())
+               std::swap(v1, v3);
+       if (v2.y() > v3.y())
+               std::swap(v2, v3);
+
+       altezza[0] = v3.y() - v1.y();
+       if (!altezza[0])
+               return;
+
+       int sezioni = 2;
+       int sezione = 1;
+
+       buf += v1.y() * stride;
+
+       altezza[1] = v2.y() - v1.y();
+       altezza[2] = v3.y() - v2.y();
+
+       int sinistra = v1.x();
+       int destra = sinistra;
+
+       if (v1.y() == v2.y())
+       {
+               if (v1.x() < v2.x())
+                       destra = v2.x();
+               else
+                       sinistra = v2.x();
+       }
+
+       sinistra <<= 16;
+       destra <<= 16;
+
+       int stmp1, stmp2, stmp3;
+
+       stmp1 = (altezza[1] << 16) / altezza[0];
+       int lunghezza = stmp1 * (v3.x() - v1.x()) + ((v1.x() - v2.x()) << 16);
+
+       if (!lunghezza )
+               return;
+
+       int delta_sinistra[2];
+       int delta_destra[2];
+
+       stmp1 = ((v3.x() - v1.x()) << 16) / altezza[0];
+
+       if (altezza[1])
+               stmp2 = ((v2.x() - v1.x()) << 16) / altezza[1];
+       if (altezza[2])
+               stmp3 = ((v3.x() - v2.x()) << 16) / altezza[2];
+
+       if (lunghezza < 0) // Il secondo vertice ~J a destra
+       {
+               delta_sinistra[0] = stmp1;
+               delta_sinistra[1] = stmp1;
+               delta_destra[0] = stmp2;
+               delta_destra[1] = stmp3;
+       }
+       else // Il secondo vertice ~J a sinistra
+       {
+               delta_sinistra[0] = stmp2;
+               delta_sinistra[1] = stmp3;
+               delta_destra[0] = stmp1;
+               delta_destra[1] = stmp1;
+       }
+
+       int len2 = lunghezza;
+
+       do
+       {
+               while (altezza [sezione])
+               {
+                       unsigned char* curpos = buf + ((sinistra )>> 16);
+                       lunghezza = ((destra ) >> 16) - ((sinistra ) >> 16);
+                       assert(lunghezza >= 0);
+                       if (lunghezza)
+                               memset(curpos, color, lunghezza);
+                       buf += stride;
+                       destra += delta_destra[sezione - 1];
+                       sinistra += delta_sinistra[sezione - 1];
+                       altezza[sezione]--;
+               }
+               if (len2 < 0)
+                       destra = v2.x() << 16;
+               else
+                       sinistra = v2.x() << 16;
+               sezione++;
+       } while (--sezioni);
+}
diff --git a/gui/levelbar.c b/gui/levelbar.c
new file mode 100755 (executable)
index 0000000..591d257
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * \file
+ * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
+ * This file is part of DevLib - See README.devlib for information.
+ *
+ * \brief Graphics user interface element to display a level bar.
+ *
+ * \version $Id$
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+
+#include "levelbar.h"
+
+
+/**
+ * Initialize the LevelBar widget with the bitmap associated,
+ * the value range and the coordinates in the bitmap.
+ * \note The levelbar should be at least 5 pixels wide and high
+ *       for correct borders drawing. No check is done on this.
+ */
+void lbar_init(struct LevelBar *lb, struct Bitmap *bmp, int type, int min, int max, int pos,
+               coord_t x1, coord_t y1, coord_t x2, coord_t y2)
+{
+       lb->bitmap = bmp;
+       lb->type = type;
+       lb->min = min;
+       lb->max = max;
+       lb->pos = pos;
+       lb->x1 = x1;
+       lb->y1 = y1;
+       lb->x2 = x2;
+       lb->y2 = y2;
+}
+
+
+/**
+ * Set the level.
+ */
+void lbar_setLevel(struct LevelBar *lb, int level)
+{
+       if (level < lb->min)
+               level = lb->min;
+       if (level > lb->max)
+               level = lb->max;
+
+       lb->pos = level;
+}
+
+
+/**
+ * Get current level.
+ */
+int lbar_getLevel(struct LevelBar *lb)
+{
+       return lb->pos;
+}
+
+
+/**
+ * Change level with respect to previous value
+ * (delta can be negative).
+ */
+void lbar_changeLevel(struct LevelBar *lb, int delta)
+{
+       lbar_setLevel(lb, lb->pos + delta);
+}
+
+
+/**
+ * Change the top limit.
+ */
+void lbar_setMax(struct LevelBar *lb, int max)
+{
+       lb->max = max;
+}
+
+
+/**
+ * Render the LevelBar on the bitmap.
+ */
+void lbar_draw(struct LevelBar *lb)
+{
+#define BORDERW 1
+#define BORDERH 1
+
+       /* Compute filled bar length in pixels */
+       int totlen = (lb->type & LBAR_HORIZONTAL) ? lb->x2 - lb->x1 - BORDERW*4 : lb->y2 - lb->y1 - BORDERH*4;
+       int range  = lb->max - lb->min;
+       int barlen = ((long)(lb->pos - lb->min) * (long)totlen + range - 1) / range;
+
+       // Draw border
+       gfx_rectDraw(lb->bitmap, lb->x1, lb->y1, lb->x2, lb->y2);
+
+       // Clear inside
+       gfx_rectClear(lb->bitmap, lb->x1 + BORDERW, lb->y1 + BORDERH, lb->x2 - BORDERW, lb->y2 - BORDERH);
+
+       // Draw bar
+       if (lb->type & LBAR_HORIZONTAL)
+               gfx_rectFill(lb->bitmap,
+                               lb->x1 + BORDERW*2, lb->y1 + BORDERH*2,
+                               lb->x1 + BORDERW*2 + barlen, lb->y2 - BORDERH*2);
+       else
+               gfx_rectFill(lb->bitmap,
+                               lb->x1 + BORDERW*2, lb->y2 - BORDERH*2 - barlen,
+                               lb->x2 - BORDERW*2, lb->y2 - BORDERH*2);
+}
diff --git a/gui/levelbar.h b/gui/levelbar.h
new file mode 100755 (executable)
index 0000000..ad8e0cd
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * \file
+ * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
+ * This file is part of DevLib - See README.devlib for information.
+ *
+ * \version $Id$
+ *
+ * \author Stefano Fedrigo <aleph@develer.com>
+ *
+ * \brief Graphics level bar widget
+ */
+
+#ifndef GUI_LEVELBAR_H
+#define GUI_LEVELBAR_H
+
+#include <gfx/gfx.h>
+
+
+/** Type of levelbar */
+#define LBAR_HORIZONTAL 1
+#define        LBAR_VERTICAL   2
+
+typedef struct LevelBar
+{
+       struct Bitmap *bitmap;
+       int type;
+       int pos;                 ///< Current level
+       int min;                 ///< Minimum level
+       int max;                 ///< Maximum level
+       coord_t x1, y1, x2, y2;  ///< Position of widget in the bitmap
+} LevelBar;
+
+void lbar_init(struct LevelBar *lb, struct Bitmap *bmp, int type, int min, int max, int pos,
+               coord_t x1, coord_t y1, coord_t x2, coord_t y2);
+void lbar_setLevel(struct LevelBar *lb, int level);
+int  lbar_getLevel(struct LevelBar *lb);
+void lbar_changeLevel(struct LevelBar *lb, int delta);
+void lbar_setMax(struct LevelBar *lb, int max);
+void lbar_draw(struct LevelBar *lb);
+
+#endif /* GUI_LEVELBAR_H */
diff --git a/gui/leveledit.c b/gui/leveledit.c
new file mode 100755 (executable)
index 0000000..e412223
--- /dev/null
@@ -0,0 +1,305 @@
+/**
+ * \file
+ * <!--
+ * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
+ * This file is part of DevLib - See README.devlib for information.
+ * -->
+ *
+ * \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;
+}
diff --git a/gui/leveledit.h b/gui/leveledit.h
new file mode 100755 (executable)
index 0000000..0b49666
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * \file
+ * <!--
+ * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
+ * This file is part of DevLib - See README.devlib for information.
+ * -->
+ *
+ * \brief Generic editor for (volume/gain/contrast/...) setting.
+ *
+ * \version $Id$
+ *
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+#ifndef GUI_LEVELEDIT_H
+#define GUI_LEVELEDIT_H
+
+//#include <gui/levelbar.h>
+
+/* Type for level_init */
+#define LEVELEDIT_NOBAR  0  /**< Edit numeber only, without bar nor units */
+#define LEVELEDIT_SINGLE 1  /**< Single channel editing */
+#define LEVELEDIT_DOUBLE 2  /**< Double channel editing */
+
+
+
+/* Fwd decl */
+struct Bitmap;
+struct LevelEdit;
+
+/** Type for callback used to set meter levels */
+typedef void level_set_callback(void);
+
+/** Type for callback used to customize display of units */
+typedef void display_callback(struct LevelEdit *);
+
+/**
+ * State of a level meter
+ */
+typedef struct LevelEdit {
+       int type;           /*<! Type of level edititing mode (see prev defines) */
+       const char *title;  /*<! Title on top of screen */
+       const char *unit;   /*<! Unit of quantity changed by this LevelEdit */
+       int min;            /*<! Minimum level */
+       int max;            /*<! Maximum level */
+       int step;           /*<! Value of a single increment/decrement */
+
+       level_set_callback *set_hook;     /*<! Callback called when a value is changed  */
+       display_callback   *display_hook; /*<! Callback for complex unit display */
+       int *ch1_val;                     /*<! (left) Value edited by this leveledit */
+       int *ch2_val;                     /*<! Right channel edited */
+
+       struct Bitmap   *bitmap;  /*<! Bitmap where the whole thing is rendered */
+} LevelEdit;
+
+
+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 *change_hook, display_callback *display_hook);
+void level_edit(struct LevelEdit *lev);
+
+#endif /* GUI_LEVELEDIT_H */