Fix various compiler warnings.
[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 void fsmenu_entry_refresh_valid(struct FSMenuEntry *fsentry)
220 {
221         if (fsentry->path && fsentry->path[0]) {
222 #ifdef WIN32
223                 /* XXX Special case, always consider those as valid.
224                  *     Thanks to Windows, which can spend five seconds to perform a mere stat() call on those paths...
225                  *     See T43684.
226                  */
227                 const char *exceptions[] = {"A:\\", "B:\\", NULL};
228                 const size_t exceptions_len[] = {strlen(exceptions[0]), strlen(exceptions[1]), 0};
229                 int i;
230
231                 for (i = 0; exceptions[i]; i++) {
232                         if (STRCASEEQLEN(fsentry->path, exceptions[i], exceptions_len[i])) {
233                                 fsentry->valid = true;
234                                 return;
235                         }
236                 }
237 #endif
238                 fsentry->valid = BLI_is_dir(fsentry->path);
239         }
240         else {
241                 fsentry->valid = false;
242         }
243 }
244
245 short fsmenu_can_save(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
246 {
247         FSMenuEntry *fsm_iter;
248
249         for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && idx; fsm_iter = fsm_iter->next) {
250                 idx--;
251         }
252
253         return fsm_iter ? fsm_iter->save : 0;
254 }
255
256 void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const char *name, FSMenuInsert flag)
257 {
258         FSMenuEntry *fsm_prev;
259         FSMenuEntry *fsm_iter;
260         FSMenuEntry *fsm_head;
261
262         fsm_head = ED_fsmenu_get_category(fsmenu, category);
263         fsm_prev = fsm_head;  /* this is odd and not really correct? */
264
265         for (fsm_iter = fsm_head; fsm_iter; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
266                 if (fsm_iter->path) {
267                         const int cmp_ret = BLI_path_cmp(path, fsm_iter->path);
268                         if (cmp_ret == 0) {
269                                 if (flag & FS_INSERT_FIRST) {
270                                         if (fsm_iter != fsm_head) {
271                                                 fsm_prev->next = fsm_iter->next;
272                                                 fsm_iter->next = fsm_head;
273                                                 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
274                                         }
275                                 }
276                                 return;
277                         }
278                         else if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
279                                 break;
280                         }
281                 }
282                 else {
283                         /* if we're bookmarking this, file should come
284                          * before the last separator, only automatically added
285                          * current dir go after the last sep. */
286                         if (flag & FS_INSERT_SAVE) {
287                                 break;
288                         }
289                 }
290         }
291
292         fsm_iter = MEM_mallocN(sizeof(*fsm_iter), "fsme");
293         fsm_iter->path = BLI_strdup(path);
294         fsm_iter->save = (flag & FS_INSERT_SAVE) != 0;
295
296         if ((category == FS_CATEGORY_RECENT) && (!name || !name[0])) {
297                 /* Special handling when adding new recent entry - check if dir exists in some other categories,
298                  * and try to use name from there if so. */
299                 FSMenuCategory cats[] = {FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS};
300                 int i = ARRAY_SIZE(cats);
301
302                 while (i--) {
303                         FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, cats[i]);
304
305                         for (; tfsm; tfsm = tfsm->next) {
306                                 if (STREQ(tfsm->path, fsm_iter->path)) {
307                                         if (tfsm->name[0]) {
308                                                 name = tfsm->name;
309                                         }
310                                         break;
311                                 }
312                         }
313                         if (tfsm) {
314                                 break;
315                         }
316                 }
317         }
318
319         if (name && name[0]) {
320                 BLI_strncpy(fsm_iter->name, name, sizeof(fsm_iter->name));
321         }
322         else {
323                 fsm_iter->name[0] = '\0';
324         }
325         fsmenu_entry_refresh_valid(fsm_iter);
326
327         if (fsm_prev) {
328                 if (flag & FS_INSERT_FIRST) {
329                         fsm_iter->next = fsm_head;
330                         ED_fsmenu_set_category(fsmenu, category, fsm_iter);
331                 }
332                 else {
333                         fsm_iter->next = fsm_prev->next;
334                         fsm_prev->next = fsm_iter;
335                 }
336         }
337         else {
338                 fsm_iter->next = fsm_head;
339                 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
340         }
341 }
342
343 void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
344 {
345         FSMenuEntry *fsm_prev = NULL;
346         FSMenuEntry *fsm_iter;
347         FSMenuEntry *fsm_head;
348
349         fsm_head = ED_fsmenu_get_category(fsmenu, category);
350
351         for (fsm_iter = fsm_head; fsm_iter && idx; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next)
352                 idx--;
353
354         if (fsm_iter) {
355                 /* you should only be able to remove entries that were 
356                  * not added by default, like windows drives.
357                  * also separators (where path == NULL) shouldn't be removed */
358                 if (fsm_iter->save && fsm_iter->path) {
359
360                         /* remove fsme from list */
361                         if (fsm_prev) {
362                                 fsm_prev->next = fsm_iter->next;
363                         }
364                         else {
365                                 fsm_head = fsm_iter->next;
366                                 ED_fsmenu_set_category(fsmenu, category, fsm_head);
367                         }
368                         /* free entry */
369                         MEM_freeN(fsm_iter->path);
370                         MEM_freeN(fsm_iter);
371                 }
372         }
373 }
374
375 void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
376 {
377         FSMenuEntry *fsm_iter = NULL;
378         char fsm_name[FILE_MAX];
379         int nwritten = 0;
380
381         FILE *fp = BLI_fopen(filename, "w");
382         if (!fp) return;
383
384         fprintf(fp, "[Bookmarks]\n");
385         for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter; fsm_iter = fsm_iter->next) {
386                 if (fsm_iter->path && fsm_iter->save) {
387                         fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
388                         if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
389                                 fprintf(fp, "!%s\n", fsm_iter->name);
390                         }
391                         fprintf(fp, "%s\n", fsm_iter->path);
392                 }
393         }
394         fprintf(fp, "[Recent]\n");
395         for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsm_iter && (nwritten < FSMENU_RECENT_MAX); fsm_iter = fsm_iter->next, ++nwritten) {
396                 if (fsm_iter->path && fsm_iter->save) {
397                         fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
398                         if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
399                                 fprintf(fp, "!%s\n", fsm_iter->name);
400                         }
401                         fprintf(fp, "%s\n", fsm_iter->path);
402                 }
403         }
404         fclose(fp);
405 }
406
407 void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename)
408 {
409         char line[FILE_MAXDIR];
410         char name[FILE_MAXFILE];
411         FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
412         FILE *fp;
413
414         fp = BLI_fopen(filename, "r");
415         if (!fp) return;
416
417         name[0] = '\0';
418
419         while (fgets(line, sizeof(line), fp) != NULL) {       /* read a line */
420                 if (STREQLEN(line, "[Bookmarks]", 11)) {
421                         category = FS_CATEGORY_BOOKMARKS;
422                 }
423                 else if (STREQLEN(line, "[Recent]", 8)) {
424                         category = FS_CATEGORY_RECENT;
425                 }
426                 else if (line[0] == '!') {
427                         int len = strlen(line);
428                         if (len > 0) {
429                                 if (line[len - 1] == '\n') {
430                                         line[len - 1] = '\0';
431                                 }
432                                 BLI_strncpy(name, line + 1, sizeof(name));
433                         }
434                 }
435                 else {
436                         int len = strlen(line);
437                         if (len > 0) {
438                                 if (line[len - 1] == '\n') {
439                                         line[len - 1] = '\0';
440                                 }
441                                 /* don't do this because it can be slow on network drives,
442                                  * having a bookmark from a drive thats ejected or so isn't
443                                  * all _that_ bad */
444 #if 0
445                                 if (BLI_exists(line))
446 #endif
447                                 {
448                                         fsmenu_insert_entry(fsmenu, category, line, name, FS_INSERT_SAVE);
449                                 }
450                         }
451                         /* always reset name. */
452                         name[0] = '\0';
453                 }
454         }
455         fclose(fp);
456 }
457
458 void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
459 {
460         char line[FILE_MAXDIR];
461 #ifdef WIN32
462         /* Add the drive names to the listing */
463         {
464                 wchar_t wline[FILE_MAXDIR];
465                 __int64 tmp;
466                 char tmps[4], *name;
467                 int i;
468
469                 tmp = GetLogicalDrives();
470
471                 for (i = 0; i < 26; i++) {
472                         if ((tmp >> i) & 1) {
473                                 tmps[0] = 'A' + i;
474                                 tmps[1] = ':';
475                                 tmps[2] = '\\';
476                                 tmps[3] = '\0';
477                                 name = NULL;
478
479                                 /* Flee from horrible win querying hover floppy drives! */
480                                 if (i > 1) {
481                                         /* Try to get volume label as well... */
482                                         BLI_strncpy_wchar_from_utf8(wline, tmps, 4);
483                                         if (GetVolumeInformationW(wline, wline + 4, FILE_MAXDIR - 4, NULL, NULL, NULL, NULL, 0)) {
484                                                 size_t label_len;
485
486                                                 BLI_strncpy_wchar_as_utf8(line, wline + 4, FILE_MAXDIR - 4);
487
488                                                 label_len = MIN2(strlen(line), FILE_MAXDIR - 6);
489                                                 BLI_snprintf(line + label_len, 6, " (%.2s)", tmps);
490
491                                                 name = line;
492                                         }
493                                 }
494
495                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, name, FS_INSERT_SORTED);
496                         }
497                 }
498
499                 /* Adding Desktop and My Documents */
500                 if (read_bookmarks) {
501                         SHGetSpecialFolderPathW(0, wline, CSIDL_PERSONAL, 0);
502                         BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR);
503                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
504                         SHGetSpecialFolderPathW(0, wline, CSIDL_DESKTOPDIRECTORY, 0);
505                         BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR);
506                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
507                 }
508         }
509 #else
510 #ifdef __APPLE__
511         {
512 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= 1050)
513                 OSErr err = noErr;
514                 int i;
515                 const char *home;
516                 
517                 /* loop through all the OS X Volumes, and add them to the SYSTEM section */
518                 for (i = 1; err != nsvErr; i++) {
519                         FSRef dir;
520                         unsigned char path[FILE_MAX];
521                         
522                         err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, NULL, kFSVolInfoNone, NULL, NULL, &dir);
523                         if (err != noErr)
524                                 continue;
525                         
526                         FSRefMakePath(&dir, path, FILE_MAX);
527                         if (!STREQ((char *)path, "/home") && !STREQ((char *)path, "/net")) {
528                                 /* /net and /home are meaningless on OSX, home folders are stored in /Users */
529                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, NULL, FS_INSERT_SORTED);
530                         }
531                 }
532
533                 /* As 10.4 doesn't provide proper API to retrieve the favorite places,
534                  * assume they are the standard ones 
535                  * TODO : replace hardcoded paths with proper BKE_appdir_folder_id calls */
536                 home = getenv("HOME");
537                 if (read_bookmarks && home) {
538                         BLI_snprintf(line, sizeof(line), "%s/", home);
539                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
540                         BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
541                         if (BLI_exists(line)) {
542                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
543                         }
544                         BLI_snprintf(line, sizeof(line), "%s/Documents/", home);
545                         if (BLI_exists(line)) {
546                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
547                         }
548                         BLI_snprintf(line, sizeof(line), "%s/Pictures/", home);
549                         if (BLI_exists(line)) {
550                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
551                         }
552                         BLI_snprintf(line, sizeof(line), "%s/Music/", home);
553                         if (BLI_exists(line)) {
554                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
555                         }
556                         BLI_snprintf(line, sizeof(line), "%s/Movies/", home);
557                         if (BLI_exists(line)) {
558                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
559                         }
560                 }
561 #else /* OSX 10.6+ */
562                 /* Get mounted volumes better method OSX 10.6 and higher, see: */
563                 /*https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html*/
564                 /* we get all volumes sorted including network and do not relay on user-defined finder visibility, less confusing */
565                 
566                 CFURLRef cfURL = NULL;
567                 CFURLEnumeratorResult result = kCFURLEnumeratorSuccess;
568                 CFURLEnumeratorRef volEnum = CFURLEnumeratorCreateForMountedVolumes(NULL, kCFURLEnumeratorSkipInvisibles, NULL);
569                 
570                 while (result != kCFURLEnumeratorEnd) {
571                         unsigned char defPath[FILE_MAX];
572
573                         result = CFURLEnumeratorGetNextURL(volEnum, &cfURL, NULL);
574                         if (result != kCFURLEnumeratorSuccess)
575                                 continue;
576                         
577                         CFURLGetFileSystemRepresentation(cfURL, false, (UInt8 *)defPath, FILE_MAX);
578                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)defPath, NULL, FS_INSERT_SORTED);
579                 }
580                 
581                 CFRelease(volEnum);
582                 
583                 /* Finally get user favorite places */
584                 if (read_bookmarks) {
585                         UInt32 seed;
586                         OSErr err = noErr;
587                         CFArrayRef pathesArray;
588                         LSSharedFileListRef list;
589                         LSSharedFileListItemRef itemRef;
590                         CFIndex i, pathesCount;
591                         CFURLRef cfURL = NULL;
592                         CFStringRef pathString = NULL;
593                         list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteItems, NULL);
594                         pathesArray = LSSharedFileListCopySnapshot(list, &seed);
595                         pathesCount = CFArrayGetCount(pathesArray);
596                         
597                         for (i = 0; i < pathesCount; i++) {
598                                 itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(pathesArray, i);
599                                 
600                                 err = LSSharedFileListItemResolve(itemRef, 
601                                                                   kLSSharedFileListNoUserInteraction |
602                                                                   kLSSharedFileListDoNotMountVolumes,
603                                                                   &cfURL, NULL);
604                                 if (err != noErr)
605                                         continue;
606                                 
607                                 pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle);
608                                 
609                                 if (pathString == NULL || !CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingUTF8))
610                                         continue;
611
612                                 /* Exclude "all my files" as it makes no sense in blender fileselector */
613                                 /* Exclude "airdrop" if wlan not active as it would show "" ) */
614                                 if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) {
615                                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_LAST);
616                                 }
617                                 
618                                 CFRelease(pathString);
619                                 CFRelease(cfURL);
620                         }
621                         
622                         CFRelease(pathesArray);
623                         CFRelease(list);
624                 }
625 #endif /* OSX 10.6+ */
626         }
627 #else
628         /* unix */
629         {
630                 const char *home = getenv("HOME");
631
632                 if (read_bookmarks && home) {
633                         BLI_snprintf(line, sizeof(line), "%s/", home);
634                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
635                         BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
636                         if (BLI_exists(line)) {
637                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, FS_INSERT_SORTED);
638                         }
639                 }
640
641                 {
642                         int found = 0;
643 #ifdef __linux__
644                         /* loop over mount points */
645                         struct mntent *mnt;
646                         int len;
647                         FILE *fp;
648
649                         fp = setmntent(MOUNTED, "r");
650                         if (fp == NULL) {
651                                 fprintf(stderr, "could not get a list of mounted filesystemts\n");
652                         }
653                         else {
654                                 while ((mnt = getmntent(fp))) {
655                                         /* not sure if this is right, but seems to give the relevant mnts */
656                                         if (!STREQLEN(mnt->mnt_fsname, "/dev", 4))
657                                                 continue;
658
659                                         len = strlen(mnt->mnt_dir);
660                                         if (len && mnt->mnt_dir[len - 1] != '/') {
661                                                 BLI_snprintf(line, sizeof(line), "%s/", mnt->mnt_dir);
662                                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, line, NULL, FS_INSERT_SORTED);
663                                         }
664                                         else {
665                                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, mnt->mnt_dir, NULL, FS_INSERT_SORTED);
666                                         }
667
668                                         found = 1;
669                                 }
670                                 if (endmntent(fp) == 0) {
671                                         fprintf(stderr, "could not close the list of mounted filesystemts\n");
672                                 }
673                         }
674 #endif
675
676                         /* fallback */
677                         if (!found)
678                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, "/", NULL, FS_INSERT_SORTED);
679                 }
680         }
681 #endif
682 #endif
683 }
684
685
686 static void fsmenu_free_category(struct FSMenu *fsmenu, FSMenuCategory category)
687 {
688         FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
689
690         while (fsm_iter) {
691                 FSMenuEntry *fsm_next = fsm_iter->next;
692
693                 if (fsm_iter->path) {
694                         MEM_freeN(fsm_iter->path);
695                 }
696                 MEM_freeN(fsm_iter);
697
698                 fsm_iter = fsm_next;
699         }
700 }
701
702 void fsmenu_refresh_system_category(struct FSMenu *fsmenu)
703 {
704         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
705         ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM, NULL);
706
707         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
708         ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, NULL);
709
710         /* Add all entries to system category */
711         fsmenu_read_system(fsmenu, true);
712 }
713
714 void fsmenu_refresh_bookmarks_status(struct FSMenu *fsmenu)
715 {
716         int categories[] = {FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS, FS_CATEGORY_RECENT};
717         int i;
718
719         for (i = sizeof(categories) / sizeof(*categories); i--; ) {
720                 FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, categories[i]);
721                 for ( ; fsm_iter; fsm_iter = fsm_iter->next) {
722                         fsmenu_entry_refresh_valid(fsm_iter);
723                 }
724         }
725 }
726
727 void fsmenu_free(void)
728 {
729         if (g_fsmenu) {
730                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_SYSTEM);
731                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
732                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_BOOKMARKS);
733                 fsmenu_free_category(g_fsmenu, FS_CATEGORY_RECENT);
734                 MEM_freeN(g_fsmenu);
735         }
736
737         g_fsmenu = NULL;
738 }
739
740 int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory category, const char *dir)
741 {
742         FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
743         int i;
744
745         for (i = 0; fsm_iter; fsm_iter = fsm_iter->next, i++) {
746                 if (BLI_path_cmp(dir, fsm_iter->path) == 0) {
747                         return i;
748                 }
749         }
750
751         return -1;
752 }