2.5: File browser
[blender.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 "DNA_space_types.h" /* FILE_MAX */
38
39 #include "BLI_blenlib.h"
40 #include "BLI_linklist.h"
41 #include "BLI_dynstr.h"
42 #include "BLI_string.h"
43
44 #ifdef WIN32
45 #include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
46 #ifndef _WIN32_IE
47 #define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
48 #endif
49 #include <shlobj.h> /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff because 'near' is disabled through BLI_windstuff */
50 #include "BLI_winstuff.h"
51 #endif
52
53 #ifdef __APPLE__
54 #include <CoreServices/CoreServices.h>
55
56 #include "BKE_utildefines.h"
57 #endif
58
59 #include "fsmenu.h"  /* include ourselves */
60
61
62 /* FSMENU HANDLING */
63
64         /* FSMenuEntry's without paths indicate seperators */
65 typedef struct _FSMenuEntry FSMenuEntry;
66 struct _FSMenuEntry {
67         FSMenuEntry *next;
68
69         char *path;
70         short save;
71 };
72
73 typedef struct FSMenu
74 {
75         FSMenuEntry *fsmenu_system;
76         FSMenuEntry *fsmenu_bookmarks;
77         FSMenuEntry *fsmenu_recent;
78
79 } FSMenu;
80
81 static FSMenu *g_fsmenu = NULL;
82
83 struct FSMenu* fsmenu_get(void)
84 {
85         if (!g_fsmenu) {
86                 g_fsmenu=MEM_callocN(sizeof(struct FSMenu), "fsmenu");
87         }
88         return g_fsmenu;
89 }
90
91 static FSMenuEntry *fsmenu_get_category(struct FSMenu* fsmenu, FSMenuCategory category)
92 {
93         FSMenuEntry *fsms = NULL;
94
95         switch(category) {
96                 case FS_CATEGORY_SYSTEM:
97                         fsms = fsmenu->fsmenu_system;
98                         break;
99                 case FS_CATEGORY_BOOKMARKS:
100                         fsms = fsmenu->fsmenu_bookmarks;
101                         break;
102                 case FS_CATEGORY_RECENT:
103                         fsms = fsmenu->fsmenu_recent;
104                         break;
105         }
106         return fsms;
107 }
108
109 static void fsmenu_set_category(struct FSMenu* fsmenu, FSMenuCategory category, FSMenuEntry *fsms)
110 {
111         switch(category) {
112                 case FS_CATEGORY_SYSTEM:
113                         fsmenu->fsmenu_system = fsms;
114                         break;
115                 case FS_CATEGORY_BOOKMARKS:
116                         fsmenu->fsmenu_bookmarks = fsms;
117                         break;
118                 case FS_CATEGORY_RECENT:
119                         fsmenu->fsmenu_recent = fsms;
120                         break;
121         }
122 }
123
124 int fsmenu_get_nentries(struct FSMenu* fsmenu, FSMenuCategory category)
125 {
126         FSMenuEntry *fsme;
127         int count= 0;
128
129         for (fsme= fsmenu_get_category(fsmenu, category); fsme; fsme= fsme->next) 
130                 count++;
131
132         return count;
133 }
134
135 char *fsmenu_get_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx)
136 {
137         FSMenuEntry *fsme;
138
139         for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next)
140                 idx--;
141
142         return fsme?fsme->path:NULL;
143 }
144
145 short fsmenu_can_save (struct FSMenu* fsmenu, FSMenuCategory category, int idx)
146 {
147         FSMenuEntry *fsme;
148
149         for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next)
150                 idx--;
151
152         return fsme?fsme->save:0;
153 }
154
155 void fsmenu_insert_entry(struct FSMenu* fsmenu, FSMenuCategory category, char *path, int sorted, short save)
156 {
157         FSMenuEntry *prev;
158         FSMenuEntry *fsme;
159         FSMenuEntry *fsms;
160
161         fsms = fsmenu_get_category(fsmenu, category);
162         prev= fsme= fsms;
163
164         for (; fsme; prev= fsme, fsme= fsme->next) {
165                 if (fsme->path) {
166                         if (BLI_streq(path, fsme->path)) {
167                                 return;
168                         } else if (sorted && strcmp(path, fsme->path)<0) {
169                                 break;
170                         }
171                 } else {
172                         // if we're bookmarking this, file should come 
173                         // before the last separator, only automatically added
174                         // current dir go after the last sep.
175                         if (save) {
176                                 break;
177                         }
178                 }
179         }
180         
181         fsme= MEM_mallocN(sizeof(*fsme), "fsme");
182         fsme->path= BLI_strdup(path);
183         fsme->save = save;
184
185         if (prev) {
186                 fsme->next= prev->next;
187                 prev->next= fsme;
188         } else {
189                 fsme->next= fsms;
190                 fsmenu_set_category(fsmenu, category, fsme);
191         }
192 }
193
194 void fsmenu_remove_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx)
195 {
196         FSMenuEntry *prev= NULL, *fsme= NULL;
197         FSMenuEntry *fsms = fsmenu_get_category(fsmenu, category);
198
199         for (fsme= fsms; fsme && idx; prev= fsme, fsme= fsme->next)             
200                 idx--;
201
202         if (fsme) {
203                 /* you should only be able to remove entries that were 
204                    not added by default, like windows drives.
205                    also separators (where path == NULL) shouldn't be removed */
206                 if (fsme->save && fsme->path) {
207
208                         /* remove fsme from list */
209                         if (prev) {
210                                 prev->next= fsme->next;
211                         } else {
212                                 fsms= fsme->next;
213                                 fsmenu_set_category(fsmenu, category, fsms);
214                         }
215                         /* free entry */
216                         MEM_freeN(fsme->path);
217                         MEM_freeN(fsme);
218                 }
219         }
220 }
221
222 void fsmenu_write_file(struct FSMenu* fsmenu, const char *filename)
223 {
224         FSMenuEntry *fsme= NULL;
225         int count=FSMENU_RECENT_MAX;
226
227         FILE *fp = fopen(filename, "w");
228         if (!fp) return;
229         
230         fprintf(fp, "[Bookmarks]\n");
231         for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsme; fsme= fsme->next) {
232                 if (fsme->path && fsme->save) {
233                         fprintf(fp, "%s\n", fsme->path);
234                 }
235         }
236         fprintf(fp, "[Recent]\n");
237         for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsme && count; fsme= fsme->next, --count) {
238                 if (fsme->path && fsme->save) {
239                         fprintf(fp, "%s\n", fsme->path);
240                 }
241         }
242         fclose(fp);
243 }
244
245 void fsmenu_read_file(struct FSMenu* fsmenu, const char *filename)
246 {
247         char line[256];
248         FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
249         FILE *fp;
250
251 #ifdef WIN32
252         /* Add the drive names to the listing */
253         {
254                 __int64 tmp;
255                 char folder[256];
256                 char tmps[4];
257                 int i;
258                         
259                 tmp= GetLogicalDrives();
260                 
261                 for (i=2; i < 26; i++) {
262                         if ((tmp>>i) & 1) {
263                                 tmps[0]='a'+i;
264                                 tmps[1]=':';
265                                 tmps[2]='\\';
266                                 tmps[3]=0;
267                                 
268                                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, 1, 0);
269                         }
270                 }
271
272                 /* Adding Desktop and My Documents */
273                 SHGetSpecialFolderPath(0, folder, CSIDL_PERSONAL, 0);
274                 fsmenu_insert_entry(fsmenu,FS_CATEGORY_BOOKMARKS, folder, 1, 0);
275                 SHGetSpecialFolderPath(0, folder, CSIDL_DESKTOPDIRECTORY, 0);
276                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, folder, 1, 0);
277         }
278 #else
279 #ifdef __APPLE__
280         {
281                 OSErr err=noErr;
282                 int i;
283                 
284                 /* loop through all the OS X Volumes, and add them to the SYSTEM section */
285                 for (i=1; err!=nsvErr; i++)
286                 {
287                         FSRef dir;
288                         unsigned char path[FILE_MAXDIR+FILE_MAXFILE];
289                         
290                         err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, NULL, kFSVolInfoNone, NULL, NULL, &dir);
291                         if (err != noErr)
292                                 continue;
293                         
294                         FSRefMakePath(&dir, path, FILE_MAXDIR+FILE_MAXFILE);
295                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, 1, 0);
296                 }
297         }
298 #else
299         /* unix */
300         {
301                 char dir[FILE_MAXDIR];
302                 char *home= BLI_gethome();
303
304                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, "/", 1, 0);
305
306                 if(home) {
307                         BLI_snprintf(dir, FILE_MAXDIR, "%s/", home);
308                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, dir, 1, 0);
309                         BLI_snprintf(dir, FILE_MAXDIR, "%s/Desktop/", home);
310                         fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, dir, 1, 0);
311                 }
312         }
313 #endif
314 #endif
315
316         fp = fopen(filename, "r");
317         if (!fp) return;
318
319         while ( fgets ( line, 256, fp ) != NULL ) /* read a line */
320         {
321                 if (strncmp(line, "[Bookmarks]", 11)==0){
322                         category = FS_CATEGORY_BOOKMARKS;
323                 } else if (strncmp(line, "[Recent]", 8)==0){
324                         category = FS_CATEGORY_RECENT;
325                 } else {
326                         int len = strlen(line);
327                         if (len>0) {
328                                 if (line[len-1] == '\n') {
329                                         line[len-1] = '\0';
330                                 }
331                                 if (BLI_exist(line)) {
332                                         fsmenu_insert_entry(fsmenu, category, line, 0, 1);
333                                 }
334                         }
335                 }
336         }
337         fclose(fp);
338 }
339
340 static void fsmenu_free_category(struct FSMenu* fsmenu, FSMenuCategory category)
341 {
342         FSMenuEntry *fsme= fsmenu_get_category(fsmenu, category);
343
344         while (fsme) {
345                 FSMenuEntry *n= fsme->next;
346
347                 if (fsme->path) MEM_freeN(fsme->path);
348                 MEM_freeN(fsme);
349
350                 fsme= n;
351         }
352 }
353
354 void fsmenu_free(struct FSMenu* fsmenu)
355 {
356         fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
357         fsmenu_free_category(fsmenu, FS_CATEGORY_BOOKMARKS);
358         fsmenu_free_category(fsmenu, FS_CATEGORY_RECENT);
359         MEM_freeN(fsmenu);
360 }
361