* Added a list of OS X volumes to the file selector bookmarks pane, consistent with...
[blender-staging.git] / source / blender / editors / space_file / fsmenu.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <math.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_linklist.h"
39 #include "BLI_dynstr.h"
40
41 #ifdef WIN32
42 #include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
43 #ifndef _WIN32_IE
44 #define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
45 #endif
46 #include <shlobj.h> /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff because 'near' is disabled through BLI_windstuff */
47 #include "BLI_winstuff.h"
48 #endif
49
50 #ifdef __APPLE__
51 #include <CoreServices/CoreServices.h>
52
53 #include "BKE_utildefines.h"
54 #endif
55
56 #include "fsmenu.h"  /* include ourselves */
57
58
59 /* FSMENU HANDLING */
60
61         /* FSMenuEntry's without paths indicate seperators */
62 typedef struct _FSMenuEntry FSMenuEntry;
63 struct _FSMenuEntry {
64         FSMenuEntry *next;
65
66         char *path;
67         short save;
68         short xs, ys;
69 };
70
71 typedef struct FSMenu
72 {
73         FSMenuEntry *fsmenu_system;
74         FSMenuEntry *fsmenu_bookmarks;
75         FSMenuEntry *fsmenu_recent;
76
77         FSMenuCategory selected_category;
78         int selected_entry;
79
80 } FSMenu;
81
82 static FSMenu *g_fsmenu = NULL;
83
84 struct FSMenu* fsmenu_get(void)
85 {
86         if (!g_fsmenu) {
87                 g_fsmenu=MEM_callocN(sizeof(struct FSMenu), "fsmenu");
88         }
89         return g_fsmenu;
90 }
91
92 void fsmenu_select_entry(struct FSMenu* fsmenu, FSMenuCategory category, int index)
93 {
94         fsmenu->selected_category = category;
95         fsmenu->selected_entry = index;
96 }
97
98 int     fsmenu_is_selected(struct FSMenu* fsmenu, FSMenuCategory category, int index)
99 {
100         return (category==fsmenu->selected_category) && (index==fsmenu->selected_entry);
101 }
102
103 static FSMenuEntry *fsmenu_get_category(struct FSMenu* fsmenu, FSMenuCategory category)
104 {
105         FSMenuEntry *fsms = NULL;
106
107         switch(category) {
108                 case FS_CATEGORY_SYSTEM:
109                         fsms = fsmenu->fsmenu_system;
110                         break;
111                 case FS_CATEGORY_BOOKMARKS:
112                         fsms = fsmenu->fsmenu_bookmarks;
113                         break;
114                 case FS_CATEGORY_RECENT:
115                         fsms = fsmenu->fsmenu_recent;
116                         break;
117         }
118         return fsms;
119 }
120
121 static void fsmenu_set_category(struct FSMenu* fsmenu, FSMenuCategory category, FSMenuEntry *fsms)
122 {
123         switch(category) {
124                 case FS_CATEGORY_SYSTEM:
125                         fsmenu->fsmenu_system = fsms;
126                         break;
127                 case FS_CATEGORY_BOOKMARKS:
128                         fsmenu->fsmenu_bookmarks = fsms;
129                         break;
130                 case FS_CATEGORY_RECENT:
131                         fsmenu->fsmenu_recent = fsms;
132                         break;
133         }
134 }
135
136 int fsmenu_get_nentries(struct FSMenu* fsmenu, FSMenuCategory category)
137 {
138         FSMenuEntry *fsme;
139         int count= 0;
140
141         for (fsme= fsmenu_get_category(fsmenu, category); fsme; fsme= fsme->next) 
142                 count++;
143
144         return count;
145 }
146
147 char *fsmenu_get_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx)
148 {
149         FSMenuEntry *fsme;
150
151         for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next)
152                 idx--;
153
154         return fsme?fsme->path:NULL;
155 }
156
157 void fsmenu_set_pos(struct FSMenu* fsmenu, FSMenuCategory category, int idx, short xs, short ys)
158 {
159         FSMenuEntry *fsme;
160
161         for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next)
162                 idx--;
163
164         if (fsme) {
165                 fsme->xs = xs;
166                 fsme->ys = ys;
167         }
168 }
169
170 int     fsmenu_get_pos (struct FSMenu* fsmenu, FSMenuCategory category, int idx, short* xs, short* ys)
171 {
172         FSMenuEntry *fsme;
173
174         for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next)
175                 idx--;
176
177         if (fsme) {
178                 *xs = fsme->xs;
179                 *ys = fsme->ys;
180                 return 1;
181         }
182
183         return 0;
184 }
185
186
187 void fsmenu_insert_entry(struct FSMenu* fsmenu, FSMenuCategory category, char *path, int sorted, short save)
188 {
189         FSMenuEntry *prev;
190         FSMenuEntry *fsme;
191         FSMenuEntry *fsms;
192
193         fsms = fsmenu_get_category(fsmenu, category);
194         prev= fsme= fsms;
195
196         for (; fsme; prev= fsme, fsme= fsme->next) {
197                 if (fsme->path) {
198                         if (BLI_streq(path, fsme->path)) {
199                                 return;
200                         } else if (sorted && strcmp(path, fsme->path)<0) {
201                                 break;
202                         }
203                 } else {
204                         // if we're bookmarking this, file should come 
205                         // before the last separator, only automatically added
206                         // current dir go after the last sep.
207                         if (save) {
208                                 break;
209                         }
210                 }
211         }
212         
213         fsme= MEM_mallocN(sizeof(*fsme), "fsme");
214         fsme->path= BLI_strdup(path);
215         fsme->save = save;
216
217         if (prev) {
218                 fsme->next= prev->next;
219                 prev->next= fsme;
220         } else {
221                 fsme->next= fsms;
222                 fsmenu_set_category(fsmenu, category, fsme);
223         }
224 }
225
226 void fsmenu_remove_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx)
227 {
228         FSMenuEntry *prev= NULL, *fsme= NULL;
229         FSMenuEntry *fsms = fsmenu_get_category(fsmenu, category);
230
231         for (fsme= fsms; fsme && idx; prev= fsme, fsme= fsme->next)             
232                 idx--;
233
234         if (fsme) {
235                 /* you should only be able to remove entries that were 
236                    not added by default, like windows drives.
237                    also separators (where path == NULL) shouldn't be removed */
238                 if (fsme->save && fsme->path) {
239
240                         /* remove fsme from list */
241                         if (prev) {
242                                 prev->next= fsme->next;
243                         } else {
244                                 fsms= fsme->next;
245                                 fsmenu_set_category(fsmenu, category, fsms);
246                         }
247                         /* free entry */
248                         MEM_freeN(fsme->path);
249                         MEM_freeN(fsme);
250                 }
251         }
252 }
253
254 void fsmenu_write_file(struct FSMenu* fsmenu, const char *filename)
255 {
256         FSMenuEntry *fsme= NULL;
257         int count=FSMENU_RECENT_MAX;
258
259         FILE *fp = fopen(filename, "w");
260         if (!fp) return;
261         
262         fprintf(fp, "[Bookmarks]\n");
263         for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsme; fsme= fsme->next) {
264                 if (fsme->path && fsme->save) {
265                         fprintf(fp, "%s\n", fsme->path);
266                 }
267         }
268         fprintf(fp, "[Recent]\n");
269         for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsme && count; fsme= fsme->next, --count) {
270                 if (fsme->path && fsme->save) {
271                         fprintf(fp, "%s\n", fsme->path);
272                 }
273         }
274         fclose(fp);
275 }
276
277 void fsmenu_read_file(struct FSMenu* fsmenu, const char *filename)
278 {
279         char line[256];
280         FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
281         FILE *fp;
282
283         #ifdef WIN32
284         /* Add the drive names to the listing */
285         {
286                 __int64 tmp;
287                 char folder[256];
288                 char tmps[4];
289                 int i;
290                         
291                 tmp= GetLogicalDrives();
292                 
293                 for (i=2; i < 26; i++) {
294                         if ((tmp>>i) & 1) {
295                                 tmps[0]='a'+i;
296                                 tmps[1]=':';
297                                 tmps[2]='\\';
298                                 tmps[3]=0;
299                                 
300                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, 1, 0);
301                         }
302                 }
303
304                 /* Adding Desktop and My Documents */
305                 SHGetSpecialFolderPath(0, folder, CSIDL_PERSONAL, 0);
306                 fsmenu_insert_entry(fsmenu,FS_CATEGORY_BOOKMARKS, folder, 1, 0);
307                 SHGetSpecialFolderPath(0, folder, CSIDL_DESKTOPDIRECTORY, 0);
308                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, folder, 1, 0);
309         }
310 #endif
311
312 #ifdef __APPLE__
313         {
314                 OSErr err=noErr;
315                 int i;
316                 
317                 /* loop through all the OS X Volumes, and add them to the SYSTEM section */
318                 for (i=1; err!=nsvErr; i++)
319                 {
320                         FSRef dir;
321                         unsigned char path[FILE_MAXDIR+FILE_MAXFILE];
322                         
323                         err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, NULL, kFSVolInfoNone, NULL, NULL, &dir);
324                         if (err != noErr)
325                                 continue;
326                         
327                         FSRefMakePath(&dir, path, FILE_MAXDIR+FILE_MAXFILE);
328                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, 1, 0);
329                 }
330         }
331 #endif
332
333         fp = fopen(filename, "r");
334         if (!fp) return;
335
336         while ( fgets ( line, 256, fp ) != NULL ) /* read a line */
337         {
338                 if (strncmp(line, "[Bookmarks]", 11)==0){
339                         category = FS_CATEGORY_BOOKMARKS;
340                 } else if (strncmp(line, "[Recent]", 8)==0){
341                         category = FS_CATEGORY_RECENT;
342                 } else {
343                         int len = strlen(line);
344                         if (len>0) {
345                                 if (line[len-1] == '\n') {
346                                         line[len-1] = '\0';
347                                 }
348                                 fsmenu_insert_entry(fsmenu, category, line, 0, 1);
349                         }
350                 }
351         }
352         fclose(fp);
353 }
354
355 static void fsmenu_free_category(struct FSMenu* fsmenu, FSMenuCategory category)
356 {
357         FSMenuEntry *fsme= fsmenu_get_category(fsmenu, category);
358
359         while (fsme) {
360                 FSMenuEntry *n= fsme->next;
361
362                 if (fsme->path) MEM_freeN(fsme->path);
363                 MEM_freeN(fsme);
364
365                 fsme= n;
366         }
367 }
368
369 void fsmenu_free(struct FSMenu* fsmenu)
370 {
371         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
372         fsmenu_free_category(fsmenu, FS_CATEGORY_BOOKMARKS);
373         fsmenu_free_category(fsmenu, FS_CATEGORY_RECENT);
374         MEM_freeN(fsmenu);
375 }
376