dd3259cca081431acca9614499060efcfb2ec68e
[bertos.git] / bertos / gui / leveledit.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Generic editor for (volume/gain/contrast/...) setting.
34  *
35  * \version $Id$
36  * \author Stefano Fedrigo <aleph@develer.com>
37  */
38
39 #include "leveledit.h"
40
41 #include "cfg/cfg_gfx.h"
42 #include <cfg/macros.h> /* MAX() */
43
44 #include <drv/kbd.h>
45 #include <drv/timer.h>
46
47 #include <gui/levelbar.h>
48
49 #include <mware/pgm.h>
50
51 #include <gfx/text.h>
52 #include <gfx/font.h>
53
54 #if CONFIG_MENU_MENUBAR
55 #include <gui/menubar.h>
56 #endif
57
58 #include <drv/lcd_gfx.h>
59
60 #warning FIXME: Revise me!
61
62 #define LBAR_HEIGHT 16
63
64 /**
65  * Allow user to change level.
66  */
67 void level_edit(struct LevelEdit *lev)
68 {
69 #if CONFIG_MENU_MENUBAR
70         /* Labels for menubars */
71         enum LabelId ch_labels[] = { LABEL_C1PLUS2, LABEL_CH_1, LABEL_CH_2 };
72         const_iptr_t labels[] =
73         {
74                 (const_iptr_t)LABEL_BACK,
75                 (const_iptr_t)LABEL_MINUS,
76                 (const_iptr_t)LABEL_PLUS,
77                 (const_iptr_t)LABEL_EMPTY
78         };
79         struct MenuBar mb;
80 #endif /* CONFIG_MENU_MENUBAR */
81
82         struct LevelBar bar1, bar2;
83         keymask_t keys, old_rpt_mask;
84         int step, rep_step;
85
86         rep_step = MAX(lev->step, ((lev->max - lev->min) / 200));
87         step = lev->step;
88
89         // Allow keys repetition.
90         old_rpt_mask = kbd_setRepeatMask(K_UP | K_DOWN);
91
92         text_clear(lev->bitmap);
93         //text_style(STYLEF_UNDERLINE, STYLEF_UNDERLINE);
94         text_puts(lev->title, lev->bitmap);
95         //text_style(0, STYLEF_UNDERLINE);
96
97         if (lev->type & LEVELEDIT_DOUBLE)
98         {
99                 int chn = 0;  /* edit both channels */
100
101                 /* Levelbars init */
102                 lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL,
103                                 lev->min, lev->max, *lev->ch1_val, 0, 16, lev->bitmap->width / 2 - 1, 23);
104                 lbar_init(&bar2, lev->bitmap, LBAR_HORIZONTAL,
105                                 lev->min, lev->max, *lev->ch2_val, lev->bitmap->width / 2 + 1, 16, lev->bitmap->width, 23);
106
107                 #if CONFIG_MENU_MENUBAR
108                         labels[3] = (const_iptr_t)ch_labels[chn];
109                         mbar_init(&mb, lev->bitmap, labels, countof(labels));
110                         mbar_draw(&mb);
111                 #endif /* CONFIG_MENU_MENUBAR */
112
113                 /* Input loop for double level setting */
114                 for (;;)
115                 {
116                         #if CONFIG_LEVELEDIT_TIMEOUT != 0
117                                 ticks_t idle_timeout = timer_clock();
118                         #endif
119                         do
120                         {
121                                 if (lev->display_hook)
122                                         lev->display_hook(lev);
123                                 else
124                                 {
125                                         text_xprintf(lev->bitmap, 1, 0, TEXT_CENTER | TEXT_FILL, lev->unit);
126                                         PGM_FUNC(text_xprintf)(lev->bitmap, 1, 3, 0, PGM_STR("%d"), *lev->ch1_val);
127                                         PGM_FUNC(text_xprintf)(lev->bitmap, 1, 14, 0, PGM_STR("%d"), *lev->ch2_val);
128
129                                         lbar_setLevel(&bar1, *lev->ch1_val);
130                                         lbar_setLevel(&bar2, *lev->ch2_val);
131                                         lbar_draw(&bar1);
132                                         lbar_draw(&bar2);
133                                 }
134
135                                 #if CONFIG_LEVELEDIT_TIMEOUT != 0
136                                         if (timer_clock() - idle_timeout > ms_to_ticks(CONFIG_LEVELEDIT_TIMEOUT))
137                                         {
138                                                 /* Accept input implicitly */
139                                                 keys = K_OK;
140                                                 break;
141                                         }
142                                 #endif
143                         }
144                         while (!(keys = kbd_peek()));
145
146                         if (keys & K_CANCEL)
147                                 break;
148
149                         if (keys & K_OK)
150                         {
151                                 chn = (chn + 1) % 3;
152
153                                 #if CONFIG_MENU_MENUBAR
154                                         labels[3] = (const_iptr_t)ch_labels[chn];
155                                         mbar_draw(&mb);
156                                 #endif /* CONFIG_MENU_MENUBAR */
157                         }
158
159                         /* Increment step to achieve greater accelerations on larger values */
160                         if (keys & K_REPEAT)
161                                 step = MIN(rep_step, step + 1);
162                         else
163                                 step = lev->step;
164
165                         if (keys & (K_UP | K_DOWN))
166                         {
167                                 if (keys & K_UP)
168                                 {
169                                         /* If changing both channels (chn == 0), don't change
170                                          * level if one of two is at min or max */
171                                         if (chn != 0 ||
172                                                         (*lev->ch1_val + step <= lev->max
173                                                          && *lev->ch2_val + step <= lev->max))
174                                         {
175                                                 /* If chn == 0 change both channels */
176                                                 if (chn != 2)
177                                                 {
178                                                         *lev->ch1_val += step;
179                                                         if (*lev->ch1_val > lev->max)
180                                                                 *lev->ch1_val = lev->max;
181                                                 }
182                                                 if (chn != 1)
183                                                 {
184                                                         *lev->ch2_val += step;
185                                                         if (*lev->ch2_val > lev->max)
186                                                                 *lev->ch2_val = lev->max;
187                                                 }
188                                         }
189                                 }
190                                 else
191                                 {
192                                         if (chn != 0 ||
193                                                         (*lev->ch1_val - step >= lev->min
194                                                          && *lev->ch2_val - step >= lev->min))
195                                         {
196                                                 if (chn != 2)
197                                                 {
198                                                         *lev->ch1_val -= step;
199                                                         if (*lev->ch1_val < lev->min)
200                                                                 *lev->ch1_val = lev->min;
201                                                 }
202                                                 if (chn != 1)
203                                                 {
204                                                         *lev->ch2_val -= step;
205                                                         if (*lev->ch2_val < lev->min)
206                                                                 *lev->ch2_val = lev->min;
207                                                 }
208                                         }
209                                 }
210
211                                 if (lev->set_hook)
212                                         lev->set_hook();
213                         }
214                 } // end for(;;)
215         }
216         else
217         {
218                 const PGM_ATTR char *fmt = lev->unit ? PGM_STR("%d %s") : PGM_STR("%d");
219
220 /*
221                 const int textw = MAX(PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->max, lev->unit),
222                                 PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->min, lev->unit));
223
224                 const coord_t barlen = lev->bitmap->width - 6 - textw;
225 */
226                 const coord_t barlen = lev->bitmap->width;
227                 const coord_t barvtop = lev->bitmap->height / 2 - LBAR_HEIGHT/2 + lev->bitmap->font->height;
228                 lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL,
229                           lev->min, lev->max, *lev->ch1_val,
230                           0, barvtop, barlen,  barvtop + LBAR_HEIGHT);
231
232                 #if CONFIG_MENU_MENUBAR
233                         mbar_init(&mb, lev->bitmap, labels, countof(labels));
234                         mbar_draw(&mb);
235                 #endif /* CONFIG_MENU_MENUBAR */
236
237                 /* Input loop for single level setting */
238                 for (;;)
239                 {
240                         #if CONFIG_LEVELEDIT_TIMEOUT != 0
241                                 ticks_t idle_timeout = timer_clock();
242                         #endif
243                         do
244                         {
245                                 if (lev->display_hook)
246                                         lev->display_hook(lev);
247                                 else
248                                 {
249                                         if (lev->type != LEVELEDIT_NOBAR)
250                                         {
251                                                 lbar_setLevel(&bar1, *lev->ch1_val);
252                                                 lbar_draw(&bar1);
253                                         }
254                                         PGM_FUNC(text_xyprintf)(lev->bitmap, 0, bar1.y1 - lev->bitmap->font->height,
255                                                 TEXT_CENTER | TEXT_FILL, fmt, *lev->ch1_val, lev->unit);
256                                 }
257
258                                 #if CONFIG_LEVELEDIT_TIMEOUT != 0
259                                         if (timer_clock() - idle_timeout > CONFIG_LEVELEDIT_TIMEOUT)
260                                         {
261                                                 /* Accept input implicitly */
262                                                 keys = K_CANCEL;
263                                                 break;
264                                         }
265                                 #endif
266
267                         }
268                         while (!(keys = kbd_peek()));
269
270                         if (keys & K_CANCEL)
271                                 break;
272
273                         /* Increment step to achieve greater accelerations on larger values */
274                         if (keys & K_REPEAT)
275                                 step = MIN(rep_step, step + 1);
276                         else
277                                 step = lev->step;
278
279                         if (keys & K_UP)
280                         {
281                                 *lev->ch1_val += step;
282                                 if (*lev->ch1_val > lev->max)
283                                         *lev->ch1_val = lev->max;
284                         }
285
286                         if (keys & K_DOWN)
287                         {
288                                 *lev->ch1_val -= step;
289                                 if (*lev->ch1_val < lev->min)
290                                         *lev->ch1_val = lev->min;
291                         }
292
293                         if (lev->set_hook)
294                                 lev->set_hook();
295                 }
296         }
297
298         kbd_setRepeatMask(old_rpt_mask);
299 }
300
301 /**
302  * LevelEdit structure initialization.
303  * Init data structure and init LevelEdit widgets.
304  */
305 void level_init(struct LevelEdit *lev,
306                 int type,
307                 struct Bitmap *bmp, const char *title, const char *unit,
308                 int min, int max, int step,
309                 int *ch1_val, int *ch2_val,
310                 level_set_callback *set_hook, display_callback *display_hook)
311 {
312         lev->type   = type;
313         lev->bitmap = bmp;
314         lev->title  = title;
315         lev->unit   = unit;
316         lev->min    = min;
317         lev->max    = max;
318         lev->step   = step;
319
320         lev->ch1_val = ch1_val;
321         lev->ch2_val = ch2_val;
322         lev->set_hook     = set_hook;
323         lev->display_hook = display_hook;
324 }