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