Latest fixes for released 0.51.0
[wmaker-crm.git] / WINGs / wfilepanel.c
blobda99ef9b09b31de6b16eab229fb1654706945dc9
5 #include "WINGsP.h"
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <dirent.h>
11 #include <limits.h>
13 #ifndef PATH_MAX
14 #define PATH_MAX 1024
15 #endif
17 typedef struct W_FilePanel {
18 WMWindow *win;
20 WMLabel *iconLabel;
21 WMLabel *titleLabel;
23 WMFrame *line;
25 WMLabel *nameLabel;
26 WMBrowser *browser;
28 WMButton *okButton;
29 WMButton *cancelButton;
31 WMButton *homeButton;
33 WMView *accessoryView;
35 WMTextField *fileField;
37 char **fileTypes;
39 struct {
40 unsigned int canExit:1;
41 unsigned int canceled:1; /* clicked on cancel */
42 unsigned int done:1;
43 unsigned int filtered:1;
44 unsigned int canChooseFiles:1;
45 unsigned int canChooseDirectories:1;
46 unsigned int autoCompletion:1;
47 unsigned int showAllFiles:1;
48 unsigned int canFreeFileTypes:1;
49 unsigned int fileMustExist:1;
50 } flags;
51 } W_FilePanel;
54 #define PWIDTH 320
55 #define PHEIGHT 360
57 static void listDirectoryOnColumn(WMFilePanel *panel, int column, char *path);
58 static void browserClick();
60 static void fillColumn(WMBrowser *bPtr, int column);
62 static void goHome();
64 static void buttonClick();
66 static char *getCurrentFileName(WMFilePanel *panel);
70 static int
71 closestListItem(WMList *list, char *text, Bool exact)
73 WMListItem *item = WMGetListItem(list, 0);
74 int i = 0;
75 int len = strlen(text);
77 if (len==0)
78 return -1;
80 while (item) {
81 if (strlen(item->text) >= len &&
82 ((exact && strcmp(item->text, text)==0) ||
83 (!exact && strncmp(item->text, text, len)==0))) {
84 return i;
86 item = item->nextPtr;
87 i++;
89 return -1;
93 static void
94 textChangedObserver(void *observerData, WMNotification *notification)
96 W_FilePanel *panel = (W_FilePanel*)observerData;
97 char *text, *text1;
98 WMList *list;
99 int col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
100 int i, j, textEvent = (int)WMGetNotificationClientData(notification);
102 list = WMGetBrowserListInColumn(panel->browser, col);
103 if (!list)
104 return;
105 text = WMGetTextFieldText(panel->fileField);
107 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
108 i = closestListItem(list, text, False);
109 else
110 i = closestListItem(list, text, True);
112 WMSelectListItem(list, i);
113 if (i>=0) {
114 WMListItem *item = WMGetListItem(list, i);
115 int textLen = strlen(text), itemTextLen = strlen(item->text);
117 WMSetListPosition(list, i);
119 if (panel->flags.autoCompletion) {
120 /* Be careful with the '<' condition below. Changing it can drop
121 * you into an endless loop. We call here, inside a function
122 * called from a notification observer, to a function that
123 * generates notification events. So make sure we do not call it
124 * recursively for ever.
125 * '<' means we only update text field if it is really not yet
126 * complete. -Dan
128 if (textLen < itemTextLen && textEvent!=WMDeleteTextEvent) {
129 WMRange range;
131 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
132 textLen);
133 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
134 range.position = textLen;
135 range.count = itemTextLen - textLen;
136 WMSelectTextFieldRange(panel->fileField, range);
139 text1 = WMGetTextFieldText(panel->fileField);
140 j = closestListItem(list, text1, True);
141 if (i != j) {
142 WMSelectListItem(list, j);
143 if (j>=0) {
144 WMSetListPosition(list, j);
147 free(text1);
151 free(text);
155 static void
156 textEditedObserver(void *observerData, WMNotification *notification)
158 W_FilePanel *panel = (W_FilePanel*)observerData;
160 if ((int)WMGetNotificationClientData(notification)==WMReturnTextMovement) {
161 WMPerformButtonClick(panel->okButton);
167 static WMFilePanel*
168 makeFilePanel(WMScreen *scrPtr, char *name, char *title)
170 WMFilePanel *fPtr;
171 WMFont *largeFont;
173 fPtr = wmalloc(sizeof(WMFilePanel));
174 memset(fPtr, 0, sizeof(WMFilePanel));
176 fPtr->win = WMCreateWindowWithStyle(scrPtr, name, WMTitledWindowMask
177 |WMResizableWindowMask);
178 WMResizeWidget(fPtr->win, PWIDTH, PHEIGHT);
179 WMSetWindowTitle(fPtr->win, "");
181 fPtr->iconLabel = WMCreateLabel(fPtr->win);
182 WMResizeWidget(fPtr->iconLabel, 64, 64);
183 WMMoveWidget(fPtr->iconLabel, 0, 0);
184 WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
185 WMSetLabelImage(fPtr->iconLabel, scrPtr->applicationIcon);
187 fPtr->titleLabel = WMCreateLabel(fPtr->win);
188 WMResizeWidget(fPtr->titleLabel, PWIDTH-64, 64);
189 WMMoveWidget(fPtr->titleLabel, 64, 0);
190 largeFont = WMBoldSystemFontOfSize(scrPtr, 24);
191 WMSetLabelFont(fPtr->titleLabel, largeFont);
192 WMReleaseFont(largeFont);
193 WMSetLabelText(fPtr->titleLabel, title);
195 fPtr->line = WMCreateFrame(fPtr->win);
196 WMMoveWidget(fPtr->line, 0, 64);
197 WMResizeWidget(fPtr->line, PWIDTH, 2);
198 WMSetFrameRelief(fPtr->line, WRGroove);
200 fPtr->browser = WMCreateBrowser(fPtr->win);
201 WMSetBrowserFillColumnProc(fPtr->browser, fillColumn);
202 WMSetBrowserAction(fPtr->browser, browserClick, fPtr);
203 WMMoveWidget(fPtr->browser, 7, 72);
204 WMHangData(fPtr->browser, fPtr);
206 fPtr->nameLabel = WMCreateLabel(fPtr->win);
207 WMMoveWidget(fPtr->nameLabel, 7, 282);
208 WMResizeWidget(fPtr->nameLabel, 55, 14);
209 WMSetLabelText(fPtr->nameLabel, "Name:");
211 fPtr->fileField = WMCreateTextField(fPtr->win);
212 WMMoveWidget(fPtr->fileField, 60, 278);
213 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
214 WMAddNotificationObserver(textEditedObserver, fPtr,
215 WMTextDidEndEditingNotification,
216 fPtr->fileField);
217 WMAddNotificationObserver(textChangedObserver, fPtr,
218 WMTextDidChangeNotification,
219 fPtr->fileField);
221 fPtr->okButton = WMCreateCommandButton(fPtr->win);
222 WMMoveWidget(fPtr->okButton, 230, 325);
223 WMResizeWidget(fPtr->okButton, 80, 28);
224 WMSetButtonText(fPtr->okButton, "OK");
225 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
226 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
227 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
228 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
230 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
231 WMMoveWidget(fPtr->cancelButton, 140, 325);
232 WMResizeWidget(fPtr->cancelButton, 80, 28);
233 WMSetButtonText(fPtr->cancelButton, "Cancel");
234 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
236 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
237 WMMoveWidget(fPtr->homeButton, 55, 325);
238 WMResizeWidget(fPtr->homeButton, 28, 28);
239 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
240 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
241 WMSetButtonAltImage(fPtr->homeButton, scrPtr->homeAltIcon);
242 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
244 WMRealizeWidget(fPtr->win);
245 WMMapSubwidgets(fPtr->win);
247 WMLoadBrowserColumnZero(fPtr->browser);
249 fPtr->flags.canChooseFiles = 1;
250 fPtr->flags.canChooseDirectories = 1;
251 fPtr->flags.autoCompletion = 1;
253 return fPtr;
257 WMOpenPanel*
258 WMGetOpenPanel(WMScreen *scrPtr)
260 WMFilePanel *panel;
262 if (scrPtr->sharedOpenPanel)
263 return scrPtr->sharedOpenPanel;
265 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
266 panel->flags.fileMustExist = 1;
268 scrPtr->sharedOpenPanel = panel;
270 return panel;
274 WMSavePanel*
275 WMGetSavePanel(WMScreen *scrPtr)
277 WMFilePanel *panel;
279 if (scrPtr->sharedSavePanel)
280 return scrPtr->sharedSavePanel;
282 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
283 panel->flags.fileMustExist = 0;
285 scrPtr->sharedSavePanel = panel;
287 return panel;
291 void
292 WMFreeFilePanel(WMFilePanel *panel)
294 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
295 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
297 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
298 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
300 WMRemoveNotificationObserver(panel);
301 WMUnmapWidget(panel->win);
302 WMDestroyWidget(panel->win);
303 free(panel);
308 WMRunModalSavePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
309 char *path, char *name)
311 WMScreen *scr = WMWidgetScreen(panel->win);
312 XEvent event;
314 WMChangePanelOwner(panel->win, owner);
316 WMSetFilePanelDirectory(panel, path);
318 panel->flags.done = 0;
319 panel->fileTypes = NULL;
321 panel->flags.filtered = 0;
323 WMMapWidget(panel->win);
325 while (!panel->flags.done) {
326 WMNextEvent(scr->display, &event);
327 WMHandleEvent(&event);
330 /* Must withdraw window because the next time we map
331 * it, it might have a different transient owner.
333 WMCloseWindow(panel->win);
335 return (panel->flags.canceled ? False : True);
341 WMRunModalOpenPanelForDirectory(WMFilePanel *panel, WMWindow *owner,
342 char *path, char *name, char **fileTypes)
344 WMScreen *scr = WMWidgetScreen(panel->win);
345 XEvent event;
347 WMChangePanelOwner(panel->win, owner);
349 WMSetFilePanelDirectory(panel, path);
351 panel->flags.done = 0;
353 if (fileTypes)
354 panel->flags.filtered = 1;
355 panel->fileTypes = fileTypes;
357 WMMapWidget(panel->win);
359 while (!panel->flags.done) {
360 WMNextEvent(scr->display, &event);
361 WMHandleEvent(&event);
364 WMCloseWindow(panel->win);
366 return (panel->flags.canceled ? False : True);
370 void
371 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
373 WMList *list;
374 WMListItem *item;
375 int col;
377 WMSetBrowserPath(panel->browser, path);
378 col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
379 list = WMGetBrowserListInColumn(panel->browser, col);
380 if (list && (item = WMGetListSelectedItem(list))) {
381 if (item->isBranch) {
382 WMSetTextFieldText(panel->fileField, NULL);
383 } else {
384 WMSetTextFieldText(panel->fileField, item->text);
386 } else {
387 WMSetTextFieldText(panel->fileField, path);
392 void
393 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
395 panel->flags.canChooseDirectories = flag;
398 void
399 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
401 panel->flags.canChooseFiles = flag;
405 void
406 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
408 panel->flags.autoCompletion = flag;
412 char*
413 WMGetFilePanelFileName(WMFilePanel *panel)
415 return getCurrentFileName(panel);
419 void
420 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
422 WMView *v;
424 panel->accessoryView = view;
426 v = WMWidgetView(panel->win);
428 W_ReparentView(view, v);
430 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
434 WMView*
435 WMGetFilePanelAccessoryView(WMFilePanel *panel)
437 return panel->accessoryView;
441 static char*
442 get_name_from_path(char *path)
444 int size;
446 assert(path!=NULL);
448 size = strlen(path);
450 /* remove trailing / */
451 while (size > 0 && path[size-1]=='/')
452 size--;
453 /* directory was root */
454 if (size == 0)
455 return wstrdup("/");
457 while (size > 0 && path[size-1] != '/')
458 size--;
460 return wstrdup(&(path[size]));
464 static int
465 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
467 return True;
471 static void
472 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
474 WMBrowser *bPtr = panel->browser;
475 struct dirent *dentry;
476 DIR *dir;
477 struct stat stat_buf;
478 char pbuf[PATH_MAX+16];
480 assert(column >= 0);
481 assert(path != NULL);
483 /* put directory name in the title */
484 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
486 dir = opendir(path);
488 if (!dir) {
489 #ifdef VERBOSE
490 printf("WINGs: could not open directory %s\n", path);
491 #endif
492 return;
495 /* list contents in the column */
496 while ((dentry = readdir(dir))) {
497 if (strcmp(dentry->d_name, ".")==0 ||
498 strcmp(dentry->d_name, "..")==0)
499 continue;
501 strcpy(pbuf, path);
502 if (strcmp(path, "/")!=0)
503 strcat(pbuf, "/");
504 strcat(pbuf, dentry->d_name);
506 if (stat(pbuf, &stat_buf)!=0) {
507 #ifdef VERBOSE
508 printf("WINGs: could not stat %s\n", pbuf);
509 #endif
510 continue;
511 } else {
512 int isDirectory;
514 isDirectory = S_ISDIR(stat_buf.st_mode);
516 if (filterFileName(panel, dentry->d_name, isDirectory))
517 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
518 isDirectory);
522 closedir(dir);
526 static void
527 fillColumn(WMBrowser *bPtr, int column)
529 char *path;
530 WMFilePanel *panel;
532 if (column > 0) {
533 path = WMGetBrowserPathToColumn(bPtr, column-1);
534 } else {
535 path = wstrdup("/");
538 panel = WMGetHangedData(bPtr);
539 listDirectoryOnColumn(panel, column, path);
540 free(path);
545 static void
546 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
548 int col = WMGetBrowserSelectedColumn(bPtr);
549 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
551 if (!item || item->isBranch)
552 WMSetTextFieldText(panel->fileField, NULL);
553 else {
554 WMSetTextFieldText(panel->fileField, item->text);
559 static void
560 goHome(WMButton *bPtr, WMFilePanel *panel)
562 char *home;
564 /* home is statically allocated. Don't free it! */
565 home = wgethomedir();
566 if (!home)
567 return;
569 WMSetFilePanelDirectory(panel, home);
573 static char*
574 getCurrentFileName(WMFilePanel *panel)
576 char *path;
577 char *file;
578 char *tmp;
579 int len;
581 path = WMGetBrowserPath(panel->browser);
583 len = strlen(path);
584 if (path[len-1]=='/') {
585 file = WMGetTextFieldText(panel->fileField);
586 tmp = wmalloc(strlen(path)+strlen(file)+8);
587 if (file[0]!='/') {
588 strcpy(tmp, path);
589 strcat(tmp, file);
590 } else
591 strcpy(tmp, file);
593 free(file);
594 free(path);
595 return tmp;
596 } else {
597 return path;
603 static Bool
604 validOpenFile(WMFilePanel *panel)
606 WMListItem *item;
607 int col;
609 /*col = WMGetBrowserSelectedColumn(panel->browser);*/
610 col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
611 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
612 if (item) {
613 if (item->isBranch && !panel->flags.canChooseDirectories)
614 return False;
615 else if (!item->isBranch && !panel->flags.canChooseFiles)
616 return False;
617 else
618 return True;
620 if (!panel->flags.canChooseDirectories)
621 return False;
622 else
623 return True;
628 static void
629 buttonClick(WMButton *bPtr, WMFilePanel *panel)
631 if (bPtr == panel->okButton) {
632 if (panel->flags.fileMustExist) {
633 char *file;
634 if (!validOpenFile(panel))
635 return;
637 file = getCurrentFileName(panel);
638 if (access(file, F_OK)!=0) {
639 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
640 "Error", "File does not exist.",
641 "Ok", NULL, NULL);
642 free(file);
643 return;
645 free(file);
647 panel->flags.canceled = 0;
648 } else
649 panel->flags.canceled = 1;
651 panel->flags.done = 1;