diff options
author | William Wilgus <wilgus.william@gmail.com> | 2021-11-11 00:05:29 -0500 |
---|---|---|
committer | William Wilgus <wilgus.william@gmail.com> | 2021-11-11 00:28:59 -0500 |
commit | 30a23fdd6de8fb46e7b1349126a9ae6921cf7555 (patch) | |
tree | 78c1103d3a6f0285cc9555a521fe45d20831379c /apps/plugins | |
parent | cf009b4cbb1eec083ab17ce370df090979eaf68e (diff) | |
download | rockbox-30a23fdd6de8fb46e7b1349126a9ae6921cf7555.tar.gz rockbox-30a23fdd6de8fb46e7b1349126a9ae6921cf7555.zip |
folder_select.c move to plugin as db_folder_select
handles the folder selection for autoresume paths and database paths
folder_select uses the pluginbuf to build the directory tree
hopefully having it as a plugin will make it stop corrupting
TSR plugins like battery_bench and announce_status
I left the original include and source in the gui directory for now
there are currently no other users..
Change-Id: If40ccb5a2bec4286a0616c10dfa7e1479a592808
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 3 | ||||
-rw-r--r-- | apps/plugins/db_folder_select.c | 652 |
3 files changed, 656 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 471a32b9e1..5b672fc8ab 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -22,6 +22,7 @@ clock,apps | |||
22 | codebuster,games | 22 | codebuster,games |
23 | credits,viewers | 23 | credits,viewers |
24 | cube,demos | 24 | cube,demos |
25 | db_folder_select,viewers | ||
25 | demystify,demos | 26 | demystify,demos |
26 | dice,games | 27 | dice,games |
27 | dict,apps | 28 | dict,apps |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index ec13c17dc9..0635a62d83 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -2,6 +2,9 @@ | |||
2 | #if !defined(SIMULATOR) && (CONFIG_BATTERY_MEASURE != 0) | 2 | #if !defined(SIMULATOR) && (CONFIG_BATTERY_MEASURE != 0) |
3 | battery_bench.c | 3 | battery_bench.c |
4 | #endif | 4 | #endif |
5 | #ifdef HAVE_TAGCACHE | ||
6 | db_folder_select.c | ||
7 | #endif | ||
5 | chessclock.c | 8 | chessclock.c |
6 | credits.c | 9 | credits.c |
7 | cube.c | 10 | cube.c |
diff --git a/apps/plugins/db_folder_select.c b/apps/plugins/db_folder_select.c new file mode 100644 index 0000000000..7f51e520cb --- /dev/null +++ b/apps/plugins/db_folder_select.c | |||
@@ -0,0 +1,652 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2012 Jonathan Gordon | ||
10 | * Copyright (C) 2012 Thomas Martitz | ||
11 | * * Copyright (C) 2021 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include "plugin.h" | ||
23 | #ifdef ROCKBOX_HAS_LOGF | ||
24 | #define logf rb->logf | ||
25 | #else | ||
26 | #define logf(...) do { } while(0) | ||
27 | #endif | ||
28 | |||
29 | /* | ||
30 | * Order for changing child states: | ||
31 | * 1) expand folder (skip to 3 if empty, skip to 4 if cannot be opened) | ||
32 | * 2) collapse and select | ||
33 | * 3) unselect (skip to 1) | ||
34 | * 4) do nothing | ||
35 | */ | ||
36 | |||
37 | enum child_state { | ||
38 | EXPANDED, | ||
39 | SELECTED, | ||
40 | COLLAPSED, | ||
41 | EACCESS, | ||
42 | }; | ||
43 | |||
44 | struct child { | ||
45 | char* name; | ||
46 | struct folder *folder; | ||
47 | enum child_state state; | ||
48 | }; | ||
49 | |||
50 | struct folder { | ||
51 | char *name; | ||
52 | struct child *children; | ||
53 | struct folder* previous; | ||
54 | uint16_t children_count; | ||
55 | uint16_t depth; | ||
56 | }; | ||
57 | |||
58 | static char *buffer_front, *buffer_end; | ||
59 | |||
60 | static struct | ||
61 | { | ||
62 | int32_t len; /* keeps count versus maxlen to give buffer full notification */ | ||
63 | uint32_t val; /* hash of all selected items */ | ||
64 | char buf[3];/* address used as identifier -- only \0 written to it */ | ||
65 | char maxlen_exceeded; /*0,1*/ | ||
66 | } hashed; | ||
67 | |||
68 | static inline void get_hash(const char *key, uint32_t *hash, int len) | ||
69 | { | ||
70 | *hash = rb->crc_32(key, len, *hash); | ||
71 | } | ||
72 | |||
73 | static char* folder_alloc(size_t size) | ||
74 | { | ||
75 | char* retval; | ||
76 | /* 32-bit aligned */ | ||
77 | size = ALIGN_UP(size, 4); | ||
78 | if (buffer_front + size > buffer_end) | ||
79 | { | ||
80 | return NULL; | ||
81 | } | ||
82 | retval = buffer_front; | ||
83 | buffer_front += size; | ||
84 | return retval; | ||
85 | } | ||
86 | |||
87 | static char* folder_alloc_from_end(size_t size) | ||
88 | { | ||
89 | if (buffer_end - size < buffer_front) | ||
90 | { | ||
91 | return NULL; | ||
92 | } | ||
93 | buffer_end -= size; | ||
94 | return buffer_end; | ||
95 | } | ||
96 | #if 0 | ||
97 | /* returns the buffer size required to store the path + \0 */ | ||
98 | static int get_full_pathsz(struct folder *start) | ||
99 | { | ||
100 | int reql = 0; | ||
101 | struct folder *next = start; | ||
102 | do | ||
103 | { | ||
104 | reql += rb->strlen(next->name) + 1; | ||
105 | } while ((next = next->previous)); | ||
106 | |||
107 | if (start->name[0] != '/') reql--; | ||
108 | if (--reql < 0) reql = 0; | ||
109 | return reql; | ||
110 | } | ||
111 | #endif | ||
112 | |||
113 | static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz) | ||
114 | { | ||
115 | size_t pos = 0; | ||
116 | struct folder *prev, *cur = NULL, *next = start; | ||
117 | dst[0] = '\0'; /* for rb->strlcat to do its thing */ | ||
118 | /* First traversal R->L mutate nodes->previous to point at child */ | ||
119 | while (next->previous != NULL) /* stop at the root */ | ||
120 | { | ||
121 | #define PATHMUTATE() \ | ||
122 | ({ \ | ||
123 | prev = cur; \ | ||
124 | cur = next; \ | ||
125 | next = cur->previous;\ | ||
126 | cur->previous = prev; \ | ||
127 | }) | ||
128 | PATHMUTATE(); | ||
129 | } | ||
130 | /*swap the next and cur nodes to reverse direction */ | ||
131 | prev = next; | ||
132 | next = cur; | ||
133 | cur = prev; | ||
134 | /* Second traversal L->R mutate nodes->previous to point back at parent | ||
135 | * copy strings to buf as they go by */ | ||
136 | while (next != NULL) | ||
137 | { | ||
138 | PATHMUTATE(); | ||
139 | pos = rb->strlcat(dst, cur->name, dst_sz); | ||
140 | /* do not append slash to paths starting with slash */ | ||
141 | if (cur->name[0] != '/') | ||
142 | pos = rb->strlcat(dst, "/", dst_sz); | ||
143 | } | ||
144 | logf("get_full_path: (%d)[%s]", (int)pos, dst); | ||
145 | return pos; | ||
146 | #undef PATHMUTATE | ||
147 | } | ||
148 | |||
149 | /* support function for rb->qsort() */ | ||
150 | static int compare(const void* p1, const void* p2) | ||
151 | { | ||
152 | struct child *left = (struct child*)p1; | ||
153 | struct child *right = (struct child*)p2; | ||
154 | return rb->strcasecmp(left->name, right->name); | ||
155 | } | ||
156 | |||
157 | static struct folder* load_folder(struct folder* parent, char *folder) | ||
158 | { | ||
159 | DIR *dir; | ||
160 | char fullpath[MAX_PATH]; | ||
161 | |||
162 | struct dirent *entry; | ||
163 | int child_count = 0; | ||
164 | char *first_child = NULL; | ||
165 | size_t len = 0; | ||
166 | |||
167 | struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder)); | ||
168 | if (this == NULL) | ||
169 | goto fail; | ||
170 | |||
171 | if (parent) | ||
172 | { | ||
173 | len = get_full_path(parent, fullpath, sizeof(fullpath)); | ||
174 | if (len >= sizeof(fullpath)) | ||
175 | goto fail; | ||
176 | } | ||
177 | rb->strlcpy(&fullpath[len], folder, sizeof(fullpath) - len); | ||
178 | logf("load_folder: [%s]", fullpath); | ||
179 | |||
180 | dir = rb->opendir(fullpath); | ||
181 | if (dir == NULL) | ||
182 | goto fail; | ||
183 | this->previous = parent; | ||
184 | this->name = folder; | ||
185 | this->children = NULL; | ||
186 | this->children_count = 0; | ||
187 | if (parent) | ||
188 | this->depth = parent->depth + 1; | ||
189 | |||
190 | while ((entry = rb->readdir(dir))) { | ||
191 | /* skip anything not a directory */ | ||
192 | if ((rb->dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) { | ||
193 | continue; | ||
194 | } | ||
195 | /* skip . and .. */ | ||
196 | char *dn = entry->d_name; | ||
197 | if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0'))) | ||
198 | continue; | ||
199 | /* copy entry name to end of buffer, save pointer */ | ||
200 | int len = rb->strlen((char *)entry->d_name); | ||
201 | char *name = folder_alloc_from_end(len+1); /*for NULL*/ | ||
202 | if (name == NULL) | ||
203 | { | ||
204 | rb->closedir(dir); | ||
205 | goto fail; | ||
206 | } | ||
207 | memcpy(name, (char *)entry->d_name, len+1); | ||
208 | child_count++; | ||
209 | first_child = name; | ||
210 | } | ||
211 | rb->closedir(dir); | ||
212 | /* now put the names in the array */ | ||
213 | this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count); | ||
214 | |||
215 | if (this->children == NULL) | ||
216 | goto fail; | ||
217 | |||
218 | while (child_count) | ||
219 | { | ||
220 | struct child *child = &this->children[this->children_count++]; | ||
221 | child->name = first_child; | ||
222 | child->folder = NULL; | ||
223 | child->state = COLLAPSED; | ||
224 | while(*first_child++ != '\0'){};/* move to next name entry */ | ||
225 | child_count--; | ||
226 | } | ||
227 | rb->qsort(this->children, this->children_count, sizeof(struct child), compare); | ||
228 | |||
229 | return this; | ||
230 | fail: | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
234 | struct folder* load_root(void) | ||
235 | { | ||
236 | static struct child root_child; | ||
237 | /* reset the root for each call */ | ||
238 | root_child.name = "/"; | ||
239 | root_child.folder = NULL; | ||
240 | root_child.state = COLLAPSED; | ||
241 | |||
242 | static struct folder root = { | ||
243 | .name = "", | ||
244 | .children = &root_child, | ||
245 | .children_count = 1, | ||
246 | .depth = 0, | ||
247 | .previous = NULL, | ||
248 | }; | ||
249 | |||
250 | return &root; | ||
251 | } | ||
252 | |||
253 | static int count_items(struct folder *start) | ||
254 | { | ||
255 | int count = 0; | ||
256 | int i; | ||
257 | |||
258 | for (i=0; i<start->children_count; i++) | ||
259 | { | ||
260 | struct child *foo = &start->children[i]; | ||
261 | if (foo->state == EXPANDED) | ||
262 | count += count_items(foo->folder); | ||
263 | count++; | ||
264 | } | ||
265 | return count; | ||
266 | } | ||
267 | |||
268 | static struct child* find_index(struct folder *start, int index, struct folder **parent) | ||
269 | { | ||
270 | int i = 0; | ||
271 | *parent = NULL; | ||
272 | |||
273 | while (i < start->children_count) | ||
274 | { | ||
275 | struct child *foo = &start->children[i]; | ||
276 | if (i == index) | ||
277 | { | ||
278 | *parent = start; | ||
279 | return foo; | ||
280 | } | ||
281 | i++; | ||
282 | if (foo->state == EXPANDED) | ||
283 | { | ||
284 | struct child *bar = find_index(foo->folder, index - i, parent); | ||
285 | if (bar) | ||
286 | { | ||
287 | return bar; | ||
288 | } | ||
289 | index -= count_items(foo->folder); | ||
290 | } | ||
291 | } | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | static const char * folder_get_name(int selected_item, void * data, | ||
296 | char * buffer, size_t buffer_len) | ||
297 | { | ||
298 | struct folder *root = (struct folder*)data; | ||
299 | struct folder *parent; | ||
300 | struct child *this = find_index(root, selected_item , &parent); | ||
301 | |||
302 | char *buf = buffer; | ||
303 | if ((int)buffer_len > parent->depth) | ||
304 | { | ||
305 | int i = parent->depth; | ||
306 | while(--i > 0) /* don't indent the parent /folders */ | ||
307 | *buf++ = '\t'; | ||
308 | } | ||
309 | *buf = '\0'; | ||
310 | rb->strlcat(buffer, this->name, buffer_len); | ||
311 | |||
312 | if (this->state == EACCESS) | ||
313 | { /* append error message to the entry if unaccessible */ | ||
314 | size_t len = rb->strlcat(buffer, " ( ", buffer_len); | ||
315 | if (buffer_len > len) | ||
316 | { | ||
317 | rb->snprintf(&buffer[len], buffer_len - len, rb->str(LANG_READ_FAILED), ")"); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | return buffer; | ||
322 | } | ||
323 | |||
324 | static enum themable_icons folder_get_icon(int selected_item, void * data) | ||
325 | { | ||
326 | struct folder *root = (struct folder*)data; | ||
327 | struct folder *parent; | ||
328 | struct child *this = find_index(root, selected_item, &parent); | ||
329 | |||
330 | switch (this->state) | ||
331 | { | ||
332 | case SELECTED: | ||
333 | return Icon_Cursor; | ||
334 | case COLLAPSED: | ||
335 | return Icon_Folder; | ||
336 | case EXPANDED: | ||
337 | return Icon_Submenu; | ||
338 | case EACCESS: | ||
339 | return Icon_Questionmark; | ||
340 | } | ||
341 | return Icon_NOICON; | ||
342 | } | ||
343 | |||
344 | static int child_set_state_expand(struct child *this, struct folder *parent) | ||
345 | { | ||
346 | int newstate = EACCESS; | ||
347 | if (this->folder == NULL) | ||
348 | this->folder = load_folder(parent, this->name); | ||
349 | |||
350 | if (this->folder != NULL) | ||
351 | { | ||
352 | if(this->folder->children_count == 0) | ||
353 | newstate = SELECTED; | ||
354 | else | ||
355 | newstate = EXPANDED; | ||
356 | } | ||
357 | this->state = newstate; | ||
358 | return newstate; | ||
359 | } | ||
360 | |||
361 | static int folder_action_callback(int action, struct gui_synclist *list) | ||
362 | { | ||
363 | struct folder *root = (struct folder*)list->data; | ||
364 | struct folder *parent; | ||
365 | struct child *this = find_index(root, list->selected_item, &parent), *child; | ||
366 | int i; | ||
367 | |||
368 | if (action == ACTION_STD_OK) | ||
369 | { | ||
370 | switch (this->state) | ||
371 | { | ||
372 | case EXPANDED: | ||
373 | this->state = SELECTED; | ||
374 | break; | ||
375 | case SELECTED: | ||
376 | this->state = COLLAPSED; | ||
377 | break; | ||
378 | case COLLAPSED: | ||
379 | child_set_state_expand(this, parent); | ||
380 | break; | ||
381 | case EACCESS: | ||
382 | /* cannot open, do nothing */ | ||
383 | return action; | ||
384 | } | ||
385 | action = ACTION_REDRAW; | ||
386 | } | ||
387 | else if (action == ACTION_STD_CONTEXT) | ||
388 | { | ||
389 | switch (this->state) | ||
390 | { | ||
391 | case EXPANDED: | ||
392 | for (i = 0; i < this->folder->children_count; i++) | ||
393 | { | ||
394 | child = &this->folder->children[i]; | ||
395 | switch (child->state) | ||
396 | { | ||
397 | case SELECTED: | ||
398 | case EXPANDED: | ||
399 | child->state = COLLAPSED; | ||
400 | break; | ||
401 | case COLLAPSED: | ||
402 | child->state = SELECTED; | ||
403 | break; | ||
404 | case EACCESS: | ||
405 | break; | ||
406 | } | ||
407 | } | ||
408 | break; | ||
409 | case SELECTED: | ||
410 | case COLLAPSED: | ||
411 | if (child_set_state_expand(this, parent) != EACCESS) | ||
412 | { | ||
413 | for (i = 0; i < (this->folder->children_count); i++) | ||
414 | { | ||
415 | child = &this->folder->children[i]; | ||
416 | child->state = SELECTED; | ||
417 | } | ||
418 | } | ||
419 | break; | ||
420 | case EACCESS: | ||
421 | /* cannot open, do nothing */ | ||
422 | return action; | ||
423 | } | ||
424 | action = ACTION_REDRAW; | ||
425 | } | ||
426 | if (action == ACTION_REDRAW) | ||
427 | list->nb_items = count_items(root); | ||
428 | return action; | ||
429 | } | ||
430 | |||
431 | static struct child* find_from_filename(const char* filename, struct folder *root) | ||
432 | { | ||
433 | if (!root) | ||
434 | return NULL; | ||
435 | const char *slash = rb->strchr(filename, '/'); | ||
436 | struct child *this; | ||
437 | |||
438 | /* filenames beginning with a / are specially treated as the | ||
439 | * loop below can't handle them. they can only occur on the first, | ||
440 | * and not recursive, calls to this function.*/ | ||
441 | if (filename[0] == '/') /* in the loop nothing starts with '/' */ | ||
442 | { | ||
443 | logf("find_from_filename [%s]", filename); | ||
444 | /* filename begins with /. in this case root must be the | ||
445 | * top level folder */ | ||
446 | this = &root->children[0]; | ||
447 | if (filename[1] == '\0') | ||
448 | { /* filename == "/" */ | ||
449 | return this; | ||
450 | } | ||
451 | else /* filename == "/XXX/YYY". cascade down */ | ||
452 | goto cascade; | ||
453 | } | ||
454 | |||
455 | for (int i = 0; i < root->children_count; i++) | ||
456 | { | ||
457 | this = &root->children[i]; | ||
458 | /* when slash == NULL n will be really large but \0 stops the compare */ | ||
459 | if (rb->strncasecmp(this->name, filename, slash - filename) == 0) | ||
460 | { | ||
461 | if (slash == NULL) | ||
462 | { /* filename == XXX */ | ||
463 | return this; | ||
464 | } | ||
465 | else | ||
466 | goto cascade; | ||
467 | } | ||
468 | } | ||
469 | return NULL; | ||
470 | |||
471 | cascade: | ||
472 | /* filename == XXX/YYY. cascade down */ | ||
473 | child_set_state_expand(this, root); | ||
474 | while (slash[0] == '/') slash++; /* eat slashes */ | ||
475 | return find_from_filename(slash, this->folder); | ||
476 | } | ||
477 | |||
478 | static int select_paths(struct folder* root, const char* filenames) | ||
479 | { | ||
480 | /* Takes a list of filenames in a ':' delimited string | ||
481 | splits filenames at the ':' character loads each into buffer | ||
482 | selects each file in the folder list | ||
483 | |||
484 | if last item or only item the rest of the string is copied to the buffer | ||
485 | *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0* | ||
486 | */ | ||
487 | char buf[MAX_PATH]; | ||
488 | const int buflen = sizeof(buf); | ||
489 | |||
490 | const char *fnp = filenames; | ||
491 | const char *lastfnp = fnp; | ||
492 | const char *sstr; | ||
493 | off_t len; | ||
494 | |||
495 | while (fnp) | ||
496 | { | ||
497 | fnp = rb->strchr(fnp, ':'); | ||
498 | if (fnp) | ||
499 | { | ||
500 | len = fnp - lastfnp; | ||
501 | fnp++; | ||
502 | } | ||
503 | else /* no ':' get the rest of the string */ | ||
504 | len = rb->strlen(lastfnp); | ||
505 | |||
506 | sstr = lastfnp; | ||
507 | lastfnp = fnp; | ||
508 | if (len <= 0 || len > buflen) | ||
509 | continue; | ||
510 | rb->strlcpy(buf, sstr, len + 1); | ||
511 | struct child *item = find_from_filename(buf, root); | ||
512 | if (item) | ||
513 | item->state = SELECTED; | ||
514 | } | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen) | ||
520 | { | ||
521 | size_t len; | ||
522 | struct folder *curfolder; | ||
523 | char* name; | ||
524 | |||
525 | for (int i = 0; i < root->children_count; i++) | ||
526 | { | ||
527 | struct child *this = &root->children[i]; | ||
528 | if (this->state == SELECTED) | ||
529 | { | ||
530 | if (this->folder == NULL) | ||
531 | { | ||
532 | curfolder = root; | ||
533 | name = this->name; | ||
534 | logf("save_folders_r: this->name[%s]", name); | ||
535 | } | ||
536 | else | ||
537 | { | ||
538 | curfolder = this->folder->previous; | ||
539 | name = this->folder->name; | ||
540 | logf("save_folders_r: this->folder->name[%s]", name); | ||
541 | } | ||
542 | |||
543 | len = get_full_path(curfolder, buffer_front, buflen); | ||
544 | |||
545 | if (len + 2 >= buflen) | ||
546 | continue; | ||
547 | |||
548 | len += rb->snprintf(&buffer_front[len], buflen - len, "%s:", name); | ||
549 | logf("save_folders_r: [%s]", buffer_front); | ||
550 | if (dst != hashed.buf) | ||
551 | { | ||
552 | int dlen = rb->strlen(dst); | ||
553 | if (dlen + len >= maxlen) | ||
554 | continue; | ||
555 | rb->strlcpy(&dst[dlen], buffer_front, maxlen - dlen); | ||
556 | } | ||
557 | else | ||
558 | { | ||
559 | if (hashed.len + len >= maxlen) | ||
560 | { | ||
561 | hashed.maxlen_exceeded = 1; | ||
562 | continue; | ||
563 | } | ||
564 | get_hash(buffer_front, &hashed.val, len); | ||
565 | hashed.len += len; | ||
566 | } | ||
567 | } | ||
568 | else if (this->state == EXPANDED) | ||
569 | save_folders_r(this->folder, dst, maxlen, buflen); | ||
570 | } | ||
571 | } | ||
572 | |||
573 | static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen) | ||
574 | { | ||
575 | hashed.len = 0; | ||
576 | hashed.val = 0; | ||
577 | hashed.maxlen_exceeded = 0; | ||
578 | size_t len = buffer_end - buffer_front; | ||
579 | dst[0] = '\0'; | ||
580 | save_folders_r(root, dst, maxlen, len); | ||
581 | len = rb->strlen(dst); | ||
582 | /* fix trailing ':' */ | ||
583 | if (len > 1) dst[len-1] = '\0'; | ||
584 | /*Notify - user will probably not see save dialog if nothing new got added*/ | ||
585 | if (hashed.maxlen_exceeded > 0) rb->splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL)); | ||
586 | return hashed.val; | ||
587 | } | ||
588 | |||
589 | bool folder_select(char * header_text, char* setting, int setting_len) | ||
590 | { | ||
591 | struct folder *root; | ||
592 | struct simplelist_info info; | ||
593 | size_t buf_size; | ||
594 | |||
595 | buffer_front = rb->plugin_get_buffer(&buf_size); | ||
596 | buffer_end = buffer_front + buf_size; | ||
597 | logf("folder_select %d bytes free", (int)(buffer_end - buffer_front)); | ||
598 | root = load_root(); | ||
599 | |||
600 | logf("folders in: %s", setting); | ||
601 | /* Load previous selection(s) */ | ||
602 | select_paths(root, setting); | ||
603 | /* get current hash to check for changes later */ | ||
604 | uint32_t hash = save_folders(root, hashed.buf, setting_len); | ||
605 | rb->simplelist_info_init(&info, header_text, | ||
606 | count_items(root), root); | ||
607 | info.get_name = folder_get_name; | ||
608 | info.action_callback = folder_action_callback; | ||
609 | info.get_icon = folder_get_icon; | ||
610 | rb->simplelist_show_list(&info); | ||
611 | logf("folder_select %d bytes free", (int)(buffer_end - buffer_front)); | ||
612 | /* done editing. check for changes */ | ||
613 | if (hash != save_folders(root, hashed.buf, setting_len)) | ||
614 | { /* prompt for saving changes and commit if yes */ | ||
615 | if (rb->yesno_pop(ID2P(LANG_SAVE_CHANGES))) | ||
616 | { | ||
617 | save_folders(root, setting, setting_len); | ||
618 | rb->settings_save(); | ||
619 | logf("folders out: %s", setting); | ||
620 | return true; | ||
621 | } | ||
622 | } | ||
623 | return false; | ||
624 | } | ||
625 | |||
626 | /* plugin entry point */ | ||
627 | enum plugin_status plugin_start(const void* parameter) | ||
628 | { | ||
629 | (void) parameter; | ||
630 | |||
631 | if(parameter) | ||
632 | { | ||
633 | |||
634 | if (rb->strcmp(parameter, rb->str(LANG_AUTORESUME)) == 0) | ||
635 | { | ||
636 | if (folder_select(rb->str(LANG_AUTORESUME), | ||
637 | rb->global_settings->autoresume_paths, | ||
638 | MAX_PATHNAME+1)) | ||
639 | { | ||
640 | return 1; | ||
641 | } | ||
642 | } | ||
643 | } | ||
644 | else if (folder_select(rb->str(LANG_SELECT_FOLDER), | ||
645 | rb->global_settings->tagcache_scan_paths, | ||
646 | sizeof(rb->global_settings->tagcache_scan_paths))) | ||
647 | { | ||
648 | return 1; | ||
649 | } | ||
650 | |||
651 | return PLUGIN_OK; | ||
652 | } | ||