]> git.lyx.org Git - lyx.git/blob - src/filedlg.C
white-space changes, removed definitions.h several enum changes because of this,...
[lyx.git] / src / filedlg.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *        
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-1999 The LyX Team.
9  *
10  * ====================================================== */
11
12 #include <config.h>
13
14 #include <unistd.h>
15 #include <cstdio>
16 #include <cstdlib>
17 #include <pwd.h>
18 #include <grp.h>
19 #include <cstring>
20
21 #include "lyx_gui_misc.h" // CancelCloseCB
22 #include "support/FileInfo.h"
23 #include "gettext.h"
24
25 #ifdef HAVE_ERRNO_H
26 #include <cerrno>
27 #endif
28
29 #if HAVE_DIRENT_H
30 # include <dirent.h>
31 # define NAMLEN(dirent) strlen((dirent)->d_name)
32 #else
33 # define dirent direct
34 # define NAMLEN(dirent) (dirent)->d_namlen
35 # if HAVE_SYS_NDIR_H
36 #  include <sys/ndir.h>
37 # endif
38 # if HAVE_SYS_DIR_H
39 #  include <sys/dir.h>
40 # endif
41 # if HAVE_NDIR_H
42 #  include <ndir.h>
43 # endif
44 #endif
45
46 #if TIME_WITH_SYS_TIME
47 # include <sys/time.h>
48 # include <ctime>
49 #else
50 # if HAVE_SYS_TIME_H
51 #  include <sys/time.h>
52 # else
53 #  include <ctime>
54 # endif
55 #endif
56
57 #ifdef BROKEN_HEADERS
58 extern "C" int gettimeofday(struct timeval *, struct timezone *);
59 #define remove(a) unlink(a)      
60 #endif
61
62 #ifdef __GNUG__
63 #pragma implementation
64 #endif
65
66 #include "support/filetools.h"
67 #include "filedlg.h"
68
69 static const long SIX_MONTH_SEC = 6L * 30L * 24L * 60L * 60L; // six months, in seconds
70 static const long ONE_HOUR_SEC = 60L * 60L;
71
72 // *** User cache class implementation
73
74 // global instance (user cache root)
75 UserCache lyxUserCache = UserCache(string(), 0, 0);
76
77 // some "C" wrappers around callbacks
78 extern "C" void C_LyXFileDlg_FileDlgCB(FL_OBJECT *, long lArgument);
79 extern "C" void C_LyXFileDlg_DoubleClickCB(FL_OBJECT *, long);
80 extern "C" int C_LyXFileDlg_CancelCB(FL_FORM *, void *);
81 extern "C" int C_LyXDirEntryC_ldeCompProc(const void * r1, 
82                                           const void * r2);
83
84 // Add: creates a new user entry
85 UserCache * UserCache::Add(uid_t ID)
86 {
87         string pszNewName;
88         struct passwd * pEntry;
89
90         // gets user name
91         if ((pEntry = getpwuid(ID)))
92                 pszNewName = pEntry->pw_name;
93         else {
94                 pszNewName = tostr(ID);
95         }
96
97         // adds new node
98         return new UserCache(pszNewName, ID, pRoot);
99 }
100
101
102 UserCache::UserCache(string const & pszName, uid_t ID, UserCache * pRoot)
103 {
104         // links node
105         if (pRoot) {
106                 this->pRoot = pRoot;
107                 pNext = pRoot->pNext;
108                 pRoot->pNext = this;
109         } else {
110                 this->pRoot = this;
111                 pNext = 0;
112         }
113
114         // stores data
115         this->pszName = pszName;
116         this->ID = ID;
117 }
118
119
120 UserCache::~UserCache()
121 {
122         if (pNext) delete pNext;
123 }
124
125
126 // Find: seeks user name from user ID
127 string UserCache::Find(uid_t ID)
128 {
129         if ((!pszName.empty()) && (this->ID == ID)) return pszName; 
130         if (pNext) return pNext->Find(ID);
131
132         return pRoot->Add(ID)->pszName;
133 }
134
135
136 // *** Group cache class implementation
137
138 // global instance (group cache root)
139 GroupCache lyxGroupCache = GroupCache(string(), 0, 0);
140
141 // Add: creates a new group entry
142 GroupCache * GroupCache::Add(gid_t ID)
143 {
144         string pszNewName;
145         struct group * pEntry;
146
147         // gets user name
148         if ((pEntry = getgrgid(ID))) pszNewName = pEntry->gr_name;
149         else {
150                 pszNewName = tostr(ID);
151         }
152
153         // adds new node
154         return new GroupCache(pszNewName, ID, pRoot);
155 }
156
157
158 GroupCache::GroupCache(string const & pszName, gid_t ID, GroupCache * pRoot)
159 {
160         // links node
161         if (pRoot) {
162                 this->pRoot = pRoot;
163                 pNext = pRoot->pNext;
164                 pRoot->pNext = this;
165         } else {
166                 this->pRoot = this;
167                 pNext = 0;
168         }
169
170         // stores data
171         this->pszName = pszName;
172         this->ID = ID;
173 }
174
175
176 GroupCache::~GroupCache()
177 {
178         if (pNext) delete pNext;
179 }
180
181
182 // Find: seeks group name from group ID
183 string GroupCache::Find(gid_t ID)
184 {
185         if ((!pszName.empty()) && (this->ID == ID)) return pszName; 
186         if (pNext) return pNext->Find(ID);
187
188         return pRoot->Add(ID)->pszName;
189 }
190
191 // *** LyXDirEntry internal structure implementation
192
193 // ldeCompProc: compares two LyXDirEntry objects content (used for qsort)
194 int LyXDirEntry::ldeCompProc(const LyXDirEntry * r1, 
195                              const LyXDirEntry * r2)
196 {
197         bool r1d = suffixIs(r1->pszName, '/'); 
198         bool r2d = suffixIs(r2->pszName, '/');
199         if (r1d && !r2d) return -1;
200         if (!r1d && r2d) return 1;
201         return r1->pszName.compare(r2->pszName);
202 }
203
204 extern "C" int C_LyXDirEntry_ldeCompProc(const void * r1, 
205                                          const void * r2)
206 {
207         return LyXDirEntry::ldeCompProc((const LyXDirEntry *)r1,
208                                         (const LyXDirEntry *)r2);
209 }
210
211 // *** LyXFileDlg class implementation
212
213 // static members
214 FD_FileDlg * LyXFileDlg::pFileDlgForm = 0;
215 LyXFileDlg * LyXFileDlg::pCurrentDlg = 0;
216
217
218 // Reread: updates dialog list to match class directory
219 void LyXFileDlg::Reread()
220 {
221         int i;
222         DIR * pDirectory;
223         struct dirent * pDirEntry;
224         string File;
225         string Buffer;
226         string Time;
227         char szMode[15];
228         FileInfo fileInfo;
229         
230         // Opens directory
231         pDirectory = opendir(pszDirectory.c_str());
232         if (!pDirectory) {
233                 WriteFSAlert(_("Warning! Couldn't open directory."), 
234                              pszDirectory);
235                 pszDirectory = GetCWD();
236                 pDirectory = opendir(pszDirectory.c_str());
237         }
238
239         // Clear the present namelist
240         if (pCurrentNames) {
241                 delete [] pCurrentNames;
242                 pCurrentNames = 0;
243         }
244
245         // Updates display
246         fl_hide_object(pFileDlgForm->List);
247         fl_clear_browser(pFileDlgForm->List);
248         fl_set_input(pFileDlgForm->DirBox, pszDirectory.c_str());
249
250         // Splits complete directory name into directories and compute depth
251         iDepth = 0;
252         string line, Temp;
253         File = pszDirectory;
254         if (File != "/") {
255                 File = split(File, Temp, '/');
256         }
257         while (!File.empty() || !Temp.empty()) {
258                 string dline = "@b"+line + Temp + '/';          
259                 fl_add_browser_line(pFileDlgForm->List, dline.c_str());
260                 File = split(File, Temp, '/');
261                 line += ' ';
262                 ++iDepth;
263         }
264
265         // Allocate names array
266         iNumNames = 0;
267         rewinddir(pDirectory);
268         while ((readdir(pDirectory))) ++iNumNames;
269         pCurrentNames = new LyXDirEntry[iNumNames];
270
271         // Parses all entries of the given subdirectory
272         iNumNames = 0;
273         time_t curTime = time(0);
274         rewinddir(pDirectory);
275         while ((pDirEntry = readdir(pDirectory))) {
276
277                 bool isLink = false, isDir = false;
278
279                 // If the pattern doesn't start with a dot, skip hidden files
280                 if (!pszMask.empty() && pszMask[0] != '.' && 
281                     pDirEntry->d_name[0] == '.')
282                         continue;
283
284                 // Gets filename
285                 string fname = pDirEntry->d_name;
286
287                 // Under all circumstances, "." and ".." are not wanted
288                 if (fname == "." || fname == "..")
289                         continue;
290
291                 // gets file status
292                 File = AddName(pszDirectory, fname);
293
294                 fileInfo.newFile(File, true);
295                 fileInfo.modeString(szMode);
296                 unsigned int nlink = fileInfo.getNumberOfLinks();
297                 string user =   lyxUserCache.Find(fileInfo.getUid());
298                 string group = lyxGroupCache.Find(fileInfo.getGid());
299
300                 time_t modtime = fileInfo.getModificationTime();
301                 Time = ctime(&modtime);
302                 
303                 if (curTime > fileInfo.getModificationTime() + SIX_MONTH_SEC
304                     || curTime < fileInfo.getModificationTime()
305                     + ONE_HOUR_SEC) {
306                         // The file is fairly old or in the future. POSIX says
307                         // the cutoff is 6 months old. Allow a 1 hour slop
308                         // factor for what is considered "the future", to
309                         // allow for NFS server/client clock disagreement.
310                         // Show the year instead of the time of day.
311                         Time.erase(10, 9);
312                         Time.erase(15, string::npos);
313                 } else {
314                         Time.erase(16, string::npos);
315                 }
316
317                 Buffer = string(szMode) + ' ' +
318                         tostr(nlink) + ' ' +
319                         user + ' ' +
320                         group + ' ' +
321                         Time.substr(4, string::npos) + ' ';
322
323                 Buffer += pDirEntry->d_name;
324                 Buffer += fileInfo.typeIndicator();
325
326                 if ((isLink = fileInfo.isLink())) {
327                   string Link;
328
329                   if (LyXReadLink(File, Link)) {
330                        Buffer += " -> ";
331                        Buffer += Link;
332
333                        // This gives the FileType of the file that
334                        // is really pointed too after resolving all
335                        // symlinks. This is not necessarily the same
336                        // as the type of Link (which could again be a
337                        // link). Is that intended?
338                        //                              JV 199902
339                        fileInfo.newFile(File);
340                        Buffer += fileInfo.typeIndicator();
341                   }
342                 }
343
344                 // filters files according to pattern and type
345                 if (fileInfo.isRegular()
346                     || fileInfo.isChar()
347                     || fileInfo.isBlock()
348                     || fileInfo.isFifo()) {
349                         if (!regexMatch(fname, pszMask))
350                                 continue;
351                 } else if (!(isDir = fileInfo.isDir()))
352                         continue;
353
354                 // Note pszLsEntry is an string!
355                 pCurrentNames[iNumNames].pszLsEntry = Buffer;
356
357                 // creates used name
358                 string temp = fname;
359                 if (isDir) temp += '/';
360                 pCurrentNames[iNumNames].pszName = temp;
361
362                 // creates displayed name
363                 temp = pDirEntry->d_name;
364                 if (isLink)
365                         temp += '@';
366                 else
367                         temp += fileInfo.typeIndicator();
368                 
369                 pCurrentNames[iNumNames++].pszDisplayed = temp;
370         }
371
372         closedir(pDirectory);
373
374         // Sort the names
375         qsort(pCurrentNames, iNumNames, sizeof(LyXDirEntry), 
376               C_LyXDirEntry_ldeCompProc);
377
378         // Add them to directory box
379         for (i = 0; i < iNumNames; ++i) {
380                 string temp = line + pCurrentNames[i].pszDisplayed;
381                 fl_add_browser_line(pFileDlgForm->List, temp.c_str());
382         }
383         fl_set_browser_topline(pFileDlgForm->List, iDepth);
384         fl_show_object(pFileDlgForm->List);
385         iLastSel = -1;
386 }
387
388
389 // SetDirectory: sets dialog current directory
390 void LyXFileDlg::SetDirectory(string const & Path)
391 {
392         if (!pszDirectory.empty()) {
393                 string TempPath = ExpandPath(Path); // Expand ~/
394                 TempPath = MakeAbsPath(TempPath, pszDirectory);
395                 pszDirectory = MakeAbsPath(TempPath);
396         } else pszDirectory = MakeAbsPath(Path);
397 }
398
399
400 // SetMask: sets dialog file mask
401 void LyXFileDlg::SetMask(string const & NewMask)
402 {
403         pszMask = NewMask;
404         fl_set_input(pFileDlgForm->PatBox, pszMask.c_str());
405 }
406
407
408 // SetInfoLine: sets dialog information line
409 void LyXFileDlg::SetInfoLine(string const & Line)
410 {
411         pszInfoLine = Line;
412         fl_set_object_label(pFileDlgForm->FileInfo, pszInfoLine.c_str());
413 }
414
415
416 LyXFileDlg::LyXFileDlg()
417 {
418         pCurrentNames = 0;
419         pszDirectory = MakeAbsPath(string("."));
420         pszMask = '*';
421
422         // Creates form if necessary. 
423         if (!pFileDlgForm) {
424                 pFileDlgForm = create_form_FileDlg();
425                 // Set callbacks. This means that we don't need a patch file
426                 fl_set_object_callback(pFileDlgForm->DirBox,
427                                        C_LyXFileDlg_FileDlgCB, 0);
428                 fl_set_object_callback(pFileDlgForm->PatBox,
429                                        C_LyXFileDlg_FileDlgCB, 1);
430                 fl_set_object_callback(pFileDlgForm->List,
431                                        C_LyXFileDlg_FileDlgCB, 2);
432                 fl_set_object_callback(pFileDlgForm->Filename,
433                                        C_LyXFileDlg_FileDlgCB, 3);
434                 fl_set_object_callback(pFileDlgForm->Rescan,
435                                        C_LyXFileDlg_FileDlgCB, 10);
436                 fl_set_object_callback(pFileDlgForm->Home,
437                                        C_LyXFileDlg_FileDlgCB, 11);
438                 fl_set_object_callback(pFileDlgForm->User1,
439                                        C_LyXFileDlg_FileDlgCB, 12);
440                 fl_set_object_callback(pFileDlgForm->User2,
441                                        C_LyXFileDlg_FileDlgCB, 13);
442                 
443                 // Make sure pressing the close box doesn't crash LyX. (RvdK)
444                 fl_set_form_atclose(pFileDlgForm->FileDlg, 
445                                     C_LyXFileDlg_CancelCB, 0);
446                 // Register doubleclick callback
447                 fl_set_browser_dblclick_callback(pFileDlgForm->List,
448                                                  C_LyXFileDlg_DoubleClickCB, 0);
449         }
450         fl_hide_object(pFileDlgForm->User1);
451         fl_hide_object(pFileDlgForm->User2);
452 }
453
454
455 LyXFileDlg::~LyXFileDlg()
456 {
457         // frees directory entries
458         if (pCurrentNames) {
459                 delete [] pCurrentNames;
460         }
461 }
462
463
464 // SetButton: sets file selector user button action
465 void LyXFileDlg::SetButton(int iIndex, string const & pszName, 
466                            string const & pszPath)
467 {
468         FL_OBJECT *pObject;
469         string *pTemp;
470
471         if (iIndex == 0) {
472                 pObject = pFileDlgForm->User1;
473                 pTemp = &pszUserPath1;
474         } else if (iIndex == 1) {                       
475                 pObject = pFileDlgForm->User2;
476                 pTemp = &pszUserPath2;
477         } else return;
478
479         if (!pszName.empty() && !pszPath.empty()) {
480                 fl_set_object_label(pObject, pszName.c_str());
481                 fl_show_object(pObject);
482                 *pTemp = pszPath;
483         } else {
484                 fl_hide_object(pObject);
485                 (*pTemp).clear();
486         }
487 }
488
489
490 // GetDirectory: gets last dialog directory
491 string LyXFileDlg::GetDirectory() 
492 {
493         if (!pszDirectory.empty())
494                 return pszDirectory;
495         else
496                 return string(".");
497 }
498
499
500 // RunDialog: handle dialog during file selection
501 bool LyXFileDlg::RunDialog()
502 {
503         force_cancel = false;
504         force_ok = false;
505         
506         // event loop
507         while(true) {
508
509                 FL_OBJECT * pObject = fl_do_forms();
510
511                 if (pObject == pFileDlgForm->Ready) {
512                         if (HandleOK())
513                                 return true;
514                 } else if (pObject == pFileDlgForm->Cancel 
515                            || force_cancel) 
516                         return false;
517                 else if (force_ok)
518                         return true;
519         }
520 }
521
522
523 // XForms objects callback (static)
524 void LyXFileDlg::FileDlgCB(FL_OBJECT *, long lArgument)
525 {
526         if (!pCurrentDlg) return;
527
528         switch (lArgument) {
529
530         case 0: // get directory
531                 pCurrentDlg->SetDirectory(fl_get_input(pFileDlgForm->DirBox));
532                 pCurrentDlg->Reread();
533                 break;
534
535         case 1: // get mask
536                 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
537                 pCurrentDlg->Reread();
538                 break;
539
540         case 2: // list
541                 pCurrentDlg->HandleListHit();
542                 break;  
543
544         case 10: // rescan
545                 pCurrentDlg->SetDirectory(fl_get_input(pFileDlgForm->DirBox));
546                 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
547                 pCurrentDlg->Reread();
548                 break;
549
550         case 11: // home
551                 pCurrentDlg->SetDirectory(GetEnvPath("HOME"));
552                 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
553                 pCurrentDlg->Reread();
554                 break;
555
556         case 12: // user button 1
557                 if (!pCurrentDlg->pszUserPath1.empty()) {
558                         pCurrentDlg->SetDirectory(pCurrentDlg->pszUserPath1);
559                         pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
560                         pCurrentDlg->Reread();
561                 }
562                 break;
563
564         case 13: // user button 2
565                 if (!pCurrentDlg->pszUserPath2.empty()) {
566                         pCurrentDlg->SetDirectory(pCurrentDlg->pszUserPath2);
567                         pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
568                         pCurrentDlg->Reread();
569                 }
570                 break;
571
572         }
573 }
574
575 extern "C" void C_LyXFileDlg_FileDlgCB(FL_OBJECT *ob, long data) 
576 {
577         LyXFileDlg::FileDlgCB(ob, data);
578 }
579
580
581 // Handle callback from list
582 void LyXFileDlg::HandleListHit()
583 {
584         // set info line
585         int iSelect = fl_get_browser(pFileDlgForm->List);
586         if (iSelect > iDepth)  {
587                 SetInfoLine(pCurrentNames[iSelect - iDepth - 1].pszLsEntry);
588         } else {
589                 SetInfoLine(string());
590         }
591 }
592
593
594 // Callback for double click in list
595 void LyXFileDlg::DoubleClickCB(FL_OBJECT *, long)
596 {
597         if (pCurrentDlg->HandleDoubleClick())
598                 // Simulate click on OK button
599                 pCurrentDlg->Force(false);
600 }
601
602 extern "C" void C_LyXFileDlg_DoubleClickCB(FL_OBJECT *ob, long data)
603 {
604         LyXFileDlg::DoubleClickCB(ob, data);
605 }
606
607 // Handle double click from list
608 bool LyXFileDlg::HandleDoubleClick()
609 {
610         bool isDir;
611         string pszTemp;
612         int iSelect;  
613
614         // set info line
615         isDir = true;
616         iSelect = fl_get_browser(pFileDlgForm->List);
617         if (iSelect > iDepth)  {
618                 pszTemp = pCurrentNames[iSelect - iDepth - 1].pszName;
619                 SetInfoLine(pCurrentNames[iSelect - iDepth - 1].pszLsEntry);
620                 if (!suffixIs(pszTemp, '/')) {
621                         isDir = false;
622                         fl_set_input(pFileDlgForm->Filename, pszTemp.c_str());
623                 }
624         } else if (iSelect != 0) {
625                 SetInfoLine(string());
626         } else
627                 return true;
628
629         // executes action
630         if (isDir) {
631
632                 int i;
633                 string Temp;
634
635                 // builds new directory name
636                 if (iSelect > iDepth) {
637                         // Directory deeper down
638                         // First, get directory with trailing /
639                         Temp = fl_get_input(pFileDlgForm->DirBox);
640                         if (!suffixIs(Temp, '/'))
641                                 Temp += '/';
642                         Temp += pszTemp;
643                 } else {
644                         // Directory higher up
645                         Temp.clear();
646                         for (i = 0; i < iSelect; ++i) {
647                                 string piece = fl_get_browser_line(pFileDlgForm->List, i+1);
648                                 // The '+2' is here to count the '@b' (JMarc)
649                                 Temp += piece.substr(i + 2);
650                         }
651                 }
652
653                 // assigns it
654                 SetDirectory(Temp);
655                 Reread();
656                 return false;
657         }
658         return true;
659 }
660
661
662 // Handle OK button call
663 bool LyXFileDlg::HandleOK()
664 {
665         string pszTemp;
666
667         // mask was changed
668         pszTemp = fl_get_input(pFileDlgForm->PatBox);
669         if (pszTemp!= pszMask) {
670                 SetMask(pszTemp);
671                 Reread();
672                 return false;
673         }
674
675         // directory was changed
676         pszTemp = fl_get_input(pFileDlgForm->DirBox);
677         if (pszTemp!= pszDirectory) {
678                 SetDirectory(pszTemp);
679                 Reread();
680                 return false;
681         }
682         
683         // Handle return from list
684         int select = fl_get_browser(pFileDlgForm->List);
685         if (select > iDepth) {
686                 string temp = pCurrentNames[select - iDepth - 1].pszName;
687                 if (!suffixIs(temp, '/')) {
688                         // If user didn't type anything, use browser
689                         string name = fl_get_input(pFileDlgForm->Filename);
690                         if (name.empty()) {
691                                 fl_set_input(pFileDlgForm->Filename, temp.c_str());
692                         }
693                         return true;
694                 }
695         }
696         
697         // Emulate a doubleclick
698         return HandleDoubleClick();
699 }
700
701
702 // Handle Cancel CB from WM close
703 int LyXFileDlg::CancelCB(FL_FORM *, void *)
704 {
705         // Simulate a click on the cancel button
706         pCurrentDlg->Force(true);
707         return FL_IGNORE;
708 }
709
710 extern "C" int C_LyXFileDlg_CancelCB(FL_FORM *fl, void *xev)
711 {
712         return LyXFileDlg::CancelCB(fl, xev);
713 }
714
715 // Simulates a click on OK/Cancel
716 void LyXFileDlg::Force(bool cancel)
717 {
718         if (cancel) {
719                 force_cancel = true;
720                 fl_set_button(pFileDlgForm->Cancel, 1);
721         } else {
722                 force_ok = true;
723                 fl_set_button(pFileDlgForm->Ready, 1);
724         }
725         // Start timer to break fl_do_forms loop soon
726         fl_set_timer(pFileDlgForm->timer, 0.1);
727 }
728
729
730 // Select: launches dialog and returns selected file
731 string LyXFileDlg::Select(string const & title, string const & path, 
732                            string const & mask, string const & suggested)
733 {
734         bool isOk;
735
736         // handles new mask and path
737         isOk = true;
738         if (!mask.empty()) {
739                 SetMask(mask);
740                 isOk = false;
741         }
742         if (!path.empty()) {
743                 SetDirectory(path);
744                 isOk = false;
745         }
746         if (!isOk) Reread();
747         else {
748                 fl_select_browser_line(pFileDlgForm->List, 1);
749                 fl_set_browser_topline(pFileDlgForm->List, 1);
750         }
751
752         // checks whether dialog can be started
753         if (pCurrentDlg) return string();
754         pCurrentDlg = this;
755
756         // runs dialog
757         SetInfoLine (string());
758         fl_set_input(pFileDlgForm->Filename, suggested.c_str());
759         fl_set_button(pFileDlgForm->Cancel, 0);
760         fl_set_button(pFileDlgForm->Ready, 0);
761         fl_set_focus_object(pFileDlgForm->FileDlg, pFileDlgForm->Filename);
762         fl_deactivate_all_forms();
763         fl_show_form(pFileDlgForm->FileDlg, FL_PLACE_MOUSE | FL_FREE_SIZE,
764                      FL_FULLBORDER, title.c_str());
765
766         isOk = RunDialog();
767
768         fl_hide_form(pFileDlgForm->FileDlg);
769         fl_activate_all_forms();
770         pCurrentDlg = 0;
771
772         // Returns filename or string() if no valid selection was made
773         if (!isOk || !fl_get_input(pFileDlgForm->Filename)[0]) return string();
774
775         pszFileName = fl_get_input(pFileDlgForm->Filename);
776
777         if (!AbsolutePath(pszFileName)) {
778                 pszFileName = AddName(fl_get_input(pFileDlgForm->DirBox), 
779                                       pszFileName);
780         }
781         return pszFileName;
782 }