implement smooth seeking acceleration for audio playback and mpegplayer
[Rockbox.git] / apps / gui / quickscreen.c
blobddc02c110340e0dc05d18d4a2beb692ba9f3521d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Jonathan Gordon
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 #include <stdio.h>
22 #include "config.h"
23 #include "system.h"
24 #include "icons.h"
25 #include "font.h"
26 #include "kernel.h"
27 #include "misc.h"
28 #include "statusbar.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"
39 static struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
40 static struct viewport vp_icons[NB_SCREENS];
41 /* vp_icons will be used like this:
42 the side icons will be aligned to the top of this vp and to their sides
43 the bottom icon wil be aligned center and at the bottom of this vp */
45 #define MIN_LINES 4
46 #define MAX_NEEDED_LINES 8
47 #define CENTER_MARGIN 10 /* pixels between the 2 center items minimum */
48 #define CENTER_ICONAREA_WIDTH (CENTER_MARGIN+8*2)
50 static void quickscreen_fix_viewports(struct gui_quickscreen *qs,
51 struct screen *display,
52 struct viewport *parent)
54 #ifdef HAVE_REMOTE_LCD
55 int screen = display->screen_type;
56 #else
57 const int screen = 0;
58 #endif
60 int char_height, i;
61 int left_width, right_width, bottom_lines = 3;
62 unsigned char *s;
63 int nb_lines = viewport_get_nb_lines(parent);
64 char_height = parent->height/nb_lines;
66 vp_icons[screen] = *parent;
68 vps[screen][QUICKSCREEN_BOTTOM] = *parent;
69 if (nb_lines <= MIN_LINES) /* make the bottom item use 1 line */
70 bottom_lines = 1;
71 else
72 bottom_lines = 2;
73 vps[screen][QUICKSCREEN_BOTTOM].height = bottom_lines*char_height;
74 vps[screen][QUICKSCREEN_BOTTOM].y =
75 parent->y + parent->height - bottom_lines*char_height;
76 if (nb_lines >= MAX_NEEDED_LINES)
78 vps[screen][QUICKSCREEN_BOTTOM].y -= char_height;
81 /* adjust the left/right items widths to fit the screen nicely */
82 s = P2STR(ID2P(qs->items[QUICKSCREEN_LEFT]->lang_id));
83 left_width = display->getstringsize(s, NULL, NULL);
84 s = P2STR(ID2P(qs->items[QUICKSCREEN_RIGHT]->lang_id));
85 right_width = display->getstringsize(s, NULL, NULL);
86 nb_lines -= bottom_lines;
88 vps[screen][QUICKSCREEN_LEFT] = *parent;
89 vps[screen][QUICKSCREEN_RIGHT] = *parent;
90 vps[screen][QUICKSCREEN_LEFT].x = parent->x;
91 if (nb_lines <= MIN_LINES)
92 i = 0;
93 else
94 i = nb_lines/2;
95 vps[screen][QUICKSCREEN_LEFT].y = parent->y + (i*char_height);
96 vps[screen][QUICKSCREEN_RIGHT].y = parent->y + (i*char_height);
97 if (nb_lines >= 3)
98 i = 3*char_height;
99 else
100 i = nb_lines*char_height;
102 vps[screen][QUICKSCREEN_LEFT].height = i;
103 vps[screen][QUICKSCREEN_RIGHT].height = i;
104 vp_icons[screen].y = vps[screen][QUICKSCREEN_LEFT].y + (char_height/2);
105 vp_icons[screen].height =
106 vps[screen][QUICKSCREEN_BOTTOM].y - vp_icons[screen].y;
108 if (left_width + right_width > display->width - CENTER_ICONAREA_WIDTH)
110 /* scrolling needed */
111 int width = (parent->width - CENTER_ICONAREA_WIDTH)/2;
112 vps[screen][QUICKSCREEN_LEFT].width = width;
113 vps[screen][QUICKSCREEN_RIGHT].width = width;
114 vps[screen][QUICKSCREEN_RIGHT].x = parent->x+parent->width - width;
115 vp_icons[screen].x = parent->x + width;
116 vp_icons[screen].width = CENTER_ICONAREA_WIDTH;
118 else
120 int width, pad = 0;
121 if (left_width > right_width)
122 width = left_width;
123 else
124 width = right_width;
125 width += CENTER_MARGIN;
126 if (width*2 < parent->width/2)
128 width += parent->width/6;
129 /* add some padding on the edges */
130 pad = CENTER_MARGIN;
132 vps[screen][QUICKSCREEN_LEFT].width = width;
133 vps[screen][QUICKSCREEN_RIGHT].width = width;
134 vps[screen][QUICKSCREEN_RIGHT].x = parent->x + parent->width - width;
135 vp_icons[screen].x = parent->x + width;
136 if (pad)
138 vp_icons[screen].x += pad;
139 vps[screen][QUICKSCREEN_LEFT].x += pad;
140 vps[screen][QUICKSCREEN_RIGHT].x -= pad;
141 /* need to add the pad to the bottom to make it all centered nicely */
142 vps[screen][QUICKSCREEN_BOTTOM].x += pad;
143 vps[screen][QUICKSCREEN_BOTTOM].width -= pad;
145 vp_icons[screen].width = vps[screen][QUICKSCREEN_RIGHT].x - width;
150 static void quickscreen_draw_text(char *s, int item, bool title,
151 struct screen *display, struct viewport *vp)
153 int nb_lines = viewport_get_nb_lines(vp);
154 int w, h, line = 0, x = 0;
155 display->getstringsize(s, &w, &h);
157 if (nb_lines > 1 && !title)
158 line = 1;
159 switch (item)
161 case QUICKSCREEN_BOTTOM:
162 x = (vp->width - w)/2;
163 break;
164 case QUICKSCREEN_LEFT:
165 x = 0;
166 break;
167 case QUICKSCREEN_RIGHT:
168 x = vp->width - w;
169 break;
171 if (w>vp->width)
172 display->puts_scroll(0, line, s);
173 else
174 display->putsxy(x, line*h, s);
177 static void gui_quickscreen_draw(struct gui_quickscreen *qs,
178 struct screen *display,
179 struct viewport *parent)
181 #ifdef HAVE_REMOTE_LCD
182 int screen = display->screen_type;
183 #else
184 const int screen = 0;
185 #endif
187 int i;
188 char buf[MAX_PATH];
189 unsigned char *title, *value;
190 void *setting;
191 int temp;
192 display->set_viewport(parent);
193 display->clear_viewport();
194 for (i=0; i<QUICKSCREEN_ITEM_COUNT; i++)
196 if (!qs->items[i])
197 continue;
198 display->set_viewport(&vps[screen][i]);
199 display->scroll_stop(&vps[screen][i]);
201 title = P2STR(ID2P(qs->items[i]->lang_id));
202 setting = qs->items[i]->setting;
203 temp = option_value_as_int(qs->items[i]);
204 value = option_get_valuestring((struct settings_list*)qs->items[i],
205 buf, MAX_PATH, temp);
207 if (vps[screen][i].height < display->char_height*2)
209 char text[MAX_PATH];
210 snprintf(text, MAX_PATH, "%s: %s", title, value);
211 quickscreen_draw_text(text, i, true, display, &vps[screen][i]);
213 else
215 quickscreen_draw_text(title, i, true, display, &vps[screen][i]);
216 quickscreen_draw_text(value, i, false, display, &vps[screen][i]);
218 display->update_viewport();
220 /* draw the icons */
221 display->set_viewport(&vp_icons[screen]);
222 display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
223 vp_icons[screen].width - 8, 0, 7, 8);
224 display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward], 0, 0, 7, 8);
225 display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
226 (vp_icons[screen].width/2) - 4,
227 vp_icons[screen].height - 7, 7, 8);
228 display->update_viewport();
230 display->set_viewport(parent);
231 display->update_viewport();
232 display->set_viewport(NULL);
235 static void talk_qs_option(struct settings_list *opt, bool enqueue)
237 if (global_settings.talk_menu) {
238 if(!enqueue)
239 talk_shutup();
240 talk_id(opt->lang_id, true);
241 option_talk_value(opt, option_value_as_int(opt), true);
246 * Does the actions associated to the given button if any
247 * - qs : the quickscreen
248 * - button : the key we are going to analyse
249 * returns : true if the button corresponded to an action, false otherwise
251 static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
253 int item;
254 switch(button)
256 case ACTION_QS_LEFT:
257 item = QUICKSCREEN_LEFT;
258 break;
260 case ACTION_QS_DOWN:
261 case ACTION_QS_DOWNINV:
262 item = QUICKSCREEN_BOTTOM;
263 break;
265 case ACTION_QS_RIGHT:
266 item = QUICKSCREEN_RIGHT;
267 break;
269 default:
270 return false;
272 option_select_next_val((struct settings_list *)qs->items[item], false, true);
273 talk_qs_option((struct settings_list *)qs->items[item], false);
274 return true;
277 bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter)
279 int button, i;
280 struct viewport vp[NB_SCREENS];
281 bool changed = false;
282 /* To quit we need either :
283 * - a second press on the button that made us enter
284 * - an action taken while pressing the enter button,
285 * then release the enter button*/
286 bool can_quit = false;
287 gui_syncstatusbar_draw(&statusbars, true);
288 FOR_NB_SCREENS(i)
290 screens[i].set_viewport(NULL);
291 screens[i].stop_scroll();
292 viewport_set_defaults(&vp[i], i);
293 quickscreen_fix_viewports(qs, &screens[i], &vp[i]);
294 gui_quickscreen_draw(qs, &screens[i], &vp[i]);
296 /* Announce current selection on entering this screen. This is all
297 queued up, but can be interrupted as soon as a setting is
298 changed. */
299 cond_talk_ids(VOICE_QUICKSCREEN);
300 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_LEFT], true);
301 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_BOTTOM], true);
302 talk_qs_option((struct settings_list *)qs->items[QUICKSCREEN_RIGHT], true);
303 while (true) {
304 button = get_action(CONTEXT_QUICKSCREEN,HZ/5);
305 if(default_event_handler(button) == SYS_USB_CONNECTED)
306 return(true);
307 if(gui_quickscreen_do_button(qs, button))
309 changed = true;
310 can_quit=true;
311 FOR_NB_SCREENS(i)
312 gui_quickscreen_draw(qs, &screens[i], &vp[i]);
313 if (qs->callback)
314 qs->callback(qs);
316 else if(button==button_enter)
317 can_quit=true;
319 if((button == button_enter) && can_quit)
320 break;
322 if(button==ACTION_STD_CANCEL)
323 break;
325 gui_syncstatusbar_draw(&statusbars, false);
327 /* Notify that we're exiting this screen */
328 cond_talk_ids_fq(VOICE_OK);
329 return changed;
332 bool quick_screen_quick(int button_enter)
334 struct gui_quickscreen qs;
335 bool oldshuffle = global_settings.playlist_shuffle;
336 int oldrepeat = global_settings.repeat_mode;
337 qs.items[QUICKSCREEN_LEFT] =
338 find_setting(&global_settings.playlist_shuffle, NULL);
339 qs.items[QUICKSCREEN_RIGHT] =
340 find_setting(&global_settings.repeat_mode, NULL);
341 qs.items[QUICKSCREEN_BOTTOM] =
342 find_setting(&global_settings.dirfilter, NULL);
343 qs.callback = NULL;
344 if (gui_syncquickscreen_run(&qs, button_enter))
346 settings_save();
347 settings_apply(false);
348 /* make sure repeat/shuffle/any other nasty ones get updated */
349 if ( oldrepeat != global_settings.repeat_mode &&
350 (audio_status() & AUDIO_STATUS_PLAY) )
352 audio_flush_and_reload_tracks();
354 if (oldshuffle != global_settings.playlist_shuffle
355 && audio_status() & AUDIO_STATUS_PLAY)
357 #if CONFIG_CODEC == SWCODEC
358 dsp_set_replaygain();
359 #endif
360 if (global_settings.playlist_shuffle)
361 playlist_randomise(NULL, current_tick, true);
362 else
363 playlist_sort(NULL, true);
366 return(0);
369 #ifdef BUTTON_F3
370 bool quick_screen_f3(int button_enter)
372 struct gui_quickscreen qs;
373 qs.items[QUICKSCREEN_LEFT] =
374 find_setting(&global_settings.scrollbar, NULL);
375 qs.items[QUICKSCREEN_RIGHT] =
376 find_setting(&global_settings.statusbar, NULL);
377 qs.items[QUICKSCREEN_BOTTOM] =
378 find_setting(&global_settings.flip_display, NULL);
379 qs.callback = NULL;
380 if (gui_syncquickscreen_run(&qs, button_enter))
382 settings_save();
383 settings_apply(false);
385 return(0);
387 #endif /* BUTTON_F3 */