Quickscreen: 4th item
[kugel-rb.git] / apps / gui / quickscreen.c
blobab13e67a811b42e74d3c1b40b47931ea9720af23
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include <stdio.h>
24 #include "config.h"
25 #include "system.h"
26 #include "icons.h"
27 #include "font.h"
28 #include "kernel.h"
29 #include "misc.h"
30 #include "action.h"
31 #include "settings_list.h"
32 #include "lang.h"
33 #include "playlist.h"
34 #include "dsp.h"
35 #include "viewport.h"
36 #include "audio.h"
37 #include "quickscreen.h"
38 #include "talk.h"
39 #include "list.h"
40 #include "option_select.h"
41 #include "debug.h"
43 static struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
44 static struct viewport vp_icons[NB_SCREENS];
45 /* 1 top, 1 bottom, 2 on either side, 1 for the icons
46 * if enough space, top and bottom have 2 lines */
47 #define MIN_LINES 5
48 #define MAX_NEEDED_LINES 10
49 /* pixels between the 2 center items minimum or between text and icons,
50 * and between text and parent boundaries */
51 #define MARGIN 10
52 #define CENTER_ICONAREA_SIZE (MARGIN+8*2)
54 static void quickscreen_fix_viewports(struct gui_quickscreen *qs,
55 struct screen *display,
56 struct viewport *parent)
58 #ifdef HAVE_REMOTE_LCD
59 int screen = display->screen_type;
60 #else
61 const int screen = 0;
62 #endif
63 int char_height, width, pad = 0;
64 int left_width, right_width, vert_lines;
65 unsigned char *s;
66 int nb_lines = viewport_get_nb_lines(parent);
68 /* nb_lines only returns the number of fully visible lines, small screens
69 or really large fonts could cause problems with the calculation below.
71 if(nb_lines==0)
72 nb_lines++;
74 char_height = parent->height/nb_lines;
76 /* center the icons VP first */
77 vp_icons[screen] = *parent;
78 vp_icons[screen].width = CENTER_ICONAREA_SIZE; /* abosulte smallest allowed */
79 vp_icons[screen].x = parent->x;
80 vp_icons[screen].x += (parent->width-CENTER_ICONAREA_SIZE)/2;
83 vps[screen][QUICKSCREEN_BOTTOM] = *parent;
84 vps[screen][QUICKSCREEN_TOP] = *parent;
85 /* depending on the space the top/buttom items use 1 or 2 lines */
86 if (nb_lines < MIN_LINES)
87 vert_lines = 1;
88 else
89 vert_lines = 2;
90 vps[screen][QUICKSCREEN_TOP].y = parent->y;
91 vps[screen][QUICKSCREEN_TOP].height = vps[screen][QUICKSCREEN_BOTTOM].height
92 = vert_lines*char_height;
93 vps[screen][QUICKSCREEN_BOTTOM].y
94 = parent->y + parent->height - vps[screen][QUICKSCREEN_BOTTOM].height;
96 /* enough space vertically, so put a nice margin */
97 if (nb_lines >= MAX_NEEDED_LINES)
99 vps[screen][QUICKSCREEN_TOP].y += MARGIN;
100 vps[screen][QUICKSCREEN_BOTTOM].y -= MARGIN;
103 vp_icons[screen].y = vps[screen][QUICKSCREEN_TOP].y
104 + vps[screen][QUICKSCREEN_TOP].height;
105 vp_icons[screen].height = parent->height - vp_icons[screen].y;
106 vp_icons[screen].height -= parent->height - vps[screen][QUICKSCREEN_BOTTOM].y;
108 /* adjust the left/right items widths to fit the screen nicely */
109 s = P2STR(ID2P(qs->items[QUICKSCREEN_LEFT]->lang_id));
110 left_width = display->getstringsize(s, NULL, NULL);
111 s = P2STR(ID2P(qs->items[QUICKSCREEN_RIGHT]->lang_id));
112 right_width = display->getstringsize(s, NULL, NULL);
114 width = MAX(left_width, right_width);
115 if (width*2 + vp_icons[screen].width > display->lcdwidth)
116 width = (display->lcdwidth - vp_icons[screen].width)/2;
117 else /* add more gap in icons vp */
119 int excess = display->lcdwidth - vp_icons[screen].width - width*2;
120 if (excess > MARGIN*4)
122 pad = MARGIN;
123 excess -= MARGIN*2;
125 vp_icons[screen].x -= excess/2;
126 vp_icons[screen].width += excess;
129 vps[screen][QUICKSCREEN_LEFT] = *parent;
130 vps[screen][QUICKSCREEN_LEFT].x = parent->x + pad;
131 vps[screen][QUICKSCREEN_LEFT].width = width;
133 vps[screen][QUICKSCREEN_RIGHT] = *parent;
134 vps[screen][QUICKSCREEN_RIGHT].x = parent->x + parent->width - width - pad;
135 vps[screen][QUICKSCREEN_RIGHT].width = width;
137 vps[screen][QUICKSCREEN_LEFT].height = vps[screen][QUICKSCREEN_RIGHT].height
138 = 2*char_height;
140 vps[screen][QUICKSCREEN_LEFT].y = vps[screen][QUICKSCREEN_RIGHT].y
141 = parent->y + (parent->height/2) - char_height;
143 /* shrink the icons vp by a few pixels if there is room so the arrows
144 aren't drawn right next to the text */
145 if (vp_icons[screen].width > CENTER_ICONAREA_SIZE*2)
147 vp_icons[screen].width -= CENTER_ICONAREA_SIZE*2/3;
148 vp_icons[screen].x += CENTER_ICONAREA_SIZE*2/6;
150 if (vp_icons[screen].height > CENTER_ICONAREA_SIZE*2)
152 vp_icons[screen].height -= CENTER_ICONAREA_SIZE*2/3;
153 vp_icons[screen].y += CENTER_ICONAREA_SIZE*2/6;
157 static void quickscreen_draw_text(const char *s, int item, bool title,
158 struct screen *display, struct viewport *vp)
160 int nb_lines = viewport_get_nb_lines(vp);
161 int w, h, line = 0, x = 0;
162 display->getstringsize(s, &w, &h);
164 if (nb_lines > 1 && !title)
165 line = 1;
166 switch (item)
168 case QUICKSCREEN_TOP:
169 case QUICKSCREEN_BOTTOM:
170 x = (vp->width - w)/2;
171 break;
172 case QUICKSCREEN_LEFT:
173 x = 0;
174 break;
175 case QUICKSCREEN_RIGHT:
176 x = vp->width - w;
177 break;
179 if (w>vp->width)
180 display->puts_scroll(0, line, s);
181 else
182 display->putsxy(x, line*h, s);
185 static void gui_quickscreen_draw(struct gui_quickscreen *qs,
186 struct screen *display,
187 struct viewport *parent)
189 #ifdef HAVE_REMOTE_LCD
190 int screen = display->screen_type;
191 #else
192 const int screen = 0;
193 #endif
195 int i;
196 char buf[MAX_PATH];
197 unsigned const char *title, *value;
198 void *setting;
199 int temp;
200 display->set_viewport(parent);
201 display->clear_viewport();
202 for (i=0; i<QUICKSCREEN_ITEM_COUNT; i++)
204 if (!qs->items[i])
205 continue;
206 display->set_viewport(&vps[screen][i]);
207 display->scroll_stop(&vps[screen][i]);
209 title = P2STR(ID2P(qs->items[i]->lang_id));
210 setting = qs->items[i]->setting;
211 temp = option_value_as_int(qs->items[i]);
212 value = option_get_valuestring((struct settings_list*)qs->items[i],
213 buf, MAX_PATH, temp);
215 if (vps[screen][i].height < display->getcharheight()*2)
217 char text[MAX_PATH];
218 snprintf(text, MAX_PATH, "%s: %s", title, value);
219 quickscreen_draw_text(text, i, true, display, &vps[screen][i]);
221 else
223 quickscreen_draw_text(title, i, true, display, &vps[screen][i]);
224 quickscreen_draw_text(value, i, false, display, &vps[screen][i]);
226 display->update_viewport();
228 /* draw the icons */
229 display->set_viewport(&vp_icons[screen]);
231 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
232 (vp_icons[screen].width/2) - 4, 0, 7, 8);
233 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
234 vp_icons[screen].width - 8,
235 (vp_icons[screen].height/2) - 4, 7, 8);
236 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward], 0,
237 (vp_icons[screen].height/2) - 4, 7, 8);
239 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
240 (vp_icons[screen].width/2) - 4,
241 vp_icons[screen].height - 8, 7, 8);
243 display->set_viewport(parent);
244 display->update_viewport();
245 display->set_viewport(NULL);
248 static void talk_qs_option(struct settings_list *opt, bool enqueue)
250 if (global_settings.talk_menu) {
251 if(!enqueue)
252 talk_shutup();
253 talk_id(opt->lang_id, true);
254 option_talk_value(opt, option_value_as_int(opt), true);
259 * Does the actions associated to the given button if any
260 * - qs : the quickscreen
261 * - button : the key we are going to analyse
262 * returns : true if the button corresponded to an action, false otherwise
264 static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
266 int item;
267 bool invert = false;
268 switch(button)
270 case ACTION_QS_TOP:
271 invert = true;
272 item = QUICKSCREEN_TOP;
273 break;
274 case ACTION_QS_LEFT:
275 invert = true;
276 item = QUICKSCREEN_LEFT;
277 break;
279 case ACTION_QS_DOWN:
280 item = QUICKSCREEN_BOTTOM;
281 break;
283 case ACTION_QS_RIGHT:
284 item = QUICKSCREEN_RIGHT;
285 break;
287 default:
288 return false;
290 option_select_next_val((struct settings_list *)qs->items[item], invert, true);
291 talk_qs_option((struct settings_list *)qs->items[item], false);
292 return true;
294 #ifdef HAVE_TOUCHSCREEN
295 /* figure out which button was pressed...
296 * top is exit, left/right/botton are the respective actions
298 static int quickscreen_touchscreen_button(void)
300 short x,y;
301 if (action_get_touchscreen_press(&x, &y) != BUTTON_REL)
302 return ACTION_NONE;
303 if (y < vps[SCREEN_MAIN][QUICKSCREEN_LEFT].y)
304 return ACTION_STD_CANCEL;
305 else if (y > vps[SCREEN_MAIN][QUICKSCREEN_LEFT].y +
306 vps[SCREEN_MAIN][QUICKSCREEN_LEFT].height)
307 return ACTION_QS_DOWN;
308 else if (x < vps[SCREEN_MAIN][QUICKSCREEN_LEFT].x +
309 vps[SCREEN_MAIN][QUICKSCREEN_LEFT].width)
310 return ACTION_QS_LEFT;
311 else if (x >= vps[SCREEN_MAIN][QUICKSCREEN_RIGHT].x)
312 return ACTION_QS_RIGHT;
313 return ACTION_STD_CANCEL;
315 #endif
316 static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter)
318 int button, i;
319 struct viewport vp[NB_SCREENS];
320 bool changed = false;
321 /* To quit we need either :
322 * - a second press on the button that made us enter
323 * - an action taken while pressing the enter button,
324 * then release the enter button*/
325 bool can_quit = false;
326 FOR_NB_SCREENS(i)
328 screens[i].set_viewport(NULL);
329 screens[i].stop_scroll();
330 viewport_set_defaults(&vp[i], i);
331 quickscreen_fix_viewports(qs, &screens[i], &vp[i]);
332 gui_quickscreen_draw(qs, &screens[i], &vp[i]);
334 /* Announce current selection on entering this screen. This is all
335 queued up, but can be interrupted as soon as a setting is
336 changed. */
337 cond_talk_ids(VOICE_QUICKSCREEN);
338 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_LEFT], true);
339 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_BOTTOM], true);
340 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_RIGHT], true);
341 while (true) {
342 button = get_action(CONTEXT_QUICKSCREEN,HZ/5);
343 #ifdef HAVE_TOUCHSCREEN
344 if (button == ACTION_TOUCHSCREEN)
345 button = quickscreen_touchscreen_button();
346 #endif
347 if(default_event_handler(button) == SYS_USB_CONNECTED)
348 return(true);
349 if(gui_quickscreen_do_button(qs, button))
351 changed = true;
352 can_quit=true;
353 FOR_NB_SCREENS(i)
354 gui_quickscreen_draw(qs, &screens[i], &vp[i]);
355 if (qs->callback)
356 qs->callback(qs);
358 else if(button==button_enter)
359 can_quit=true;
361 if((button == button_enter) && can_quit)
362 break;
364 if(button==ACTION_STD_CANCEL)
365 break;
367 /* Notify that we're exiting this screen */
368 cond_talk_ids_fq(VOICE_OK);
369 return changed;
372 static inline const struct settings_list *get_setting(int gs_value,
373 const struct settings_list *defaultval)
375 if (gs_value != -1 && gs_value < nb_settings &&
376 is_setting_quickscreenable(&settings[gs_value]))
377 return &settings[gs_value];
378 return defaultval;
380 bool quick_screen_quick(int button_enter)
382 struct gui_quickscreen qs;
383 bool oldshuffle = global_settings.playlist_shuffle;
384 int oldrepeat = global_settings.repeat_mode;
386 qs.items[QUICKSCREEN_TOP] =
387 get_setting(global_settings.qs_item_top,
388 find_setting(&global_settings.party_mode, NULL));
389 qs.items[QUICKSCREEN_LEFT] =
390 get_setting(global_settings.qs_item_left,
391 find_setting(&global_settings.playlist_shuffle, NULL));
392 qs.items[QUICKSCREEN_RIGHT] =
393 get_setting(global_settings.qs_item_right,
394 find_setting(&global_settings.repeat_mode, NULL));
395 qs.items[QUICKSCREEN_BOTTOM] =
396 get_setting(global_settings.qs_item_bottom,
397 find_setting(&global_settings.dirfilter, NULL));
399 qs.callback = NULL;
400 if (gui_syncquickscreen_run(&qs, button_enter))
402 settings_save();
403 settings_apply(false);
404 /* make sure repeat/shuffle/any other nasty ones get updated */
405 if ( oldrepeat != global_settings.repeat_mode &&
406 (audio_status() & AUDIO_STATUS_PLAY) )
408 audio_flush_and_reload_tracks();
410 if (oldshuffle != global_settings.playlist_shuffle
411 && audio_status() & AUDIO_STATUS_PLAY)
413 #if CONFIG_CODEC == SWCODEC
414 dsp_set_replaygain();
415 #endif
416 if (global_settings.playlist_shuffle)
417 playlist_randomise(NULL, current_tick, true);
418 else
419 playlist_sort(NULL, true);
422 return(0);
425 #ifdef BUTTON_F3
426 bool quick_screen_f3(int button_enter)
428 struct gui_quickscreen qs;
429 qs.items[QUICKSCREEN_LEFT] =
430 find_setting(&global_settings.scrollbar, NULL);
431 qs.items[QUICKSCREEN_RIGHT] =
432 find_setting(&global_settings.statusbar, NULL);
433 qs.items[QUICKSCREEN_BOTTOM] =
434 find_setting(&global_settings.flip_display, NULL);
435 qs.callback = NULL;
436 if (gui_syncquickscreen_run(&qs, button_enter))
438 settings_save();
439 settings_apply(false);
441 return(0);
443 #endif /* BUTTON_F3 */
445 /* stuff to make the quickscreen configurable */
446 bool is_setting_quickscreenable(const struct settings_list *setting)
448 /* to keep things simple, only settings which have a lang_id set are ok */
449 if (setting->lang_id < 0 || (setting->flags&F_BANFROMQS))
450 return false;
451 switch (setting->flags&F_T_MASK)
453 case F_T_BOOL:
454 return true;
455 case F_T_INT:
456 case F_T_UINT:
457 return (setting->RESERVED != NULL);
458 default:
459 return false;
463 void set_as_qs_item(const struct settings_list *setting,
464 enum quickscreen_item item)
466 int i;
467 for(i=0;i<nb_settings;i++)
469 if (&settings[i] == setting)
470 break;
472 switch (item)
474 case QUICKSCREEN_TOP:
475 global_settings.qs_item_top = i;
476 break;
477 case QUICKSCREEN_LEFT:
478 global_settings.qs_item_left = i;
479 break;
480 case QUICKSCREEN_RIGHT:
481 global_settings.qs_item_right = i;
482 break;
483 case QUICKSCREEN_BOTTOM:
484 global_settings.qs_item_bottom = i;
485 break;
486 default: /* shut the compiler up */
487 break;