|
Blender
V2.59
|
00001 /* 00002 * $Id: fsmenu.c 36031 2011-04-06 06:03:48Z campbellbarton $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00021 * All rights reserved. 00022 * 00023 * The Original Code is: all of this file. 00024 * 00025 * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation. 00026 * 00027 * ***** END GPL LICENSE BLOCK ***** 00028 */ 00029 00035 #include <stdlib.h> 00036 #include <string.h> 00037 #include <stdio.h> 00038 #include <math.h> 00039 00040 #include "MEM_guardedalloc.h" 00041 00042 #include "DNA_space_types.h" /* FILE_MAX */ 00043 00044 #include "BLI_blenlib.h" 00045 #include "BLI_linklist.h" 00046 #include "BLI_dynstr.h" 00047 #include "BLI_string.h" 00048 00049 #ifdef WIN32 00050 #include <windows.h> /* need to include windows.h so _WIN32_IE is defined */ 00051 #ifndef _WIN32_IE 00052 #define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */ 00053 #endif 00054 #include <shlobj.h> /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff because 'near' is disabled through BLI_windstuff */ 00055 #include "BLI_winstuff.h" 00056 #endif 00057 00058 #ifdef __APPLE__ 00059 /* XXX BIG WARNING: carbon.h can not be included in blender code, it conflicts with struct ID */ 00060 #define ID ID_ 00061 #include <CoreServices/CoreServices.h> 00062 00063 #endif 00064 00065 #ifdef __linux__ 00066 #include <mntent.h> 00067 #endif 00068 00069 #include "fsmenu.h" /* include ourselves */ 00070 00071 00072 /* FSMENU HANDLING */ 00073 00074 /* FSMenuEntry's without paths indicate seperators */ 00075 typedef struct _FSMenuEntry FSMenuEntry; 00076 struct _FSMenuEntry { 00077 FSMenuEntry *next; 00078 00079 char *path; 00080 short save; 00081 }; 00082 00083 typedef struct FSMenu 00084 { 00085 FSMenuEntry *fsmenu_system; 00086 FSMenuEntry *fsmenu_bookmarks; 00087 FSMenuEntry *fsmenu_recent; 00088 00089 } FSMenu; 00090 00091 static FSMenu *g_fsmenu = NULL; 00092 00093 struct FSMenu* fsmenu_get(void) 00094 { 00095 if (!g_fsmenu) { 00096 g_fsmenu=MEM_callocN(sizeof(struct FSMenu), "fsmenu"); 00097 } 00098 return g_fsmenu; 00099 } 00100 00101 static FSMenuEntry *fsmenu_get_category(struct FSMenu* fsmenu, FSMenuCategory category) 00102 { 00103 FSMenuEntry *fsms = NULL; 00104 00105 switch(category) { 00106 case FS_CATEGORY_SYSTEM: 00107 fsms = fsmenu->fsmenu_system; 00108 break; 00109 case FS_CATEGORY_BOOKMARKS: 00110 fsms = fsmenu->fsmenu_bookmarks; 00111 break; 00112 case FS_CATEGORY_RECENT: 00113 fsms = fsmenu->fsmenu_recent; 00114 break; 00115 } 00116 return fsms; 00117 } 00118 00119 static void fsmenu_set_category(struct FSMenu* fsmenu, FSMenuCategory category, FSMenuEntry *fsms) 00120 { 00121 switch(category) { 00122 case FS_CATEGORY_SYSTEM: 00123 fsmenu->fsmenu_system = fsms; 00124 break; 00125 case FS_CATEGORY_BOOKMARKS: 00126 fsmenu->fsmenu_bookmarks = fsms; 00127 break; 00128 case FS_CATEGORY_RECENT: 00129 fsmenu->fsmenu_recent = fsms; 00130 break; 00131 } 00132 } 00133 00134 int fsmenu_get_nentries(struct FSMenu* fsmenu, FSMenuCategory category) 00135 { 00136 FSMenuEntry *fsme; 00137 int count= 0; 00138 00139 for (fsme= fsmenu_get_category(fsmenu, category); fsme; fsme= fsme->next) 00140 count++; 00141 00142 return count; 00143 } 00144 00145 char *fsmenu_get_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx) 00146 { 00147 FSMenuEntry *fsme; 00148 00149 for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next) 00150 idx--; 00151 00152 return fsme?fsme->path:NULL; 00153 } 00154 00155 short fsmenu_can_save (struct FSMenu* fsmenu, FSMenuCategory category, int idx) 00156 { 00157 FSMenuEntry *fsme; 00158 00159 for (fsme= fsmenu_get_category(fsmenu, category); fsme && idx; fsme= fsme->next) 00160 idx--; 00161 00162 return fsme?fsme->save:0; 00163 } 00164 00165 void fsmenu_insert_entry(struct FSMenu* fsmenu, FSMenuCategory category, const char *path, int sorted, short save) 00166 { 00167 FSMenuEntry *prev; 00168 FSMenuEntry *fsme; 00169 FSMenuEntry *fsms; 00170 00171 fsms = fsmenu_get_category(fsmenu, category); 00172 prev= fsme= fsms; 00173 00174 for (; fsme; prev= fsme, fsme= fsme->next) { 00175 if (fsme->path) { 00176 const int cmp_ret= BLI_path_cmp(path, fsme->path); 00177 if (cmp_ret == 0) { 00178 return; 00179 } 00180 else if (sorted && cmp_ret < 0) { 00181 break; 00182 } 00183 } else { 00184 // if we're bookmarking this, file should come 00185 // before the last separator, only automatically added 00186 // current dir go after the last sep. 00187 if (save) { 00188 break; 00189 } 00190 } 00191 } 00192 00193 fsme= MEM_mallocN(sizeof(*fsme), "fsme"); 00194 fsme->path= BLI_strdup(path); 00195 fsme->save = save; 00196 00197 if (prev) { 00198 fsme->next= prev->next; 00199 prev->next= fsme; 00200 } else { 00201 fsme->next= fsms; 00202 fsmenu_set_category(fsmenu, category, fsme); 00203 } 00204 } 00205 00206 void fsmenu_remove_entry(struct FSMenu* fsmenu, FSMenuCategory category, int idx) 00207 { 00208 FSMenuEntry *prev= NULL, *fsme= NULL; 00209 FSMenuEntry *fsms = fsmenu_get_category(fsmenu, category); 00210 00211 for (fsme= fsms; fsme && idx; prev= fsme, fsme= fsme->next) 00212 idx--; 00213 00214 if (fsme) { 00215 /* you should only be able to remove entries that were 00216 not added by default, like windows drives. 00217 also separators (where path == NULL) shouldn't be removed */ 00218 if (fsme->save && fsme->path) { 00219 00220 /* remove fsme from list */ 00221 if (prev) { 00222 prev->next= fsme->next; 00223 } else { 00224 fsms= fsme->next; 00225 fsmenu_set_category(fsmenu, category, fsms); 00226 } 00227 /* free entry */ 00228 MEM_freeN(fsme->path); 00229 MEM_freeN(fsme); 00230 } 00231 } 00232 } 00233 00234 void fsmenu_write_file(struct FSMenu* fsmenu, const char *filename) 00235 { 00236 FSMenuEntry *fsme= NULL; 00237 int nskip= 0; 00238 00239 FILE *fp = fopen(filename, "w"); 00240 if (!fp) return; 00241 00242 fprintf(fp, "[Bookmarks]\n"); 00243 for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsme; fsme= fsme->next) { 00244 if (fsme->path && fsme->save) { 00245 fprintf(fp, "%s\n", fsme->path); 00246 } 00247 } 00248 fprintf(fp, "[Recent]\n"); 00249 nskip = fsmenu_get_nentries(fsmenu, FS_CATEGORY_RECENT) - FSMENU_RECENT_MAX; 00250 // skip first entries if list too long 00251 for (fsme= fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT); fsme && (nskip>0); fsme= fsme->next, --nskip) 00252 ; 00253 for (; fsme; fsme= fsme->next) { 00254 if (fsme->path && fsme->save) { 00255 fprintf(fp, "%s\n", fsme->path); 00256 } 00257 } 00258 fclose(fp); 00259 } 00260 00261 void fsmenu_read_bookmarks(struct FSMenu* fsmenu, const char *filename) 00262 { 00263 char line[256]; 00264 FSMenuCategory category = FS_CATEGORY_BOOKMARKS; 00265 FILE *fp; 00266 00267 fp = fopen(filename, "r"); 00268 if (!fp) return; 00269 00270 while ( fgets ( line, 256, fp ) != NULL ) /* read a line */ 00271 { 00272 if (strncmp(line, "[Bookmarks]", 11)==0){ 00273 category = FS_CATEGORY_BOOKMARKS; 00274 } else if (strncmp(line, "[Recent]", 8)==0){ 00275 category = FS_CATEGORY_RECENT; 00276 } else { 00277 int len = strlen(line); 00278 if (len>0) { 00279 if (line[len-1] == '\n') { 00280 line[len-1] = '\0'; 00281 } 00282 if (BLI_exist(line)) { 00283 fsmenu_insert_entry(fsmenu, category, line, 0, 1); 00284 } 00285 } 00286 } 00287 } 00288 fclose(fp); 00289 } 00290 00291 void fsmenu_read_system(struct FSMenu* fsmenu) 00292 { 00293 char line[256]; 00294 #ifdef WIN32 00295 /* Add the drive names to the listing */ 00296 { 00297 __int64 tmp; 00298 char tmps[4]; 00299 int i; 00300 00301 tmp= GetLogicalDrives(); 00302 00303 for (i=2; i < 26; i++) { 00304 if ((tmp>>i) & 1) { 00305 tmps[0]='A'+i; 00306 tmps[1]=':'; 00307 tmps[2]='\\'; 00308 tmps[3]=0; 00309 00310 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, 1, 0); 00311 } 00312 } 00313 00314 /* Adding Desktop and My Documents */ 00315 SHGetSpecialFolderPath(0, line, CSIDL_PERSONAL, 0); 00316 fsmenu_insert_entry(fsmenu,FS_CATEGORY_BOOKMARKS, line, 1, 0); 00317 SHGetSpecialFolderPath(0, line, CSIDL_DESKTOPDIRECTORY, 0); 00318 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00319 } 00320 #else 00321 #ifdef __APPLE__ 00322 { 00323 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) 00324 OSErr err=noErr; 00325 int i; 00326 const char *home; 00327 00328 /* loop through all the OS X Volumes, and add them to the SYSTEM section */ 00329 for (i=1; err!=nsvErr; i++) 00330 { 00331 FSRef dir; 00332 unsigned char path[FILE_MAXDIR+FILE_MAXFILE]; 00333 00334 err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, NULL, kFSVolInfoNone, NULL, NULL, &dir); 00335 if (err != noErr) 00336 continue; 00337 00338 FSRefMakePath(&dir, path, FILE_MAXDIR+FILE_MAXFILE); 00339 if (strcmp((char*)path, "/home") && strcmp((char*)path, "/net")) 00340 { /* /net and /home are meaningless on OSX, home folders are stored in /Users */ 00341 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, 1, 0); 00342 } 00343 } 00344 00345 /* As 10.4 doesn't provide proper API to retrieve the favorite places, 00346 assume they are the standard ones 00347 TODO : replace hardcoded paths with proper BLI_get_folder calls */ 00348 home = getenv("HOME"); 00349 if(home) { 00350 BLI_snprintf(line, 256, "%s/", home); 00351 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00352 BLI_snprintf(line, 256, "%s/Desktop/", home); 00353 if (BLI_exists(line)) { 00354 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00355 } 00356 BLI_snprintf(line, 256, "%s/Documents/", home); 00357 if (BLI_exists(line)) { 00358 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00359 } 00360 BLI_snprintf(line, 256, "%s/Pictures/", home); 00361 if (BLI_exists(line)) { 00362 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00363 } 00364 BLI_snprintf(line, 256, "%s/Music/", home); 00365 if (BLI_exists(line)) { 00366 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00367 } 00368 BLI_snprintf(line, 256, "%s/Movies/", home); 00369 if (BLI_exists(line)) { 00370 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00371 } 00372 } 00373 #else 00374 /* 10.5 provides ability to retrieve Finder favorite places */ 00375 UInt32 seed; 00376 OSErr err = noErr; 00377 CFArrayRef pathesArray; 00378 LSSharedFileListRef list; 00379 LSSharedFileListItemRef itemRef; 00380 CFIndex i, pathesCount; 00381 CFURLRef cfURL = NULL; 00382 CFStringRef pathString = NULL; 00383 00384 /* First get local mounted volumes */ 00385 list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteVolumes, NULL); 00386 pathesArray = LSSharedFileListCopySnapshot(list, &seed); 00387 pathesCount = CFArrayGetCount(pathesArray); 00388 00389 for (i=0; i<pathesCount; i++) 00390 { 00391 itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(pathesArray, i); 00392 00393 err = LSSharedFileListItemResolve(itemRef, 00394 kLSSharedFileListNoUserInteraction 00395 | kLSSharedFileListDoNotMountVolumes, 00396 &cfURL, NULL); 00397 if (err != noErr) 00398 continue; 00399 00400 pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle); 00401 00402 if (!CFStringGetCString(pathString,line,256,kCFStringEncodingASCII)) 00403 continue; 00404 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, line, 1, 0); 00405 00406 CFRelease(pathString); 00407 CFRelease(cfURL); 00408 } 00409 00410 CFRelease(pathesArray); 00411 CFRelease(list); 00412 00413 /* Then get network volumes */ 00414 err = noErr; 00415 for (i=1; err!=nsvErr; i++) 00416 { 00417 FSRef dir; 00418 FSVolumeRefNum volRefNum; 00419 struct GetVolParmsInfoBuffer volParmsBuffer; 00420 unsigned char path[FILE_MAXDIR+FILE_MAXFILE]; 00421 00422 err = FSGetVolumeInfo(kFSInvalidVolumeRefNum, i, &volRefNum, kFSVolInfoNone, NULL, NULL, &dir); 00423 if (err != noErr) 00424 continue; 00425 00426 err = FSGetVolumeParms(volRefNum, &volParmsBuffer, sizeof(volParmsBuffer)); 00427 if ((err != noErr) || (volParmsBuffer.vMServerAdr == 0)) /* Exclude local devices */ 00428 continue; 00429 00430 00431 FSRefMakePath(&dir, path, FILE_MAXDIR+FILE_MAXFILE); 00432 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, 1, 0); 00433 } 00434 00435 /* Finally get user favorite places */ 00436 list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteItems, NULL); 00437 pathesArray = LSSharedFileListCopySnapshot(list, &seed); 00438 pathesCount = CFArrayGetCount(pathesArray); 00439 00440 for (i=0; i<pathesCount; i++) 00441 { 00442 itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(pathesArray, i); 00443 00444 err = LSSharedFileListItemResolve(itemRef, 00445 kLSSharedFileListNoUserInteraction 00446 | kLSSharedFileListDoNotMountVolumes, 00447 &cfURL, NULL); 00448 if (err != noErr) 00449 continue; 00450 00451 pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle); 00452 00453 if (!CFStringGetCString(pathString,line,256,kCFStringEncodingASCII)) 00454 continue; 00455 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00456 00457 CFRelease(pathString); 00458 CFRelease(cfURL); 00459 } 00460 00461 CFRelease(pathesArray); 00462 CFRelease(list); 00463 #endif /* OSX 10.5+ */ 00464 } 00465 #else 00466 /* unix */ 00467 { 00468 const char *home= getenv("HOME"); 00469 00470 if(home) { 00471 BLI_snprintf(line, FILE_MAXDIR, "%s/", home); 00472 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00473 BLI_snprintf(line, FILE_MAXDIR, "%s/Desktop/", home); 00474 if (BLI_exists(line)) { 00475 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, line, 1, 0); 00476 } 00477 } 00478 00479 { 00480 int found= 0; 00481 #ifdef __linux__ 00482 /* loop over mount points */ 00483 struct mntent *mnt; 00484 int len; 00485 FILE *fp; 00486 00487 fp = setmntent (MOUNTED, "r"); 00488 if (fp == NULL) { 00489 fprintf(stderr, "could not get a list of mounted filesystemts\n"); 00490 } 00491 else { 00492 while ((mnt = getmntent (fp))) { 00493 /* not sure if this is right, but seems to give the relevant mnts */ 00494 if(strncmp(mnt->mnt_fsname, "/dev", 4)) 00495 continue; 00496 00497 len= strlen(mnt->mnt_dir); 00498 if(len && mnt->mnt_dir[len-1] != '/') { 00499 BLI_snprintf(line, FILE_MAXDIR, "%s/", mnt->mnt_dir); 00500 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, line, 1, 0); 00501 } 00502 else 00503 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, mnt->mnt_dir, 1, 0); 00504 00505 found= 1; 00506 } 00507 if (endmntent (fp) == 0) { 00508 fprintf(stderr, "could not close the list of mounted filesystemts\n"); 00509 } 00510 } 00511 #endif 00512 00513 /* fallback */ 00514 if(!found) 00515 fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, "/", 1, 0); 00516 } 00517 } 00518 #endif 00519 #endif 00520 } 00521 00522 00523 static void fsmenu_free_category(struct FSMenu* fsmenu, FSMenuCategory category) 00524 { 00525 FSMenuEntry *fsme= fsmenu_get_category(fsmenu, category); 00526 00527 while (fsme) { 00528 FSMenuEntry *n= fsme->next; 00529 00530 if (fsme->path) MEM_freeN(fsme->path); 00531 MEM_freeN(fsme); 00532 00533 fsme= n; 00534 } 00535 } 00536 00537 void fsmenu_free(struct FSMenu* fsmenu) 00538 { 00539 fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM); 00540 fsmenu_free_category(fsmenu, FS_CATEGORY_BOOKMARKS); 00541 fsmenu_free_category(fsmenu, FS_CATEGORY_RECENT); 00542 MEM_freeN(fsmenu); 00543 } 00544