diff options
Diffstat (limited to 'apps/gui')
-rw-r--r-- | apps/gui/folder_select.c | 429 |
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 { | |||
56 | struct folder { | 61 | struct 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 | ||
65 | static char *buffer_front, *buffer_end; | 69 | static char *buffer_front, *buffer_end; |
70 | |||
71 | static 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 | |||
79 | static inline void get_hash(const char *key, uint32_t *hash, int len) | ||
80 | { | ||
81 | *hash = crc_32(key, len, *hash); | ||
82 | } | ||
83 | |||
66 | static char* folder_alloc(size_t size) | 84 | static 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 | |
90 | static void get_full_path_r(struct folder *start, char* dst) | 108 | /* returns the buffer size required to store the path + \0 */ |
109 | static 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 | ||
102 | static char* get_full_path(struct folder *start) | 124 | static 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) | |||
125 | static struct folder* load_folder(struct folder* parent, char *folder) | 168 | static 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; |
241 | fail: | ||
242 | return NULL; | ||
194 | } | 243 | } |
195 | 244 | ||
196 | struct folder* load_root(void) | 245 | struct 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) | |||
230 | static struct child* find_index(struct folder *start, int index, struct folder **parent) | 279 | static 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 | ||
355 | static 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 | |||
307 | static int folder_action_callback(int action, struct gui_synclist *list) | 372 | static 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 | ||
378 | static struct child* find_from_filename(char* filename, struct folder *root) | 442 | static 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 | |||
482 | cascade: | ||
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 */ | 489 | static int select_paths(struct folder* root, const char* filenames) |
436 | int 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 | ||
444 | static void save_folders_r(struct folder *root, char* dst, size_t maxlen) | 530 | static 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 | ||
471 | static void save_folders(struct folder *root, char* dst, size_t maxlen) | 584 | static 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 | ||
481 | bool folder_select(char* setting, int setting_len) | 600 | bool 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 | } |