Update for 0.51.2-pre2
[wmaker-crm.git] / WINGs / wfilepanel.c
blob6cc4fbc3ab04957129dbc9cc623e3041502e1444
2 #include "WINGsP.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <dirent.h>
8 #include <limits.h>
10 #ifndef PATH_MAX
11 #define PATH_MAX 1024
12 #endif
14 typedef struct W_FilePanel {
15 WMWindow *win;
17 WMLabel *iconLabel;
18 WMLabel *titleLabel;
20 WMFrame *line;
22 WMLabel *nameLabel;
23 WMBrowser *browser;
25 WMButton *okButton;
26 WMButton *cancelButton;
28 WMButton *homeButton;
30 WMView *accessoryView;
32 WMTextField *fileField;
34 char **fileTypes;
36 struct {
37 unsigned int canExit:1;
38 unsigned int canceled:1; /* clicked on cancel */
39 unsigned int done:1;
40 unsigned int filtered:1;
41 unsigned int canChooseFiles:1;
42 unsigned int canChooseDirectories:1;
43 unsigned int autoCompletion:1;
44 unsigned int showAllFiles:1;
45 unsigned int canFreeFileTypes:1;
46 unsigned int fileMustExist:1;
47 unsigned int panelType:1;
49 /**/
50 unsigned int ignoreTextChangeNotification:1;
51 } flags;
52 } W_FilePanel;
55 /* Type of panel */
56 #define WP_OPEN 0
57 #define WP_SAVE 1
59 #define PWIDTH 320
60 #define PHEIGHT 360
62 static void listDirectoryOnColumn(WMFilePanel *panel, int column, char *path);
63 static void browserClick();
64 static void browserDClick();
66 static void fillColumn(WMBrowser *bPtr, int column);
68 static void goHome();
70 static void buttonClick();
72 static char *getCurrentFileName(WMFilePanel *panel);
74 static void handleEvents(XEvent *event, void *data);
77 static int
78 closestListItem(WMList *list, char *text, Bool exact)
80 WMListItem *item = WMGetListItem(list, 0);
81 int i = 0;
82 int len = strlen(text);
84 if (len==0)
85 return -1;
87 while (item) {
88 if (strlen(item->text) >= len &&
89 ((exact && strcmp(item->text, text)==0) ||
90 (!exact && strncmp(item->text, text, len)==0))) {
91 return i;
93 item = item->nextPtr;
94 i++;
96 return -1;
100 static void
101 textChangedObserver(void *observerData, WMNotification *notification)
103 W_FilePanel *panel = (W_FilePanel*)observerData;
104 char *text;
105 WMList *list;
106 int col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
107 int i, textEvent = (int)WMGetNotificationClientData(notification);
108 static int running = 0;
110 if (running)
111 return;
113 running = 1;
115 if (panel->flags.ignoreTextChangeNotification)
116 return;
118 list = WMGetBrowserListInColumn(panel->browser, col);
119 if (!list)
120 return;
122 text = WMGetTextFieldText(panel->fileField);
124 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
125 i = closestListItem(list, text, False);
126 else
127 i = closestListItem(list, text, True);
129 WMSelectListItem(list, i);
130 if (i>=0 && panel->flags.autoCompletion) {
131 WMListItem *item = WMGetListItem(list, i);
132 int textLen = strlen(text), itemTextLen = strlen(item->text);
133 int visibleItems = WMWidgetHeight(list)/WMGetListItemHeight(list);
135 if (textEvent!=WMSetTextEvent || textLen<itemTextLen)
136 WMSetListPosition(list, i - visibleItems/2);
138 if (textEvent!=WMDeleteTextEvent && textLen<itemTextLen) {
139 WMRange range;
141 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
142 textLen);
143 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
144 range.position = textLen;
145 range.count = itemTextLen - textLen;
146 WMSelectTextFieldRange(panel->fileField, range);
150 free(text);
151 running = 0;
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 WMCreateEventHandler(WMWidgetView(fPtr->win), StructureNotifyMask,
182 handleEvents, fPtr);
183 WMSetWindowMinSize(fPtr->win, PWIDTH, PHEIGHT);
186 fPtr->iconLabel = WMCreateLabel(fPtr->win);
187 WMResizeWidget(fPtr->iconLabel, 64, 64);
188 WMMoveWidget(fPtr->iconLabel, 0, 0);
189 WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
190 WMSetLabelImage(fPtr->iconLabel, scrPtr->applicationIcon);
192 fPtr->titleLabel = WMCreateLabel(fPtr->win);
193 WMResizeWidget(fPtr->titleLabel, PWIDTH-64, 64);
194 WMMoveWidget(fPtr->titleLabel, 64, 0);
195 largeFont = WMBoldSystemFontOfSize(scrPtr, 24);
196 WMSetLabelFont(fPtr->titleLabel, largeFont);
197 WMReleaseFont(largeFont);
198 WMSetLabelText(fPtr->titleLabel, title);
200 fPtr->line = WMCreateFrame(fPtr->win);
201 WMMoveWidget(fPtr->line, 0, 64);
202 WMResizeWidget(fPtr->line, PWIDTH, 2);
203 WMSetFrameRelief(fPtr->line, WRGroove);
205 fPtr->browser = WMCreateBrowser(fPtr->win);
206 WMSetBrowserFillColumnProc(fPtr->browser, fillColumn);
207 WMSetBrowserAction(fPtr->browser, browserClick, fPtr);
208 WMSetBrowserDoubleAction(fPtr->browser, browserDClick, fPtr);
209 WMMoveWidget(fPtr->browser, 7, 72);
210 WMHangData(fPtr->browser, fPtr);
212 fPtr->nameLabel = WMCreateLabel(fPtr->win);
213 WMMoveWidget(fPtr->nameLabel, 7, 282);
214 WMResizeWidget(fPtr->nameLabel, 55, 14);
215 WMSetLabelText(fPtr->nameLabel, "Name:");
217 fPtr->fileField = WMCreateTextField(fPtr->win);
218 WMMoveWidget(fPtr->fileField, 60, 278);
219 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
220 WMAddNotificationObserver(textEditedObserver, fPtr,
221 WMTextDidEndEditingNotification,
222 fPtr->fileField);
223 WMAddNotificationObserver(textChangedObserver, fPtr,
224 WMTextDidChangeNotification,
225 fPtr->fileField);
227 fPtr->okButton = WMCreateCommandButton(fPtr->win);
228 WMMoveWidget(fPtr->okButton, 230, 325);
229 WMResizeWidget(fPtr->okButton, 80, 28);
230 WMSetButtonText(fPtr->okButton, "OK");
231 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
232 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
233 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
234 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
236 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
237 WMMoveWidget(fPtr->cancelButton, 140, 325);
238 WMResizeWidget(fPtr->cancelButton, 80, 28);
239 WMSetButtonText(fPtr->cancelButton, "Cancel");
240 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
242 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
243 WMMoveWidget(fPtr->homeButton, 55, 325);
244 WMResizeWidget(fPtr->homeButton, 28, 28);
245 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
246 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
247 WMSetButtonAltImage(fPtr->homeButton, scrPtr->altHomeIcon);
248 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
250 WMRealizeWidget(fPtr->win);
251 WMMapSubwidgets(fPtr->win);
253 WMSetFocusToWidget(fPtr->fileField);
254 WMSetTextFieldCursorPosition(fPtr->fileField, 0);
256 WMLoadBrowserColumnZero(fPtr->browser);
258 fPtr->flags.canChooseFiles = 1;
259 fPtr->flags.canChooseDirectories = 1;
260 fPtr->flags.autoCompletion = 1;
262 return fPtr;
266 WMOpenPanel*
267 WMGetOpenPanel(WMScreen *scrPtr)
269 WMFilePanel *panel;
271 if (scrPtr->sharedOpenPanel)
272 return scrPtr->sharedOpenPanel;
274 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
275 panel->flags.fileMustExist = 1;
276 panel->flags.panelType = WP_OPEN;
278 scrPtr->sharedOpenPanel = panel;
280 return panel;
284 WMSavePanel*
285 WMGetSavePanel(WMScreen *scrPtr)
287 WMFilePanel *panel;
289 if (scrPtr->sharedSavePanel)
290 return scrPtr->sharedSavePanel;
292 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
293 panel->flags.fileMustExist = 0;
294 panel->flags.panelType = WP_SAVE;
296 scrPtr->sharedSavePanel = panel;
298 return panel;
302 void
303 WMFreeFilePanel(WMFilePanel *panel)
305 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
306 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
308 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
309 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
311 WMRemoveNotificationObserver(panel);
312 WMUnmapWidget(panel->win);
313 WMDestroyWidget(panel->win);
314 free(panel);
319 WMRunModalFilePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
320 char *path, char *name, char **fileTypes)
322 WMScreen *scr = WMWidgetScreen(panel->win);
323 XEvent event;
325 if (name && !owner) {
326 WMSetWindowTitle(panel->win, name);
329 WMChangePanelOwner(panel->win, owner);
331 WMSetFilePanelDirectory(panel, path);
333 panel->flags.done = 0;
334 switch(panel->flags.panelType) {
335 case WP_OPEN:
336 if (fileTypes)
337 panel->flags.filtered = 1;
338 panel->fileTypes = fileTypes;
339 if (name == NULL)
340 name = "Open";
341 break;
342 case WP_SAVE:
343 panel->fileTypes = NULL;
344 panel->flags.filtered = 0;
345 if (name == NULL)
346 name = "Save";
347 break;
348 default:
349 break;
352 WMSetLabelText(panel->titleLabel, name);
354 scr->modalView = W_VIEW(panel->win);
355 WMMapWidget(panel->win);
357 WMMoveWidget(panel->win,
358 (scr->rootView->size.width - WMWidgetWidth(panel->win))/2,
359 (scr->rootView->size.height - WMWidgetHeight(panel->win))/2);
361 scr->modal = 1;
362 while (!panel->flags.done) {
363 WMNextEvent(scr->display, &event);
364 WMHandleEvent(&event);
366 scr->modal = 0;
368 /* Must withdraw window because the next time we map
369 * it, it might have a different transient owner.
371 WMCloseWindow(panel->win);
373 return (panel->flags.canceled ? False : True);
379 void
380 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
382 WMList *list;
383 WMListItem *item;
384 int col;
385 char *rest;
387 rest = WMSetBrowserPath(panel->browser, path);
388 if (strcmp(path, "/")==0)
389 rest = NULL;
391 col = WMGetBrowserSelectedColumn(panel->browser);
392 list = WMGetBrowserListInColumn(panel->browser, col);
393 if (list && (item = WMGetListSelectedItem(list))) {
394 if (item->isBranch) {
395 WMSetTextFieldText(panel->fileField, rest);
396 } else {
397 WMSetTextFieldText(panel->fileField, item->text);
399 } else {
400 WMSetTextFieldText(panel->fileField, rest);
405 void
406 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
408 panel->flags.canChooseDirectories = flag;
411 void
412 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
414 panel->flags.canChooseFiles = flag;
418 void
419 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
421 panel->flags.autoCompletion = flag;
425 char*
426 WMGetFilePanelFileName(WMFilePanel *panel)
428 return getCurrentFileName(panel);
432 void
433 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
435 WMView *v;
437 panel->accessoryView = view;
439 v = WMWidgetView(panel->win);
441 W_ReparentView(view, v, 0, 0);
443 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
447 WMView*
448 WMGetFilePanelAccessoryView(WMFilePanel *panel)
450 return panel->accessoryView;
454 static char*
455 get_name_from_path(char *path)
457 int size;
459 assert(path!=NULL);
461 size = strlen(path);
463 /* remove trailing / */
464 while (size > 0 && path[size-1]=='/')
465 size--;
466 /* directory was root */
467 if (size == 0)
468 return wstrdup("/");
470 while (size > 0 && path[size-1] != '/')
471 size--;
473 return wstrdup(&(path[size]));
477 static int
478 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
480 return True;
484 static void
485 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
487 WMBrowser *bPtr = panel->browser;
488 struct dirent *dentry;
489 DIR *dir;
490 struct stat stat_buf;
491 char pbuf[PATH_MAX+16];
493 assert(column >= 0);
494 assert(path != NULL);
496 /* put directory name in the title */
497 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
499 dir = opendir(path);
501 if (!dir) {
502 #ifdef VERBOSE
503 printf("WINGs: could not open directory %s\n", path);
504 #endif
505 return;
508 /* list contents in the column */
509 while ((dentry = readdir(dir))) {
510 if (strcmp(dentry->d_name, ".")==0 ||
511 strcmp(dentry->d_name, "..")==0)
512 continue;
514 strcpy(pbuf, path);
515 if (strcmp(path, "/")!=0)
516 strcat(pbuf, "/");
517 strcat(pbuf, dentry->d_name);
519 if (stat(pbuf, &stat_buf)!=0) {
520 #ifdef VERBOSE
521 printf("WINGs: could not stat %s\n", pbuf);
522 #endif
523 continue;
524 } else {
525 int isDirectory;
527 isDirectory = S_ISDIR(stat_buf.st_mode);
529 if (filterFileName(panel, dentry->d_name, isDirectory))
530 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
531 isDirectory);
535 closedir(dir);
539 static void
540 fillColumn(WMBrowser *bPtr, int column)
542 char *path;
543 WMFilePanel *panel;
545 if (column > 0) {
546 path = WMGetBrowserPathToColumn(bPtr, column-1);
547 } else {
548 path = wstrdup("/");
551 panel = WMGetHangedData(bPtr);
552 listDirectoryOnColumn(panel, column, path);
553 free(path);
557 static void
558 browserDClick(WMBrowser *bPtr, WMFilePanel *panel)
560 WMPerformButtonClick(panel->okButton);
563 static void
564 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
566 int col = WMGetBrowserSelectedColumn(bPtr);
567 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
569 panel->flags.ignoreTextChangeNotification = 1;
571 if (!item || item->isBranch)
572 WMSetTextFieldText(panel->fileField, NULL);
573 else {
574 WMSetTextFieldText(panel->fileField, item->text);
577 panel->flags.ignoreTextChangeNotification = 0;
581 static void
582 goHome(WMButton *bPtr, WMFilePanel *panel)
584 char *home;
586 /* home is statically allocated. Don't free it! */
587 home = wgethomedir();
588 if (!home)
589 return;
591 WMSetFilePanelDirectory(panel, home);
595 static void
596 handleEvents(XEvent *event, void *data)
598 W_FilePanel *pPtr = (W_FilePanel*)data;
599 W_View *view = WMWidgetView(pPtr->win);
601 if (event->type == ConfigureNotify) {
602 if (event->xconfigure.width != view->size.width
603 || event->xconfigure.height != view->size.height) {
604 unsigned int newWidth = event->xconfigure.width;
605 unsigned int newHeight = event->xconfigure.height;
606 int newColumnCount;
608 W_ResizeView(view, newWidth, newHeight);
609 WMResizeWidget(pPtr->line, newWidth, 2);
610 WMResizeWidget(pPtr->browser, newWidth-14,
611 newHeight-(PHEIGHT-200));
612 WMResizeWidget(pPtr->fileField, newWidth-60-10, 24);
613 WMMoveWidget(pPtr->nameLabel, 7, newHeight-(PHEIGHT-282));
614 WMMoveWidget(pPtr->fileField, 60, newHeight-(PHEIGHT-278));
615 WMMoveWidget(pPtr->okButton, newWidth-(PWIDTH-230),
616 newHeight-(PHEIGHT-325));
617 WMMoveWidget(pPtr->cancelButton, newWidth-(PWIDTH-140),
618 newHeight-(PHEIGHT-325));
619 WMMoveWidget(pPtr->homeButton, 55, newHeight-(PHEIGHT-325));
621 newColumnCount = (newWidth - 14) / 140;
622 WMSetBrowserMaxVisibleColumns(pPtr->browser, newColumnCount);
628 static char*
629 getCurrentFileName(WMFilePanel *panel)
631 char *path;
632 char *file;
633 char *tmp;
634 int len;
636 path = WMGetBrowserPath(panel->browser);
638 len = strlen(path);
639 if (path[len-1]=='/') {
640 file = WMGetTextFieldText(panel->fileField);
641 tmp = wmalloc(strlen(path)+strlen(file)+8);
642 if (file[0]!='/') {
643 strcpy(tmp, path);
644 strcat(tmp, file);
645 } else
646 strcpy(tmp, file);
648 free(file);
649 free(path);
650 return tmp;
651 } else {
652 return path;
658 static Bool
659 validOpenFile(WMFilePanel *panel)
661 WMListItem *item;
662 int col, haveFile = 0;
663 char *file = WMGetTextFieldText(panel->fileField);
665 if (file[0] != '\0')
666 haveFile = 1;
667 free(file);
669 col = WMGetBrowserSelectedColumn(panel->browser);
670 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
671 if (item) {
672 if (item->isBranch && !panel->flags.canChooseDirectories && !haveFile)
673 return False;
674 else if (!item->isBranch && !panel->flags.canChooseFiles)
675 return False;
676 else
677 return True;
678 } else {
679 /* we compute for / here */
680 if (!panel->flags.canChooseDirectories && !haveFile)
681 return False;
682 else
683 return True;
685 return True;
690 static void
691 buttonClick(WMButton *bPtr, WMFilePanel *panel)
693 WMRange range;
695 if (bPtr == panel->okButton) {
696 if (!validOpenFile(panel))
697 return;
698 if (panel->flags.fileMustExist) {
699 char *file;
701 file = getCurrentFileName(panel);
702 if (access(file, F_OK)!=0) {
703 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
704 "Error", "File does not exist.",
705 "Ok", NULL, NULL);
706 free(file);
707 return;
709 free(file);
711 panel->flags.canceled = 0;
712 } else
713 panel->flags.canceled = 1;
715 range.count = range.position = 0;
716 WMSelectTextFieldRange(panel->fileField, range);
717 panel->flags.done = 1;