A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Daniel Rigby
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include "lib/playback_control.h"
23
24
25
26#define MAX_LIST_SIZE 400
27#define DESC_SIZE 40
28#define MAX_LINE_LEN (DESC_SIZE + 1)
29
30enum flag_type {
31 FL_CLEARED = 0,
32 FL_SET,
33 FL_CATEGORY
34};
35
36enum view_type {
37 EDIT_SHOPPING_LIST = 0,
38 VIEW_SHOPPING_LIST
39};
40
41#define VIEW_TYPE_SIZE VIEW_SHOPPING_LIST + 1
42
43struct items_list_s {
44 unsigned int id;
45 enum flag_type flag;
46 char desc[DESC_SIZE];
47};
48
49static struct items_list_s items_list[MAX_LIST_SIZE];
50static int total_item_count = 0;
51static int view_id_list[MAX_LIST_SIZE];
52static int view_item_count;
53static enum view_type view = EDIT_SHOPPING_LIST;
54static char filename[MAX_PATH];
55static bool changed = false;
56static bool show_categories = true;
57static char category_string[] = "Hide categories";
58
59static const char *list_get_name_cb(int selected_item, void* data,
60 char* buf, size_t buf_len)
61{
62 (void)data;
63 rb->strlcpy(buf, items_list[view_id_list[selected_item]].desc, buf_len);
64 return buf;
65}
66
67static enum themable_icons list_get_icon_cb(int selected_item, void *data)
68{
69 (void)data;
70 if (items_list[view_id_list[selected_item]].flag == FL_CATEGORY)
71 return Icon_Rockbox;
72 else if (items_list[view_id_list[selected_item]].flag == FL_SET)
73 return Icon_Cursor;
74 else
75 return Icon_NOICON;
76}
77
78static bool save_changes(void)
79{
80 int fd;
81 int i;
82
83 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC);
84 if (fd < 0)
85 {
86 rb->splash(HZ*2, "Changes NOT saved");
87 return false;
88 }
89
90 rb->lcd_clear_display();
91#ifdef HAVE_ADJUSTABLE_CPU_FREQ
92 rb->cpu_boost(1);
93#endif
94 for (i = 0;i < total_item_count; i++)
95 {
96 switch (items_list[i].flag)
97 {
98 case FL_CATEGORY:
99 {
100 rb->fdprintf(fd,"#%s\n",items_list[i].desc);
101 break;
102 }
103 case FL_SET:
104 {
105 rb->fdprintf(fd,"!%s\n",items_list[i].desc);
106 break;
107 }
108 case FL_CLEARED:
109 {
110 rb->fdprintf(fd," %s\n",items_list[i].desc);
111 break;
112 }
113 }
114 }
115 /* save current view */
116 rb->fdprintf(fd,"$%d%d\n",view, show_categories);
117
118#ifdef HAVE_ADJUSTABLE_CPU_FREQ
119 rb->cpu_boost(0);
120#endif
121 rb->close(fd);
122
123 return true;
124}
125
126static void create_view(struct gui_synclist *lists)
127{
128 unsigned int cnt = 0;
129 int i, j;
130
131 switch (view)
132 {
133 case EDIT_SHOPPING_LIST:
134 {
135 for (i = 0; i < total_item_count; i++)
136 {
137 if (show_categories || (items_list[i].flag != FL_CATEGORY))
138 view_id_list[cnt++] = i;
139 }
140 view_item_count = cnt;
141 rb->gui_synclist_set_title(lists,"Select items",Icon_Playlist);
142 break;
143 }
144 case VIEW_SHOPPING_LIST:
145 {
146 for (i = 0; i < total_item_count; i++)
147 {
148 if ((items_list[i].flag == FL_CATEGORY) && show_categories)
149 {
150 for (j = i+1; j < total_item_count; j++)
151 {
152 if (items_list[j].flag == FL_SET)
153 {
154 view_id_list[cnt++] = i;
155 break;
156 }
157 if (items_list[j].flag == FL_CATEGORY)
158 break;
159 }
160 }
161 else if (items_list[i].flag == FL_SET)
162 view_id_list[cnt++] = i;
163 }
164 view_item_count = cnt;
165 rb->gui_synclist_set_title(lists,"Shopping list",Icon_Playlist);
166 break;
167 }
168 }
169}
170
171static bool toggle(int selected_item)
172{
173 if (items_list[view_id_list[selected_item]].flag == FL_CATEGORY)
174 return false;
175 else if (items_list[view_id_list[selected_item]].flag == FL_SET)
176 items_list[view_id_list[selected_item]].flag = FL_CLEARED;
177 else
178 items_list[view_id_list[selected_item]].flag = FL_SET;
179 return true;
180}
181
182static void update_category_string(void)
183{
184 if (show_categories)
185 rb->strcpy(category_string,"Hide categories");
186 else
187 rb->strcpy(category_string,"Show categories");
188}
189
190static enum plugin_status load_file(void)
191{
192 int fd;
193 static char temp_line[DESC_SIZE];
194 static struct items_list_s new_item;
195 static int count = 0;
196 int linelen;
197 total_item_count = 0;
198
199 fd = rb->open(filename,O_RDONLY);
200 if (fd < 0)
201 {
202 rb->splashf(HZ*2,"Couldn't open file: %s",filename);
203 return PLUGIN_ERROR;
204 }
205
206 /* read in the file */
207 while (rb->read_line(fd,temp_line,MAX_LINE_LEN))
208 {
209 if (rb->strncmp(temp_line, "$", 1) == 0)
210 {
211 /* read view preferences */
212 linelen = rb->strlen(temp_line);
213 if (linelen >= 2)
214 {
215 unsigned int val = temp_line[1] - '0';
216 if (val < VIEW_TYPE_SIZE)
217 {
218 view = val;
219 }
220 }
221 if (linelen >= 3)
222 {
223 unsigned int val = temp_line[2] - '0';
224 if (val <= 2)
225 {
226 show_categories = val;
227 update_category_string();
228 }
229 }
230 }
231 else
232 {
233 new_item.id = count;
234 if (rb->strncmp(temp_line, " ", 1) == 0)
235 {
236 /* read description, flag = cleared */
237 new_item.flag = FL_CLEARED;
238 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE);
239 }
240 else if (rb->strncmp(temp_line, "!", 1) == 0)
241 {
242 /* read description, flag = set */
243 new_item.flag = FL_SET;
244 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE);
245 }
246 else if (rb->strncmp(temp_line, "#", 1) == 0)
247 {
248 /* read description, flag = category */
249 new_item.flag = FL_CATEGORY;
250 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE);
251 }
252 else
253 {
254 /* read description, flag = cleared */
255 new_item.flag = FL_CLEARED;
256 rb->memcpy(new_item.desc, temp_line, DESC_SIZE);
257 }
258 items_list[total_item_count] = new_item;
259 total_item_count++;
260 if (total_item_count == MAX_LIST_SIZE)
261 {
262 total_item_count = MAX_LIST_SIZE - 1;
263 rb->splashf(HZ*2, "Truncating shopping list to %d items",
264 MAX_LIST_SIZE - 1);
265 changed = true;
266 rb->close(fd);
267 return PLUGIN_OK;
268 }
269 }
270 }
271 rb->close(fd);
272 changed = false;
273 return PLUGIN_OK;
274}
275
276/* this is the plugin entry point */
277enum plugin_status plugin_start(const void* parameter)
278{
279 struct gui_synclist lists;
280 bool exit = false;
281 int button;
282 int cur_sel = 0;
283
284#if LCD_DEPTH > 1
285 rb->lcd_set_backdrop(NULL);
286#endif
287
288#ifdef HAVE_ADJUSTABLE_CPU_FREQ
289 rb->cpu_boost(1);
290#endif
291 if (parameter)
292 {
293 rb->strcpy(filename,(char*)parameter);
294
295 if (load_file() == PLUGIN_ERROR)
296 return PLUGIN_ERROR;
297 }
298 else
299 return PLUGIN_ERROR;
300
301#ifdef HAVE_ADJUSTABLE_CPU_FREQ
302 rb->cpu_boost(0);
303#endif
304 /* now dump it in the list */
305 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
306 rb->gui_synclist_set_icon_callback(&lists, list_get_icon_cb);
307 create_view(&lists);
308 rb->gui_synclist_set_nb_items(&lists,view_item_count);
309 rb->gui_synclist_select_item(&lists, 0);
310 rb->gui_synclist_draw(&lists);
311 rb->lcd_update();
312
313 while (!exit)
314 {
315 rb->gui_synclist_draw(&lists);
316 cur_sel = rb->gui_synclist_get_sel_pos(&lists);
317 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
318 if (rb->gui_synclist_do_button(&lists, &button))
319 continue;
320 switch (button)
321 {
322 case ACTION_STD_OK:
323 {
324 changed |= toggle(cur_sel);
325 break;
326 }
327 case ACTION_STD_MENU:
328 case ACTION_STD_CONTEXT:
329 {
330 switch(view)
331 {
332 case EDIT_SHOPPING_LIST:
333 {
334 MENUITEM_STRINGLIST(menu, "Options", NULL,
335 "View shopping list",
336 "Clear all items",
337 "Mark all items",
338 category_string,
339 "Revert to saved",
340 "Show Playback Menu",
341 "Quit without saving",
342 "Quit");
343
344 switch (rb->do_menu(&menu, NULL, NULL, false))
345 {
346 case 0:
347 {
348 /* view shopping list */
349 view = VIEW_SHOPPING_LIST;
350 changed = true;
351 break;
352 }
353 case 1:
354 {
355 /* clear all items */
356 int i;
357 for (i = 0; i < total_item_count; i++)
358 {
359 if (items_list[i].flag == FL_SET)
360 items_list[i].flag = FL_CLEARED;
361 }
362 changed = true;
363 break;
364 }
365 case 2:
366 {
367 /* mark all items */
368 int i;
369 for (i = 0; i < total_item_count; i++)
370 {
371 if (items_list[i].flag == FL_CLEARED)
372 items_list[i].flag = FL_SET;
373 }
374 changed = true;
375 break;
376 }
377 case 3:
378 {
379 /* toggle categories */
380 show_categories ^= true;
381 update_category_string();
382 changed = true;
383 break;
384 }
385 case 4:
386 {
387 /* revert to saved */
388 if (load_file() == PLUGIN_ERROR)
389 return PLUGIN_ERROR;
390 break;
391 }
392 case 5:
393 {
394 /* playback menu */
395 playback_control(NULL);
396 break;
397 }
398 case 6:
399 {
400 /* Quit without saving */
401 exit = 1;
402 break;
403 }
404 case 7:
405 {
406 /* Save and quit */
407 if (changed)
408 save_changes();
409 exit = 1;
410 break;
411 }
412 default:
413 {
414 break;
415 }
416 }
417 break;
418 }
419
420 case VIEW_SHOPPING_LIST:
421 {
422 MENUITEM_STRINGLIST(menu, "Options", NULL,
423 "Edit list",
424 "Reset list",
425 category_string,
426 "Revert to saved",
427 "Show Playback Menu",
428 "Quit without saving",
429 "Quit");
430
431 switch (rb->do_menu(&menu, NULL, NULL, false))
432 {
433 case 0:
434 {
435 /* edit list */
436 view = EDIT_SHOPPING_LIST;
437 changed = true;
438 break;
439 }
440 case 1:
441 {
442 /* reset list */
443 int i;
444 for (i = 0; i < total_item_count; i++)
445 {
446 if (items_list[i].flag == FL_SET)
447 items_list[i].flag = FL_CLEARED;
448 }
449 view = EDIT_SHOPPING_LIST;
450 changed = true;
451 break;
452 }
453 case 2:
454 {
455 /* toggle categories */
456 show_categories ^= true;
457 update_category_string();
458 changed = true;
459 break;
460 }
461 case 3:
462 {
463 /* revert to saved */
464 if (load_file() == PLUGIN_ERROR)
465 return PLUGIN_ERROR;
466 break;
467 }
468 case 4:
469 {
470 /* playback menu */
471 playback_control(NULL);
472 break;
473 }
474 case 5:
475 {
476 /* Quit without saving */
477 exit = 1;
478 break;
479 }
480 case 6:
481 {
482 /* Save and quit */
483 if (changed)
484 save_changes();
485 exit = 1;
486 break;
487 }
488 default:
489 {
490 break;
491 }
492 }
493 break;
494 }
495 }
496 break;
497 }
498 case ACTION_STD_CANCEL:
499 {
500 if (changed)
501 save_changes();
502 exit = 1;
503 break;
504 }
505 }
506
507 create_view(&lists);
508 rb->gui_synclist_set_nb_items(&lists,view_item_count);
509 if (view_item_count > 0 && view_item_count <= cur_sel)
510 rb->gui_synclist_select_item(&lists,view_item_count-1);
511 }
512 return PLUGIN_OK;
513}