2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright (C) 1995 Matthias Ettrich
8 * Copyright (C) 1995-1998 The LyX Team.
10 *======================================================*/
14 // $Id: filedlg.C,v 1.1 1999/09/27 18:44:37 larsbj Exp $
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 $";
27 #include "lyx_gui_misc.h" // CancelCloseCB
37 # define NAMLEN(dirent) strlen((dirent)->d_name)
39 # define dirent direct
40 # define NAMLEN(dirent) (dirent)->d_namlen
42 # include <sys/ndir.h>
52 #if TIME_WITH_SYS_TIME
53 # include <sys/time.h>
57 # include <sys/time.h>
64 extern "C" int gettimeofday(struct timeval *,struct timezone *);
65 #define remove(a) unlink(a)
69 #pragma implementation
72 #include "filetools.h"
74 #include "definitions.h"
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;
79 // *** User cache class implementation
81 // global instance (user cache root)
82 UserCache lyxUserCache = UserCache(LString(),0,0);
85 // Add: creates a new user entry
86 UserCache *UserCache::Add(uid_t ID)
89 struct passwd *pEntry;
92 if ((pEntry = getpwuid(ID)))
93 pszNewName = pEntry->pw_name;
95 pszNewName = LString() + int(ID); // We don't have int cast to LString
99 return new UserCache(pszNewName, ID, pRoot);
103 UserCache::UserCache(LString const & pszName, uid_t ID, UserCache *pRoot)
108 pNext = pRoot->pNext;
116 this->pszName = pszName;
121 UserCache::~UserCache()
123 if (pNext) delete pNext;
127 // Find: seeks user name from user ID
128 LString UserCache::Find(uid_t ID)
130 if ((!pszName.empty()) && (this->ID == ID)) return pszName;
131 if (pNext) return pNext->Find(ID);
133 return pRoot->Add(ID)->pszName;
137 // *** Group cache class implementation
139 // global instance (group cache root)
140 GroupCache lyxGroupCache = GroupCache(LString(),0,0);
142 // Add: creates a new group entry
143 GroupCache *GroupCache::Add(gid_t ID)
146 struct group *pEntry;
149 if ((pEntry = getgrgid(ID))) pszNewName = pEntry->gr_name;
151 pszNewName = LString() + int(ID); // We don't have int cast to LString
155 return new GroupCache(pszNewName, ID, pRoot);
159 GroupCache::GroupCache(LString const & pszName, gid_t ID, GroupCache *pRoot)
164 pNext = pRoot->pNext;
172 this->pszName = pszName;
177 GroupCache::~GroupCache()
179 if (pNext) delete pNext;
183 // Find: seeks group name from group ID
184 LString GroupCache::Find(gid_t ID)
186 if ((!pszName.empty()) && (this->ID == ID)) return pszName;
187 if (pNext) return pNext->Find(ID);
189 return pRoot->Add(ID)->pszName;
192 // *** LyXDirEntry internal structure implementation
194 // ldeCompProc: compares two LyXDirEntry objects content (used for qsort)
195 int LyXDirEntry::ldeCompProc(const LyXDirEntry *r1,
196 const LyXDirEntry *r2)
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());
205 // *** LyXFileDlg class implementation
208 FD_FileDlg *LyXFileDlg::pFileDlgForm = NULL;
209 LyXFileDlg *LyXFileDlg::pCurrentDlg = NULL;
212 // Reread: updates dialog list to match class directory
213 void LyXFileDlg::Reread()
217 struct dirent *pDirEntry;
218 LString File, Buffer;
219 char szMode[15], szTime[40];
223 pDirectory = opendir(pszDirectory.c_str());
225 WriteFSAlert(_("Warning! Couldn't open directory."),
227 pszDirectory = GetCWD();
228 pDirectory = opendir(pszDirectory.c_str());
231 // Clear the present namelist
233 delete [] pCurrentNames;
238 fl_hide_object(pFileDlgForm->List);
239 fl_clear_browser(pFileDlgForm->List);
240 fl_set_input(pFileDlgForm->DirBox, pszDirectory.c_str());
242 // Splits complete directory name into directories and compute depth
247 File.split(Temp, '/');
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, '/');
257 // Allocate names array
259 rewinddir(pDirectory);
260 while ((readdir(pDirectory))) ++iNumNames;
261 pCurrentNames = new LyXDirEntry[iNumNames];
263 // Parses all entries of the given subdirectory
265 time_t curTime = time(NULL);
266 rewinddir(pDirectory);
267 while ((pDirEntry = readdir(pDirectory))) {
269 bool isLink = false, isDir = false;
271 // If the pattern doesn't start with a dot, skip hidden files
272 if (!pszMask.empty() && pszMask[0] != '.' &&
273 pDirEntry->d_name[0] == '.')
277 LString fname = pDirEntry->d_name;
279 // Under all circumstances, "." and ".." are not wanted
280 if (fname == "." || fname == "..")
284 File = AddName(pszDirectory, fname);
286 fileInfo.newFile(File, true);
288 fileInfo.modeString(szMode);
289 unsigned int nlink = fileInfo.getNumberOfLinks();
290 LString user = lyxUserCache.Find(fileInfo.getUid());
291 LString group = lyxGroupCache.Find(fileInfo.getGid());
293 time_t modtime = fileInfo.getModificationTime();
294 strcpy(szTime, ctime(&modtime));
296 if (curTime > fileInfo.getModificationTime() + SIX_MONTH_SEC
297 || curTime < fileInfo.getModificationTime()
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);
310 sprintf(szHeadBuf, "%s %u %s %s %s ", szMode,
317 Buffer += pDirEntry->d_name;
318 Buffer += fileInfo.typeIndicator();
320 if ((isLink = fileInfo.isLink())) {
323 if (LyXReadLink(File,Link)) {
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?
333 fileInfo.newFile(File);
334 Buffer += fileInfo.typeIndicator();
338 // filters files according to pattern and type
339 if (fileInfo.isRegular()
341 || fileInfo.isBlock()
342 || fileInfo.isFifo()) {
343 if (!fname.regexMatch(pszMask))
345 } else if (!(isDir = fileInfo.isDir()))
348 // Note pszLsEntry is an LString!
349 pCurrentNames[iNumNames].pszLsEntry = Buffer;
352 LString temp = fname;
353 if (isDir) temp += '/';
354 pCurrentNames[iNumNames].pszName = temp;
356 // creates displayed name
357 temp = pDirEntry->d_name;
361 temp += fileInfo.typeIndicator();
363 pCurrentNames[iNumNames++].pszDisplayed = temp;
366 closedir(pDirectory);
369 qsort(pCurrentNames, iNumNames, sizeof(LyXDirEntry),
370 (int (*)(const void *, const void *))LyXDirEntry::ldeCompProc);
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());
377 fl_set_browser_topline(pFileDlgForm->List,iDepth);
378 fl_show_object(pFileDlgForm->List);
383 // SetDirectory: sets dialog current directory
384 void LyXFileDlg::SetDirectory(LString const & Path)
386 if (!pszDirectory.empty()) {
387 LString TempPath = ExpandPath(Path); // Expand ~/
388 TempPath = MakeAbsPath(TempPath, pszDirectory);
389 pszDirectory = MakeAbsPath(TempPath);
390 } else pszDirectory = MakeAbsPath(Path);
394 // SetMask: sets dialog file mask
395 void LyXFileDlg::SetMask(LString const & NewMask)
398 fl_set_input(pFileDlgForm->PatBox, pszMask.c_str());
402 // SetInfoLine: sets dialog information line
403 void LyXFileDlg::SetInfoLine(LString const & Line)
406 fl_set_object_label(pFileDlgForm->FileInfo, pszInfoLine.c_str());
410 LyXFileDlg::LyXFileDlg()
413 pszDirectory = MakeAbsPath(LString('.'));
416 // Creates form if necessary.
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);
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,
443 fl_hide_object(pFileDlgForm->User1);
444 fl_hide_object(pFileDlgForm->User2);
448 LyXFileDlg::~LyXFileDlg()
450 // frees directory entries
452 delete [] pCurrentNames;
457 // SetButton: sets file selector user button action
458 void LyXFileDlg::SetButton(int iIndex, LString const & pszName,
459 LString const & pszPath)
465 pObject = pFileDlgForm->User1;
466 pTemp = &pszUserPath1;
467 } else if (iIndex == 1) {
468 pObject = pFileDlgForm->User2;
469 pTemp = &pszUserPath2;
472 if (!pszName.empty() && !pszPath.empty()) {
473 fl_set_object_label(pObject, pszName.c_str());
474 fl_show_object(pObject);
477 fl_hide_object(pObject);
483 // GetDirectory: gets last dialog directory
484 LString LyXFileDlg::GetDirectory()
486 if (!pszDirectory.empty())
493 // RunDialog: handle dialog during file selection
494 bool LyXFileDlg::RunDialog()
496 force_cancel = false;
502 FL_OBJECT * pObject = fl_do_forms();
504 if (pObject == pFileDlgForm->Ready) {
507 } else if (pObject == pFileDlgForm->Cancel
516 // XForms objects callback (static)
517 void LyXFileDlg::FileDlgCB(FL_OBJECT *, long lArgument)
519 if (!pCurrentDlg) return;
523 case 0: // get directory
524 pCurrentDlg->SetDirectory(fl_get_input(pFileDlgForm->DirBox));
525 pCurrentDlg->Reread();
529 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
530 pCurrentDlg->Reread();
534 pCurrentDlg->HandleListHit();
538 pCurrentDlg->SetDirectory(fl_get_input(pFileDlgForm->DirBox));
539 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
540 pCurrentDlg->Reread();
544 pCurrentDlg->SetDirectory(getEnvPath("HOME"));
545 pCurrentDlg->SetMask(fl_get_input(pFileDlgForm->PatBox));
546 pCurrentDlg->Reread();
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();
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();
569 // Handle callback from list
570 void LyXFileDlg::HandleListHit()
573 int iSelect = fl_get_browser(pFileDlgForm->List);
574 if (iSelect > iDepth) {
575 SetInfoLine(pCurrentNames[iSelect - iDepth - 1].pszLsEntry);
577 SetInfoLine(LString());
582 // Callback for double click in list
583 void LyXFileDlg::DoubleClickCB(FL_OBJECT *, long)
585 if (pCurrentDlg->HandleDoubleClick())
586 // Simulate click on OK button
587 pCurrentDlg->Force(false);
591 // Handle double click from list
592 bool LyXFileDlg::HandleDoubleClick()
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('/')) {
606 fl_set_input(pFileDlgForm->Filename, pszTemp.c_str());
608 } else if (iSelect !=0) {
609 SetInfoLine(LString());
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('/'))
628 // Directory higher up
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);
646 // Handle OK button call
647 bool LyXFileDlg::HandleOK()
652 pszTemp = fl_get_input(pFileDlgForm->PatBox);
653 if (pszTemp!=pszMask) {
659 // directory was changed
660 pszTemp = fl_get_input(pFileDlgForm->DirBox);
661 if (pszTemp!=pszDirectory) {
662 SetDirectory(pszTemp);
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);
675 fl_set_input(pFileDlgForm->Filename, temp.c_str());
681 // Emulate a doubleclick
682 return HandleDoubleClick();
686 // Handle Cancel CB from WM close
687 int LyXFileDlg::CancelCB(FL_FORM *, void *)
689 // Simulate a click on the cancel button
690 pCurrentDlg->Force(true);
695 // Simulates a click on OK/Cancel
696 void LyXFileDlg::Force(bool cancel)
700 fl_set_button(pFileDlgForm->Cancel, 1);
703 fl_set_button(pFileDlgForm->Ready, 1);
705 // Start timer to break fl_do_forms loop soon
706 fl_set_timer(pFileDlgForm->timer, 0.1);
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)
716 // handles new mask and path
728 fl_select_browser_line(pFileDlgForm->List, 1);
729 fl_set_browser_topline(pFileDlgForm->List, 1);
732 // checks whether dialog can be started
733 if (pCurrentDlg) return LString();
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());
748 fl_hide_form(pFileDlgForm->FileDlg);
749 fl_activate_all_forms();
752 // Returns filename or LString() if no valid selection was made
753 if (!isOk || !fl_get_input(pFileDlgForm->Filename)[0]) return LString();
755 pszFileName = fl_get_input(pFileDlgForm->Filename);
757 if (!AbsolutePath(pszFileName)) {
758 pszFileName = AddName(fl_get_input(pFileDlgForm->DirBox),