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