]> git.lyx.org Git - lyx.git/blobdiff - src/filedlg.C
citation patch from Angus
[lyx.git] / src / filedlg.C
index 2369672d98fd5777a0799e4ad3ca75eb07963344..1413a9fffaabee899f14994f64aef726604f96c8 100644 (file)
@@ -1,22 +1,25 @@
 // -*- C++ -*-
 /* This file is part of
- * ======================================================
+ * ====================================================== 
  * 
  *           LyX, The Document Processor
  *        
  *           Copyright 1995 Matthias Ettrich
- *           Copyright 1995-1999 The LyX Team.
+ *           Copyright 1995-2000 The LyX Team.
  *
- * ======================================================*/
+ * ====================================================== */
 
 #include <config.h>
 
 #include <unistd.h>
-#include <cstdio>
 #include <cstdlib>
 #include <pwd.h>
 #include <grp.h>
 #include <cstring>
+#include <map>
+#include <algorithm>
+using std::map;
+using std::sort;
 
 #include "lyx_gui_misc.h" // CancelCloseCB
 #include "support/FileInfo.h"
@@ -55,7 +58,7 @@
 #endif
 
 #ifdef BROKEN_HEADERS
-extern "C" int gettimeofday(struct timeval *,struct timezone *);
+extern "C" int gettimeofday(struct timeval *, struct timezone *);
 #define remove(a) unlink(a)      
 #endif
 
@@ -65,136 +68,108 @@ extern "C" int gettimeofday(struct timeval *,struct timezone *);
 
 #include "support/filetools.h"
 #include "filedlg.h"
-#include "definitions.h"
 
-static const long SIX_MONTH_SEC = 6L * 30L * 24L * 60L * 60L; // six months, in seconds
+// six months, in seconds
+static const long SIX_MONTH_SEC = 6L * 30L * 24L * 60L * 60L;
 static const long ONE_HOUR_SEC = 60L * 60L;
 
 // *** User cache class implementation
-
-// global instance (user cache root)
-UserCache lyxUserCache = UserCache(string(),0,0);
-
-
-// Add: creates a new user entry
-UserCache * UserCache::Add(uid_t ID)
+/// User cache class definition
+class UserCache {
+public:
+       /// seeks user name from group ID
+       string const & find(uid_t ID) const {
+               Users::const_iterator cit = users.find(ID);
+               if (cit == users.end()) {
+                       add(ID);
+                       return users[ID];
+               }
+               return (*cit).second;
+       }
+private:
+       ///
+       void add(uid_t ID) const;
+       ///
+       typedef map<uid_t, string> Users;
+       ///
+       mutable Users users;
+};
+
+void UserCache::add(uid_t ID) const 
 {
        string pszNewName;
        struct passwd * pEntry;
-
+       
        // gets user name
        if ((pEntry = getpwuid(ID)))
                pszNewName = pEntry->pw_name;
        else {
                pszNewName = tostr(ID);
        }
-
+       
        // adds new node
-       return new UserCache(pszNewName, ID, pRoot);
-}
-
-
-UserCache::UserCache(string const & pszName, uid_t ID, UserCache * pRoot)
-{
-       // links node
-       if (pRoot) {
-               this->pRoot = pRoot;
-               pNext = pRoot->pNext;
-               pRoot->pNext = this;
-       } else {
-               this->pRoot = this;
-               pNext = 0;
+       users[ID] = pszNewName;
+}      
+
+
+/// Group cache class definition
+class GroupCache {
+public:
+       /// seeks group name from group ID
+       string const & find(gid_t ID) const {
+               Groups::const_iterator cit = groups.find(ID);
+               if (cit == groups.end()) {
+                       add(ID);
+                       return groups[ID];
+               }
+               return (*cit).second;
        }
-
-       // stores data
-       this->pszName = pszName;
-       this->ID = ID;
-}
-
-
-UserCache::~UserCache()
-{
-       if (pNext) delete pNext;
-}
-
-
-// Find: seeks user name from user ID
-string UserCache::Find(uid_t ID)
-{
-       if ((!pszName.empty()) && (this->ID == ID)) return pszName; 
-       if (pNext) return pNext->Find(ID);
-
-       return pRoot->Add(ID)->pszName;
-}
-
-
-// *** Group cache class implementation
-
-// global instance (group cache root)
-GroupCache lyxGroupCache = GroupCache(string(),0,0);
-
-// Add: creates a new group entry
-GroupCache * GroupCache::Add(gid_t ID)
+private:
+       ///
+       void add(gid_t ID) const;
+       ///
+       typedef map<gid_t, string> Groups;
+       ///
+       mutable Groups groups;
+};
+
+void GroupCache::add(gid_t ID) const 
 {
        string pszNewName;
        struct group * pEntry;
-
+       
        // gets user name
        if ((pEntry = getgrgid(ID))) pszNewName = pEntry->gr_name;
        else {
                pszNewName = tostr(ID);
        }
-
        // adds new node
-       return new GroupCache(pszNewName, ID, pRoot);
+       groups[ID] = pszNewName;
 }
-
-
-GroupCache::GroupCache(string const & pszName, gid_t ID, GroupCache * pRoot)
-{
-       // links node
-       if (pRoot) {
-               this->pRoot = pRoot;
-               pNext = pRoot->pNext;
-               pRoot->pNext = this;
-       } else {
-               this->pRoot = this;
-               pNext = 0;
+       
+// static instances
+static UserCache lyxUserCache;
+static GroupCache lyxGroupCache;
+
+// some "C" wrappers around callbacks
+extern "C" void C_LyXFileDlg_FileDlgCB(FL_OBJECT *, long lArgument);
+extern "C" void C_LyXFileDlg_DoubleClickCB(FL_OBJECT *, long);
+extern "C" int C_LyXFileDlg_CancelCB(FL_FORM *, void *);
+
+
+// compares two LyXDirEntry objects content (used for sort)
+class comp_direntry {
+public:
+       int operator()(LyXDirEntry const & r1,
+                      LyXDirEntry const & r2) const {
+               bool r1d = suffixIs(r1.pszName, '/');
+               bool r2d = suffixIs(r2.pszName, '/');
+               if (r1d && !r2d) return 1;
+               if (!r1d && r2d) return 0;
+               return r1.pszName < r2.pszName;
        }
+};
 
-       // stores data
-       this->pszName = pszName;
-       this->ID = ID;
-}
-
-
-GroupCache::~GroupCache()
-{
-       if (pNext) delete pNext;
-}
-
-
-// Find: seeks group name from group ID
-string GroupCache::Find(gid_t ID)
-{
-       if ((!pszName.empty()) && (this->ID == ID)) return pszName; 
-       if (pNext) return pNext->Find(ID);
-
-       return pRoot->Add(ID)->pszName;
-}
-
-// *** LyXDirEntry internal structure implementation
-
-// ldeCompProc: compares two LyXDirEntry objects content (used for qsort)
-int LyXDirEntry::ldeCompProc(const LyXDirEntry * r1, 
-                            const LyXDirEntry * r2)
-{
-       bool r1d = suffixIs(r1->pszName, '/'); 
-       bool r2d = suffixIs(r2->pszName, '/');
-       if (r1d && !r2d) return -1;
-       if (!r1d && r2d) return 1;
-       return r1->pszName.compare(r2->pszName);
-}
 
 // *** LyXFileDlg class implementation
 
@@ -206,17 +181,8 @@ LyXFileDlg * LyXFileDlg::pCurrentDlg = 0;
 // Reread: updates dialog list to match class directory
 void LyXFileDlg::Reread()
 {
-       int i;
-       DIR * pDirectory;
-       struct dirent * pDirEntry;
-       string File;
-       string Buffer;
-       string Time;
-       char szMode[15];
-       FileInfo fileInfo;
-       
        // Opens directory
-       pDirectory = opendir(pszDirectory.c_str());
+       DIR * pDirectory = opendir(pszDirectory.c_str());
        if (!pDirectory) {
                WriteFSAlert(_("Warning! Couldn't open directory."), 
                             pszDirectory);
@@ -225,10 +191,7 @@ void LyXFileDlg::Reread()
        }
 
        // Clear the present namelist
-       if (pCurrentNames) {
-               delete [] pCurrentNames;
-               pCurrentNames = 0;
-       }
+       direntries.clear();
 
        // Updates display
        fl_hide_object(pFileDlgForm->List);
@@ -238,7 +201,9 @@ void LyXFileDlg::Reread()
        // Splits complete directory name into directories and compute depth
        iDepth = 0;
        string line, Temp;
-       File = pszDirectory;
+       char szMode[15];
+       FileInfo fileInfo;
+       string File = pszDirectory;
        if (File != "/") {
                File = split(File, Temp, '/');
        }
@@ -250,18 +215,11 @@ void LyXFileDlg::Reread()
                ++iDepth;
        }
 
-       // Allocate names array
-       iNumNames = 0;
-       rewinddir(pDirectory);
-       while ((readdir(pDirectory))) ++iNumNames;
-       pCurrentNames = new LyXDirEntry[iNumNames];
-
        // Parses all entries of the given subdirectory
-       iNumNames = 0;
        time_t curTime = time(0);
        rewinddir(pDirectory);
+       struct dirent * pDirEntry;
        while ((pDirEntry = readdir(pDirectory))) {
-
                bool isLink = false, isDir = false;
 
                // If the pattern doesn't start with a dot, skip hidden files
@@ -282,11 +240,11 @@ void LyXFileDlg::Reread()
                fileInfo.newFile(File, true);
                fileInfo.modeString(szMode);
                unsigned int nlink = fileInfo.getNumberOfLinks();
-               string user =   lyxUserCache.Find(fileInfo.getUid());
-               string group = lyxGroupCache.Find(fileInfo.getGid());
+               string user =   lyxUserCache.find(fileInfo.getUid());
+               string group = lyxGroupCache.find(fileInfo.getGid());
 
                time_t modtime = fileInfo.getModificationTime();
-               Time = ctime(&modtime);
+               string Time = ctime(&modtime);
                
                if (curTime > fileInfo.getModificationTime() + SIX_MONTH_SEC
                    || curTime < fileInfo.getModificationTime()
@@ -296,15 +254,13 @@ void LyXFileDlg::Reread()
                        // factor for what is considered "the future", to
                        // allow for NFS server/client clock disagreement.
                        // Show the year instead of the time of day.
-#warning fix!
-                       
                        Time.erase(10, 9);
                        Time.erase(15, string::npos);
                } else {
                        Time.erase(16, string::npos);
                }
 
-               Buffer = string(szMode) + ' ' +
+               string Buffer = string(szMode) + ' ' +
                        tostr(nlink) + ' ' +
                        user + ' ' +
                        group + ' ' +
@@ -314,21 +270,21 @@ void LyXFileDlg::Reread()
                Buffer += fileInfo.typeIndicator();
 
                if ((isLink = fileInfo.isLink())) {
-                 string Link;
-
-                 if (LyXReadLink(File,Link)) {
-                      Buffer += " -> ";
-                      Buffer += Link;
-
-                      // This gives the FileType of the file that
-                      // is really pointed too after resolving all
-                      // symlinks. This is not necessarily the same
-                      // as the type of Link (which could again be a
-                      // link). Is that intended?
-                      //                              JV 199902
-                      fileInfo.newFile(File);
-                      Buffer += fileInfo.typeIndicator();
-                 }
+                       string Link;
+
+                       if (LyXReadLink(File, Link)) {
+                               Buffer += " -> ";
+                               Buffer += Link;
+
+                               // This gives the FileType of the file that
+                               // is really pointed too after resolving all
+                               // symlinks. This is not necessarily the same
+                               // as the type of Link (which could again be a
+                               // link). Is that intended?
+                               //                              JV 199902
+                               fileInfo.newFile(File);
+                               Buffer += fileInfo.typeIndicator();
+                       }
                }
 
                // filters files according to pattern and type
@@ -341,36 +297,38 @@ void LyXFileDlg::Reread()
                } else if (!(isDir = fileInfo.isDir()))
                        continue;
 
-               // Note pszLsEntry is an string!
-               pCurrentNames[iNumNames].pszLsEntry = Buffer;
+               LyXDirEntry tmp;
 
+               // Note pszLsEntry is an string!
+               tmp.pszLsEntry = Buffer;
                // creates used name
                string temp = fname;
                if (isDir) temp += '/';
-               pCurrentNames[iNumNames].pszName = temp;
 
+               tmp.pszName = temp;
                // creates displayed name
                temp = pDirEntry->d_name;
                if (isLink)
                        temp += '@';
                else
                        temp += fileInfo.typeIndicator();
-               
-               pCurrentNames[iNumNames++].pszDisplayed = temp;
+               tmp.pszDisplayed = temp;
+
+               direntries.push_back(tmp);
        }
 
        closedir(pDirectory);
 
        // Sort the names
-       qsort(pCurrentNames, iNumNames, sizeof(LyXDirEntry), 
-             (int (*)(const void *, const void *))LyXDirEntry::ldeCompProc);
-
+       sort(direntries.begin(), direntries.end(), comp_direntry());
+       
        // Add them to directory box
-       for (i = 0; i < iNumNames; ++i) {
-               string temp = line + pCurrentNames[i].pszDisplayed;
+       for (DirEntries::const_iterator cit = direntries.begin();
+            cit != direntries.end(); ++cit) {
+               string temp = line + (*cit).pszDisplayed;
                fl_add_browser_line(pFileDlgForm->List, temp.c_str());
        }
-       fl_set_browser_topline(pFileDlgForm->List,iDepth);
+       fl_set_browser_topline(pFileDlgForm->List, iDepth);
        fl_show_object(pFileDlgForm->List);
        iLastSel = -1;
 }
@@ -405,7 +363,6 @@ void LyXFileDlg::SetInfoLine(string const & Line)
 
 LyXFileDlg::LyXFileDlg()
 {
-       pCurrentNames = 0;
        pszDirectory = MakeAbsPath(string("."));
        pszMask = '*';
 
@@ -414,48 +371,41 @@ LyXFileDlg::LyXFileDlg()
                pFileDlgForm = create_form_FileDlg();
                // Set callbacks. This means that we don't need a patch file
                fl_set_object_callback(pFileDlgForm->DirBox,
-                                      LyXFileDlg::FileDlgCB,0);
+                                      C_LyXFileDlg_FileDlgCB, 0);
                fl_set_object_callback(pFileDlgForm->PatBox,
-                                      LyXFileDlg::FileDlgCB,1);
+                                      C_LyXFileDlg_FileDlgCB, 1);
                fl_set_object_callback(pFileDlgForm->List,
-                                      LyXFileDlg::FileDlgCB,2);
+                                      C_LyXFileDlg_FileDlgCB, 2);
                fl_set_object_callback(pFileDlgForm->Filename,
-                                      LyXFileDlg::FileDlgCB,3);
+                                      C_LyXFileDlg_FileDlgCB, 3);
                fl_set_object_callback(pFileDlgForm->Rescan,
-                                      LyXFileDlg::FileDlgCB,10);
+                                      C_LyXFileDlg_FileDlgCB, 10);
                fl_set_object_callback(pFileDlgForm->Home,
-                                      LyXFileDlg::FileDlgCB,11);
+                                      C_LyXFileDlg_FileDlgCB, 11);
                fl_set_object_callback(pFileDlgForm->User1,
-                                      LyXFileDlg::FileDlgCB,12);
+                                      C_LyXFileDlg_FileDlgCB, 12);
                fl_set_object_callback(pFileDlgForm->User2,
-                                      LyXFileDlg::FileDlgCB,13);
+                                      C_LyXFileDlg_FileDlgCB, 13);
                
                // Make sure pressing the close box doesn't crash LyX. (RvdK)
-               fl_set_form_atclose(pFileDlgForm->FileDlg, CancelCB, 0);
+               fl_set_form_atclose(pFileDlgForm->FileDlg, 
+                                   C_LyXFileDlg_CancelCB, 0);
                // Register doubleclick callback
                fl_set_browser_dblclick_callback(pFileDlgForm->List,
-                                                DoubleClickCB,0);
+                                                C_LyXFileDlg_DoubleClickCB,
+                                                0);
        }
        fl_hide_object(pFileDlgForm->User1);
        fl_hide_object(pFileDlgForm->User2);
 }
 
 
-LyXFileDlg::~LyXFileDlg()
-{
-       // frees directory entries
-       if (pCurrentNames) {
-               delete [] pCurrentNames;
-       }
-}
-
-
 // SetButton: sets file selector user button action
 void LyXFileDlg::SetButton(int iIndex, string const & pszName, 
                           string const & pszPath)
 {
-       FL_OBJECT *pObject;
-       string *pTemp;
+       FL_OBJECT * pObject;
+       string * pTemp;
 
        if (iIndex == 0) {
                pObject = pFileDlgForm->User1;
@@ -471,13 +421,13 @@ void LyXFileDlg::SetButton(int iIndex, string const & pszName,
                *pTemp = pszPath;
        } else {
                fl_hide_object(pObject);
-               (*pTemp).clear();
+               (*pTemp).erase();
        }
 }
 
 
 // GetDirectory: gets last dialog directory
-string LyXFileDlg::GetDirectory() 
+string LyXFileDlg::GetDirectory() const
 {
        if (!pszDirectory.empty())
                return pszDirectory;
@@ -545,7 +495,8 @@ void LyXFileDlg::FileDlgCB(FL_OBJECT *, long lArgument)
        case 12: // user button 1
                if (!pCurrentDlg->pszUserPath1.empty()) {
                        pCurrentDlg->SetDirectory(pCurrentDlg->pszUserPath1);
-                       pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
+                       pCurrentDlg->SetMask(fl_get_input(pFileDlgForm
+                                                         ->PatBox));
                        pCurrentDlg->Reread();
                }
                break;
@@ -553,7 +504,8 @@ void LyXFileDlg::FileDlgCB(FL_OBJECT *, long lArgument)
        case 13: // user button 2
                if (!pCurrentDlg->pszUserPath2.empty()) {
                        pCurrentDlg->SetDirectory(pCurrentDlg->pszUserPath2);
-                       pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
+                       pCurrentDlg->SetMask(fl_get_input(pFileDlgForm
+                                                         ->PatBox));
                        pCurrentDlg->Reread();
                }
                break;
@@ -562,13 +514,19 @@ void LyXFileDlg::FileDlgCB(FL_OBJECT *, long lArgument)
 }
 
 
+extern "C" void C_LyXFileDlg_FileDlgCB(FL_OBJECT * ob, long data) 
+{
+       LyXFileDlg::FileDlgCB(ob, data);
+}
+
+
 // Handle callback from list
 void LyXFileDlg::HandleListHit()
 {
        // set info line
        int iSelect = fl_get_browser(pFileDlgForm->List);
        if (iSelect > iDepth)  {
-               SetInfoLine(pCurrentNames[iSelect - iDepth - 1].pszLsEntry);
+               SetInfoLine(direntries[iSelect - iDepth - 1].pszLsEntry);
        } else {
                SetInfoLine(string());
        }
@@ -583,33 +541,33 @@ void LyXFileDlg::DoubleClickCB(FL_OBJECT *, long)
                pCurrentDlg->Force(false);
 }
 
+extern "C" void C_LyXFileDlg_DoubleClickCB(FL_OBJECT * ob, long data)
+{
+       LyXFileDlg::DoubleClickCB(ob, data);
+}
 
 // Handle double click from list
 bool LyXFileDlg::HandleDoubleClick()
 {
-       bool isDir;
        string pszTemp;
-       int iSelect;  
 
        // set info line
-       isDir = true;
-       iSelect = fl_get_browser(pFileDlgForm->List);
+       bool isDir = true;
+       int iSelect = fl_get_browser(pFileDlgForm->List);
        if (iSelect > iDepth)  {
-               pszTemp = pCurrentNames[iSelect - iDepth - 1].pszName;
-               SetInfoLine(pCurrentNames[iSelect - iDepth - 1].pszLsEntry);
+               pszTemp = direntries[iSelect - iDepth - 1].pszName;
+               SetInfoLine(direntries[iSelect - iDepth - 1].pszLsEntry);
                if (!suffixIs(pszTemp, '/')) {
                        isDir = false;
                        fl_set_input(pFileDlgForm->Filename, pszTemp.c_str());
                }
-       } else if (iSelect !=0) {
+       } else if (iSelect != 0) {
                SetInfoLine(string());
        } else
                return true;
 
        // executes action
        if (isDir) {
-
-               int i;
                string Temp;
 
                // builds new directory name
@@ -622,8 +580,8 @@ bool LyXFileDlg::HandleDoubleClick()
                        Temp += pszTemp;
                } else {
                        // Directory higher up
-                       Temp.clear();
-                       for (i = 0; i < iSelect; ++i) {
+                       Temp.erase();
+                       for (int i = 0; i < iSelect; ++i) {
                                string piece = fl_get_browser_line(pFileDlgForm->List, i+1);
                                // The '+2' is here to count the '@b' (JMarc)
                                Temp += piece.substr(i + 2);
@@ -642,11 +600,9 @@ bool LyXFileDlg::HandleDoubleClick()
 // Handle OK button call
 bool LyXFileDlg::HandleOK()
 {
-       string pszTemp;
-
        // mask was changed
-       pszTemp = fl_get_input(pFileDlgForm->PatBox);
-       if (pszTemp!=pszMask) {
+       string pszTemp = fl_get_input(pFileDlgForm->PatBox);
+       if (pszTemp!= pszMask) {
                SetMask(pszTemp);
                Reread();
                return false;
@@ -654,7 +610,7 @@ bool LyXFileDlg::HandleOK()
 
        // directory was changed
        pszTemp = fl_get_input(pFileDlgForm->DirBox);
-       if (pszTemp!=pszDirectory) {
+       if (pszTemp!= pszDirectory) {
                SetDirectory(pszTemp);
                Reread();
                return false;
@@ -663,7 +619,7 @@ bool LyXFileDlg::HandleOK()
        // Handle return from list
        int select = fl_get_browser(pFileDlgForm->List);
        if (select > iDepth) {
-               string temp = pCurrentNames[select - iDepth - 1].pszName;
+               string temp = direntries[select - iDepth - 1].pszName;
                if (!suffixIs(temp, '/')) {
                        // If user didn't type anything, use browser
                        string name = fl_get_input(pFileDlgForm->Filename);
@@ -688,6 +644,12 @@ int LyXFileDlg::CancelCB(FL_FORM *, void *)
 }
 
 
+extern "C" int C_LyXFileDlg_CancelCB(FL_FORM *fl, void *xev)
+{
+       return LyXFileDlg::CancelCB(fl, xev);
+}
+
+
 // Simulates a click on OK/Cancel
 void LyXFileDlg::Force(bool cancel)
 {
@@ -705,12 +667,10 @@ void LyXFileDlg::Force(bool cancel)
 
 // Select: launches dialog and returns selected file
 string LyXFileDlg::Select(string const & title, string const & path, 
-                          string const & mask, string const & suggested)
+                         string const & mask, string const & suggested)
 {
-       bool isOk;
-
        // handles new mask and path
-       isOk = true;
+       bool isOk = true;
        if (!mask.empty()) {
                SetMask(mask);
                isOk = false;