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