Blender V2.61 - r43446

fsmenu.c

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