]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormFiledialog.C
If I ever see another licence blurb again, it'll be too soon...
[lyx.git] / src / frontends / xforms / FormFiledialog.C
1 /**
2  * \file FormFiledialog.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author unknown
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include <unistd.h>
15 #include <cstdlib>
16 #include <pwd.h>
17 #include <grp.h>
18 #include <map>
19 #include <algorithm>
20
21 using std::map;
22 using std::max;
23 using std::sort;
24
25 #include "gettext.h"
26 #include "forms_gettext.h"
27 #include "xforms_helpers.h"
28
29 #include "support/FileInfo.h"
30 #include "support/lyxlib.h"
31 #include "support/lstrings.h"
32 #include "support/tostr.h"
33 #include "support/filetools.h"
34
35 #include "frontends/Alert.h"
36 #include "frontends/Dialogs.h"
37
38 #include <boost/bind.hpp>
39
40 //#ifdef HAVE_ERRNO_H
41 //#include <cerrno>
42 //#endif
43
44 #if HAVE_DIRENT_H
45 # include <dirent.h>
46 #else
47 # define dirent direct
48 # if HAVE_SYS_NDIR_H
49 #  include <sys/ndir.h>
50 # endif
51 # if HAVE_SYS_DIR_H
52 #  include <sys/dir.h>
53 # endif
54 # if HAVE_NDIR_H
55 #  include <ndir.h>
56 # endif
57 #endif
58
59
60 #include "FormFiledialog.h"
61 #include "forms/form_filedialog.h"
62 #include "lyx_forms.h"
63
64 using namespace lyx::support;
65
66 namespace {
67
68 // six months, in seconds
69 long const SIX_MONTH_SEC = 6L * 30L * 24L * 60L * 60L;
70 //static
71 long const ONE_HOUR_SEC = 60L * 60L;
72
73 extern "C" {
74
75         static
76         int C_LyXFileDlg_CancelCB(FL_FORM *fl, void *xev)
77         {
78                 return FileDialog::Private::CancelCB(fl, xev);
79         }
80
81         static
82         void C_LyXFileDlg_DoubleClickCB(FL_OBJECT * ob, long data)
83         {
84                 FileDialog::Private::DoubleClickCB(ob, data);
85         }
86
87         static
88         void C_LyXFileDlg_FileDlgCB(FL_OBJECT * ob, long data)
89         {
90                 FileDialog::Private::FileDlgCB(ob, data);
91         }
92
93 }
94
95 // *** User cache class implementation
96 /// User cache class definition
97 class UserCache {
98 public:
99         /// seeks user name from group ID
100         string const & find(uid_t ID) const {
101                 Users::const_iterator cit = users.find(ID);
102                 if (cit == users.end()) {
103                         add(ID);
104                         return users[ID];
105                 }
106                 return cit->second;
107         }
108 private:
109         ///
110         void add(uid_t ID) const;
111         ///
112         typedef map<uid_t, string> Users;
113         ///
114         mutable Users users;
115 };
116
117
118 void UserCache::add(uid_t ID) const
119 {
120         struct passwd const * entry = getpwuid(ID);
121         users[ID] = entry ? entry->pw_name : tostr(ID);
122 }
123
124
125 /// Group cache class definition
126 class GroupCache {
127 public:
128         /// seeks group name from group ID
129         string const & find(gid_t ID) const ;
130 private:
131         ///
132         void add(gid_t ID) const;
133         ///
134         typedef map<gid_t, string> Groups;
135         ///
136         mutable Groups groups;
137 };
138
139
140 string const & GroupCache::find(gid_t ID) const
141 {
142         Groups::const_iterator cit = groups.find(ID);
143         if (cit == groups.end()) {
144                 add(ID);
145                 return groups[ID];
146         }
147         return cit->second;
148 }
149
150
151 void GroupCache::add(gid_t ID) const
152 {
153         struct group const * entry = getgrgid(ID);
154         groups[ID] = entry ? entry->gr_name : tostr(ID);
155 }
156
157 // local instances
158 UserCache lyxUserCache;
159 GroupCache lyxGroupCache;
160
161 // compares two LyXDirEntry objects content (used for sort)
162 class comp_direntry {
163 public:
164         bool operator()(DirEntry const & r1, DirEntry const & r2) const
165         {
166                 bool const r1d = suffixIs(r1.name_, '/');
167                 bool const r2d = suffixIs(r2.name_, '/');
168                 if (r1d && !r2d)
169                         return true;
170                 if (!r1d && r2d)
171                         return false;
172                 return r1.name_ < r2.name_;
173         }
174 };
175
176
177 } // namespace anon
178
179
180
181 // *** FileDialog::Private class implementation
182
183 // static members
184 FD_filedialog * FileDialog::Private::file_dlg_form_ = 0;
185 FileDialog::Private * FileDialog::Private::current_dlg_ = 0;
186 int FileDialog::Private::minw_ = 0;
187 int FileDialog::Private::minh_ = 0;
188
189
190 // Reread: updates dialog list to match class directory
191 void FileDialog::Private::Reread()
192 {
193         // Opens directory
194         DIR * dir = ::opendir(directory_.c_str());
195         if (!dir) {
196 // FIXME: re-add ...
197 #if 0
198                 Alert::err_alert(_("Warning! Couldn't open directory."),
199                         directory_);
200 #endif
201                 directory_ = getcwd();
202                 dir = ::opendir(directory_.c_str());
203         }
204
205         // Clear the present namelist
206         dir_entries_.clear();
207
208         // Updates display
209         fl_hide_object(file_dlg_form_->List);
210         fl_clear_browser(file_dlg_form_->List);
211         fl_set_input(file_dlg_form_->DirBox, directory_.c_str());
212
213         // Splits complete directory name into directories and compute depth
214         depth_ = 0;
215         string line, Temp;
216         string mode;
217         string File = directory_;
218         if (File != "/") {
219                 File = split(File, Temp, '/');
220         }
221         while (!File.empty() || !Temp.empty()) {
222                 string dline = "@b" + line + Temp + '/';
223                 fl_add_browser_line(file_dlg_form_->List, dline.c_str());
224                 File = split(File, Temp, '/');
225                 line += ' ';
226                 ++depth_;
227         }
228
229         // Parses all entries of the given subdirectory
230         time_t curTime = time(0);
231         rewinddir(dir);
232         while (dirent * entry = readdir(dir)) {
233                 bool isLink = false, isDir = false;
234
235                 // If the pattern doesn't start with a dot, skip hidden files
236                 if (!mask_.empty() && mask_[0] != '.' &&
237                     entry->d_name[0] == '.')
238                         continue;
239
240                 // Gets filename
241                 string fname = entry->d_name;
242
243                 // Under all circumstances, "." and ".." are not wanted
244                 if (fname == "." || fname == "..")
245                         continue;
246
247                 // gets file status
248                 File = AddName(directory_, fname);
249
250                 FileInfo fileInfo(File, true);
251
252                 // can this really happen?
253                 if (!fileInfo.isOK())
254                         continue;
255
256                 mode = fileInfo.modeString();
257                 unsigned int const nlink = fileInfo.getNumberOfLinks();
258                 string const user  = lyxUserCache.find(fileInfo.getUid());
259                 string const group = lyxGroupCache.find(fileInfo.getGid());
260
261                 time_t modtime = fileInfo.getModificationTime();
262                 string Time = ctime(&modtime);
263
264                 if (curTime > modtime + SIX_MONTH_SEC
265                     || curTime < modtime + ONE_HOUR_SEC) {
266                         // The file is fairly old or in the future. POSIX says
267                         // the cutoff is 6 months old. Allow a 1 hour slop
268                         // factor for what is considered "the future", to
269                         // allow for NFS server/client clock disagreement.
270                         // Show the year instead of the time of day.
271                         Time.erase(10, 9);
272                         Time.erase(15, string::npos);
273                 } else {
274                         Time.erase(16, string::npos);
275                 }
276
277                 string buffer = mode + ' ' +
278                         tostr(nlink) + ' ' +
279                         user + ' ' +
280                         group + ' ' +
281                         Time.substr(4, string::npos) + ' ';
282
283                 buffer += entry->d_name;
284                 buffer += fileInfo.typeIndicator();
285
286                 isLink = fileInfo.isLink();
287                 if (isLink) {
288                         string Link;
289
290                         if (LyXReadLink(File, Link)) {
291                                 buffer += " -> ";
292                                 buffer += Link;
293
294                                 // This gives the FileType of the file that
295                                 // is really pointed too after resolving all
296                                 // symlinks. This is not necessarily the same
297                                 // as the type of Link (which could again be a
298                                 // link). Is that intended?
299                                 //                              JV 199902
300                                 fileInfo.newFile(File);
301                                 if (fileInfo.isOK())
302                                         buffer += fileInfo.typeIndicator();
303                                 else
304                                         continue;
305                         }
306                 }
307
308                 // filters files according to pattern and type
309                 if (fileInfo.isRegular()
310                     || fileInfo.isChar()
311                     || fileInfo.isBlock()
312                     || fileInfo.isFifo()) {
313                         if (!regexMatch(fname, mask_))
314                                 continue;
315                 } else if (!(isDir = fileInfo.isDir()))
316                         continue;
317
318                 DirEntry tmp;
319
320                 // Note ls_entry_ is an string!
321                 tmp.ls_entry_ = buffer;
322                 // creates used name
323                 string temp = fname;
324                 if (isDir)
325                         temp += '/';
326
327                 tmp.name_ = temp;
328                 // creates displayed name
329                 temp = entry->d_name;
330                 if (isLink)
331                         temp += '@';
332                 else
333                         temp += fileInfo.typeIndicator();
334                 tmp.displayed_ = temp;
335
336                 dir_entries_.push_back(tmp);
337         }
338
339         closedir(dir);
340
341         // Sort the names
342         sort(dir_entries_.begin(), dir_entries_.end(), comp_direntry());
343
344         // Add them to directory box
345         for (DirEntries::const_iterator cit = dir_entries_.begin();
346              cit != dir_entries_.end(); ++cit) {
347                 string const temp = line + cit->displayed_;
348                 fl_add_browser_line(file_dlg_form_->List, temp.c_str());
349         }
350         fl_set_browser_topline(file_dlg_form_->List, depth_);
351         fl_show_object(file_dlg_form_->List);
352         last_sel_ = -1;
353 }
354
355
356 // SetDirectory: sets dialog current directory
357 void FileDialog::Private::SetDirectory(string const & path)
358 {
359         string tmp;
360         if (path.empty())
361                 tmp = getcwd();
362         else
363                 tmp = MakeAbsPath(ExpandPath(path), directory_);
364
365         // must check the directory exists
366         DIR * dir = ::opendir(tmp.c_str());
367         if (!dir) {
368 // FIXME: re-add ...
369 #if 0
370                 Alert::err_alert(_("Warning! Couldn't open directory."), tmp);
371 #endif
372         } else {
373                 ::closedir(dir);
374                 directory_ = tmp;
375         }
376 }
377
378
379 // SetMask: sets dialog file mask
380 void FileDialog::Private::SetMask(string const & newmask)
381 {
382         mask_ = newmask;
383         fl_set_input(file_dlg_form_->PatBox, mask_.c_str());
384 }
385
386
387 // SetInfoLine: sets dialog information line
388 void FileDialog::Private::SetInfoLine(string const & line)
389 {
390         info_line_ = line;
391         fl_set_object_label(file_dlg_form_->FileInfo, info_line_.c_str());
392 }
393
394
395 FileDialog::Private::Private()
396 {
397         directory_ = MakeAbsPath(string("."));
398         mask_ = '*';
399
400         // Creates form if necessary.
401         if (!file_dlg_form_) {
402                 file_dlg_form_ = build_filedialog(this);
403                 minw_ = file_dlg_form_->form->w;
404                 minh_ = file_dlg_form_->form->h;
405                 // Set callbacks. This means that we don't need a patch file
406                 fl_set_object_callback(file_dlg_form_->DirBox,
407                                        C_LyXFileDlg_FileDlgCB, 0);
408                 fl_set_object_callback(file_dlg_form_->PatBox,
409                                        C_LyXFileDlg_FileDlgCB, 1);
410                 fl_set_object_callback(file_dlg_form_->List,
411                                        C_LyXFileDlg_FileDlgCB, 2);
412                 fl_set_object_callback(file_dlg_form_->Filename,
413                                        C_LyXFileDlg_FileDlgCB, 3);
414                 fl_set_object_callback(file_dlg_form_->Rescan,
415                                        C_LyXFileDlg_FileDlgCB, 10);
416                 fl_set_object_callback(file_dlg_form_->Home,
417                                        C_LyXFileDlg_FileDlgCB, 11);
418                 fl_set_object_callback(file_dlg_form_->User1,
419                                        C_LyXFileDlg_FileDlgCB, 12);
420                 fl_set_object_callback(file_dlg_form_->User2,
421                                        C_LyXFileDlg_FileDlgCB, 13);
422
423                 // Make sure pressing the close box doesn't crash LyX. (RvdK)
424                 fl_set_form_atclose(file_dlg_form_->form,
425                                     C_LyXFileDlg_CancelCB, 0);
426                 // Register doubleclick callback
427                 fl_set_browser_dblclick_callback(file_dlg_form_->List,
428                                                  C_LyXFileDlg_DoubleClickCB,
429                                                  0);
430         }
431         fl_hide_object(file_dlg_form_->User1);
432         fl_hide_object(file_dlg_form_->User2);
433
434         r_ = Dialogs::redrawGUI().connect(boost::bind(&FileDialog::Private::redraw, this));
435 }
436
437
438 FileDialog::Private::~Private()
439 {
440         r_.disconnect();
441 }
442
443
444 void FileDialog::Private::redraw()
445 {
446         if (file_dlg_form_->form && file_dlg_form_->form->visible)
447                 fl_redraw_form(file_dlg_form_->form);
448 }
449
450
451 // SetButton: sets file selector user button action
452 void FileDialog::Private::SetButton(int index, string const & name,
453                            string const & path)
454 {
455         FL_OBJECT * ob;
456         string * tmp;
457
458         if (index == 0) {
459                 ob = file_dlg_form_->User1;
460                 tmp = &user_path1_;
461         } else if (index == 1) {
462                 ob = file_dlg_form_->User2;
463                 tmp = &user_path2_;
464         } else {
465                 return;
466         }
467
468         if (!name.empty()) {
469                 fl_set_object_label(ob, idex(name).c_str());
470                 fl_set_button_shortcut(ob, scex(name).c_str(), 1);
471                 fl_show_object(ob);
472                 *tmp = path;
473         } else {
474                 fl_hide_object(ob);
475                 tmp->erase();
476         }
477 }
478
479
480 // GetDirectory: gets last dialog directory
481 string const FileDialog::Private::GetDirectory() const
482 {
483         if (!directory_.empty())
484                 return directory_;
485         else
486                 return string(".");
487 }
488
489 namespace {
490         bool x_sync_kludge(bool ret)
491         {
492                 XSync(fl_get_display(), false);
493                 return ret;
494         }
495 } // namespace anon
496
497 // RunDialog: handle dialog during file selection
498 bool FileDialog::Private::RunDialog()
499 {
500         force_cancel_ = false;
501         force_ok_ = false;
502
503         // event loop
504         while (true) {
505                 FL_OBJECT * ob = fl_do_forms();
506
507                 if (ob == file_dlg_form_->Ready) {
508                         if (HandleOK())
509                                 return x_sync_kludge(true);
510
511                 } else if (ob == file_dlg_form_->Cancel || force_cancel_)
512                         return x_sync_kludge(false);
513
514                 else if (force_ok_)
515                         return x_sync_kludge(true);
516         }
517 }
518
519
520 // XForms objects callback (static)
521 void FileDialog::Private::FileDlgCB(FL_OBJECT *, long arg)
522 {
523         if (!current_dlg_)
524                 return;
525
526         switch (arg) {
527
528         case 0: // get directory
529                 current_dlg_->SetDirectory(fl_get_input(file_dlg_form_->DirBox));
530                 current_dlg_->Reread();
531                 break;
532
533         case 1: // get mask
534                 current_dlg_->SetMask(fl_get_input(file_dlg_form_->PatBox));
535                 current_dlg_->Reread();
536                 break;
537
538         case 2: // list
539                 current_dlg_->HandleListHit();
540                 break;
541
542         case 10: // rescan
543                 current_dlg_->SetDirectory(fl_get_input(file_dlg_form_->DirBox));
544                 current_dlg_->SetMask(fl_get_input(file_dlg_form_->PatBox));
545                 current_dlg_->Reread();
546                 break;
547
548         case 11: // home
549                 current_dlg_->SetDirectory(GetEnvPath("HOME"));
550                 current_dlg_->SetMask(fl_get_input(file_dlg_form_->PatBox));
551                 current_dlg_->Reread();
552                 break;
553
554         case 12: // user button 1
555                 current_dlg_->SetDirectory(current_dlg_->user_path1_);
556                 current_dlg_->SetMask(fl_get_input(file_dlg_form_->PatBox));
557                 current_dlg_->Reread();
558                 break;
559
560         case 13: // user button 2
561                 current_dlg_->SetDirectory(current_dlg_->user_path2_);
562                 current_dlg_->SetMask(fl_get_input(file_dlg_form_->PatBox));
563                 current_dlg_->Reread();
564                 break;
565
566         }
567 }
568
569
570 // Handle callback from list
571 void FileDialog::Private::HandleListHit()
572 {
573         // set info line
574         int const select_ = fl_get_browser(file_dlg_form_->List);
575         if (select_ > depth_)
576                 SetInfoLine(dir_entries_[select_ - depth_ - 1].ls_entry_);
577         else
578                 SetInfoLine(string());
579 }
580
581
582 // Callback for double click in list
583 void FileDialog::Private::DoubleClickCB(FL_OBJECT *, long)
584 {
585         // Simulate click on OK button
586         if (current_dlg_->HandleDoubleClick())
587                 current_dlg_->Force(false);
588 }
589
590
591 // Handle double click from list
592 bool FileDialog::Private::HandleDoubleClick()
593 {
594         string tmp;
595
596         // set info line
597         bool isDir = true;
598         int const select_ = fl_get_browser(file_dlg_form_->List);
599         if (select_ > depth_) {
600                 tmp = dir_entries_[select_ - depth_ - 1].name_;
601                 SetInfoLine(dir_entries_[select_ - depth_ - 1].ls_entry_);
602                 if (!suffixIs(tmp, '/')) {
603                         isDir = false;
604                         fl_set_input(file_dlg_form_->Filename, tmp.c_str());
605                 }
606         } else if (select_ != 0) {
607                 SetInfoLine(string());
608         } else
609                 return true;
610
611         // executes action
612         if (isDir) {
613                 string Temp;
614
615                 // builds new directory name
616                 if (select_ > depth_) {
617                         // Directory deeper down
618                         // First, get directory with trailing /
619                         Temp = fl_get_input(file_dlg_form_->DirBox);
620                         if (!suffixIs(Temp, '/'))
621                                 Temp += '/';
622                         Temp += tmp;
623                 } else {
624                         // Directory higher up
625                         Temp.erase();
626                         for (int i = 0; i < select_; ++i) {
627                                 string piece = fl_get_browser_line(file_dlg_form_->List, i+1);
628                                 // The '+2' is here to count the '@b' (JMarc)
629                                 Temp += piece.substr(i + 2);
630                         }
631                 }
632
633                 // assigns it
634                 SetDirectory(Temp);
635                 Reread();
636                 return false;
637         }
638         return true;
639 }
640
641
642 // Handle OK button call
643 bool FileDialog::Private::HandleOK()
644 {
645         // mask was changed
646         string tmp = fl_get_input(file_dlg_form_->PatBox);
647         if (tmp != mask_) {
648                 SetMask(tmp);
649                 Reread();
650                 return false;
651         }
652
653         // directory was changed
654         tmp = fl_get_input(file_dlg_form_->DirBox);
655         if (tmp != directory_) {
656                 SetDirectory(tmp);
657                 Reread();
658                 return false;
659         }
660
661         // Handle return from list
662         int const select = fl_get_browser(file_dlg_form_->List);
663         if (select > depth_) {
664                 string const temp = dir_entries_[select - depth_ - 1].name_;
665                 if (!suffixIs(temp, '/')) {
666                         // If user didn't type anything, use browser
667                         string const name = fl_get_input(file_dlg_form_->Filename);
668                         if (name.empty())
669                                 fl_set_input(file_dlg_form_->Filename, temp.c_str());
670                         return true;
671                 }
672         }
673
674         // Emulate a doubleclick
675         return HandleDoubleClick();
676 }
677
678
679 // Handle Cancel CB from WM close
680 int FileDialog::Private::CancelCB(FL_FORM *, void *)
681 {
682         // Simulate a click on the cancel button
683         current_dlg_->Force(true);
684         return FL_IGNORE;
685 }
686
687
688 // Simulates a click on OK/Cancel
689 void FileDialog::Private::Force(bool cancel)
690 {
691         if (cancel) {
692                 force_cancel_ = true;
693                 fl_set_button(file_dlg_form_->Cancel, 1);
694         } else {
695                 force_ok_ = true;
696                 fl_set_button(file_dlg_form_->Ready, 1);
697         }
698         // Start timer to break fl_do_forms loop soon
699         fl_set_timer(file_dlg_form_->timer, 0.1);
700 }
701
702
703 // Select: launches dialog and returns selected file
704 string const FileDialog::Private::Select(string const & title,
705                                          string const & path,
706                                          string const & mask,
707                                          string const & suggested)
708 {
709         // handles new mask and path
710         bool isOk = true;
711         if (!mask.empty()) {
712                 SetMask(mask);
713                 isOk = false;
714         }
715         if (!path.empty()) {
716                 SetDirectory(path);
717                 isOk = false;
718         }
719         if (!isOk)
720                 Reread();
721
722         // highlight the suggested file in the browser, if it exists.
723         int sel = 0;
724         string const filename = OnlyFilename(suggested);
725         if (!filename.empty()) {
726                 for (int i = 0; i < fl_get_browser_maxline(file_dlg_form_->List); ++i) {
727                         string s = fl_get_browser_line(file_dlg_form_->List, i + 1);
728                         s = trim(s);
729                         if (s == filename) {
730                                 sel = i + 1;
731                                 break;
732                         }
733                 }
734         }
735
736         if (sel != 0)
737                 fl_select_browser_line(file_dlg_form_->List, sel);
738         int const top = max(sel - 5, 1);
739         fl_set_browser_topline(file_dlg_form_->List, top);
740
741         // checks whether dialog can be started
742         if (current_dlg_)
743                 return string();
744         current_dlg_ = this;
745
746         // runs dialog
747         SetInfoLine(string());
748         setEnabled(file_dlg_form_->Filename, true);
749         fl_set_input(file_dlg_form_->Filename, suggested.c_str());
750         fl_set_button(file_dlg_form_->Cancel, 0);
751         fl_set_button(file_dlg_form_->Ready, 0);
752         fl_set_focus_object(file_dlg_form_->form, file_dlg_form_->Filename);
753         fl_deactivate_all_forms();
754         // Prevent xforms crashing if the dialog gets too small by preventing
755         // it from being shrunk beyond a minimum size.
756         // calls to fl_set_form_minsize/maxsize apply only to the next
757         // fl_show_form(), so this comes first.
758         fl_set_form_minsize(file_dlg_form_->form, minw_, minh_);
759
760         fl_show_form(file_dlg_form_->form,
761                      FL_PLACE_MOUSE | FL_FREE_SIZE, 0,
762                      title.c_str());
763
764         isOk = RunDialog();
765
766         fl_hide_form(file_dlg_form_->form);
767         fl_activate_all_forms();
768         current_dlg_ = 0;
769
770         // Returns filename or string() if no valid selection was made
771         if (!isOk || !fl_get_input(file_dlg_form_->Filename)[0])
772                 return string();
773
774         file_name_ = fl_get_input(file_dlg_form_->Filename);
775
776         if (!AbsolutePath(file_name_))
777                 file_name_ = AddName(fl_get_input(file_dlg_form_->DirBox), file_name_);
778         return file_name_;
779 }
780
781
782 // SelectDir: launches dialog and returns selected directory
783 string const FileDialog::Private::SelectDir(string const & title,
784                                          string const & path,
785                                          string const & suggested)
786 {
787         SetMask("*/");
788         // handles new path
789         bool isOk = true;
790         if (!path.empty()) {
791                 // handle case where path does not end with "/"
792                 // remerge path+suggested and check if it is a valid path
793                 if (!suggested.empty()) {
794                         string tmp = suggested;
795                         if (!suffixIs(tmp, '/'))
796                                 tmp += '/';
797                         string full_path = path;
798                         full_path += tmp;
799                         // check if this is really a directory
800                         DIR * dir = ::opendir(full_path.c_str());
801                         if (dir)
802                                 SetDirectory(full_path);
803                         else
804                                 SetDirectory(path);
805                 } else
806                         SetDirectory(path);
807                 isOk = false;
808         }
809         if (!isOk)
810                 Reread();
811
812         // checks whether dialog can be started
813         if (current_dlg_)
814                 return string();
815         current_dlg_ = this;
816
817         // runs dialog
818         SetInfoLine(string());
819         fl_set_input(file_dlg_form_->Filename, "");
820         setEnabled(file_dlg_form_->Filename, false);
821         fl_set_button(file_dlg_form_->Cancel, 0);
822         fl_set_button(file_dlg_form_->Ready, 0);
823         fl_set_focus_object(file_dlg_form_->form, file_dlg_form_->DirBox);
824         fl_deactivate_all_forms();
825         fl_show_form(file_dlg_form_->form,
826                      FL_PLACE_MOUSE | FL_FREE_SIZE, 0,
827                      title.c_str());
828
829         isOk = RunDialog();
830
831         fl_hide_form(file_dlg_form_->form);
832         fl_activate_all_forms();
833         current_dlg_ = 0;
834
835         // Returns directory or string() if no valid selection was made
836         if (!isOk)
837                 return string();
838
839         file_name_ = fl_get_input(file_dlg_form_->DirBox);
840         return file_name_;
841 }