FileBrowser: Editable Bookmarks.
[blender-staging.git] / source / blender / editors / space_file / fsmenu.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_file/fsmenu.c
29  *  \ingroup spfile
30  */
31
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <math.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_utildefines.h"
41 #include "BLI_blenlib.h"
42
43 #include "BKE_appdir.h"
44
45 #include "DNA_space_types.h"
46
47 #include "ED_fileselect.h"
48
49 #ifdef WIN32
50 #  include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
51 #  ifndef _WIN32_IE
52 #    define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
53 #  endif
54 #  include <shlobj.h>  /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff
55                         * because 'near' is disabled through BLI_windstuff */
56 #  include "BLI_winstuff.h"
57 #endif
58
59 #ifdef __APPLE__
60 #include <Carbon/Carbon.h>
61 #endif /* __APPLE__ */
62
63 #ifdef __linux__
64 #include <mntent.h>
65 #endif
66
67 #include "fsmenu.h"  /* include ourselves */
68
69
70 /* FSMENU HANDLING */
71
72 typedef struct FSMenu {
73         FSMenuEntry *fsmenu_system;
74         FSMenuEntry *fsmenu_system_bookmarks;
75         FSMenuEntry *fsmenu_bookmarks;
76         FSMenuEntry *fsmenu_recent;
77 } FSMenu;
78
79 static FSMenu *g_fsmenu = NULL;
80
81 FSMenu *ED_fsmenu_get(void)
82 {
83         if (!g_fsmenu) {
84                 g_fsmenu = MEM_callocN(sizeof(struct FSMenu), "fsmenu");
85         }
86         return g_fsmenu;
87 }
88
89 struct FSMenuEntry *ED_fsmenu_get_category(struct FSMenu *fsmenu, FSMenuCategory category)
90 {
91         FSMenuEntry *fsm_head = NULL;
92
93         switch (category) {
94                 case FS_CATEGORY_SYSTEM:
95                         fsm_head = fsmenu->fsmenu_system;
96                         break;
97                 case FS_CATEGORY_SYSTEM_BOOKMARKS:
98                         fsm_head = fsmenu->fsmenu_system_bookmarks;
99                         break;
100                 case FS_CATEGORY_BOOKMARKS:
101                         fsm_head = fsmenu->fsmenu_bookmarks;
102                         break;
103                 case FS_CATEGORY_RECENT:
104                         fsm_head = fsmenu->fsmenu_recent;
105                         break;
106         }
107         return fsm_head;
108 }
109
110 void ED_fsmenu_set_category(struct FSMenu *fsmenu, FSMenuCategory category, FSMenuEntry *fsm_head)
111 {
112         switch (category) {
113                 case FS_CATEGORY_SYSTEM:
114                         fsmenu->fsmenu_system = fsm_head;
115                         break;
116                 case FS_CATEGORY_SYSTEM_BOOKMARKS:
117                         fsmenu->fsmenu_system_bookmarks = fsm_head;
118                         break;
119                 case FS_CATEGORY_BOOKMARKS:
120                         fsmenu->fsmenu_bookmarks = fsm_head;
121                         break;
122                 case FS_CATEGORY_RECENT:
123                         fsmenu->fsmenu_recent = fsm_head;
124                         break;
125         }
126 }
127
128 int ED_fsmenu_get_nentries(struct FSMenu *fsmenu, FSMenuCategory category)
129 {
130         FSMenuEntry *fsm_iter;
131         int count = 0;
132
133         for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter; fsm_iter = fsm_iter->next) {
134                 count++;
135         }
136
137         return count;
138 }
139
140 FSMenuEntry *ED_fsmenu_get_entry(struct FSMenu *fsmenu, FSMenuCategory category, int index)
141 {
142         FSMenuEntry *fsm_iter;
143
144         for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && index; fsm_iter = fsm_iter->next) {
145                 index--;
146         }
147
148         return fsm_iter;
149 }
150
151 char *ED_fsmenu_entry_get_path(struct FSMenuEntry *fsentry)
152 {
153         return fsentry->path;
154 }
155
156 void ED_fsmenu_entry_set_path(struct FSMenuEntry *fsentry, const char *path)
157 {
158         if ((!fsentry->path || !path || !STREQ(path, fsentry->path)) && (fsentry->path != path)) {
159                 char tmp_name[FILE_MAXFILE];
160
161                 MEM_SAFE_FREE(fsentry->path);
162
163                 fsentry->path = (path && path[0]) ? BLI_strdup(path) : NULL;
164
165                 BLI_make_file_string("/", tmp_name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
166                 fsmenu_write_file(ED_fsmenu_get(), tmp_name);
167         }
168 }
169
170 static void fsmenu_entry_generate_name(struct FSMenuEntry *fsentry, char *name, size_t name_size)
171 {
172         char temp[FILE_MAX];
173
174         BLI_strncpy(temp, fsentry->path, FILE_MAX);
175         BLI_add_slash(temp);
176         BLI_getlastdir(temp, name, name_size);
177         BLI_del_slash(name);
178         if (!name[0]) {
179                 name[0] = '/';
180                 name[1] = '\0';
181         }
182 }
183
184 char *ED_fsmenu_entry_get_name(struct FSMenuEntry *fsentry)
185 {
186         if (fsentry->name[0]) {
187                 return fsentry->name;
188         }
189         else {
190                 /* Here we abuse fsm_iter->name, keeping first char NULL. */
191                 char *name = fsentry->name + 1;
192                 size_t name_size = sizeof(fsentry->name) - 1;
193
194                 fsmenu_entry_generate_name(fsentry, name, name_size);
195                 return name;
196         }
197 }
198
199 void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name)
200 {
201         if (!STREQ(name, fsentry->name)) {
202                 char tmp_name[FILE_MAXFILE];
203                 size_t tmp_name_size = sizeof(tmp_name);
204
205                 fsmenu_entry_generate_name(fsentry, tmp_name, tmp_name_size);
206                 if (!name[0] || STREQ(tmp_name, name)) {
207                         /* reset name to default behavior. */
208                         fsentry->name[0] = '\0';
209                 }
210                 else {
211                         BLI_strncpy(fsentry->name, name, sizeof(fsentry->name));
212                 }
213
214                 BLI_make_file_string("/", tmp_name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
215                 fsmenu_write_file(ED_fsmenu_get(), tmp_name);
216         }
217 }
218
219 short fsmenu_can_save(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
220 {
221         FSMenuEntry *fsm_iter;
222
223         for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && idx; fsm_iter = fsm_iter->next) {
224                 idx--;
225         }
226
227         return fsm_iter ? fsm_iter->save : 0;
228 }
229
230 void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const char *name, FSMenuInsert flag)
231 {
232         FSMenuEntry *fsm_prev;
233         FSMenuEntry *fsm_iter;
234         FSMenuEntry *fsm_head;
235
236         fsm_head = ED_fsmenu_get_category(fsmenu, category);
237         fsm_prev = fsm_head;  /* this is odd and not really correct? */
238
239         for (fsm_iter = fsm_head; fsm_iter; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
240                 if (fsm_iter->path) {
241                         const int cmp_ret = BLI_path_cmp(path, fsm_iter->path);
242                         if (cmp_ret == 0) {
243                                 if (flag & FS_INSERT_FIRST) {
244                                         if (fsm_iter != fsm_head) {
245                                                 fsm_prev->next = fsm_iter->next;
246                                                 fsm_iter->next = fsm_head;
247                                                 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
248                                         }
249                                 }
250                                 return;
251                         }
252                         else if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
253                                 break;
254                         }
255                 }
256                 else {
257                         /* if we're bookmarking this, file should come
258                          * before the last separator, only automatically added
259                          * current dir go after the last sep. */
260                         if (flag & FS_INSERT_SAVE) {
261                                 break;
262                         }
263                 }
264         }
265
266         fsm_iter = MEM_mallocN(sizeof(*fsm_iter), "fsme");
267         fsm_iter->path = BLI_strdup(path);
268         fsm_iter->save = (flag & FS_INSERT_SAVE) != 0;
269         if (name && name[0]) {
270                 BLI_strncpy(fsm_iter->name, name, sizeof(fsm_iter->name));
271         }
272         else {
273                 fsm_iter->name[0] = '\0';
274         }
275
276         if (fsm_prev) {
277                 if (flag & FS_INSERT_FIRST) {
278                         fsm_iter->next = fsm_head;
279                         ED_fsmenu_set_category(fsmenu, category, fsm_iter);
280                 }
281                 else {
282                         fsm_iter->next = fsm_prev->next;
283                         fsm_prev->next = fsm_iter;
284                 }
285         }
286         else {
287                 fsm_iter->next = fsm_head;
288                 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
289         }
290 }
291
292 void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
293 {
294         FSMenuEntry *fsm_prev = NULL;
295         FSMenuEntry *fsm_iter;
296         FSMenuEntry *fsm_head;
297
298         fsm_head = ED_fsmenu_get_category(fsmenu, category);
299
300         for (fsm_iter = fsm_head; fsm_iter && idx; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next)
301                 idx--;
302
303         if (fsm_iter) {
304                 /* you should only be able to remove entries that were 
305                  * not added by default, like windows drives.
306                  * also separators (where path == NULL) shouldn't be removed */
307                 if (fsm_iter->save && fsm_iter->path) {
308
309                         /* remove fsme from list */
310                         if (fsm_prev) {
311                                 fsm_prev->next = fsm_iter->next;
312                         }
313                         else {
314                                 fsm_head = fsm_iter->next;
315                                 ED_fsmenu_set_category(fsmenu, category, fsm_head);
316                         }
317                         /* free entry */
318                         MEM_freeN(fsm_iter->path);
319                         MEM_freeN(fsm_iter);
320                 }
321         }
322 }
323
324 void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
325 {
326         FSMenuEntry *fsm_iter = NULL;
327         char fsm_name[FILE_MAX];
328         int nwritten = 0;
329
330         FILE *fp = BLI_fopen(filename, "w");
331         if (!fp) return;
332
333         fprintf(fp, "[Bookmarks]\n");
334         for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter; fsm_iter = fsm_iter->next) {
335                 if (fsm_iter->path && fsm_iter->save) {
336                         fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
337                         if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
338                                 fprintf(fp, "!%s\n", fsm_iter->name);
339                         }
340                         fprintf(fp, "%s\n", fsm_iter->path);
341                 }
342         }
343         fprintf(fp, "[Recent]\n");
344         for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsm_iter && (nwritten < FSMENU_RECENT_MAX); fsm_iter = fsm_iter->next, ++nwritten) {
345                 if (fsm_iter->path && fsm_iter->save) {
346                         fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
347                         if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
348                                 fprintf(fp, "!%s\n", fsm_iter->name);
349                         }
350                         fprintf(fp, "%s\n", fsm_iter->path);
351                 }
352         }
353         fclose(fp);
354 }
355
356 void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename)
357 {
358         char line[FILE_MAXDIR];
359         char name[FILE_MAXFILE];
360         FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
361         FILE *fp;
362
363         fp = BLI_fopen(filename, "r");
364         if (!fp) return;
365
366         name[0] = '\0';
367
368         while (fgets(line, sizeof(line), fp) != NULL) {       /* read a line */
369                 if (STREQLEN(line, "[Bookmarks]", 11)) {
370                         category = FS_CATEGORY_BOOKMARKS;
371                 }
372                 else if (STREQLEN(line, "[Recent]", 8)) {
373                         category = FS_CATEGORY_RECENT;
374                 }
375                 else if (line[0] == '!') {
376                         int len = strlen(line);
377                         if (len > 0) {
378                                 if (line[len - 1] == '\n') {
379                                         line[len - 1] = '\0';
380                                 }
381                                 BLI_strncpy(name, line + 1, sizeof(name));
382                         }
383                 }
384                 else {
385                         int len = strlen(line);
386                         if (len > 0) {
387                                 if (line[len - 1] == '\n') {
388                                         line[len - 1] = '\0';
389                                 }
390                                 /* don't do this because it can be slow on network drives,
391                                  * having a bookmark from a drive thats ejected or so isn't
392                                  * all _that_ bad */
393 #if 0
394                                 if (BLI_exists(line))
395 #endif
396                                 {
397                                         fsmenu_insert_entry(fsmenu, category, line, name, FS_INSERT_SAVE);
398                                 }
399                         }
400                         /* always reset name. */
401                         name[0] = '\0';
402                 }
403         }
404         fclose(fp);
405 }
406
407 void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
408 {
409         char line[FILE_MAXDIR];
410 #ifdef WIN32
411         /* Add the drive names to the listing */
412         {
413                 __int64 tmp;
414                 char tmps[4];
415                 int i;
416                         
417                 tmp = GetLogicalDrives();
418                 
419                 for (i = 0; i < 26; i++) {
420                         if ((tmp >> i) & 1) {
421                                 tmps[0] = 'A' + i;
422                                 tmps[1] = ':';
423                                 tmps[2] = '\\';
424                                 tmps[3] = 0;
425                                 
426                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, NULL, FS_INSERT_SORTED);
427                         }
428                 }
429
430                 /* Adding Desktop and My Documents */
431                 if (read_bookmarks) {
432                         SHGetSpecialFolderPath(0, line, CSIDL_PERSONAL, 0);
433                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
434                         SHGetSpecialFolderPath(0, line, CSIDL_DESKTOPDIRECTORY, 0);
435                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
436                 }
437         }
438 #else
439 #ifdef __APPLE__
440         {
441 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= 1050)
442                 OSErr err = noErr;
443                 int i;
444                 const char *home;
445                 
446                 /* loop through all the OS X Volumes, and add them to the SYSTEM section */
447                 for (i = 1; err != nsvErr; i++) {
448                         FSRef dir;
449                         unsigned char path[FILE_MAX];
450                         
451                         err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, NULL, kFSVolInfoNone, NULL, NULL, &dir);
452                         if (err != noErr)
453                                 continue;
454                         
455                         FSRefMakePath(&dir, path, FILE_MAX);
456                         if (!STREQ((char *)path, "/home") && !STREQ((char *)path, "/net")) {
457                                 /* /net and /home are meaningless on OSX, home folders are stored in /Users */
458                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, NULL, FS_INSERT_SORTED);
459                         }
460                 }
461
462                 /* As 10.4 doesn't provide proper API to retrieve the favorite places,
463                  * assume they are the standard ones 
464                  * TODO : replace hardcoded paths with proper BKE_appdir_folder_id calls */
465                 home = getenv("HOME");
466                 if (read_bookmarks && home) {
467                         BLI_snprintf(line, sizeof(line), "%s/", home);
468                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
469                         BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
470                         if (BLI_exists(line)) {
471                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
472                         }
473                         BLI_snprintf(line, sizeof(line), "%s/Documents/", home);
474                         if (BLI_exists(line)) {
475                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
476                         }
477                         BLI_snprintf(line, sizeof(line), "%s/Pictures/", home);
478                         if (BLI_exists(line)) {
479                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
480                         }
481                         BLI_snprintf(line, sizeof(line), "%s/Music/", home);
482                         if (BLI_exists(line)) {
483                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
484                         }
485                         BLI_snprintf(line, sizeof(line), "%s/Movies/", home);
486                         if (BLI_exists(line)) {
487                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
488                         }
489                 }
490 #else /* OSX 10.6+ */
491                 /* Get mounted volumes better method OSX 10.6 and higher, see: */
492                 /*https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html*/
493                 /* we get all volumes sorted including network and do not relay on user-defined finder visibility, less confusing */
494                 
495                 CFURLRef cfURL = NULL;
496                 CFURLEnumeratorResult result = kCFURLEnumeratorSuccess;
497                 CFURLEnumeratorRef volEnum = CFURLEnumeratorCreateForMountedVolumes(NULL, kCFURLEnumeratorSkipInvisibles, NULL);
498                 
499                 while (result != kCFURLEnumeratorEnd) {
500                         unsigned char defPath[FILE_MAX];
501
502                         result = CFURLEnumeratorGetNextURL(volEnum, &cfURL, NULL);
503                         if (result != kCFURLEnumeratorSuccess)
504                                 continue;
505                         
506                         CFURLGetFileSystemRepresentation(cfURL, false, (UInt8 *)defPath, FILE_MAX);
507                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)defPath, NULL, FS_INSERT_SORTED);
508                 }
509                 
510                 CFRelease(volEnum);
511                 
512                 /* Finally get user favorite places */
513                 if (read_bookmarks) {
514                         UInt32 seed;
515                         OSErr err = noErr;
516                         CFArrayRef pathesArray;
517                         LSSharedFileListRef list;
518                         LSSharedFileListItemRef itemRef;
519                         CFIndex i, pathesCount;
520                         CFURLRef cfURL = NULL;
521                         CFStringRef pathString = NULL;
522                         list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteItems, NULL);
523                         pathesArray = LSSharedFileListCopySnapshot(list, &seed);
524                         pathesCount = CFArrayGetCount(pathesArray);
525                         
526                         for (i = 0; i < pathesCount; i++) {
527                                 itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(pathesArray, i);
528                                 
529                                 err = LSSharedFileListItemResolve(itemRef, 
530                                                                   kLSSharedFileListNoUserInteraction |
531                                                                   kLSSharedFileListDoNotMountVolumes,
532                                                                   &cfURL, NULL);
533                                 if (err != noErr)
534                                         continue;
535                                 
536                                 pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle);
537                                 
538                                 if (pathString == NULL || !CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingASCII))
539                                         continue;
540
541                                 /* Exclude "all my files" as it makes no sense in blender fileselector */
542                                 /* Exclude "airdrop" if wlan not active as it would show "" ) */
543                                 if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) {
544                                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_LAST);
545                                 }
546                                 
547                                 CFRelease(pathString);
548                                 CFRelease(cfURL);
549                         }
550                         
551                         CFRelease(pathesArray);
552                         CFRelease(list);
553                 }
554 #endif /* OSX 10.6+ */
555         }
556 #else
557         /* unix */
558         {
559                 const char *home = getenv("HOME");
560
561                 if (read_bookmarks && home) {
562                         BLI_snprintf(line, sizeof(line), "%s/", home);
563                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
564                         BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
565                         if (BLI_exists(line)) {
566                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
567                         }
568                 }
569
570                 {
571                         int found = 0;
572 #ifdef __linux__
573                         /* loop over mount points */
574                         struct mntent *mnt;
575                         int len;
576                         FILE *fp;
577
578                         fp = setmntent(MOUNTED, "r");
579                         if (fp == NULL) {
580                                 fprintf(stderr, "could not get a list of mounted filesystemts\n");
581                         }
582                         else {
583                                 while ((mnt = getmntent(fp))) {
584                                         /* not sure if this is right, but seems to give the relevant mnts */
585                                         if (!STREQLEN(mnt->mnt_fsname, "/dev", 4))
586                                                 continue;
587
588                                         len = strlen(mnt->mnt_dir);
589                                         if (len && mnt->mnt_dir[len - 1] != '/') {
590                                                 BLI_snprintf(line, sizeof(line), "%s/", mnt->mnt_dir);
591                                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, line, NULL, FS_INSERT_SORTED);
592                                         }
593                                         else {
594                                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, mnt->mnt_dir, NULL, FS_INSERT_SORTED);
595                                         }
596
597                                         found = 1;
598                                 }
599                                 if (endmntent(fp) == 0) {
600                                         fprintf(stderr, "could not close the list of mounted filesystemts\n");
601                                 }
602                         }
603 #endif
604
605                         /* fallback */
606                         if (!found)
607                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, "/", NULL, FS_INSERT_SORTED);
608                 }
609         }
610 #endif
611 #endif
612 }
613
614
615 static void fsmenu_free_category(struct FSMenu *fsmenu, FSMenuCategory category)
616 {
617         FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
618
619         while (fsm_iter) {
620                 FSMenuEntry *fsm_next = fsm_iter->next;
621
622                 if (fsm_iter->path) {
623                         MEM_freeN(fsm_iter->path);
624                 }
625                 MEM_freeN(fsm_iter);
626
627                 fsm_iter = fsm_next;
628         }
629 }
630
631 void fsmenu_refresh_system_category(struct FSMenu *fsmenu)
632 {
633         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
634         ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM, NULL);
635
636         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
637         ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, NULL);
638
639         /* Add all entries to system category */
640         fsmenu_read_system(fsmenu, true);
641 }
642
643 void fsmenu_free(void)
644 {
645         if (g_fsmenu) {
646                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_SYSTEM);
647                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
648                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_BOOKMARKS);
649                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_RECENT);
650                 MEM_freeN(g_fsmenu);
651         }
652
653         g_fsmenu = NULL;
654 }
655
656 int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory category, const char *dir)
657 {
658         FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
659         int i;
660
661         for (i = 0; fsm_iter; fsm_iter = fsm_iter->next, i++) {
662                 if (BLI_path_cmp(dir, fsm_iter->path) == 0) {
663                         return i;
664                 }
665         }
666
667         return -1;
668 }