quickscreen: optionally display the shortcuts menu instead of the QS
[maemo-rb.git] / apps / gui / quickscreen.c
blob965fe67a4c557e5eeafc58e033e1186790a560b1
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 ****************************************************************************/
22 #include <stdio.h>
23 #include "config.h"
24 #include "system.h"
25 #include "icons.h"
26 #include "font.h"
27 #include "kernel.h"
28 #include "misc.h"
29 #include "action.h"
30 #include "settings_list.h"
31 #include "lang.h"
32 #include "playlist.h"
33 #include "dsp.h"
34 #include "viewport.h"
35 #include "audio.h"
36 #include "quickscreen.h"
37 #include "talk.h"
38 #include "list.h"
39 #include "option_select.h"
40 #include "debug.h"
41 #include "shortcuts.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 = 0, right_width = 0, 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;
78 vps[QUICKSCREEN_BOTTOM] = *parent;
79 vps[QUICKSCREEN_TOP] = *parent;
80 /* depending on the space the top/buttom items use 1 or 2 lines */
81 if (nb_lines < MIN_LINES)
82 vert_lines = 1;
83 else
84 vert_lines = 2;
85 vps[QUICKSCREEN_TOP].y = parent->y;
86 vps[QUICKSCREEN_TOP].height = vps[QUICKSCREEN_BOTTOM].height
87 = vert_lines*char_height;
88 vps[QUICKSCREEN_BOTTOM].y
89 = parent->y + parent->height - vps[QUICKSCREEN_BOTTOM].height;
91 /* enough space vertically, so put a nice margin */
92 if (nb_lines >= MAX_NEEDED_LINES)
94 vps[QUICKSCREEN_TOP].y += MARGIN;
95 vps[QUICKSCREEN_BOTTOM].y -= MARGIN;
98 vp_icons->y = vps[QUICKSCREEN_TOP].y
99 + vps[QUICKSCREEN_TOP].height;
100 vp_icons->height = vps[QUICKSCREEN_BOTTOM].y - vp_icons->y;
102 /* adjust the left/right items widths to fit the screen nicely */
103 if (qs->items[QUICKSCREEN_LEFT])
105 s = P2STR(ID2P(qs->items[QUICKSCREEN_LEFT]->lang_id));
106 left_width = display->getstringsize(s, NULL, NULL);
108 if (qs->items[QUICKSCREEN_RIGHT])
110 s = P2STR(ID2P(qs->items[QUICKSCREEN_RIGHT]->lang_id));
111 right_width = display->getstringsize(s, NULL, NULL);
114 width = MAX(left_width, right_width);
115 if (width*2 + vp_icons->width > parent->width)
116 { /* crop text viewports */
117 width = (parent->width - vp_icons->width)/2;
119 else
120 { /* add more gap in icons vp */
121 int excess = parent->width - vp_icons->width - width*2;
122 if (excess > MARGIN*4)
124 pad = MARGIN;
125 excess -= MARGIN*2;
127 vp_icons->x -= excess/2;
128 vp_icons->width += excess;
131 vps[QUICKSCREEN_LEFT] = *parent;
132 vps[QUICKSCREEN_LEFT].x = parent->x + pad;
133 vps[QUICKSCREEN_LEFT].width = width;
135 vps[QUICKSCREEN_RIGHT] = *parent;
136 vps[QUICKSCREEN_RIGHT].x = parent->x + parent->width - width - pad;
137 vps[QUICKSCREEN_RIGHT].width = width;
139 vps[QUICKSCREEN_LEFT].height = vps[QUICKSCREEN_RIGHT].height
140 = 2*char_height;
142 vps[QUICKSCREEN_LEFT].y = vps[QUICKSCREEN_RIGHT].y
143 = parent->y + (parent->height/2) - char_height;
145 /* shrink the icons vp by a few pixels if there is room so the arrows
146 aren't drawn right next to the text */
147 if (vp_icons->width > CENTER_ICONAREA_SIZE*2)
149 vp_icons->width -= CENTER_ICONAREA_SIZE*2/3;
150 vp_icons->x += CENTER_ICONAREA_SIZE*2/6;
152 if (vp_icons->height > CENTER_ICONAREA_SIZE*2)
154 vp_icons->height -= CENTER_ICONAREA_SIZE*2/3;
155 vp_icons->y += CENTER_ICONAREA_SIZE*2/6;
158 /* text alignment */
159 vps[QUICKSCREEN_LEFT].flags &= ~VP_FLAG_ALIGNMENT_MASK; /* left-aligned */
160 vps[QUICKSCREEN_TOP].flags |= VP_FLAG_ALIGN_CENTER; /* centered */
161 vps[QUICKSCREEN_BOTTOM].flags |= VP_FLAG_ALIGN_CENTER; /* centered */
162 vps[QUICKSCREEN_RIGHT].flags &= ~VP_FLAG_ALIGNMENT_MASK;/* right aligned*/
163 vps[QUICKSCREEN_RIGHT].flags |= VP_FLAG_ALIGN_RIGHT;
166 static void gui_quickscreen_draw(const struct gui_quickscreen *qs,
167 struct screen *display,
168 struct viewport *parent,
169 struct viewport vps[QUICKSCREEN_ITEM_COUNT],
170 struct viewport *vp_icons)
172 int i;
173 char buf[MAX_PATH];
174 unsigned const char *title, *value;
175 int temp;
176 display->set_viewport(parent);
177 display->clear_viewport();
179 for (i = 0; i < QUICKSCREEN_ITEM_COUNT; i++)
181 struct viewport *vp = &vps[i];
182 if (!qs->items[i])
183 continue;
184 display->set_viewport(vp);
186 title = P2STR(ID2P(qs->items[i]->lang_id));
187 temp = option_value_as_int(qs->items[i]);
188 value = option_get_valuestring(qs->items[i],
189 buf, MAX_PATH, temp);
191 if (viewport_get_nb_lines(vp) < 2)
193 char text[MAX_PATH];
194 snprintf(text, MAX_PATH, "%s: %s", title, value);
195 display->puts_scroll(0, 0, text);
197 else
199 display->puts_scroll(0, 0, title);
200 display->puts_scroll(0, 1, value);
203 /* draw the icons */
204 display->set_viewport(vp_icons);
206 if (qs->items[QUICKSCREEN_TOP] != NULL)
208 display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
209 (vp_icons->width/2) - 4, 0, 7, 8);
211 if (qs->items[QUICKSCREEN_RIGHT] != NULL)
213 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
214 vp_icons->width - 8, (vp_icons->height/2) - 4, 7, 8);
216 if (qs->items[QUICKSCREEN_LEFT] != NULL)
218 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
219 0, (vp_icons->height/2) - 4, 7, 8);
221 if (qs->items[QUICKSCREEN_BOTTOM] != NULL)
223 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
224 (vp_icons->width/2) - 4, vp_icons->height - 8, 7, 8);
227 display->set_viewport(parent);
228 display->update_viewport();
229 display->set_viewport(NULL);
232 static void talk_qs_option(const struct settings_list *opt, bool enqueue)
234 if (!global_settings.talk_menu || !opt)
235 return;
237 if (!enqueue)
238 talk_shutup();
239 talk_id(opt->lang_id, true);
240 option_talk_value(opt, option_value_as_int(opt), true);
244 * Does the actions associated to the given button if any
245 * - qs : the quickscreen
246 * - button : the key we are going to analyse
247 * returns : true if the button corresponded to an action, false otherwise
249 static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
251 int item;
252 bool invert = false;
253 switch(button)
255 case ACTION_QS_TOP:
256 invert = true;
257 item = QUICKSCREEN_TOP;
258 break;
259 case ACTION_QS_LEFT:
260 invert = true;
261 item = QUICKSCREEN_LEFT;
262 break;
264 case ACTION_QS_DOWN:
265 item = QUICKSCREEN_BOTTOM;
266 break;
268 case ACTION_QS_RIGHT:
269 item = QUICKSCREEN_RIGHT;
270 break;
272 default:
273 return false;
275 if (qs->items[item] == NULL)
276 return false;
277 #ifdef ASCENDING_INT_SETTINGS
278 if (((qs->items[item]->flags & F_INT_SETTING) == F_INT_SETTING) &&
279 ( button == ACTION_QS_DOWN || button == ACTION_QS_TOP))
281 invert = !invert;
283 #endif
284 option_select_next_val(qs->items[item], invert, true);
285 talk_qs_option(qs->items[item], false);
286 return true;
289 #ifdef HAVE_TOUCHSCREEN
290 static int quickscreen_touchscreen_button(const struct viewport
291 vps[QUICKSCREEN_ITEM_COUNT])
293 short x,y;
294 /* only hitting the text counts, everything else is exit */
295 if (action_get_touchscreen_press(&x, &y) != BUTTON_REL)
296 return ACTION_NONE;
297 else if (viewport_point_within_vp(&vps[QUICKSCREEN_TOP], x, y))
298 return ACTION_QS_TOP;
299 else if (viewport_point_within_vp(&vps[QUICKSCREEN_BOTTOM], x, y))
300 return ACTION_QS_DOWN;
301 else if (viewport_point_within_vp(&vps[QUICKSCREEN_LEFT], x, y))
302 return ACTION_QS_LEFT;
303 else if (viewport_point_within_vp(&vps[QUICKSCREEN_RIGHT], x, y))
304 return ACTION_QS_RIGHT;
305 return ACTION_STD_CANCEL;
307 #endif
309 static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter)
311 int button;
312 struct viewport parent[NB_SCREENS];
313 struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
314 struct viewport vp_icons[NB_SCREENS];
315 bool changed = false;
316 /* To quit we need either :
317 * - a second press on the button that made us enter
318 * - an action taken while pressing the enter button,
319 * then release the enter button*/
320 bool can_quit = false;
322 push_current_activity(ACTIVITY_QUICKSCREEN);
324 FOR_NB_SCREENS(i)
326 screens[i].set_viewport(NULL);
327 screens[i].stop_scroll();
328 viewportmanager_theme_enable(i, true, &parent[i]);
329 quickscreen_fix_viewports(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
330 gui_quickscreen_draw(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
332 /* Announce current selection on entering this screen. This is all
333 queued up, but can be interrupted as soon as a setting is
334 changed. */
335 cond_talk_ids(VOICE_QUICKSCREEN);
336 talk_qs_option(qs->items[QUICKSCREEN_TOP], true);
337 talk_qs_option(qs->items[QUICKSCREEN_LEFT], true);
338 talk_qs_option(qs->items[QUICKSCREEN_BOTTOM], true);
339 talk_qs_option(qs->items[QUICKSCREEN_RIGHT], true);
340 while (true) {
341 button = get_action(CONTEXT_QUICKSCREEN, HZ/5);
342 #ifdef HAVE_TOUCHSCREEN
343 if (button == ACTION_TOUCHSCREEN)
344 button = quickscreen_touchscreen_button(vps[SCREEN_MAIN]);
345 #endif
346 if (default_event_handler(button) == SYS_USB_CONNECTED)
347 return(true);
348 if (gui_quickscreen_do_button(qs, button))
350 changed = true;
351 can_quit = true;
352 FOR_NB_SCREENS(i)
353 gui_quickscreen_draw(qs, &screens[i], &parent[i],
354 vps[i], &vp_icons[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 FOR_NB_SCREENS(i)
370 { /* stop scrolling before exiting */
371 for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++)
372 screens[i].scroll_stop(&vps[i][j]);
373 viewportmanager_theme_undo(i, true);
376 pop_current_activity();
377 return changed;
380 static const struct settings_list *get_setting(int gs_value,
381 const struct settings_list *defaultval)
383 if (gs_value != -1 && gs_value < nb_settings &&
384 is_setting_quickscreenable(&settings[gs_value]))
385 return &settings[gs_value];
386 return defaultval;
389 bool quick_screen_quick(int button_enter)
391 struct gui_quickscreen qs;
392 bool oldshuffle = global_settings.playlist_shuffle;
393 int oldrepeat = global_settings.repeat_mode;
395 if (global_settings.shortcuts_replaces_qs)
396 return do_shortcut_menu(NULL);
398 qs.items[QUICKSCREEN_TOP] =
399 get_setting(global_settings.qs_items[QUICKSCREEN_TOP], NULL);
400 qs.items[QUICKSCREEN_LEFT] =
401 get_setting(global_settings.qs_items[QUICKSCREEN_LEFT],
402 find_setting(&global_settings.playlist_shuffle, NULL));
403 qs.items[QUICKSCREEN_RIGHT] =
404 get_setting(global_settings.qs_items[QUICKSCREEN_RIGHT],
405 find_setting(&global_settings.repeat_mode, NULL));
406 qs.items[QUICKSCREEN_BOTTOM] =
407 get_setting(global_settings.qs_items[QUICKSCREEN_BOTTOM], NULL);
409 qs.callback = NULL;
410 if (gui_syncquickscreen_run(&qs, button_enter))
412 settings_save();
413 settings_apply(false);
414 /* make sure repeat/shuffle/any other nasty ones get updated */
415 if ( oldrepeat != global_settings.repeat_mode &&
416 (audio_status() & AUDIO_STATUS_PLAY) )
418 audio_flush_and_reload_tracks();
420 if (oldshuffle != global_settings.playlist_shuffle
421 && audio_status() & AUDIO_STATUS_PLAY)
423 #if CONFIG_CODEC == SWCODEC
424 dsp_set_replaygain();
425 #endif
426 if (global_settings.playlist_shuffle)
427 playlist_randomise(NULL, current_tick, true);
428 else
429 playlist_sort(NULL, true);
432 return(0);
435 #ifdef BUTTON_F3
436 bool quick_screen_f3(int button_enter)
438 struct gui_quickscreen qs;
439 qs.items[QUICKSCREEN_TOP] = NULL;
440 qs.items[QUICKSCREEN_LEFT] =
441 find_setting(&global_settings.scrollbar, NULL);
442 qs.items[QUICKSCREEN_RIGHT] =
443 find_setting(&global_settings.statusbar, NULL);
444 qs.items[QUICKSCREEN_BOTTOM] =
445 find_setting(&global_settings.flip_display, NULL);
446 qs.callback = NULL;
447 if (gui_syncquickscreen_run(&qs, button_enter))
449 settings_save();
450 settings_apply(false);
452 return(0);
454 #endif /* BUTTON_F3 */
456 /* stuff to make the quickscreen configurable */
457 bool is_setting_quickscreenable(const struct settings_list *setting)
459 /* to keep things simple, only settings which have a lang_id set are ok */
460 if (setting->lang_id < 0 || (setting->flags&F_BANFROMQS))
461 return false;
462 switch (setting->flags&F_T_MASK)
464 case F_T_BOOL:
465 return true;
466 case F_T_INT:
467 case F_T_UINT:
468 return (setting->RESERVED != NULL);
469 default:
470 return false;
474 void set_as_qs_item(const struct settings_list *setting,
475 enum quickscreen_item item)
477 int i;
478 for (i = 0; i < nb_settings; i++)
480 if (&settings[i] == setting)
481 break;
484 global_settings.qs_items[item] = i;