summaryrefslogtreecommitdiff
path: root/apps/gui
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2021-11-01 00:22:22 -0400
committerWilliam Wilgus <me.theuser@yahoo.com>2021-11-10 02:34:06 -0500
commit58462184d16ac26c77957224bb0ed94567a2eff5 (patch)
tree78b39e7f7bd394152546431fdcf77ce13f7278e6 /apps/gui
parentb14df9077ff87d978577e17457dabfb997114863 (diff)
downloadrockbox-58462184d16ac26c77957224bb0ed94567a2eff5.tar.gz
rockbox-58462184d16ac26c77957224bb0ed94567a2eff5.zip
folder_select.c partial rewrite -- remove static, add full notification
| with some code refactoring we can eliminate the static char buffer in get_full_path() I'm guessing making the functions recursive prompted the static buffer I don't see any reason we can't just pass the same buffer | During tested I noted failure after buffer was full -- splash added for buffer full notification added some logic to not add partial entries and try to find a | selected item that will fit in the remaing buffer ------------------------------------------------------------------------ While looking through the source I noticed a few potential pitfalls with the current code. Namely the stack allocated temporary buffer sized to setting_len. I also noted the rigid vect[32] with the timeless -- /* 32 separate folders should be Enough For Everybody(TM) */ and decided to make it a bit more robust The saved items are hashed with crc32 on all the paths and a hashed_len is kept to aid in the buffer full message before the user actually goes to save the changes (assuming they even get the message) In the old code, buffer might be the same since it ran out of space and didn't get their later selections the hashed_len could easily be turned into a way to get a needed buffer size as well for someone in the future just pass a really large maxlen I made get_full_path non recursive since it liked to blow the stack while embedded in all the other recursive calls making it a pain to debug (the real reason the buffer was static?) traverse first from the current folder to root mutating the parent pointers to point at the previous child next traverse back to the folder unmutating & taking names on the way Folder depth is now uint16 65535 levels is probably excessive children_count is also uint16 as well I made the first folders below root '/' stay below root instead of shifting since the horizontal real estate is limited slightly smaller than it began but hopefully faster & more reliable Change-Id: I348f61baf865cccdeacddfd9d50641a882e890fc
Diffstat (limited to 'apps/gui')
-rw-r--r--apps/gui/folder_select.c429
1 files changed, 270 insertions, 159 deletions
diff --git a/apps/gui/folder_select.c b/apps/gui/folder_select.c
index 706b166941..e324e8649a 100644
--- a/apps/gui/folder_select.c
+++ b/apps/gui/folder_select.c
@@ -8,6 +8,7 @@
8 * 8 *
9 * Copyright (C) 2012 Jonathan Gordon 9 * Copyright (C) 2012 Jonathan Gordon
10 * Copyright (C) 2012 Thomas Martitz 10 * Copyright (C) 2012 Thomas Martitz
11* * Copyright (C) 2021 William Wilgus
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -30,7 +31,11 @@
30#include "language.h" 31#include "language.h"
31#include "list.h" 32#include "list.h"
32#include "plugin.h" 33#include "plugin.h"
34#include "splash.h"
33 35
36/* Define LOGF_ENABLE to enable logf output in this file */
37//#define LOGF_ENABLE
38#include "logf.h"
34 39
35/* 40/*
36 * Order for changing child states: 41 * Order for changing child states:
@@ -56,18 +61,31 @@ struct child {
56struct folder { 61struct folder {
57 char *name; 62 char *name;
58 struct child *children; 63 struct child *children;
59 int children_count;
60 int depth;
61
62 struct folder* previous; 64 struct folder* previous;
65 uint16_t children_count;
66 uint16_t depth;
63}; 67};
64 68
65static char *buffer_front, *buffer_end; 69static char *buffer_front, *buffer_end;
70
71static struct
72{
73 int32_t len; /* keeps count versus maxlen to give buffer full notification */
74 uint32_t val; /* hash of all selected items */
75 char buf[3];/* address used as identifier -- only \0 written to it */
76 char maxlen_exceeded; /*0,1*/
77} hashed;
78
79static inline void get_hash(const char *key, uint32_t *hash, int len)
80{
81 *hash = crc_32(key, len, *hash);
82}
83
66static char* folder_alloc(size_t size) 84static char* folder_alloc(size_t size)
67{ 85{
68 char* retval; 86 char* retval;
69 /* 32-bit aligned */ 87 /* 32-bit aligned */
70 size = (size + 3) & ~3; 88 size = ALIGN_UP(size, 4);
71 if (buffer_front + size > buffer_end) 89 if (buffer_front + size > buffer_end)
72 { 90 {
73 return NULL; 91 return NULL;
@@ -86,32 +104,57 @@ static char* folder_alloc_from_end(size_t size)
86 buffer_end -= size; 104 buffer_end -= size;
87 return buffer_end; 105 return buffer_end;
88} 106}
89 107#if 0
90static void get_full_path_r(struct folder *start, char* dst) 108/* returns the buffer size required to store the path + \0 */
109static int get_full_pathsz(struct folder *start)
91{ 110{
92 if (start->previous) 111 int reql = 0;
93 get_full_path_r(start->previous, dst); 112 struct folder *next = start;
94 113 do
95 if (start->name && start->name[0] && strcmp(start->name, "/"))
96 { 114 {
97 strlcat(dst, "/", MAX_PATH); 115 reql += strlen(next->name) + 1;
98 strlcat(dst, start->name, MAX_PATH); 116 } while ((next = next->previous));
99 } 117
118 if (start->name[0] != '/') reql--;
119 if (--reql < 0) reql = 0;
120 return reql;
100} 121}
122#endif
101 123
102static char* get_full_path(struct folder *start) 124static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz)
103{ 125{
104 static char buffer[MAX_PATH]; 126 size_t pos = 0;
105 127 struct folder *prev, *cur = NULL, *next = start;
106 if (strcmp(start->name, "/")) 128 dst[0] = '\0'; /* for strlcat to do its thing */
129 /* First traversal R->L mutate nodes->previous to point at child */
130 while (next->previous != NULL) /* stop at the root */
107 { 131 {
108 buffer[0] = 0; 132#define PATHMUTATE() \
109 get_full_path_r(start, buffer); 133 ({ \
134 prev = cur; \
135 cur = next; \
136 next = cur->previous;\
137 cur->previous = prev; \
138 })
139 PATHMUTATE();
110 } 140 }
111 else /* get_full_path_r() does the wrong thing for / */ 141 /*swap the next and cur nodes to reverse direction */
112 return "/"; 142 prev = next;
113 143 next = cur;
114 return buffer; 144 cur = prev;
145 /* Second traversal L->R mutate nodes->previous to point back at parent
146 * copy strings to buf as they go by */
147 while (next != NULL)
148 {
149 PATHMUTATE();
150 pos = strlcat(dst, cur->name, dst_sz);
151 /* do not append slash to paths starting with slash */
152 if (cur->name[0] != '/')
153 pos = strlcat(dst, "/", dst_sz);
154 }
155 logf("get_full_path: (%d)[%s]", (int)pos, dst);
156 return pos;
157#undef PATHMUTATE
115} 158}
116 159
117/* support function for qsort() */ 160/* support function for qsort() */
@@ -125,49 +168,52 @@ static int compare(const void* p1, const void* p2)
125static struct folder* load_folder(struct folder* parent, char *folder) 168static struct folder* load_folder(struct folder* parent, char *folder)
126{ 169{
127 DIR *dir; 170 DIR *dir;
128 char* path = get_full_path(parent);
129 char fullpath[MAX_PATH]; 171 char fullpath[MAX_PATH];
172
130 struct dirent *entry; 173 struct dirent *entry;
131 struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
132 int child_count = 0; 174 int child_count = 0;
133 char *first_child = NULL; 175 char *first_child = NULL;
176 size_t len = 0;
134 177
135 if (!strcmp(folder,"/")) 178 struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
136 strlcpy(fullpath, folder, 2); 179 if (this == NULL)
137 else 180 goto fail;
138 snprintf(fullpath, MAX_PATH, "%s/%s", parent ? path : "", folder); 181
182 if (parent)
183 {
184 len = get_full_path(parent, fullpath, sizeof(fullpath));
185 if (len >= sizeof(fullpath))
186 goto fail;
187 }
188 strlcpy(&fullpath[len], folder, sizeof(fullpath) - len);
189 logf("load_folder: [%s]", fullpath);
139 190
140 if (!this)
141 return NULL;
142 dir = opendir(fullpath); 191 dir = opendir(fullpath);
143 if (!dir) 192 if (dir == NULL)
144 return NULL; 193 goto fail;
145 this->previous = parent; 194 this->previous = parent;
146 this->name = folder; 195 this->name = folder;
147 this->children = NULL; 196 this->children = NULL;
148 this->children_count = 0; 197 this->children_count = 0;
149 this->depth = parent ? parent->depth + 1 : 0; 198 if (parent)
199 this->depth = parent->depth + 1;
150 200
151 while ((entry = readdir(dir))) { 201 while ((entry = readdir(dir))) {
152 int len = strlen((char *)entry->d_name);
153 struct dirinfo info;
154
155 info = dir_get_info(dir, entry);
156
157 /* skip anything not a directory */ 202 /* skip anything not a directory */
158 if ((info.attribute & ATTR_DIRECTORY) == 0) { 203 if ((dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) {
159 continue; 204 continue;
160 } 205 }
161 /* skip directories . and .. */ 206 /* skip . and .. */
162 if ((!strcmp((char *)entry->d_name, ".")) || 207 char *dn = entry->d_name;
163 (!strcmp((char *)entry->d_name, ".."))) { 208 if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0')))
164 continue; 209 continue;
165 } 210 /* copy entry name to end of buffer, save pointer */
166 char *name = folder_alloc_from_end(len+1); 211 int len = strlen((char *)entry->d_name);
167 if (!name) 212 char *name = folder_alloc_from_end(len+1); /*for NULL*/
213 if (name == NULL)
168 { 214 {
169 closedir(dir); 215 closedir(dir);
170 return NULL; 216 goto fail;
171 } 217 }
172 memcpy(name, (char *)entry->d_name, len+1); 218 memcpy(name, (char *)entry->d_name, len+1);
173 child_count++; 219 child_count++;
@@ -177,26 +223,29 @@ static struct folder* load_folder(struct folder* parent, char *folder)
177 /* now put the names in the array */ 223 /* now put the names in the array */
178 this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count); 224 this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
179 225
180 if (!this->children) 226 if (this->children == NULL)
181 return NULL; 227 goto fail;
228
182 while (child_count) 229 while (child_count)
183 { 230 {
184 this->children[this->children_count].name = first_child; 231 struct child *child = &this->children[this->children_count++];
185 this->children[this->children_count].folder = NULL; 232 child->name = first_child;
186 this->children[this->children_count].state = COLLAPSED; 233 child->folder = NULL;
187 this->children_count++; 234 child->state = COLLAPSED;
188 first_child += strlen(first_child) + 1; 235 while(*first_child++ != '\0'){};/* move to next name entry */
189 child_count--; 236 child_count--;
190 } 237 }
191 qsort(this->children, this->children_count, sizeof(struct child), compare); 238 qsort(this->children, this->children_count, sizeof(struct child), compare);
192 239
193 return this; 240 return this;
241fail:
242 return NULL;
194} 243}
195 244
196struct folder* load_root(void) 245struct folder* load_root(void)
197{ 246{
198 static struct child root_child; 247 static struct child root_child;
199 248 /* reset the root for each call */
200 root_child.name = "/"; 249 root_child.name = "/";
201 root_child.folder = NULL; 250 root_child.folder = NULL;
202 root_child.state = COLLAPSED; 251 root_child.state = COLLAPSED;
@@ -205,7 +254,7 @@ struct folder* load_root(void)
205 .name = "", 254 .name = "",
206 .children = &root_child, 255 .children = &root_child,
207 .children_count = 1, 256 .children_count = 1,
208 .depth = -1, 257 .depth = 0,
209 .previous = NULL, 258 .previous = NULL,
210 }; 259 };
211 260
@@ -230,7 +279,6 @@ static int count_items(struct folder *start)
230static struct child* find_index(struct folder *start, int index, struct folder **parent) 279static struct child* find_index(struct folder *start, int index, struct folder **parent)
231{ 280{
232 int i = 0; 281 int i = 0;
233
234 *parent = NULL; 282 *parent = NULL;
235 283
236 while (i < start->children_count) 284 while (i < start->children_count)
@@ -262,22 +310,22 @@ static const char * folder_get_name(int selected_item, void * data,
262 struct folder *parent; 310 struct folder *parent;
263 struct child *this = find_index(root, selected_item , &parent); 311 struct child *this = find_index(root, selected_item , &parent);
264 312
265 buffer[0] = '\0'; 313 char *buf = buffer;
266 314 if ((int)buffer_len > parent->depth)
267 if (parent->depth >= 0) 315 {
268 for(int i = 0; i <= parent->depth; i++) 316 int i = parent->depth;
269 strcat(buffer, "\t"); 317 while(--i > 0) /* don't indent the parent /folders */
270 318 *buf++ = '\t';
319 }
320 *buf = '\0';
271 strlcat(buffer, this->name, buffer_len); 321 strlcat(buffer, this->name, buffer_len);
272 322
273 if (this->state == EACCESS) 323 if (this->state == EACCESS)
274 { /* append error message to the entry if unaccessible */ 324 { /* append error message to the entry if unaccessible */
275 size_t len = strlcat(buffer, " (", buffer_len); 325 size_t len = strlcat(buffer, " ( ", buffer_len);
276 if (buffer_len > len) 326 if (buffer_len > len)
277 { 327 {
278 snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED), 328 snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED), ")");
279 this->name);
280 strlcat(buffer, ")", buffer_len);
281 } 329 }
282 } 330 }
283 331
@@ -304,6 +352,23 @@ static enum themable_icons folder_get_icon(int selected_item, void * data)
304 return Icon_NOICON; 352 return Icon_NOICON;
305} 353}
306 354
355static int child_set_state_expand(struct child *this, struct folder *parent)
356{
357 int newstate = EACCESS;
358 if (this->folder == NULL)
359 this->folder = load_folder(parent, this->name);
360
361 if (this->folder != NULL)
362 {
363 if(this->folder->children_count == 0)
364 newstate = SELECTED;
365 else
366 newstate = EXPANDED;
367 }
368 this->state = newstate;
369 return newstate;
370}
371
307static int folder_action_callback(int action, struct gui_synclist *list) 372static int folder_action_callback(int action, struct gui_synclist *list)
308{ 373{
309 struct folder *root = (struct folder*)list->data; 374 struct folder *root = (struct folder*)list->data;
@@ -322,17 +387,13 @@ static int folder_action_callback(int action, struct gui_synclist *list)
322 this->state = COLLAPSED; 387 this->state = COLLAPSED;
323 break; 388 break;
324 case COLLAPSED: 389 case COLLAPSED:
325 if (this->folder == NULL) 390 child_set_state_expand(this, parent);
326 this->folder = load_folder(parent, this->name);
327 this->state = this->folder ? (this->folder->children_count == 0 ?
328 SELECTED : EXPANDED) : EACCESS;
329 break; 391 break;
330 case EACCESS: 392 case EACCESS:
331 /* cannot open, do nothing */ 393 /* cannot open, do nothing */
332 return action; 394 return action;
333 } 395 }
334 list->nb_items = count_items(root); 396 action = ACTION_REDRAW;
335 return ACTION_REDRAW;
336 } 397 }
337 else if (action == ACTION_STD_CONTEXT) 398 else if (action == ACTION_STD_CONTEXT)
338 { 399 {
@@ -342,140 +403,198 @@ static int folder_action_callback(int action, struct gui_synclist *list)
342 for (i = 0; i < this->folder->children_count; i++) 403 for (i = 0; i < this->folder->children_count; i++)
343 { 404 {
344 child = &this->folder->children[i]; 405 child = &this->folder->children[i];
345 if (child->state == SELECTED || 406 switch (child->state)
346 child->state == EXPANDED) 407 {
347 child->state = COLLAPSED; 408 case SELECTED:
348 else if (child->state == COLLAPSED) 409 case EXPANDED:
349 child->state = SELECTED; 410 child->state = COLLAPSED;
411 break;
412 case COLLAPSED:
413 child->state = SELECTED;
414 break;
415 case EACCESS:
416 break;
417 }
350 } 418 }
351 break; 419 break;
352 case SELECTED: 420 case SELECTED:
353 case COLLAPSED: 421 case COLLAPSED:
354 if (this->folder == NULL) 422 if (child_set_state_expand(this, parent) != EACCESS)
355 this->folder = load_folder(parent, this->name);
356 this->state = this->folder ? (this->folder->children_count == 0 ?
357 SELECTED : EXPANDED) : EACCESS;
358 if (this->state == EACCESS)
359 break;
360 for (i = 0; i < this->folder->children_count; i++)
361 { 423 {
362 child = &this->folder->children[i]; 424 for (i = 0; i < (this->folder->children_count); i++)
363 child->state = SELECTED; 425 {
426 child = &this->folder->children[i];
427 child->state = SELECTED;
428 }
364 } 429 }
365 break; 430 break;
366 case EACCESS: 431 case EACCESS:
367 /* cannot open, do nothing */ 432 /* cannot open, do nothing */
368 return action; 433 return action;
369 } 434 }
370 list->nb_items = count_items(root); 435 action = ACTION_REDRAW;
371 return ACTION_REDRAW;
372 } 436 }
373 437 if (action == ACTION_REDRAW)
374 438 list->nb_items = count_items(root);
375 return action; 439 return action;
376} 440}
377 441
378static struct child* find_from_filename(char* filename, struct folder *root) 442static struct child* find_from_filename(const char* filename, struct folder *root)
379{ 443{
380 char *slash = strchr(filename, '/');
381 int i = 0;
382 if (slash)
383 *slash = '\0';
384 if (!root) 444 if (!root)
385 return NULL; 445 return NULL;
386 446 const char *slash = strchr(filename, '/');
387 struct child *this; 447 struct child *this;
388 448
389 /* filenames beginning with a / are specially treated as the 449 /* filenames beginning with a / are specially treated as the
390 * loop below can't handle them. they can only occur on the first, 450 * loop below can't handle them. they can only occur on the first,
391 * and not recursive, calls to this function.*/ 451 * and not recursive, calls to this function.*/
392 if (slash == filename) 452 if (filename[0] == '/') /* in the loop nothing starts with '/' */
393 { 453 {
454 logf("find_from_filename [%s]", filename);
394 /* filename begins with /. in this case root must be the 455 /* filename begins with /. in this case root must be the
395 * top level folder */ 456 * top level folder */
396 this = &root->children[0]; 457 this = &root->children[0];
397 if (!slash[1]) 458 if (filename[1] == '\0')
398 { /* filename == "/" */ 459 { /* filename == "/" */
399 return this; 460 return this;
400 } 461 }
401 else 462 else /* filename == "/XXX/YYY". cascade down */
402 { 463 goto cascade;
403 /* filename == "/XXX/YYY". cascade down */
404 if (!this->folder)
405 this->folder = load_folder(root, this->name);
406 this->state = EXPANDED;
407 /* recurse with XXX/YYY */
408 return find_from_filename(slash+1, this->folder);
409 }
410 } 464 }
411 465
412 while (i < root->children_count) 466 for (int i = 0; i < root->children_count; i++)
413 { 467 {
414 this = &root->children[i]; 468 this = &root->children[i];
415 if (!strcasecmp(this->name, filename)) 469 /* when slash == NULL n will be really large but \0 stops the compare */
470 if (strncasecmp(this->name, filename, slash - filename) == 0)
416 { 471 {
417 if (!slash) 472 if (slash == NULL)
418 { /* filename == XXX */ 473 { /* filename == XXX */
419 return this; 474 return this;
420 } 475 }
421 else 476 else
422 { 477 goto cascade;
423 /* filename == XXX/YYY. cascade down */
424 if (!this->folder)
425 this->folder = load_folder(root, this->name);
426 this->state = EXPANDED;
427 return find_from_filename(slash+1, this->folder);
428 }
429 } 478 }
430 i++;
431 } 479 }
432 return NULL; 480 return NULL;
481
482cascade:
483 /* filename == XXX/YYY. cascade down */
484 child_set_state_expand(this, root);
485 while (slash[0] == '/') slash++; /* eat slashes */
486 return find_from_filename(slash, this->folder);
433} 487}
434 488
435/* _modifies_ buf */ 489static int select_paths(struct folder* root, const char* filenames)
436int select_paths(struct folder* root, char* buf)
437{ 490{
438 struct child *item = find_from_filename(buf, root); 491 /* Takes a list of filenames in a ':' delimited string
439 if (item) 492 splits filenames at the ':' character loads each into buffer
440 item->state = SELECTED; 493 selects each file in the folder list
494
495 if last item or only item the rest of the string is copied to the buffer
496 *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0*
497 */
498 char buf[MAX_PATH];
499 const int buflen = sizeof(buf);
500
501 const char *fnp = filenames;
502 const char *lastfnp = fnp;
503 const char *sstr;
504 off_t len;
505
506 while (fnp)
507 {
508 fnp = strchr(fnp, ':');
509 if (fnp)
510 {
511 len = fnp - lastfnp;
512 fnp++;
513 }
514 else /* no ':' get the rest of the string */
515 len = strlen(lastfnp);
516
517 sstr = lastfnp;
518 lastfnp = fnp;
519 if (len <= 0 || len > buflen)
520 continue;
521 strlcpy(buf, sstr, len + 1);
522 struct child *item = find_from_filename(buf, root);
523 if (item)
524 item->state = SELECTED;
525 }
526
441 return 0; 527 return 0;
442} 528}
443 529
444static void save_folders_r(struct folder *root, char* dst, size_t maxlen) 530static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen)
445{ 531{
446 int i = 0; 532 size_t len;
533 struct folder *curfolder;
534 char* name;
447 535
448 while (i < root->children_count) 536 for (int i = 0; i < root->children_count; i++)
449 { 537 {
450 struct child *this = &root->children[i]; 538 struct child *this = &root->children[i];
451 if (this->state == SELECTED) 539 if (this->state == SELECTED)
452 { 540 {
453 if (this->folder) 541 if (this->folder == NULL)
454 snprintf(buffer_front, buffer_end - buffer_front, 542 {
455 "%s:", get_full_path(this->folder)); 543 curfolder = root;
544 name = this->name;
545 logf("save_folders_r: this->name[%s]", name);
546 }
547 else
548 {
549 curfolder = this->folder->previous;
550 name = this->folder->name;
551 logf("save_folders_r: this->folder->name[%s]", name);
552 }
553
554 len = get_full_path(curfolder, buffer_front, buflen);
555
556 if (len + 2 >= buflen)
557 continue;
558
559 len += snprintf(&buffer_front[len], buflen - len, "%s:", name);
560 logf("save_folders_r: [%s]", buffer_front);
561 if (dst != hashed.buf)
562 {
563 int dlen = strlen(dst);
564 if (dlen + len >= maxlen)
565 continue;
566 strlcpy(&dst[dlen], buffer_front, maxlen - dlen);
567 }
456 else 568 else
457 { 569 {
458 char *p = get_full_path(root); 570 if (hashed.len + len >= maxlen)
459 snprintf(buffer_front, buffer_end - buffer_front, 571 {
460 "%s/%s:", strcmp(p, "/") ? p : "", 572 hashed.maxlen_exceeded = 1;
461 strcmp(this->name, "/") ? this->name : ""); 573 continue;
574 }
575 get_hash(buffer_front, &hashed.val, len);
576 hashed.len += len;
462 } 577 }
463 strlcat(dst, buffer_front, maxlen);
464 } 578 }
465 else if (this->state == EXPANDED) 579 else if (this->state == EXPANDED)
466 save_folders_r(this->folder, dst, maxlen); 580 save_folders_r(this->folder, dst, maxlen, buflen);
467 i++;
468 } 581 }
469} 582}
470 583
471static void save_folders(struct folder *root, char* dst, size_t maxlen) 584static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen)
472{ 585{
473 int len; 586 hashed.len = 0;
587 hashed.val = 0;
588 hashed.maxlen_exceeded = 0;
589 size_t len = buffer_end - buffer_front;
474 dst[0] = '\0'; 590 dst[0] = '\0';
475 save_folders_r(root, dst, maxlen); 591 save_folders_r(root, dst, maxlen, len);
476 len = strlen(dst); 592 len = strlen(dst);
477 /* fix trailing ':' */ 593 /* fix trailing ':' */
478 if (len > 1) dst[len-1] = '\0'; 594 if (len > 1) dst[len-1] = '\0';
595 /*Notify - user will probably not see save dialog if nothing new got added*/
596 if (hashed.maxlen_exceeded > 0) splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL));
597 return hashed.val;
479} 598}
480 599
481bool folder_select(char* setting, int setting_len) 600bool folder_select(char* setting, int setting_len)
@@ -483,40 +602,32 @@ bool folder_select(char* setting, int setting_len)
483 struct folder *root; 602 struct folder *root;
484 struct simplelist_info info; 603 struct simplelist_info info;
485 size_t buf_size; 604 size_t buf_size;
486 /* 32 separate folders should be Enough For Everybody(TM) */
487 char *vect[32];
488 char copy[setting_len];
489 int nb_items;
490
491 /* copy onto stack as split_string() modifies it */
492 strlcpy(copy, setting, setting_len);
493 nb_items = split_string(copy, ':', vect, ARRAYLEN(vect));
494 605
495 buffer_front = plugin_get_buffer(&buf_size); 606 buffer_front = plugin_get_buffer(&buf_size);
496 buffer_end = buffer_front + buf_size; 607 buffer_end = buffer_front + buf_size;
608 logf("%d bytes free", (int)(buffer_end - buffer_front));
497 root = load_root(); 609 root = load_root();
498 610
499 if (nb_items > 0) 611 logf("folders in: %s", setting);
500 { 612 /* Load previous selection(s) */
501 for(int i = 0; i < nb_items; i++) 613 select_paths(root, setting);
502 select_paths(root, vect[i]); 614 /* get current hash to check for changes later */
503 } 615 uint32_t hash = save_folders(root, hashed.buf, setting_len);
504
505 simplelist_info_init(&info, str(LANG_SELECT_FOLDER), 616 simplelist_info_init(&info, str(LANG_SELECT_FOLDER),
506 count_items(root), root); 617 count_items(root), root);
507 info.get_name = folder_get_name; 618 info.get_name = folder_get_name;
508 info.action_callback = folder_action_callback; 619 info.action_callback = folder_action_callback;
509 info.get_icon = folder_get_icon; 620 info.get_icon = folder_get_icon;
510 simplelist_show_list(&info); 621 simplelist_show_list(&info);
511 622 logf("%d bytes free", (int)(buffer_end - buffer_front));
512 /* done editing. check for changes */ 623 /* done editing. check for changes */
513 save_folders(root, copy, setting_len); 624 if (hash != save_folders(root, hashed.buf, setting_len))
514 if (strcmp(copy, setting)) 625 { /* prompt for saving changes and commit if yes */
515 { /* prompt for saving changes and commit if yes */
516 if (yesno_pop(ID2P(LANG_SAVE_CHANGES))) 626 if (yesno_pop(ID2P(LANG_SAVE_CHANGES)))
517 { 627 {
518 strcpy(setting, copy); 628 save_folders(root, setting, setting_len);
519 settings_save(); 629 settings_save();
630 logf("folders out: %s", setting);
520 return true; 631 return true;
521 } 632 }
522 } 633 }