Don't forget the touchscreen targets!
[kugel-rb.git] / apps / gui / quickscreen.c
blob639233b3b15aebf1950d8f238f9cf4fa6fa6189f
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 /* 1 top, 1 bottom, 2 on either side, 1 for the icons
44 * if enough space, top and bottom have 2 lines */
45 #define MIN_LINES 5
46 #define MAX_NEEDED_LINES 10
47 /* pixels between the 2 center items minimum or between text and icons,
48 * and between text and parent boundaries */
49 #define MARGIN 10
50 #define CENTER_ICONAREA_SIZE (MARGIN+8*2)
52 static void quickscreen_fix_viewports(struct gui_quickscreen *qs,
53 struct screen *display,
54 struct viewport *parent,
55 struct viewport
56 vps[QUICKSCREEN_ITEM_COUNT],
57 struct viewport *vp_icons)
59 int char_height, width, pad = 0;
60 int left_width, right_width, vert_lines;
61 unsigned char *s;
62 int nb_lines = viewport_get_nb_lines(parent);
64 /* nb_lines only returns the number of fully visible lines, small screens
65 or really large fonts could cause problems with the calculation below.
67 if(nb_lines==0)
68 nb_lines++;
70 char_height = parent->height/nb_lines;
72 /* center the icons VP first */
73 *vp_icons = *parent;
74 vp_icons->width = CENTER_ICONAREA_SIZE; /* abosulte smallest allowed */
75 vp_icons->x = parent->x;
76 vp_icons->x += (parent->width-CENTER_ICONAREA_SIZE)/2;
79 vps[QUICKSCREEN_BOTTOM] = *parent;
80 vps[QUICKSCREEN_TOP] = *parent;
81 /* depending on the space the top/buttom items use 1 or 2 lines */
82 if (nb_lines < MIN_LINES)
83 vert_lines = 1;
84 else
85 vert_lines = 2;
86 vps[QUICKSCREEN_TOP].y = parent->y;
87 vps[QUICKSCREEN_TOP].height = vps[QUICKSCREEN_BOTTOM].height
88 = vert_lines*char_height;
89 vps[QUICKSCREEN_BOTTOM].y
90 = parent->y + parent->height - vps[QUICKSCREEN_BOTTOM].height;
92 /* enough space vertically, so put a nice margin */
93 if (nb_lines >= MAX_NEEDED_LINES)
95 vps[QUICKSCREEN_TOP].y += MARGIN;
96 vps[QUICKSCREEN_BOTTOM].y -= MARGIN;
99 vp_icons->y = vps[QUICKSCREEN_TOP].y
100 + vps[QUICKSCREEN_TOP].height;
101 vp_icons->height = parent->height - vp_icons->y;
102 vp_icons->height -= parent->height - vps[QUICKSCREEN_BOTTOM].y;
104 /* adjust the left/right items widths to fit the screen nicely */
105 s = P2STR(ID2P(qs->items[QUICKSCREEN_LEFT]->lang_id));
106 left_width = display->getstringsize(s, NULL, NULL);
107 s = P2STR(ID2P(qs->items[QUICKSCREEN_RIGHT]->lang_id));
108 right_width = display->getstringsize(s, NULL, NULL);
110 width = MAX(left_width, right_width);
111 if (width*2 + vp_icons->width > parent->width)
112 { /* crop text viewports */
113 width = (parent->width - vp_icons->width)/2;
115 else
116 { /* add more gap in icons vp */
117 int excess = parent->width - vp_icons->width - width*2;
118 if (excess > MARGIN*4)
120 pad = MARGIN;
121 excess -= MARGIN*2;
123 vp_icons->x -= excess/2;
124 vp_icons->width += excess;
127 vps[QUICKSCREEN_LEFT] = *parent;
128 vps[QUICKSCREEN_LEFT].x = parent->x + pad;
129 vps[QUICKSCREEN_LEFT].width = width;
131 vps[QUICKSCREEN_RIGHT] = *parent;
132 vps[QUICKSCREEN_RIGHT].x = parent->x + parent->width - width - pad;
133 vps[QUICKSCREEN_RIGHT].width = width;
135 vps[QUICKSCREEN_LEFT].height = vps[QUICKSCREEN_RIGHT].height
136 = 2*char_height;
138 vps[QUICKSCREEN_LEFT].y = vps[QUICKSCREEN_RIGHT].y
139 = parent->y + (parent->height/2) - char_height;
141 /* shrink the icons vp by a few pixels if there is room so the arrows
142 aren't drawn right next to the text */
143 if (vp_icons->width > CENTER_ICONAREA_SIZE*2)
145 vp_icons->width -= CENTER_ICONAREA_SIZE*2/3;
146 vp_icons->x += CENTER_ICONAREA_SIZE*2/6;
148 if (vp_icons->height > CENTER_ICONAREA_SIZE*2)
150 vp_icons->height -= CENTER_ICONAREA_SIZE*2/3;
151 vp_icons->y += CENTER_ICONAREA_SIZE*2/6;
155 static void quickscreen_draw_text(const char *s, int item, bool title,
156 struct screen *display, struct viewport *vp)
158 int nb_lines = viewport_get_nb_lines(vp);
159 int w, h, line = 0, x = 0;
160 display->getstringsize(s, &w, &h);
162 if (nb_lines > 1 && !title)
163 line = 1;
164 switch (item)
166 case QUICKSCREEN_TOP:
167 case QUICKSCREEN_BOTTOM:
168 x = (vp->width - w)/2;
169 break;
170 case QUICKSCREEN_LEFT:
171 x = 0;
172 break;
173 case QUICKSCREEN_RIGHT:
174 x = vp->width - w;
175 break;
177 if (w>vp->width)
178 display->puts_scroll(0, line, s);
179 else
180 display->putsxy(x, line*h, s);
183 static void gui_quickscreen_draw(const struct gui_quickscreen *qs,
184 struct screen *display,
185 struct viewport *parent,
186 struct viewport vps[QUICKSCREEN_ITEM_COUNT],
187 struct viewport *vp_icons)
189 int i;
190 char buf[MAX_PATH];
191 unsigned const char *title, *value;
192 void *setting;
193 int temp;
194 display->set_viewport(parent);
195 display->clear_viewport();
196 for (i=0; i<QUICKSCREEN_ITEM_COUNT; i++)
198 if (!qs->items[i])
199 continue;
200 display->set_viewport(&vps[i]);
201 display->scroll_stop(&vps[i]);
203 title = P2STR(ID2P(qs->items[i]->lang_id));
204 setting = qs->items[i]->setting;
205 temp = option_value_as_int(qs->items[i]);
206 value = option_get_valuestring((struct settings_list*)qs->items[i],
207 buf, MAX_PATH, temp);
209 if (vps[i].height < display->getcharheight()*2)
211 char text[MAX_PATH];
212 snprintf(text, MAX_PATH, "%s: %s", title, value);
213 quickscreen_draw_text(text, i, true, display, &vps[i]);
215 else
217 quickscreen_draw_text(title, i, true, display, &vps[i]);
218 quickscreen_draw_text(value, i, false, display, &vps[i]);
220 display->update_viewport();
222 /* draw the icons */
223 display->set_viewport(vp_icons);
225 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
226 (vp_icons->width/2) - 4, 0, 7, 8);
227 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
228 vp_icons->width - 8,
229 (vp_icons->height/2) - 4, 7, 8);
230 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward], 0,
231 (vp_icons->height/2) - 4, 7, 8);
233 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
234 (vp_icons->width/2) - 4,
235 vp_icons->height - 8, 7, 8);
237 display->set_viewport(parent);
238 display->update_viewport();
239 display->set_viewport(NULL);
242 static void talk_qs_option(struct settings_list *opt, bool enqueue)
244 if (global_settings.talk_menu) {
245 if(!enqueue)
246 talk_shutup();
247 talk_id(opt->lang_id, true);
248 option_talk_value(opt, option_value_as_int(opt), true);
253 * Does the actions associated to the given button if any
254 * - qs : the quickscreen
255 * - button : the key we are going to analyse
256 * returns : true if the button corresponded to an action, false otherwise
258 static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
260 int item;
261 bool invert = false;
262 switch(button)
264 case ACTION_QS_TOP:
265 invert = true;
266 item = QUICKSCREEN_TOP;
267 break;
268 case ACTION_QS_LEFT:
269 invert = true;
270 item = QUICKSCREEN_LEFT;
271 break;
273 case ACTION_QS_DOWN:
274 item = QUICKSCREEN_BOTTOM;
275 break;
277 case ACTION_QS_RIGHT:
278 item = QUICKSCREEN_RIGHT;
279 break;
281 default:
282 return false;
284 option_select_next_val((struct settings_list *)qs->items[item], invert, true);
285 talk_qs_option((struct settings_list *)qs->items[item], false);
286 return true;
288 #ifdef HAVE_TOUCHSCREEN
289 /* figure out which button was pressed...
290 * top is exit, left/right/botton are the respective actions
292 static int quickscreen_touchscreen_button(const struct viewport
293 vps[QUICKSCREEN_ITEM_COUNT])
295 short x,y;
296 if (action_get_touchscreen_press(&x, &y) != BUTTON_REL)
297 return ACTION_NONE;
298 if (y < vps[QUICKSCREEN_LEFT].y)
299 return ACTION_STD_CANCEL;
300 else if (y > vps[QUICKSCREEN_LEFT].y +
301 vps[QUICKSCREEN_LEFT].height)
302 return ACTION_QS_DOWN;
303 else if (x < vps[QUICKSCREEN_LEFT].x +
304 vps[QUICKSCREEN_LEFT].width)
305 return ACTION_QS_LEFT;
306 else if (x >= vps[QUICKSCREEN_RIGHT].x)
307 return ACTION_QS_RIGHT;
308 return ACTION_STD_CANCEL;
310 #endif
311 static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter)
313 int button, i, j;
314 struct viewport parent[NB_SCREENS];
315 struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
316 struct viewport vp_icons[NB_SCREENS];
317 bool changed = false;
318 /* To quit we need either :
319 * - a second press on the button that made us enter
320 * - an action taken while pressing the enter button,
321 * then release the enter button*/
322 bool can_quit = false;
323 FOR_NB_SCREENS(i)
325 screens[i].set_viewport(NULL);
326 screens[i].stop_scroll();
327 viewport_set_defaults(&parent[i], i);
328 quickscreen_fix_viewports(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
329 gui_quickscreen_draw(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
331 /* Announce current selection on entering this screen. This is all
332 queued up, but can be interrupted as soon as a setting is
333 changed. */
334 cond_talk_ids(VOICE_QUICKSCREEN);
335 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_TOP], true);
336 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_LEFT], true);
337 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_BOTTOM], true);
338 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_RIGHT], true);
339 while (true) {
340 button = get_action(CONTEXT_QUICKSCREEN,HZ/5);
341 #ifdef HAVE_TOUCHSCREEN
342 if (button == ACTION_TOUCHSCREEN)
343 button = quickscreen_touchscreen_button(vps[SCREEN_MAIN]);
344 #endif
345 if(default_event_handler(button) == SYS_USB_CONNECTED)
346 return(true);
347 if(gui_quickscreen_do_button(qs, button))
349 changed = true;
350 can_quit=true;
351 FOR_NB_SCREENS(i)
352 gui_quickscreen_draw(qs, &screens[i], &parent[i], vps[i],&vp_icons[i]);
353 if (qs->callback)
354 qs->callback(qs);
356 else if(button==button_enter)
357 can_quit=true;
359 if((button == button_enter) && can_quit)
360 break;
362 if(button==ACTION_STD_CANCEL)
363 break;
365 /* Notify that we're exiting this screen */
366 cond_talk_ids_fq(VOICE_OK);
367 FOR_NB_SCREENS(i)
368 { /* stop scrolling before exiting */
369 for (j = 0; j < QUICKSCREEN_ITEM_COUNT; j++)
370 screens[i].scroll_stop(&vps[i][j]);
373 return changed;
376 static inline const struct settings_list *get_setting(int gs_value,
377 const struct settings_list *defaultval)
379 if (gs_value != -1 && gs_value < nb_settings &&
380 is_setting_quickscreenable(&settings[gs_value]))
381 return &settings[gs_value];
382 return defaultval;
384 bool quick_screen_quick(int button_enter)
386 struct gui_quickscreen qs;
387 bool oldshuffle = global_settings.playlist_shuffle;
388 int oldrepeat = global_settings.repeat_mode;
390 qs.items[QUICKSCREEN_TOP] =
391 get_setting(global_settings.qs_item_top,
392 find_setting(&global_settings.party_mode, NULL));
393 qs.items[QUICKSCREEN_LEFT] =
394 get_setting(global_settings.qs_item_left,
395 find_setting(&global_settings.playlist_shuffle, NULL));
396 qs.items[QUICKSCREEN_RIGHT] =
397 get_setting(global_settings.qs_item_right,
398 find_setting(&global_settings.repeat_mode, NULL));
399 qs.items[QUICKSCREEN_BOTTOM] =
400 get_setting(global_settings.qs_item_bottom,
401 find_setting(&global_settings.dirfilter, NULL));
403 qs.callback = NULL;
404 if (gui_syncquickscreen_run(&qs, button_enter))
406 settings_save();
407 settings_apply(false);
408 /* make sure repeat/shuffle/any other nasty ones get updated */
409 if ( oldrepeat != global_settings.repeat_mode &&
410 (audio_status() & AUDIO_STATUS_PLAY) )
412 audio_flush_and_reload_tracks();
414 if (oldshuffle != global_settings.playlist_shuffle
415 && audio_status() & AUDIO_STATUS_PLAY)
417 #if CONFIG_CODEC == SWCODEC
418 dsp_set_replaygain();
419 #endif
420 if (global_settings.playlist_shuffle)
421 playlist_randomise(NULL, current_tick, true);
422 else
423 playlist_sort(NULL, true);
426 return(0);
429 #ifdef BUTTON_F3
430 bool quick_screen_f3(int button_enter)
432 struct gui_quickscreen qs;
433 qs.items[QUICKSCREEN_LEFT] =
434 find_setting(&global_settings.scrollbar, NULL);
435 qs.items[QUICKSCREEN_RIGHT] =
436 find_setting(&global_settings.statusbar, NULL);
437 qs.items[QUICKSCREEN_BOTTOM] =
438 find_setting(&global_settings.flip_display, NULL);
439 qs.callback = NULL;
440 if (gui_syncquickscreen_run(&qs, button_enter))
442 settings_save();
443 settings_apply(false);
445 return(0);
447 #endif /* BUTTON_F3 */
449 /* stuff to make the quickscreen configurable */
450 bool is_setting_quickscreenable(const struct settings_list *setting)
452 /* to keep things simple, only settings which have a lang_id set are ok */
453 if (setting->lang_id < 0 || (setting->flags&F_BANFROMQS))
454 return false;
455 switch (setting->flags&F_T_MASK)
457 case F_T_BOOL:
458 return true;
459 case F_T_INT:
460 case F_T_UINT:
461 return (setting->RESERVED != NULL);
462 default:
463 return false;
467 void set_as_qs_item(const struct settings_list *setting,
468 enum quickscreen_item item)
470 int i;
471 for(i=0;i<nb_settings;i++)
473 if (&settings[i] == setting)
474 break;
476 switch (item)
478 case QUICKSCREEN_TOP:
479 global_settings.qs_item_top = i;
480 break;
481 case QUICKSCREEN_LEFT:
482 global_settings.qs_item_left = i;
483 break;
484 case QUICKSCREEN_RIGHT:
485 global_settings.qs_item_right = i;
486 break;
487 case QUICKSCREEN_BOTTOM:
488 global_settings.qs_item_bottom = i;
489 break;
490 default: /* shut the compiler up */
491 break;