started Appearance update in WPrefs
[wmaker-crm.git] / WINGs / wfilepanel.c
blob7c45e9ff382ea2ca3fa4822a10ea58a75c1fb610
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(WMBrowserDelegate *self, WMBrowser *bPtr, int column,
67 WMList *list);
69 static void goHome();
71 static void buttonClick();
73 static char *getCurrentFileName(WMFilePanel *panel);
75 static void handleEvents(XEvent *event, void *data);
79 static WMBrowserDelegate browserDelegate = {
80 NULL, /* data */
81 fillColumn, /* createRowsForColumn */
82 NULL, /* titleOfColumn */
83 NULL, /* didScroll */
84 NULL /* willScroll */
88 static int
89 closestListItem(WMList *list, char *text, Bool exact)
91 WMListItem *item = WMGetListItem(list, 0);
92 int i = 0;
93 int len = strlen(text);
95 if (len==0)
96 return -1;
98 while (item) {
99 if (strlen(item->text) >= len &&
100 ((exact && strcmp(item->text, text)==0) ||
101 (!exact && strncmp(item->text, text, len)==0))) {
102 return i;
104 item = item->nextPtr;
105 i++;
107 return -1;
111 static void
112 textChangedObserver(void *observerData, WMNotification *notification)
114 W_FilePanel *panel = (W_FilePanel*)observerData;
115 char *text;
116 WMList *list;
117 int col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
118 int i, textEvent;
120 if (panel->flags.ignoreTextChangeNotification)
121 return;
123 if (!(list = WMGetBrowserListInColumn(panel->browser, col)))
124 return;
126 text = WMGetTextFieldText(panel->fileField);
127 textEvent = (int)WMGetNotificationClientData(notification);
129 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
130 i = closestListItem(list, text, False);
131 else
132 i = closestListItem(list, text, True);
134 WMSelectListItem(list, i);
135 if (i>=0 && panel->flags.autoCompletion) {
136 WMListItem *item = WMGetListItem(list, i);
137 int textLen = strlen(text), itemTextLen = strlen(item->text);
138 int visibleItems = WMWidgetHeight(list)/WMGetListItemHeight(list);
140 WMSetListPosition(list, i - visibleItems/2);
142 if (textEvent!=WMDeleteTextEvent) {
143 WMRange range;
145 panel->flags.ignoreTextChangeNotification = 1;
146 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
147 textLen);
148 panel->flags.ignoreTextChangeNotification = 0;
150 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
151 range.position = textLen;
152 range.count = itemTextLen - textLen;
153 WMSelectTextFieldRange(panel->fileField, range);
157 free(text);
161 static void
162 textEditedObserver(void *observerData, WMNotification *notification)
164 W_FilePanel *panel = (W_FilePanel*)observerData;
166 if ((int)WMGetNotificationClientData(notification)==WMReturnTextMovement) {
167 WMPerformButtonClick(panel->okButton);
173 static WMFilePanel*
174 makeFilePanel(WMScreen *scrPtr, char *name, char *title)
176 WMFilePanel *fPtr;
177 WMFont *largeFont;
179 fPtr = wmalloc(sizeof(WMFilePanel));
180 memset(fPtr, 0, sizeof(WMFilePanel));
182 fPtr->win = WMCreateWindowWithStyle(scrPtr, name, WMTitledWindowMask
183 |WMResizableWindowMask);
184 WMResizeWidget(fPtr->win, PWIDTH, PHEIGHT);
185 WMSetWindowTitle(fPtr->win, "");
187 WMCreateEventHandler(WMWidgetView(fPtr->win), StructureNotifyMask,
188 handleEvents, fPtr);
189 WMSetWindowMinSize(fPtr->win, PWIDTH, PHEIGHT);
192 fPtr->iconLabel = WMCreateLabel(fPtr->win);
193 WMResizeWidget(fPtr->iconLabel, 64, 64);
194 WMMoveWidget(fPtr->iconLabel, 0, 0);
195 WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
196 WMSetLabelImage(fPtr->iconLabel, scrPtr->applicationIcon);
198 fPtr->titleLabel = WMCreateLabel(fPtr->win);
199 WMResizeWidget(fPtr->titleLabel, PWIDTH-64, 64);
200 WMMoveWidget(fPtr->titleLabel, 64, 0);
201 largeFont = WMBoldSystemFontOfSize(scrPtr, 24);
202 WMSetLabelFont(fPtr->titleLabel, largeFont);
203 WMReleaseFont(largeFont);
204 WMSetLabelText(fPtr->titleLabel, title);
206 fPtr->line = WMCreateFrame(fPtr->win);
207 WMMoveWidget(fPtr->line, 0, 64);
208 WMResizeWidget(fPtr->line, PWIDTH, 2);
209 WMSetFrameRelief(fPtr->line, WRGroove);
211 fPtr->browser = WMCreateBrowser(fPtr->win);
212 WMSetBrowserDelegate(fPtr->browser, &browserDelegate);
213 WMSetBrowserAction(fPtr->browser, browserClick, fPtr);
214 WMSetBrowserDoubleAction(fPtr->browser, browserDClick, fPtr);
215 WMMoveWidget(fPtr->browser, 7, 72);
216 WMHangData(fPtr->browser, fPtr);
218 fPtr->nameLabel = WMCreateLabel(fPtr->win);
219 WMMoveWidget(fPtr->nameLabel, 7, 282);
220 WMResizeWidget(fPtr->nameLabel, 55, 14);
221 WMSetLabelText(fPtr->nameLabel, "Name:");
223 fPtr->fileField = WMCreateTextField(fPtr->win);
224 WMMoveWidget(fPtr->fileField, 60, 278);
225 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
226 WMAddNotificationObserver(textEditedObserver, fPtr,
227 WMTextDidEndEditingNotification,
228 fPtr->fileField);
229 WMAddNotificationObserver(textChangedObserver, fPtr,
230 WMTextDidChangeNotification,
231 fPtr->fileField);
233 fPtr->okButton = WMCreateCommandButton(fPtr->win);
234 WMMoveWidget(fPtr->okButton, 230, 325);
235 WMResizeWidget(fPtr->okButton, 80, 28);
236 WMSetButtonText(fPtr->okButton, "OK");
237 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
238 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
239 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
240 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
242 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
243 WMMoveWidget(fPtr->cancelButton, 140, 325);
244 WMResizeWidget(fPtr->cancelButton, 80, 28);
245 WMSetButtonText(fPtr->cancelButton, "Cancel");
246 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
248 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
249 WMMoveWidget(fPtr->homeButton, 55, 325);
250 WMResizeWidget(fPtr->homeButton, 28, 28);
251 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
252 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
253 WMSetButtonAltImage(fPtr->homeButton, scrPtr->altHomeIcon);
254 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
256 WMRealizeWidget(fPtr->win);
257 WMMapSubwidgets(fPtr->win);
259 WMSetFocusToWidget(fPtr->fileField);
260 WMSetTextFieldCursorPosition(fPtr->fileField, 0);
262 WMLoadBrowserColumnZero(fPtr->browser);
264 fPtr->flags.canChooseFiles = 1;
265 fPtr->flags.canChooseDirectories = 1;
266 fPtr->flags.autoCompletion = 1;
268 return fPtr;
272 WMOpenPanel*
273 WMGetOpenPanel(WMScreen *scrPtr)
275 WMFilePanel *panel;
277 if (scrPtr->sharedOpenPanel)
278 return scrPtr->sharedOpenPanel;
280 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
281 panel->flags.fileMustExist = 1;
282 panel->flags.panelType = WP_OPEN;
284 scrPtr->sharedOpenPanel = panel;
286 return panel;
290 WMSavePanel*
291 WMGetSavePanel(WMScreen *scrPtr)
293 WMFilePanel *panel;
295 if (scrPtr->sharedSavePanel)
296 return scrPtr->sharedSavePanel;
298 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
299 panel->flags.fileMustExist = 0;
300 panel->flags.panelType = WP_SAVE;
302 scrPtr->sharedSavePanel = panel;
304 return panel;
308 void
309 WMFreeFilePanel(WMFilePanel *panel)
311 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
312 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
314 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
315 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
317 WMRemoveNotificationObserver(panel);
318 WMUnmapWidget(panel->win);
319 WMDestroyWidget(panel->win);
320 free(panel);
325 WMRunModalFilePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
326 char *path, char *name, char **fileTypes)
328 WMScreen *scr = WMWidgetScreen(panel->win);
329 XEvent event;
331 if (name && !owner) {
332 WMSetWindowTitle(panel->win, name);
335 WMChangePanelOwner(panel->win, owner);
337 WMSetFilePanelDirectory(panel, path);
339 panel->flags.done = 0;
340 switch(panel->flags.panelType) {
341 case WP_OPEN:
342 if (fileTypes)
343 panel->flags.filtered = 1;
344 panel->fileTypes = fileTypes;
345 if (name == NULL)
346 name = "Open";
347 break;
348 case WP_SAVE:
349 panel->fileTypes = NULL;
350 panel->flags.filtered = 0;
351 if (name == NULL)
352 name = "Save";
353 break;
354 default:
355 break;
358 WMSetWindowUPosition(panel->win,
359 (scr->rootView->size.width - WMWidgetWidth(panel->win))/2,
360 (scr->rootView->size.height - WMWidgetHeight(panel->win))/2);
361 WMSetLabelText(panel->titleLabel, name);
363 scr->modalView = W_VIEW(panel->win);
364 WMMapWidget(panel->win);
366 scr->modal = 1;
367 while (!panel->flags.done) {
368 WMNextEvent(scr->display, &event);
369 WMHandleEvent(&event);
371 scr->modal = 0;
373 /* Must withdraw window because the next time we map
374 * it, it might have a different transient owner.
376 WMCloseWindow(panel->win);
378 return (panel->flags.canceled ? False : True);
384 void
385 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
387 WMList *list;
388 WMListItem *item;
389 int col;
390 char *rest;
392 rest = WMSetBrowserPath(panel->browser, path);
393 if (strcmp(path, "/")==0)
394 rest = NULL;
396 col = WMGetBrowserSelectedColumn(panel->browser);
397 list = WMGetBrowserListInColumn(panel->browser, col);
398 if (list && (item = WMGetListSelectedItem(list))) {
399 if (item->isBranch) {
400 WMSetTextFieldText(panel->fileField, rest);
401 } else {
402 WMSetTextFieldText(panel->fileField, item->text);
404 } else {
405 WMSetTextFieldText(panel->fileField, rest);
410 void
411 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
413 panel->flags.canChooseDirectories = flag;
416 void
417 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
419 panel->flags.canChooseFiles = flag;
423 void
424 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
426 panel->flags.autoCompletion = flag;
430 char*
431 WMGetFilePanelFileName(WMFilePanel *panel)
433 return getCurrentFileName(panel);
437 void
438 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
440 WMView *v;
442 panel->accessoryView = view;
444 v = WMWidgetView(panel->win);
446 W_ReparentView(view, v, 0, 0);
448 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
452 WMView*
453 WMGetFilePanelAccessoryView(WMFilePanel *panel)
455 return panel->accessoryView;
459 static char*
460 get_name_from_path(char *path)
462 int size;
464 assert(path!=NULL);
466 size = strlen(path);
468 /* remove trailing / */
469 while (size > 0 && path[size-1]=='/')
470 size--;
471 /* directory was root */
472 if (size == 0)
473 return wstrdup("/");
475 while (size > 0 && path[size-1] != '/')
476 size--;
478 return wstrdup(&(path[size]));
482 static int
483 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
485 return True;
489 static void
490 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
492 WMBrowser *bPtr = panel->browser;
493 struct dirent *dentry;
494 DIR *dir;
495 struct stat stat_buf;
496 char pbuf[PATH_MAX+16];
498 assert(column >= 0);
499 assert(path != NULL);
501 /* put directory name in the title */
502 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
504 dir = opendir(path);
506 if (!dir) {
507 #ifdef VERBOSE
508 printf("WINGs: could not open directory %s\n", path);
509 #endif
510 return;
513 /* list contents in the column */
514 while ((dentry = readdir(dir))) {
515 if (strcmp(dentry->d_name, ".")==0 ||
516 strcmp(dentry->d_name, "..")==0)
517 continue;
519 strcpy(pbuf, path);
520 if (strcmp(path, "/")!=0)
521 strcat(pbuf, "/");
522 strcat(pbuf, dentry->d_name);
524 if (stat(pbuf, &stat_buf)!=0) {
525 #ifdef VERBOSE
526 printf("WINGs: could not stat %s\n", pbuf);
527 #endif
528 continue;
529 } else {
530 int isDirectory;
532 isDirectory = S_ISDIR(stat_buf.st_mode);
534 if (filterFileName(panel, dentry->d_name, isDirectory))
535 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
536 isDirectory);
540 closedir(dir);
544 static void
545 fillColumn(WMBrowserDelegate *self, WMBrowser *bPtr, int column, WMList *list)
547 char *path;
548 WMFilePanel *panel;
550 if (column > 0) {
551 path = WMGetBrowserPathToColumn(bPtr, column-1);
552 } else {
553 path = wstrdup("/");
556 panel = WMGetHangedData(bPtr);
557 listDirectoryOnColumn(panel, column, path);
558 free(path);
562 static void
563 browserDClick(WMBrowser *bPtr, WMFilePanel *panel)
565 WMPerformButtonClick(panel->okButton);
568 static void
569 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
571 int col = WMGetBrowserSelectedColumn(bPtr);
572 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
574 panel->flags.ignoreTextChangeNotification = 1;
576 if (!item || item->isBranch)
577 WMSetTextFieldText(panel->fileField, NULL);
578 else {
579 WMSetTextFieldText(panel->fileField, item->text);
582 panel->flags.ignoreTextChangeNotification = 0;
586 static void
587 goHome(WMButton *bPtr, WMFilePanel *panel)
589 char *home;
591 /* home is statically allocated. Don't free it! */
592 home = wgethomedir();
593 if (!home)
594 return;
596 WMSetFilePanelDirectory(panel, home);
600 static void
601 handleEvents(XEvent *event, void *data)
603 W_FilePanel *pPtr = (W_FilePanel*)data;
604 W_View *view = WMWidgetView(pPtr->win);
606 if (event->type == ConfigureNotify) {
607 if (event->xconfigure.width != view->size.width
608 || event->xconfigure.height != view->size.height) {
609 unsigned int newWidth = event->xconfigure.width;
610 unsigned int newHeight = event->xconfigure.height;
611 int newColumnCount;
613 W_ResizeView(view, newWidth, newHeight);
614 WMResizeWidget(pPtr->line, newWidth, 2);
615 WMResizeWidget(pPtr->browser, newWidth-14,
616 newHeight-(PHEIGHT-200));
617 WMResizeWidget(pPtr->fileField, newWidth-60-10, 24);
618 WMMoveWidget(pPtr->nameLabel, 7, newHeight-(PHEIGHT-282));
619 WMMoveWidget(pPtr->fileField, 60, newHeight-(PHEIGHT-278));
620 WMMoveWidget(pPtr->okButton, newWidth-(PWIDTH-230),
621 newHeight-(PHEIGHT-325));
622 WMMoveWidget(pPtr->cancelButton, newWidth-(PWIDTH-140),
623 newHeight-(PHEIGHT-325));
624 WMMoveWidget(pPtr->homeButton, 55, newHeight-(PHEIGHT-325));
626 newColumnCount = (newWidth - 14) / 140;
627 WMSetBrowserMaxVisibleColumns(pPtr->browser, newColumnCount);
633 static char*
634 getCurrentFileName(WMFilePanel *panel)
636 char *path;
637 char *file;
638 char *tmp;
639 int len;
641 path = WMGetBrowserPath(panel->browser);
643 len = strlen(path);
644 if (path[len-1]=='/') {
645 file = WMGetTextFieldText(panel->fileField);
646 tmp = wmalloc(strlen(path)+strlen(file)+8);
647 if (file[0]!='/') {
648 strcpy(tmp, path);
649 strcat(tmp, file);
650 } else
651 strcpy(tmp, file);
653 free(file);
654 free(path);
655 return tmp;
656 } else {
657 return path;
663 static Bool
664 validOpenFile(WMFilePanel *panel)
666 WMListItem *item;
667 int col, haveFile = 0;
668 char *file = WMGetTextFieldText(panel->fileField);
670 if (file[0] != '\0')
671 haveFile = 1;
672 free(file);
674 col = WMGetBrowserSelectedColumn(panel->browser);
675 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
676 if (item) {
677 if (item->isBranch && !panel->flags.canChooseDirectories && !haveFile)
678 return False;
679 else if (!item->isBranch && !panel->flags.canChooseFiles)
680 return False;
681 else
682 return True;
683 } else {
684 /* we compute for / here */
685 if (!panel->flags.canChooseDirectories && !haveFile)
686 return False;
687 else
688 return True;
690 return True;
695 static void
696 buttonClick(WMButton *bPtr, WMFilePanel *panel)
698 WMRange range;
700 if (bPtr == panel->okButton) {
701 if (!validOpenFile(panel))
702 return;
703 if (panel->flags.fileMustExist) {
704 char *file;
706 file = getCurrentFileName(panel);
707 if (access(file, F_OK)!=0) {
708 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
709 "Error", "File does not exist.",
710 "Ok", NULL, NULL);
711 free(file);
712 return;
714 free(file);
716 panel->flags.canceled = 0;
717 } else
718 panel->flags.canceled = 1;
720 range.count = range.position = 0;
721 WMSelectTextFieldRange(panel->fileField, range);
722 panel->flags.done = 1;