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