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