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