Blender  V2.59
fsmenu.c
Go to the documentation of this file.
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