forked from len0rd/rockbox
		
	Temporarily enable icons for required lists. Probably not a proper fix but a simple workaround. Fixes FS#13574 Change-Id: I011a80e2a9f03019927b32e92cd5a35275c051e1
		
			
				
	
	
		
			655 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			655 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  *
 | |
|  * Copyright (C) 2012 Jonathan Gordon
 | |
|  * Copyright (C) 2012 Thomas Martitz
 | |
| * * Copyright (C) 2021 William Wilgus
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version 2
 | |
|  * of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| #include "plugin.h"
 | |
| #ifdef ROCKBOX_HAS_LOGF
 | |
| #define logf rb->logf
 | |
| #else
 | |
| #define logf(...) do { } while(0)
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Order for changing child states:
 | |
|  * 1) expand folder (skip to 3 if empty, skip to 4 if cannot be opened)
 | |
|  * 2) collapse and select
 | |
|  * 3) unselect (skip to 1)
 | |
|  * 4) do nothing
 | |
|  */
 | |
| 
 | |
| enum child_state {
 | |
|     EXPANDED,
 | |
|     SELECTED,
 | |
|     COLLAPSED,
 | |
|     EACCESS,
 | |
| };
 | |
| 
 | |
| struct child {
 | |
|     char* name;
 | |
|     struct folder *folder;
 | |
|     enum child_state state;
 | |
| };
 | |
| 
 | |
| struct folder {
 | |
|     char *name;
 | |
|     struct child *children;
 | |
|     struct folder* previous;
 | |
|     uint16_t children_count;
 | |
|     uint16_t depth;
 | |
| };
 | |
| 
 | |
| static char *buffer_front, *buffer_end;
 | |
| 
 | |
| static struct
 | |
| {
 | |
|     int32_t len; /* keeps count versus maxlen to give buffer full notification */
 | |
|     uint32_t val; /* hash of all selected items */
 | |
|     char buf[3];/* address used as identifier -- only \0 written to it */
 | |
|     char maxlen_exceeded; /*0,1*/
 | |
| } hashed;
 | |
| 
 | |
| static inline void get_hash(const char *key, uint32_t *hash, int len)
 | |
| {
 | |
|     *hash = rb->crc_32(key, len, *hash);
 | |
| }
 | |
| 
 | |
| static char* folder_alloc(size_t size)
 | |
| {
 | |
|     char* retval;
 | |
|     /* 32-bit aligned */
 | |
|     size = ALIGN_UP(size, 4);
 | |
|     if (buffer_front + size > buffer_end)
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
|     retval = buffer_front;
 | |
|     buffer_front += size;
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| static char* folder_alloc_from_end(size_t size)
 | |
| {
 | |
|     if (buffer_end - size < buffer_front)
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
|     buffer_end -= size;
 | |
|     return buffer_end;
 | |
| }
 | |
| #if 0
 | |
| /* returns the buffer size required to store the path + \0 */
 | |
| static int get_full_pathsz(struct folder *start)
 | |
| {
 | |
|     int reql = 0;
 | |
|     struct folder *next = start;
 | |
|     do
 | |
|     {
 | |
|         reql += rb->strlen(next->name) + 1;
 | |
|     } while ((next = next->previous));
 | |
| 
 | |
|     if (start->name[0] != '/') reql--;
 | |
|     if (--reql < 0) reql = 0;
 | |
|     return reql;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz)
 | |
| {
 | |
|     size_t pos = 0;
 | |
|     struct folder *prev, *cur = NULL, *next = start;
 | |
|     dst[0] = '\0'; /* for rb->strlcat to do its thing */
 | |
|     /* First traversal R->L mutate nodes->previous to point at child */
 | |
|     while (next->previous != NULL) /* stop at the root */
 | |
|     {
 | |
| #define PATHMUTATE()              \
 | |
|         ({                        \
 | |
|             prev = cur;           \
 | |
|             cur = next;           \
 | |
|             next = cur->previous;\
 | |
|             cur->previous = prev; \
 | |
|         })
 | |
|         PATHMUTATE();
 | |
|     }
 | |
|     /*swap the next and cur nodes to reverse direction */
 | |
|     prev = next;
 | |
|     next = cur;
 | |
|     cur = prev;
 | |
|     /* Second traversal L->R mutate nodes->previous to point back at parent
 | |
|      * copy strings to buf as they go by */
 | |
|     while (next != NULL)
 | |
|     {
 | |
|         PATHMUTATE();
 | |
|         pos = rb->strlcat(dst, cur->name, dst_sz);
 | |
|         /* do not append slash to paths starting with slash */
 | |
|         if (cur->name[0] != '/')
 | |
|             pos = rb->strlcat(dst, "/", dst_sz);
 | |
|     }
 | |
|     logf("get_full_path: (%d)[%s]", (int)pos, dst);
 | |
|     return pos;
 | |
| #undef PATHMUTATE
 | |
| }
 | |
| 
 | |
| /* support function for rb->qsort() */
 | |
| static int compare(const void* p1, const void* p2)
 | |
| {
 | |
|     struct child *left = (struct child*)p1;
 | |
|     struct child *right = (struct child*)p2;
 | |
|     return rb->strcasecmp(left->name, right->name);
 | |
| }
 | |
| 
 | |
| static struct folder* load_folder(struct folder* parent, char *folder)
 | |
| {
 | |
|     DIR *dir;
 | |
|     char fullpath[MAX_PATH];
 | |
| 
 | |
|     struct dirent *entry;
 | |
|     int child_count = 0;
 | |
|     char *first_child = NULL;
 | |
|     size_t len = 0;
 | |
| 
 | |
|     struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
 | |
|     if (this == NULL)
 | |
|         goto fail;
 | |
| 
 | |
|     if (parent)
 | |
|     {
 | |
|         len = get_full_path(parent, fullpath, sizeof(fullpath));
 | |
|         if (len >= sizeof(fullpath))
 | |
|             goto fail;
 | |
|     }
 | |
|     rb->strlcpy(&fullpath[len], folder, sizeof(fullpath) - len);
 | |
|     logf("load_folder: [%s]", fullpath);
 | |
| 
 | |
|     dir = rb->opendir(fullpath);
 | |
|     if (dir == NULL)
 | |
|         goto fail;
 | |
|     this->previous = parent;
 | |
|     this->name = folder;
 | |
|     this->children = NULL;
 | |
|     this->children_count = 0;
 | |
|     if (parent)
 | |
|         this->depth = parent->depth + 1;
 | |
| 
 | |
|     while ((entry = rb->readdir(dir))) {
 | |
|         /* skip anything not a directory */
 | |
|         if ((rb->dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) {
 | |
|             continue;
 | |
|         }
 | |
|         /* skip . and .. */
 | |
|         char *dn = entry->d_name;
 | |
|         if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0')))
 | |
|             continue;
 | |
|         /* copy entry name to end of buffer, save pointer */
 | |
|         int len = rb->strlen((char *)entry->d_name);
 | |
|         char *name = folder_alloc_from_end(len+1); /*for NULL*/
 | |
|         if (name == NULL)
 | |
|         {
 | |
|             rb->closedir(dir);
 | |
|             goto fail;
 | |
|         }
 | |
|         memcpy(name, (char *)entry->d_name, len+1);
 | |
|         child_count++;
 | |
|         first_child = name;
 | |
|     }
 | |
|     rb->closedir(dir);
 | |
|     /* now put the names in the array */
 | |
|     this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
 | |
| 
 | |
|     if (this->children == NULL)
 | |
|         goto fail;
 | |
| 
 | |
|     while (child_count)
 | |
|     {
 | |
|         struct child *child = &this->children[this->children_count++];
 | |
|         child->name = first_child;
 | |
|         child->folder = NULL;
 | |
|         child->state = COLLAPSED;
 | |
|         while(*first_child++ != '\0'){};/* move to next name entry */
 | |
|         child_count--;
 | |
|     }
 | |
|     rb->qsort(this->children, this->children_count, sizeof(struct child), compare);
 | |
| 
 | |
|     return this;
 | |
| fail:
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| struct folder* load_root(void)
 | |
| {
 | |
|     static struct child root_child;
 | |
|     /* reset the root for each call */
 | |
|     root_child.name = "/";
 | |
|     root_child.folder = NULL;
 | |
|     root_child.state = COLLAPSED;
 | |
| 
 | |
|     static struct folder root = {
 | |
|         .name = "",
 | |
|         .children = &root_child,
 | |
|         .children_count = 1,
 | |
|         .depth = 0,
 | |
|         .previous = NULL,
 | |
|     };
 | |
| 
 | |
|     return &root;
 | |
| }
 | |
| 
 | |
| static int count_items(struct folder *start)
 | |
| {
 | |
|     int count = 0;
 | |
|     int i;
 | |
| 
 | |
|     for (i=0; i<start->children_count; i++)
 | |
|     {
 | |
|         struct child *foo = &start->children[i];
 | |
|         if (foo->state == EXPANDED)
 | |
|             count += count_items(foo->folder);
 | |
|         count++;
 | |
|     }
 | |
|     return count;
 | |
| }
 | |
| 
 | |
| static struct child* find_index(struct folder *start, int index, struct folder **parent)
 | |
| {
 | |
|     int i = 0;
 | |
|     *parent = NULL;
 | |
| 
 | |
|     while (i < start->children_count)
 | |
|     {
 | |
|         struct child *foo = &start->children[i];
 | |
|         if (i == index)
 | |
|         {
 | |
|             *parent = start;
 | |
|             return foo;
 | |
|         }
 | |
|         i++;
 | |
|         if (foo->state == EXPANDED)
 | |
|         {
 | |
|             struct child *bar = find_index(foo->folder, index - i, parent);
 | |
|             if (bar)
 | |
|             {
 | |
|                 return bar;
 | |
|             }
 | |
|             index -= count_items(foo->folder);
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static const char * folder_get_name(int selected_item, void * data,
 | |
|                                    char * buffer, size_t buffer_len)
 | |
| {
 | |
|     struct folder *root = (struct folder*)data;
 | |
|     struct folder *parent;
 | |
|     struct child *this = find_index(root, selected_item , &parent);
 | |
| 
 | |
|     char *buf = buffer;
 | |
|     if ((int)buffer_len > parent->depth)
 | |
|     {
 | |
|         int i = parent->depth;
 | |
|         while(--i > 0) /* don't indent the parent /folders */
 | |
|             *buf++ = '\t';
 | |
|     }
 | |
|     *buf = '\0';
 | |
|     rb->strlcat(buffer, this->name, buffer_len);
 | |
| 
 | |
|     if (this->state == EACCESS)
 | |
|     {   /* append error message to the entry if unaccessible */
 | |
|         size_t len = rb->strlcat(buffer, " ( ", buffer_len);
 | |
|         if (buffer_len > len)
 | |
|         {
 | |
|             rb->snprintf(&buffer[len], buffer_len - len, rb->str(LANG_READ_FAILED), ")");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return buffer;
 | |
| }
 | |
| 
 | |
| static enum themable_icons folder_get_icon(int selected_item, void * data)
 | |
| {
 | |
|     struct folder *root = (struct folder*)data;
 | |
|     struct folder *parent;
 | |
|     struct child *this = find_index(root, selected_item, &parent);
 | |
| 
 | |
|     switch (this->state)
 | |
|     {
 | |
|         case SELECTED:
 | |
|             return Icon_Cursor;
 | |
|         case COLLAPSED:
 | |
|             return Icon_Folder;
 | |
|         case EXPANDED:
 | |
|             return Icon_Submenu;
 | |
|         case EACCESS:
 | |
|             return Icon_Questionmark;
 | |
|     }
 | |
|     return Icon_NOICON;
 | |
| }
 | |
| 
 | |
| static int child_set_state_expand(struct child *this, struct folder *parent)
 | |
| {
 | |
|     int newstate = EACCESS;
 | |
|     if (this->folder == NULL)
 | |
|         this->folder = load_folder(parent, this->name);
 | |
| 
 | |
|     if (this->folder != NULL)
 | |
|     {
 | |
|         if(this->folder->children_count == 0)
 | |
|             newstate = SELECTED;
 | |
|         else
 | |
|             newstate = EXPANDED;
 | |
|     }
 | |
|     this->state = newstate;
 | |
|     return newstate;
 | |
| }
 | |
| 
 | |
| static int folder_action_callback(int action, struct gui_synclist *list)
 | |
| {
 | |
|     struct folder *root = (struct folder*)list->data;
 | |
|     struct folder *parent;
 | |
|     struct child *this = find_index(root, list->selected_item, &parent), *child;
 | |
|     int i;
 | |
| 
 | |
|     if (action == ACTION_STD_OK)
 | |
|     {
 | |
|         switch (this->state)
 | |
|         {
 | |
|             case EXPANDED:
 | |
|                 this->state = SELECTED;
 | |
|                 break;
 | |
|             case SELECTED:
 | |
|                 this->state = COLLAPSED;
 | |
|                 break;
 | |
|             case COLLAPSED:
 | |
|                 child_set_state_expand(this, parent);
 | |
|                 break;
 | |
|             case EACCESS:
 | |
|                 /* cannot open, do nothing */
 | |
|                 return action;
 | |
|         }
 | |
|         action = ACTION_REDRAW;
 | |
|     }
 | |
|     else if (action == ACTION_STD_CONTEXT)
 | |
|     {
 | |
|         switch (this->state)
 | |
|         {
 | |
|             case EXPANDED:
 | |
|                 for (i = 0; i < this->folder->children_count; i++)
 | |
|                 {
 | |
|                     child = &this->folder->children[i];
 | |
|                     switch (child->state)
 | |
|                     {
 | |
|                         case SELECTED:
 | |
|                         case EXPANDED:
 | |
|                             child->state = COLLAPSED;
 | |
|                             break;
 | |
|                         case COLLAPSED:
 | |
|                             child->state = SELECTED;
 | |
|                             break;
 | |
|                         case EACCESS:
 | |
|                             break;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case SELECTED:
 | |
|             case COLLAPSED:
 | |
|                 if (child_set_state_expand(this, parent) != EACCESS)
 | |
|                 {
 | |
|                     for (i = 0; i < (this->folder->children_count); i++)
 | |
|                     {
 | |
|                         child = &this->folder->children[i];
 | |
|                         child->state = SELECTED;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case EACCESS:
 | |
|                 /* cannot open, do nothing */
 | |
|                 return action;
 | |
|         }
 | |
|         action = ACTION_REDRAW;
 | |
|     }
 | |
|     if (action == ACTION_REDRAW)
 | |
|         list->nb_items = count_items(root);
 | |
|     return action;
 | |
| }
 | |
| 
 | |
| static struct child* find_from_filename(const char* filename, struct folder *root)
 | |
| {
 | |
|     if (!root)
 | |
|         return NULL;
 | |
|     const char *slash = rb->strchr(filename, '/');
 | |
|     struct child *this;
 | |
| 
 | |
|     /* filenames beginning with a / are specially treated as the
 | |
|      * loop below can't handle them. they can only occur on the first,
 | |
|      * and not recursive, calls to this function.*/
 | |
|     if (filename[0] == '/') /* in the loop nothing starts with '/' */
 | |
|     {
 | |
|         logf("find_from_filename [%s]", filename);
 | |
|         /* filename begins with /. in this case root must be the
 | |
|          * top level folder */
 | |
|         this = &root->children[0];
 | |
|         if (filename[1] == '\0')
 | |
|         {   /* filename == "/" */
 | |
|             return this;
 | |
|         }
 | |
|         else /* filename == "/XXX/YYY". cascade down */
 | |
|             goto cascade;
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < root->children_count; i++)
 | |
|     {
 | |
|         this = &root->children[i];
 | |
|         /* when slash == NULL n will be really large but \0 stops the compare */
 | |
|         if (rb->strncasecmp(this->name, filename, slash - filename) == 0)
 | |
|         {
 | |
|             if (slash == NULL)
 | |
|             {   /* filename == XXX */
 | |
|                 return this;
 | |
|             }
 | |
|             else
 | |
|                 goto cascade;
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| 
 | |
| cascade:
 | |
|     /* filename == XXX/YYY. cascade down */
 | |
|     child_set_state_expand(this, root);
 | |
|     while (slash[0] == '/') slash++; /* eat slashes */
 | |
|     return find_from_filename(slash, this->folder);
 | |
| }
 | |
| 
 | |
| static int select_paths(struct folder* root, const char* filenames)
 | |
| {
 | |
|     /* Takes a list of filenames in a ':' delimited string
 | |
|        splits filenames at the ':' character loads each into buffer
 | |
|        selects each file in the folder list
 | |
| 
 | |
|        if last item or only item the rest of the string is copied to the buffer
 | |
|        *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0*
 | |
|    */
 | |
|     char buf[MAX_PATH];
 | |
|     const int buflen = sizeof(buf);
 | |
| 
 | |
|     const char *fnp = filenames;
 | |
|     const char *lastfnp = fnp;
 | |
|     const char *sstr;
 | |
|     off_t len;
 | |
| 
 | |
|     while (fnp)
 | |
|     {
 | |
|         fnp = rb->strchr(fnp, ':');
 | |
|         if (fnp)
 | |
|         {
 | |
|             len = fnp - lastfnp;
 | |
|             fnp++;
 | |
|         }
 | |
|         else /* no ':' get the rest of the string */
 | |
|             len = rb->strlen(lastfnp);
 | |
| 
 | |
|         sstr = lastfnp;
 | |
|         lastfnp = fnp;
 | |
|         if (len <= 0 || len > buflen)
 | |
|             continue;
 | |
|         rb->strlcpy(buf, sstr, len + 1);
 | |
|         struct child *item = find_from_filename(buf, root);
 | |
|         if (item)
 | |
|             item->state = SELECTED;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen)
 | |
| {
 | |
|     size_t len;
 | |
|     struct folder *curfolder;
 | |
|     char* name;
 | |
| 
 | |
|     for (int i = 0; i < root->children_count; i++)
 | |
|     {
 | |
|         struct child *this = &root->children[i];
 | |
|         if (this->state == SELECTED)
 | |
|         {
 | |
|             if (this->folder == NULL)
 | |
|             {
 | |
|                 curfolder = root;
 | |
|                 name = this->name;
 | |
|                 logf("save_folders_r: this->name[%s]", name);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 curfolder = this->folder->previous;
 | |
|                 name = this->folder->name;
 | |
|                 logf("save_folders_r: this->folder->name[%s]", name);
 | |
|             }
 | |
| 
 | |
|             len = get_full_path(curfolder, buffer_front, buflen);
 | |
| 
 | |
|             if (len + 2 >= buflen)
 | |
|                 continue;
 | |
| 
 | |
|             len += rb->snprintf(&buffer_front[len], buflen - len, "%s:", name);
 | |
|             logf("save_folders_r: [%s]", buffer_front);
 | |
|             if (dst != hashed.buf)
 | |
|             {
 | |
|                 int dlen = rb->strlen(dst);
 | |
|                 if (dlen + len >= maxlen)
 | |
|                     continue;
 | |
|                 rb->strlcpy(&dst[dlen], buffer_front, maxlen - dlen);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 if (hashed.len + len >= maxlen)
 | |
|                 {
 | |
|                     hashed.maxlen_exceeded = 1;
 | |
|                     continue;
 | |
|                 }
 | |
|                 get_hash(buffer_front, &hashed.val, len);
 | |
|                 hashed.len += len;
 | |
|             }
 | |
|         }
 | |
|         else if (this->state == EXPANDED)
 | |
|             save_folders_r(this->folder, dst, maxlen, buflen);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen)
 | |
| {
 | |
|     hashed.len = 0;
 | |
|     hashed.val = 0;
 | |
|     hashed.maxlen_exceeded = 0;
 | |
|     size_t len = buffer_end - buffer_front;
 | |
|     dst[0] = '\0';
 | |
|     save_folders_r(root, dst, maxlen, len);
 | |
|     len = rb->strlen(dst);
 | |
|     /* fix trailing ':' */
 | |
|     if (len > 1) dst[len-1] = '\0';
 | |
|     /*Notify - user will probably not see save dialog if nothing new got added*/
 | |
|     if (hashed.maxlen_exceeded > 0) rb->splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL));
 | |
|     return hashed.val;
 | |
| }
 | |
| 
 | |
| bool folder_select(char * header_text, char* setting, int setting_len)
 | |
| {
 | |
|     struct folder *root;
 | |
|     struct simplelist_info info;
 | |
|     size_t buf_size;
 | |
| 
 | |
|     buffer_front = rb->plugin_get_buffer(&buf_size);
 | |
|     buffer_end = buffer_front + buf_size;
 | |
|     logf("folder_select %d bytes free", (int)(buffer_end - buffer_front));
 | |
|     root = load_root();
 | |
| 
 | |
|     logf("folders in: %s", setting);
 | |
|     /* Load previous selection(s) */
 | |
|     select_paths(root, setting);
 | |
|     /* get current hash to check for changes later */
 | |
|     uint32_t hash = save_folders(root, hashed.buf, setting_len);
 | |
|     rb->simplelist_info_init(&info, header_text,
 | |
|             count_items(root), root);
 | |
|     info.get_name = folder_get_name;
 | |
|     info.action_callback = folder_action_callback;
 | |
|     info.get_icon = folder_get_icon;
 | |
|     bool show_icons = rb->global_settings->show_icons;
 | |
|     rb->global_settings->show_icons = true;
 | |
|     rb->simplelist_show_list(&info);
 | |
|     rb->global_settings->show_icons = show_icons;
 | |
|     logf("folder_select %d bytes free", (int)(buffer_end - buffer_front));
 | |
|     /* done editing. check for changes */
 | |
|     if (hash != save_folders(root, hashed.buf, setting_len))
 | |
|     {  /* prompt for saving changes and commit if yes */
 | |
|         if (rb->yesno_pop(ID2P(LANG_SAVE_CHANGES)))
 | |
|         {
 | |
|             save_folders(root, setting, setting_len);
 | |
|             rb->settings_save();
 | |
|             logf("folders out: %s", setting);
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| /* plugin entry point */
 | |
| enum plugin_status plugin_start(const void* parameter)
 | |
| {
 | |
|     (void) parameter;
 | |
| 
 | |
|     if(parameter)
 | |
|     {
 | |
| 
 | |
|         if (rb->strcmp(parameter, rb->str(LANG_AUTORESUME)) == 0)
 | |
|         {
 | |
|             if (folder_select(rb->str(LANG_AUTORESUME),
 | |
|                               rb->global_settings->autoresume_paths,
 | |
|                               MAX_PATHNAME+1))
 | |
|             {
 | |
|                 return 1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     else if (folder_select(rb->str(LANG_SELECT_FOLDER),
 | |
|                            rb->global_settings->tagcache_scan_paths,
 | |
|                            sizeof(rb->global_settings->tagcache_scan_paths)))
 | |
|     {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     return PLUGIN_OK;
 | |
| }
 |