summaryrefslogtreecommitdiff
path: root/apps/playlist_viewer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/playlist_viewer.c')
-rw-r--r--apps/playlist_viewer.c929
1 files changed, 315 insertions, 614 deletions
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 70c1cf8995..58f27409b1 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -17,7 +17,10 @@
17 * KIND, either express or implied. 17 * KIND, either express or implied.
18 * 18 *
19 ****************************************************************************/ 19 ****************************************************************************/
20 20/*
21 * Kevin Ferrare 2005/10/16
22 * multi-screen support, rewrote a lot of code
23 */
21#include <string.h> 24#include <string.h>
22#include <sprintf.h> 25#include <sprintf.h>
23#include "playlist.h" 26#include "playlist.h"
@@ -35,76 +38,24 @@
35#include "misc.h" 38#include "misc.h"
36#include "action.h" 39#include "action.h"
37 40
38#ifdef HAVE_LCD_BITMAP
39#include "widgets.h"
40#endif
41
42#include "lang.h" 41#include "lang.h"
43 42
44#include "playlist_viewer.h" 43#include "playlist_viewer.h"
45 44#include "icon.h"
46/* Defines for LCD display purposes. Taken from tree.c */ 45#include "list.h"
47#ifdef HAVE_LCD_BITMAP 46#include "statusbar.h"
48 #define CURSOR_X (global_settings.scrollbar && \ 47#include "splash.h"
49 viewer.num_tracks>viewer.num_display_lines?1:0)
50 #define CURSOR_Y 0
51 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
52
53 #define ICON_WIDTH ((viewer.char_width > 6) ? viewer.char_width : 6)
54
55 #define MARGIN_X ((global_settings.scrollbar && \
56 viewer.num_tracks > viewer.num_display_lines ? \
57 SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + \
58 (global_settings.playlist_viewer_icons ? \
59 ICON_WIDTH : 0))
60 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
61
62 #define LINE_X 0
63 #define LINE_Y (global_settings.statusbar ? 1 : 0)
64
65 #define SCROLLBAR_X 0
66 #define SCROLLBAR_Y lcd_getymargin()
67 #define SCROLLBAR_WIDTH 6
68#else
69 #define MARGIN_X 0
70 #define MARGIN_Y 0
71 #define LINE_X 2
72 #define LINE_Y 0
73 #define CURSOR_X 0
74 #define CURSOR_Y 0
75#endif
76 48
77/* Maximum number of tracks we can have loaded at one time */ 49/* Maximum number of tracks we can have loaded at one time */
78#define MAX_PLAYLIST_ENTRIES 200 50#define MAX_PLAYLIST_ENTRIES 40
51
52/* The number of items between the selected one and the end/start of
53 * the buffer under which the buffer must reload */
54#define MIN_BUFFER_MARGIN screens[0].nb_lines
79 55
80/* Default playlist name for saving */ 56/* Default playlist name for saving */
81#define DEFAULT_VIEWER_PLAYLIST_NAME "/viewer.m3u" 57#define DEFAULT_VIEWER_PLAYLIST_NAME "/viewer.m3u"
82 58
83/* Index of track on display line _pos */
84#define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos))
85
86/* Global playlist viewer settings */
87struct playlist_viewer_info {
88 struct playlist_info* playlist; /* playlist being viewed */
89 char *name_buffer; /* Buffer used to store track names */
90 int buffer_size; /* Size of name buffer */
91
92 int num_display_lines; /* Number of lines on lcd */
93 int line_height; /* Height (in pixels) of display line */
94 int char_width; /* Width (in pixels) of a character */
95
96 int num_tracks; /* Number of tracks in playlist */
97 int current_playing_track; /* Index of current playing track */
98
99 int num_loaded; /* Number of track entries loaded in viewer */
100 int first_index; /* Index of first loaded track */
101 int last_index; /* Index of last loaded track */
102 int first_display_index; /* Index of first track on display */
103 int last_display_index; /* Index of last track on display */
104 int cursor_pos; /* Line number of cursor */
105
106 int move_track; /* Playlist index of track to move or -1 */
107};
108 59
109/* Information about a specific track */ 60/* Information about a specific track */
110struct playlist_entry { 61struct playlist_entry {
@@ -115,22 +66,60 @@ struct playlist_entry {
115 bool skipped; /* Is track marked as bad? */ 66 bool skipped; /* Is track marked as bad? */
116}; 67};
117 68
118static struct playlist_viewer_info viewer; 69enum direction
119static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES]; 70{
71 FORWARD,
72 BACKWARD
73};
74
75struct playlist_buffer
76{
77 char *name_buffer; /* Buffer used to store track names */
78 int buffer_size; /* Size of name buffer */
79
80 int first_index; /* Real index of first track loaded inside
81 the buffer */
82
83 enum direction direction; /* Direction of the buffer (if the buffer
84 was loaded BACKWARD, the last track in
85 the buffer has a real index < to the
86 real index of the the first track)*/
87
88 struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
89 int num_loaded; /* Number of track entries loaded in buffer */
90};
91
92/* Global playlist viewer settings */
93struct playlist_viewer {
94 struct playlist_info* playlist; /* playlist being viewed */
95 int num_tracks; /* Number of tracks in playlist */
96 int current_playing_track; /* Index of current playing track */
97 int selected_track; /* The selected track, relative (first is 0)*/
98 int move_track; /* Playlist index of track to move or -1 */
99 struct playlist_buffer buffer;
100};
101
102static struct playlist_viewer viewer;
120 103
121/* Used when viewing playlists on disk */ 104/* Used when viewing playlists on disk */
122static struct playlist_info temp_playlist; 105static struct playlist_info temp_playlist;
123 106
124static bool initialize(char* filename, bool reload); 107void playlist_buffer_init(struct playlist_buffer * pb, char * names_buffer,
125static void load_playlist_entries(int start_index); 108 int names_buffer_size);
126static void load_playlist_entries_r(int end_index); 109void playlist_buffer_load_entries(struct playlist_buffer * pb, int index,
127static int load_entry(int index, int pos, char* p, int size); 110 enum direction direction);
111int playlist_entry_load(struct playlist_entry *entry, int index,
112 char* name_buffer, int remaining_size);
113
114struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer * pb,
115 int index);
116
117static bool playlist_viewer_init(struct playlist_viewer * viewer,
118 char* filename, bool reload);
119
128static void format_name(char* dest, const char* src); 120static void format_name(char* dest, const char* src);
129static void format_line(const struct playlist_entry* track, char* str, int len); 121static void format_line(const struct playlist_entry* track, char* str, int len);
130static void display_playlist(void); 122
131static void update_display_line(int line, bool scroll);
132static void scroll_display(int lines);
133static void update_first_index(void);
134static bool update_playlist(bool force); 123static bool update_playlist(bool force);
135static int onplay_menu(int index); 124static int onplay_menu(int index);
136static bool viewer_menu(void); 125static bool viewer_menu(void);
@@ -139,8 +128,148 @@ static bool show_indices(void);
139static bool track_display(void); 128static bool track_display(void);
140static bool save_playlist(void); 129static bool save_playlist(void);
141 130
131void playlist_buffer_init(struct playlist_buffer * pb, char * names_buffer,
132 int names_buffer_size)
133{
134 pb->name_buffer=names_buffer;
135 pb->buffer_size=names_buffer_size;
136 pb->first_index=0;
137 pb->num_loaded=0;
138}
139/*
140 * Loads the entries following 'index' in the playlist buffer
141 */
142void playlist_buffer_load_entries(struct playlist_buffer * pb, int index,
143 enum direction direction)
144{
145 int num_entries = viewer.num_tracks;
146 char* p = pb->name_buffer;
147 int remaining = pb->buffer_size;
148 int i;
149
150 pb->first_index = index;
151 if (num_entries > MAX_PLAYLIST_ENTRIES)
152 num_entries = MAX_PLAYLIST_ENTRIES;
153
154 for(i=0; i<num_entries; i++)
155 {
156 int len = playlist_entry_load(&(pb->tracks[i]), index, p, remaining);
157 if (len < 0)
158 {
159 /* Out of name buffer space */
160 num_entries = i;
161 break;
162 }
163
164 p += len;
165 remaining -= len;
166
167 if(direction==FORWARD)
168 index++;
169 else
170 index--;
171 index+=viewer.num_tracks;
172 index%=viewer.num_tracks;
173 }
174 pb->direction=direction;
175 pb->num_loaded = i;
176}
177
178void playlist_buffer_load_entries_screen(struct playlist_buffer * pb,
179 enum direction direction)
180{
181 if(direction==FORWARD)
182 {
183 int min_start=viewer.selected_track-2*screens[0].nb_lines;
184 if(min_start<0)
185 min_start=0;
186 playlist_buffer_load_entries(pb, min_start, FORWARD);
187 }
188 else
189 {
190 int max_start=viewer.selected_track+2*screens[0].nb_lines;
191 max_start%=viewer.num_tracks;
192 playlist_buffer_load_entries(pb, max_start, BACKWARD);
193 }
194}
195
196int playlist_entry_load(struct playlist_entry *entry, int index,
197 char* name_buffer, int remaining_size)
198{
199 struct playlist_track_info info;
200 int len;
201
202 /* Playlist viewer orders songs based on display index. We need to
203 convert to real playlist index to access track */
204 index = (index + playlist_get_first_index(viewer.playlist)) %
205 viewer.num_tracks;
206 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
207 return -1;
208
209 len = strlen(info.filename) + 1;
210
211 if (len <= remaining_size)
212 {
213 strcpy(name_buffer, info.filename);
214
215 entry->name = name_buffer;
216 entry->index = info.index;
217 entry->display_index = info.display_index;
218 entry->queued = info.attr & PLAYLIST_ATTR_QUEUED;
219 entry->skipped = info.attr & PLAYLIST_ATTR_SKIPPED;
220 return len;
221 }
222 return -1;
223}
224
225int playlist_buffer_get_index(struct playlist_buffer * pb, int index )
226{
227 int buffer_index;
228 if(pb->direction==FORWARD)
229 {
230 if(index>=pb->first_index)
231 buffer_index=index-pb->first_index;
232 else /* rotation : track0 in buffer + requested track */
233 buffer_index=(viewer.num_tracks-pb->first_index)+(index);
234 }
235 else
236 {
237 if(index<=pb->first_index)
238 buffer_index=pb->first_index-index;
239 else /* rotation : track0 in buffer + dist from the last track
240 to the requested track (num_tracks-requested track) */
241 buffer_index=(pb->first_index)+(viewer.num_tracks-index);
242 }
243 return(buffer_index);
244}
245
246#define distance(a, b) \
247 a>b? (a) - (b) : (b) - (a)
248bool playlist_buffer_needs_reload(struct playlist_buffer * pb, int track_index)
249{
250 if(pb->num_loaded==viewer.num_tracks)
251 return(false);
252 int selected_index=playlist_buffer_get_index(pb, track_index);
253 int first_buffer_index=playlist_buffer_get_index(pb, pb->first_index);
254 int distance_beginning=distance(selected_index, first_buffer_index);
255 if(distance_beginning<MIN_BUFFER_MARGIN)
256 return(true);
257
258 if(pb->num_loaded - distance_beginning < MIN_BUFFER_MARGIN)
259 return(true);
260 return(false);
261}
262
263struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer * pb,
264 int index)
265{
266 int buffer_index=playlist_buffer_get_index(pb, index);
267 return(&(pb->tracks[buffer_index]));
268}
269
142/* Initialize the playlist viewer. */ 270/* Initialize the playlist viewer. */
143static bool initialize(char* filename, bool reload) 271static bool playlist_viewer_init(struct playlist_viewer * viewer,
272 char* filename, bool reload)
144{ 273{
145 char* buffer; 274 char* buffer;
146 int buffer_size; 275 int buffer_size;
@@ -155,15 +284,15 @@ static bool initialize(char* filename, bool reload)
155 return false; 284 return false;
156 285
157 if (!filename) 286 if (!filename)
158 viewer.playlist = NULL; 287 viewer->playlist = NULL;
159 else 288 else
160 { 289 {
161 /* Viewing playlist on disk */ 290 /* Viewing playlist on disk */
162 char *dir, *file, *temp_ptr; 291 char *dir, *file, *temp_ptr;
163 char *index_buffer = NULL; 292 char *index_buffer = NULL;
164 int index_buffer_size = 0; 293 int index_buffer_size = 0;
165 294
166 viewer.playlist = &temp_playlist; 295 viewer->playlist = &temp_playlist;
167 296
168 /* Separate directory from filename */ 297 /* Separate directory from filename */
169 temp_ptr = strrchr(filename+1,'/'); 298 temp_ptr = strrchr(filename+1,'/');
@@ -187,7 +316,7 @@ static bool initialize(char* filename, bool reload)
187 index_buffer = buffer; 316 index_buffer = buffer;
188 } 317 }
189 318
190 playlist_create_ex(viewer.playlist, dir, file, index_buffer, 319 playlist_create_ex(viewer->playlist, dir, file, index_buffer,
191 index_buffer_size, buffer+index_buffer_size, 320 index_buffer_size, buffer+index_buffer_size,
192 buffer_size-index_buffer_size); 321 buffer_size-index_buffer_size);
193 322
@@ -197,201 +326,18 @@ static bool initialize(char* filename, bool reload)
197 buffer += index_buffer_size; 326 buffer += index_buffer_size;
198 buffer_size -= index_buffer_size; 327 buffer_size -= index_buffer_size;
199 } 328 }
329 playlist_buffer_init(&viewer->buffer, buffer, buffer_size );
200 330
201 viewer.name_buffer = buffer; 331 viewer->move_track = -1;
202 viewer.buffer_size = buffer_size;
203
204#ifdef HAVE_LCD_BITMAP
205 {
206 char icon_chars[] = "MQ"; /* characters used as icons */
207 unsigned int i;
208
209 viewer.char_width = 0;
210 viewer.line_height = 0;
211
212 /* Use icon characters to calculate largest possible width/height so
213 that we set proper margins */
214 for (i=0; i<sizeof(icon_chars); i++)
215 {
216 char str[2];
217 int w, h;
218
219 snprintf(str, sizeof(str), "%c", icon_chars[i]);
220 lcd_getstringsize(str, &w, &h);
221
222 if (w > viewer.char_width)
223 viewer.char_width = w;
224
225 if (h > viewer.line_height)
226 {
227 viewer.line_height = h;
228 viewer.num_display_lines = (LCD_HEIGHT - MARGIN_Y)/h;
229 }
230 }
231 }
232#else
233 viewer.num_display_lines = 2;
234 viewer.char_width = 1;
235 viewer.line_height = 1;
236#endif
237
238 viewer.move_track = -1;
239 332
240 if (!reload) 333 if (!reload)
241 { 334 viewer->selected_track = 0;
242 viewer.cursor_pos = 0;
243
244 if (!viewer.playlist)
245 /* Start displaying at current playing track */
246 viewer.first_display_index = playlist_get_display_index() - 1;
247 else
248 viewer.first_display_index = 0;
249
250 update_first_index();
251 }
252 335
253 if (!update_playlist(true)) 336 if (!update_playlist(true))
254 return false; 337 return false;
255
256 return true; 338 return true;
257} 339}
258 340
259/* Load tracks starting at start_index */
260static void load_playlist_entries(int start_index)
261{
262 int num_entries = viewer.num_tracks - start_index;
263 char* p = viewer.name_buffer;
264 int remaining = viewer.buffer_size;
265 int i;
266
267 viewer.first_index = start_index;
268
269 if (num_entries > MAX_PLAYLIST_ENTRIES)
270 num_entries = MAX_PLAYLIST_ENTRIES;
271
272 for(i=0; i<num_entries; i++, start_index++)
273 {
274 int len = load_entry(start_index, i, p, remaining);
275 if (len < 0)
276 {
277 /* Out of name buffer space */
278 num_entries = i;
279 break;
280 }
281
282 p += len;
283 remaining -= len;
284 }
285
286 viewer.num_loaded = num_entries;
287 viewer.last_index = viewer.first_index + (viewer.num_loaded - 1);
288}
289
290/* Load tracks in reverse, ending at end_index */
291static void load_playlist_entries_r(int end_index)
292{
293 int num_entries = end_index;
294 char* p = viewer.name_buffer;
295 int remaining = viewer.buffer_size;
296 int i;
297
298 viewer.last_index = end_index;
299
300 if (num_entries >= MAX_PLAYLIST_ENTRIES)
301 num_entries = MAX_PLAYLIST_ENTRIES-1;
302
303 for(i=num_entries; i>=0; i--, end_index--)
304 {
305 int len = load_entry(end_index, i, p, remaining);
306 if (len < 0)
307 {
308 int j;
309
310 /* Out of name buffer space */
311 num_entries -= i;
312
313 /* Shift loaded tracks up such that first track is index 0 */
314 for (j=0; j<num_entries; j++, i++)
315 {
316 tracks[j].name = tracks[i].name;
317 tracks[j].index = tracks[i].index;
318 tracks[j].display_index = tracks[i].display_index;
319 tracks[j].queued = tracks[i].queued;
320 }
321
322 break;
323 }
324
325 p += len;
326 remaining -= len;
327 }
328
329 viewer.first_index = viewer.last_index - num_entries;
330
331 num_entries++;
332 if (!viewer.first_index &&
333 num_entries < viewer.num_tracks &&
334 num_entries < MAX_PLAYLIST_ENTRIES)
335 {
336 /* Lets see if we can load more data at the end of the list */
337 int max = viewer.num_tracks;
338 if (max > MAX_PLAYLIST_ENTRIES)
339 max = MAX_PLAYLIST_ENTRIES;
340
341 for (i = num_entries; i<max; i++)
342 {
343 int len = load_entry(num_entries, num_entries, p, remaining);
344 if (len < 0)
345 /* Out of name buffer space */
346 break;
347
348 p += len;
349 remaining -= len;
350
351 num_entries++;
352 viewer.last_index++;
353 }
354 }
355
356 viewer.num_loaded = num_entries;
357}
358
359/* Load track at playlist index. pos is the position in the tracks array and
360 p is a pointer to the name buffer (max size), Returns -1 if buffer is
361 full. */
362static int load_entry(int index, int pos, char* p, int size)
363{
364 struct playlist_track_info info;
365 int len;
366 int result = 0;
367
368 /* Playlist viewer orders songs based on display index. We need to
369 convert to real playlist index to access track */
370 index = (index + playlist_get_first_index(viewer.playlist)) %
371 viewer.num_tracks;
372 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
373 return -1;
374
375 len = strlen(info.filename) + 1;
376
377 if (len <= size)
378 {
379 strcpy(p, info.filename);
380
381 tracks[pos].name = p;
382 tracks[pos].index = info.index;
383 tracks[pos].display_index = info.display_index;
384 tracks[pos].queued = info.attr & PLAYLIST_ATTR_QUEUED;
385 tracks[pos].skipped = info.attr & PLAYLIST_ATTR_SKIPPED;
386
387 result = len;
388 }
389 else
390 result = -1;
391
392 return result;
393}
394
395/* Format trackname for display purposes */ 341/* Format trackname for display purposes */
396static void format_name(char* dest, const char* src) 342static void format_name(char* dest, const char* src)
397{ 343{
@@ -403,10 +349,10 @@ static void format_name(char* dest, const char* src)
403 /* Only display the mp3 filename */ 349 /* Only display the mp3 filename */
404 char* p = strrchr(src, '/'); 350 char* p = strrchr(src, '/');
405 int len; 351 int len;
406 352
407 strcpy(dest, p+1); 353 strcpy(dest, p+1);
408 len = strlen(dest); 354 len = strlen(dest);
409 355
410 /* Remove the extension */ 356 /* Remove the extension */
411 if (!strcasecmp(&dest[len-4], ".mp3") || 357 if (!strcasecmp(&dest[len-4], ".mp3") ||
412 !strcasecmp(&dest[len-4], ".mp2") || 358 !strcasecmp(&dest[len-4], ".mp2") ||
@@ -441,205 +387,6 @@ static void format_line(const struct playlist_entry* track, char* str, int len)
441 387
442} 388}
443 389
444/* Display tracks on screen */
445static void display_playlist(void)
446{
447 int i;
448 int num_display_tracks =
449 viewer.last_display_index - viewer.first_display_index;
450
451 lcd_clear_display();
452
453#ifdef HAVE_LCD_BITMAP
454 lcd_setmargins(MARGIN_X, MARGIN_Y);
455 lcd_setfont(FONT_UI);
456#endif
457
458 for (i=0; i<=num_display_tracks; i++)
459 {
460 if (global_settings.playlist_viewer_icons)
461 {
462 /* Icons */
463 if (tracks[INDEX(i)].index == viewer.current_playing_track)
464 {
465 /* Current playing track */
466#ifdef HAVE_LCD_BITMAP
467 int offset=0;
468 if ( viewer.line_height > 8 )
469 offset = (viewer.line_height - 8) / 2;
470 lcd_mono_bitmap(bitmap_icons_6x8[Icon_Audio],
471 CURSOR_X * 6 + CURSOR_WIDTH,
472 MARGIN_Y+(i*viewer.line_height) + offset, 6, 8);
473#else
474 lcd_putc(LINE_X-1, i, Icon_Audio);
475#endif
476 }
477 else if (tracks[INDEX(i)].index == viewer.move_track)
478 {
479 /* Track we are moving */
480#ifdef HAVE_LCD_BITMAP
481 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
482 MARGIN_Y+(i*viewer.line_height), "M");
483#else
484 lcd_putc(LINE_X-1, i, 'M');
485#endif
486 }
487 else if (tracks[INDEX(i)].queued)
488 {
489 /* Queued track */
490#ifdef HAVE_LCD_BITMAP
491 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
492 MARGIN_Y+(i*viewer.line_height), "Q");
493#else
494 lcd_putc(LINE_X-1, i, 'Q');
495#endif
496 }
497 }
498
499 update_display_line(i, false);
500 }
501
502#ifdef HAVE_LCD_BITMAP
503 if (global_settings.scrollbar &&
504 (viewer.num_tracks > viewer.num_display_lines))
505 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
506 LCD_HEIGHT - SCROLLBAR_Y, viewer.num_tracks-1,
507 viewer.first_display_index, viewer.last_display_index,
508 VERTICAL);
509#endif
510
511 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
512 status_draw(true);
513}
514
515/* Scroll cursor or display by num lines */
516static void scroll_display(int lines)
517{
518 int new_index = viewer.first_display_index + viewer.cursor_pos + lines;
519 bool pagescroll = false;
520 bool wrap = false;
521
522 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
523
524 if (lines > 1 || lines < -1)
525 pagescroll = true;
526
527 if (new_index < 0)
528 {
529 /* Wrap around if not pageup */
530 if (pagescroll)
531 new_index = 0;
532 else
533 {
534 new_index += viewer.num_tracks;
535 viewer.cursor_pos = viewer.num_display_lines-1;
536 wrap = true;
537 }
538 }
539 else if (new_index >= viewer.num_tracks)
540 {
541 /* Wrap around if not pagedown */
542 if (pagescroll)
543 new_index = viewer.num_tracks - 1;
544 else
545 {
546 new_index -= viewer.num_tracks;
547 viewer.cursor_pos = 0;
548 wrap = true;
549 }
550 }
551
552 if (new_index >= viewer.first_display_index &&
553 new_index <= viewer.last_display_index)
554 {
555 /* Just update the cursor */
556 viewer.cursor_pos = new_index - viewer.first_display_index;
557 }
558 else
559 {
560 /* New track is outside of display */
561 if (wrap)
562 viewer.first_display_index = new_index;
563 else
564 viewer.first_display_index = viewer.first_display_index + lines;
565
566 if (viewer.first_display_index < 0)
567 viewer.first_display_index = 0;
568
569 viewer.last_display_index =
570 viewer.first_display_index + (viewer.num_display_lines - 1);
571 if (viewer.last_display_index >= viewer.num_tracks)
572 {
573 /* display as many tracks as possible on screen */
574 if (viewer.first_display_index > 0)
575 {
576 viewer.first_display_index -=
577 (viewer.last_display_index - viewer.num_tracks + 1);
578 if (viewer.first_display_index < 0)
579 viewer.first_display_index = 0;
580 }
581
582 viewer.last_display_index = viewer.num_tracks - 1;
583 }
584
585 if (viewer.cursor_pos >
586 (viewer.last_display_index - viewer.first_display_index))
587 viewer.cursor_pos =
588 viewer.last_display_index - viewer.first_display_index;
589
590 /* Load more data if needed */
591 if (viewer.first_display_index < viewer.first_index)
592 load_playlist_entries_r(viewer.last_display_index);
593 else if (viewer.last_display_index > viewer.last_index)
594 load_playlist_entries(viewer.first_display_index);
595
596 display_playlist();
597 }
598
599 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
600}
601
602/* Update lcd line. Scroll line if requested */
603static void update_display_line(int line, bool scroll)
604{
605 char str[MAX_PATH + 16];
606
607 format_line(&tracks[INDEX(line)], str, sizeof(str));
608
609 if (scroll)
610 {
611#ifdef HAVE_LCD_BITMAP
612 if (global_settings.invert_cursor)
613 lcd_puts_scroll_style(LINE_X, line, str, STYLE_INVERT);
614 else
615#endif
616 lcd_puts_scroll(LINE_X, line, str);
617 }
618 else
619 lcd_puts(LINE_X, line, str);
620}
621
622/* Update first index, if necessary, to put as much as possible on the
623 screen */
624static void update_first_index(void)
625{
626 /* viewer.num_tracks may be invalid at this point */
627 int num_tracks = playlist_amount_ex(viewer.playlist);
628
629 if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines)
630 {
631 /* Try to display as much as possible */
632 int old_index = viewer.first_display_index;
633
634 viewer.first_display_index = num_tracks - viewer.num_display_lines;
635 if (viewer.first_display_index < 0)
636 viewer.first_display_index = 0;
637
638 /* Cursor should still point at current track */
639 viewer.cursor_pos += old_index - viewer.first_display_index;
640 }
641}
642
643/* Update playlist in case something has changed or forced */ 390/* Update playlist in case something has changed or forced */
644static bool update_playlist(bool force) 391static bool update_playlist(bool force)
645{ 392{
@@ -647,32 +394,18 @@ static bool update_playlist(bool force)
647 playlist_get_resume_info(&viewer.current_playing_track); 394 playlist_get_resume_info(&viewer.current_playing_track);
648 else 395 else
649 viewer.current_playing_track = -1; 396 viewer.current_playing_track = -1;
650 397 int nb_tracks=playlist_amount_ex(viewer.playlist);
651 if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks) 398 force=force || nb_tracks != viewer.num_tracks;
399 if (force)
652 { 400 {
653 int index;
654
655 /* Reload tracks */ 401 /* Reload tracks */
656 viewer.num_tracks = playlist_amount_ex(viewer.playlist); 402 viewer.num_tracks = nb_tracks;
657 if (viewer.num_tracks < 0) 403 if (viewer.num_tracks < 0)
658 return false; 404 return false;
659 405 playlist_buffer_load_entries_screen(&viewer.buffer, FORWARD);
660 index = viewer.first_display_index; 406 if (viewer.buffer.num_loaded <= 0)
661
662 load_playlist_entries(index);
663
664 if (viewer.num_loaded <= 0)
665 return false; 407 return false;
666
667 viewer.first_display_index = viewer.first_index;
668 viewer.last_display_index =
669 viewer.first_index + viewer.num_display_lines - 1;
670 if (viewer.last_display_index >= viewer.num_tracks)
671 viewer.last_display_index = viewer.num_tracks - 1;
672 } 408 }
673
674 display_playlist();
675
676 return true; 409 return true;
677} 410}
678 411
@@ -683,7 +416,9 @@ static int onplay_menu(int index)
683{ 416{
684 struct menu_item items[3]; /* increase this if you add entries! */ 417 struct menu_item items[3]; /* increase this if you add entries! */
685 int m, i=0, result, ret = 0; 418 int m, i=0, result, ret = 0;
686 bool current = (tracks[index].index == viewer.current_playing_track); 419 struct playlist_entry * current_track=
420 playlist_buffer_get_track(&viewer.buffer, index);
421 bool current = (current_track->index == viewer.current_playing_track);
687 422
688 items[i].desc = ID2P(LANG_REMOVE); 423 items[i].desc = ID2P(LANG_REMOVE);
689 i++; 424 i++;
@@ -707,13 +442,14 @@ static int onplay_menu(int index)
707 { 442 {
708 case 0: 443 case 0:
709 /* delete track */ 444 /* delete track */
710 playlist_delete(viewer.playlist, tracks[index].index); 445 playlist_delete(viewer.playlist, current_track->index);
711
712 if (current) 446 if (current)
713 { 447 {
714 /* Start playing new track except if it's the last track 448 /* Start playing new track except if it's the last track
715 in the playlist and repeat mode is disabled */ 449 in the playlist and repeat mode is disabled */
716 if (tracks[index].display_index != viewer.num_tracks || 450 current_track=
451 playlist_buffer_get_track(&viewer.buffer, index);
452 if (current_track->display_index != viewer.num_tracks ||
717 global_settings.repeat_mode == REPEAT_ALL) 453 global_settings.repeat_mode == REPEAT_ALL)
718 { 454 {
719 talk_buffer_steal(); /* will use the mp3 buffer */ 455 talk_buffer_steal(); /* will use the mp3 buffer */
@@ -721,30 +457,26 @@ static int onplay_menu(int index)
721 viewer.current_playing_track = -1; 457 viewer.current_playing_track = -1;
722 } 458 }
723 } 459 }
724
725 ret = 1; 460 ret = 1;
726 break; 461 break;
727 case 1: 462 case 1:
728 /* move track */ 463 /* move track */
729 viewer.move_track = tracks[index].index; 464 viewer.move_track = current_track->index;
730 ret = 0; 465 ret = 0;
731 break; 466 break;
732 case 2: 467 case 2:
733 { 468 {
734 onplay(tracks[index].name, TREE_ATTR_MPA, CONTEXT_TREE); 469 onplay(current_track->name, TREE_ATTR_MPA, CONTEXT_TREE);
735 470
736 if (!viewer.playlist) 471 if (!viewer.playlist)
737 ret = 1; 472 ret = 1;
738 else 473 else
739 ret = 0; 474 ret = 0;
740
741 break; 475 break;
742 } 476 }
743 } 477 }
744 } 478 }
745
746 menu_exit(m); 479 menu_exit(m);
747
748 return ret; 480 return ret;
749} 481}
750 482
@@ -792,7 +524,7 @@ static bool track_display(void)
792 { STR(LANG_DISPLAY_TRACK_NAME_ONLY) }, 524 { STR(LANG_DISPLAY_TRACK_NAME_ONLY) },
793 { STR(LANG_DISPLAY_FULL_PATH) } 525 { STR(LANG_DISPLAY_FULL_PATH) }
794 }; 526 };
795 527
796 return set_option(str(LANG_TRACK_DISPLAY), 528 return set_option(str(LANG_TRACK_DISPLAY),
797 &global_settings.playlist_viewer_track_display, INT, names, 2, NULL); 529 &global_settings.playlist_viewer_track_display, INT, names, 2, NULL);
798} 530}
@@ -807,7 +539,6 @@ static bool save_playlist(void)
807 if (!kbd_input(filename, sizeof(filename))) 539 if (!kbd_input(filename, sizeof(filename)))
808 { 540 {
809 playlist_save(viewer.playlist, filename); 541 playlist_save(viewer.playlist, filename);
810
811 /* reload in case playlist was saved to cwd */ 542 /* reload in case playlist was saved to cwd */
812 reload_directory(); 543 reload_directory();
813 } 544 }
@@ -821,63 +552,89 @@ bool playlist_viewer(void)
821 return playlist_viewer_ex(NULL); 552 return playlist_viewer_ex(NULL);
822} 553}
823 554
555
556
557
558char * playlist_callback_name(int selected_item, void * data, char *buffer)
559{
560 struct playlist_viewer * local_viewer = (struct playlist_viewer *)data;
561 struct playlist_entry *track=
562 playlist_buffer_get_track(&(local_viewer->buffer), selected_item);
563 format_line(track, buffer, MAX_PATH);
564 return(buffer);
565}
566
567
568void playlist_callback_icons(int selected_item, void * data, ICON * icon)
569{
570 struct playlist_viewer * local_viewer=(struct playlist_viewer *)data;
571 struct playlist_entry *track=
572 playlist_buffer_get_track(&(local_viewer->buffer), selected_item);
573 if (track->index == local_viewer->current_playing_track)
574 {
575 /* Current playing track */
576#ifdef HAVE_LCD_BITMAP
577 *icon=bitmap_icons_6x8[Icon_Audio];
578#else
579 *icon=Icon_Audio;
580#endif
581 }
582 else if (track->index == local_viewer->move_track)
583 {
584 /* Track we are moving */
585#ifdef HAVE_LCD_BITMAP
586 *icon=bitmap_icons_6x8[Icon_Moving];
587#else
588 *icon=Icon_Moving;
589#endif
590 }
591 else if (track->queued)
592 {
593 /* Queued track */
594#ifdef HAVE_LCD_BITMAP
595 *icon=bitmap_icons_6x8[Icon_Queued];
596#else
597 *icon=Icon_Queued;
598#endif
599 }
600 else
601 *icon=0;
602}
603
604
605
824/* Main viewer function. Filename identifies playlist to be viewed. If NULL, 606/* Main viewer function. Filename identifies playlist to be viewed. If NULL,
825 view current playlist. */ 607 view current playlist. */
826bool playlist_viewer_ex(char* filename) 608bool playlist_viewer_ex(char* filename)
827{ 609{
828 bool ret = false; /* return value */ 610 bool ret = false; /* return value */
829 bool exit=false; /* exit viewer */ 611 bool exit=false; /* exit viewer */
830 bool update=true; /* update display */
831 bool cursor_on=true; /* used for flashing cursor */
832 int old_cursor_pos; /* last cursor position */
833 int button, lastbutton = BUTTON_NONE; 612 int button, lastbutton = BUTTON_NONE;
834 613 struct gui_synclist playlist_lists;
835 if (!initialize(filename, false)) 614 if (!playlist_viewer_init(&viewer, filename, false))
836 goto exit; 615 goto exit;
837 616
838 old_cursor_pos = viewer.cursor_pos; 617 gui_synclist_init(&playlist_lists, playlist_callback_icons,
839 618 playlist_callback_name, &viewer);
619 gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
620 gui_synclist_select_item(&playlist_lists, viewer.selected_track);
621 gui_synclist_draw(&playlist_lists);
840 while (!exit) 622 while (!exit)
841 { 623 {
842 int track; 624 int track;
843
844 if (!viewer.playlist && !(audio_status() & AUDIO_STATUS_PLAY)) 625 if (!viewer.playlist && !(audio_status() & AUDIO_STATUS_PLAY))
845 { 626 {
846 /* Play has stopped */ 627 /* Play has stopped */
847#ifdef HAVE_LCD_CHARCELLS 628#ifdef HAVE_LCD_CHARCELLS
848 splash(HZ, true, str(LANG_END_PLAYLIST_PLAYER)); 629 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER));
849#else 630#else
850 splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER)); 631 gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
851#endif 632#endif
852 goto exit; 633 goto exit;
853 } 634 }
854 635
855 if (viewer.move_track != -1 || !cursor_on) 636 if (viewer.move_track != -1)
856 { 637 gui_synclist_flash(&playlist_lists);
857 /* Flash cursor to identify that we are moving a track */
858 cursor_on = !cursor_on;
859#ifdef HAVE_LCD_BITMAP
860 if (global_settings.invert_cursor)
861 {
862 lcd_set_drawmode(DRMODE_COMPLEMENT);
863 lcd_fillrect(
864 MARGIN_X, MARGIN_Y+(viewer.cursor_pos*viewer.line_height),
865 LCD_WIDTH, viewer.line_height);
866 lcd_set_drawmode(DRMODE_SOLID);
867 lcd_invertscroll(LINE_X, viewer.cursor_pos);
868 }
869 else
870 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos,
871 cursor_on);
872
873 lcd_update_rect(
874 0, MARGIN_Y + (viewer.cursor_pos * viewer.line_height),
875 LCD_WIDTH, viewer.line_height);
876#else
877 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, cursor_on);
878 lcd_update();
879#endif
880 }
881 638
882 if (!viewer.playlist) 639 if (!viewer.playlist)
883 playlist_get_resume_info(&track); 640 playlist_get_resume_info(&track);
@@ -888,19 +645,27 @@ bool playlist_viewer_ex(char* filename)
888 playlist_amount_ex(viewer.playlist) != viewer.num_tracks) 645 playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
889 { 646 {
890 /* Playlist has changed (new track started?) */ 647 /* Playlist has changed (new track started?) */
891 update_first_index();
892 if (!update_playlist(false)) 648 if (!update_playlist(false))
893 goto exit; 649 goto exit;
894 else 650 gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
895 update = true;
896
897 /* Abort move on playlist change */ 651 /* Abort move on playlist change */
898 viewer.move_track = -1; 652 viewer.move_track = -1;
899 } 653 }
900 654
901 /* Timeout so we can determine if play status has changed */ 655 /* Timeout so we can determine if play status has changed */
902 button = button_get_w_tmo(HZ/2); 656 button = button_get_w_tmo(HZ/2);
903 657 int list_action;
658 if( (list_action=gui_synclist_do_button(&playlist_lists, button))!=0 )
659 {
660 viewer.selected_track=gui_synclist_get_sel_pos(&playlist_lists);
661 if(playlist_buffer_needs_reload(&viewer.buffer, viewer.selected_track))
662 playlist_buffer_load_entries_screen(&viewer.buffer,
663 list_action==LIST_NEXT?
664 FORWARD
665 :
666 BACKWARD
667 );
668 }
904 switch (button) 669 switch (button)
905 { 670 {
906 case TREE_EXIT: 671 case TREE_EXIT:
@@ -910,48 +675,20 @@ bool playlist_viewer_ex(char* filename)
910 exit = true; 675 exit = true;
911 break; 676 break;
912 677
913 case TREE_PREV:
914 case TREE_PREV | BUTTON_REPEAT:
915 scroll_display(-1);
916 update = true;
917 break;
918
919 case TREE_NEXT:
920 case TREE_NEXT | BUTTON_REPEAT:
921 scroll_display(1);
922 update = true;
923 break;
924
925#ifdef TREE_PGUP
926 case TREE_PGUP:
927 case TREE_PGUP | BUTTON_REPEAT:
928 /* Pageup */
929 scroll_display(-viewer.num_display_lines);
930 update = true;
931 break;
932
933 case TREE_PGDN:
934 case TREE_PGDN | BUTTON_REPEAT:
935 /* Pagedown */
936 scroll_display(viewer.num_display_lines);
937 update = true;
938 break;
939#endif
940
941 case TREE_RUN: 678 case TREE_RUN:
942#ifdef TREE_RUN_PRE 679#ifdef TREE_RUN_PRE
943 if (lastbutton != TREE_RUN_PRE) 680 if (lastbutton != TREE_RUN_PRE)
944 break; 681 break;
945#endif 682#endif
683 struct playlist_entry * current_track=playlist_buffer_get_track(&viewer.buffer, viewer.selected_track);
946 if (viewer.move_track >= 0) 684 if (viewer.move_track >= 0)
947 { 685 {
948 /* Move track */ 686 /* Move track */
949 int ret; 687 int ret;
950 688
951 ret = playlist_move(viewer.playlist, viewer.move_track, 689 ret = playlist_move(viewer.playlist, viewer.move_track, current_track->index);
952 tracks[INDEX(viewer.cursor_pos)].index);
953 if (ret < 0) 690 if (ret < 0)
954 splash(HZ, true, str(LANG_MOVE_FAILED)); 691 gui_syncsplash(HZ, true, str(LANG_MOVE_FAILED));
955 692
956 update_playlist(true); 693 update_playlist(true);
957 viewer.move_track = -1; 694 viewer.move_track = -1;
@@ -959,7 +696,7 @@ bool playlist_viewer_ex(char* filename)
959 else if (!viewer.playlist) 696 else if (!viewer.playlist)
960 { 697 {
961 /* play new track */ 698 /* play new track */
962 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0); 699 playlist_start(current_track->index, 0);
963 update_playlist(false); 700 update_playlist(false);
964 } 701 }
965 else 702 else
@@ -968,15 +705,14 @@ bool playlist_viewer_ex(char* filename)
968 if (playlist_set_current(viewer.playlist) < 0) 705 if (playlist_set_current(viewer.playlist) < 0)
969 goto exit; 706 goto exit;
970 707
971 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0); 708 playlist_start(current_track->index, 0);
972 709
973 /* Our playlist is now the current list */ 710 /* Our playlist is now the current list */
974 if (!initialize(NULL, true)) 711 if (!playlist_viewer_init(&viewer, NULL, true))
975 goto exit; 712 goto exit;
976 } 713 }
714 gui_synclist_draw(&playlist_lists);
977 715
978 display_playlist();
979 update = true;
980 break; 716 break;
981 717
982 case TREE_CONTEXT: 718 case TREE_CONTEXT:
@@ -987,7 +723,7 @@ bool playlist_viewer_ex(char* filename)
987 /* ON+PLAY menu */ 723 /* ON+PLAY menu */
988 int ret; 724 int ret;
989 725
990 ret = onplay_menu(INDEX(viewer.cursor_pos)); 726 ret = onplay_menu(viewer.selected_track);
991 727
992 if (ret < 0) 728 if (ret < 0)
993 { 729 {
@@ -997,15 +733,12 @@ bool playlist_viewer_ex(char* filename)
997 else if (ret > 0) 733 else if (ret > 0)
998 { 734 {
999 /* Playlist changed */ 735 /* Playlist changed */
1000 update_first_index(); 736 gui_synclist_del_item(&playlist_lists);
1001 update_playlist(false); 737 update_playlist(true);
1002 if (viewer.num_tracks <= 0) 738 if (viewer.num_tracks <= 0)
1003 exit = true; 739 exit = true;
1004 } 740 }
1005 else 741 gui_synclist_draw(&playlist_lists);
1006 display_playlist();
1007
1008 update = true;
1009 break; 742 break;
1010 } 743 }
1011 744
@@ -1015,13 +748,11 @@ bool playlist_viewer_ex(char* filename)
1015 ret = true; 748 ret = true;
1016 goto exit; 749 goto exit;
1017 } 750 }
1018 751 gui_synclist_draw(&playlist_lists);
1019 display_playlist();
1020 update = true;
1021 break; 752 break;
1022 753
1023 case BUTTON_NONE: 754 case BUTTON_NONE:
1024 status_draw(false); 755 gui_syncstatusbar_draw(&statusbars, false);
1025 break; 756 break;
1026 757
1027 default: 758 default:
@@ -1032,36 +763,6 @@ bool playlist_viewer_ex(char* filename)
1032 } 763 }
1033 break; 764 break;
1034 } 765 }
1035
1036 if (update && !exit)
1037 {
1038 lcd_stop_scroll();
1039
1040 if (viewer.cursor_pos >
1041 (viewer.last_display_index - viewer.first_display_index))
1042 {
1043 /* Cursor position is invalid */
1044 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, false);
1045 viewer.cursor_pos =
1046 viewer.last_display_index - viewer.first_display_index;
1047 put_cursorxy(CURSOR_X, CURSOR_Y + viewer.cursor_pos, true);
1048 }
1049
1050 if (viewer.cursor_pos != old_cursor_pos &&
1051 old_cursor_pos <=
1052 (viewer.last_display_index - viewer.first_display_index))
1053 /* Stop scrolling previous line */
1054 update_display_line(old_cursor_pos, false);
1055
1056 /* Scroll line at new cursor position */
1057 update_display_line(viewer.cursor_pos, true);
1058
1059 lcd_update();
1060
1061 old_cursor_pos = viewer.cursor_pos;
1062 cursor_on = true;
1063 update = false;
1064 }
1065 lastbutton = button; 766 lastbutton = button;
1066 } 767 }
1067 768