WPrefs appearance stuff update (shows preview)
[wmaker-crm.git] / WINGs / wfilepanel.c
blob5768be23d2a99d2e3abc01efefba49a377f3c57e
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;
109 if (panel->flags.ignoreTextChangeNotification)
110 return;
112 if (!(list = WMGetBrowserListInColumn(panel->browser, col)))
113 return;
115 text = WMGetTextFieldText(panel->fileField);
116 textEvent = (int)WMGetNotificationClientData(notification);
118 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
119 i = closestListItem(list, text, False);
120 else
121 i = closestListItem(list, text, True);
123 WMSelectListItem(list, i);
124 if (i>=0 && panel->flags.autoCompletion) {
125 WMListItem *item = WMGetListItem(list, i);
126 int textLen = strlen(text), itemTextLen = strlen(item->text);
127 int visibleItems = WMWidgetHeight(list)/WMGetListItemHeight(list);
129 WMSetListPosition(list, i - visibleItems/2);
131 if (textEvent!=WMDeleteTextEvent) {
132 WMRange range;
134 panel->flags.ignoreTextChangeNotification = 1;
135 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
136 textLen);
137 panel->flags.ignoreTextChangeNotification = 0;
139 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
140 range.position = textLen;
141 range.count = itemTextLen - textLen;
142 WMSelectTextFieldRange(panel->fileField, range);
146 free(text);
150 static void
151 textEditedObserver(void *observerData, WMNotification *notification)
153 W_FilePanel *panel = (W_FilePanel*)observerData;
155 if ((int)WMGetNotificationClientData(notification)==WMReturnTextMovement) {
156 WMPerformButtonClick(panel->okButton);
162 static WMFilePanel*
163 makeFilePanel(WMScreen *scrPtr, char *name, char *title)
165 WMFilePanel *fPtr;
166 WMFont *largeFont;
168 fPtr = wmalloc(sizeof(WMFilePanel));
169 memset(fPtr, 0, sizeof(WMFilePanel));
171 fPtr->win = WMCreateWindowWithStyle(scrPtr, name, WMTitledWindowMask
172 |WMResizableWindowMask);
173 WMResizeWidget(fPtr->win, PWIDTH, PHEIGHT);
174 WMSetWindowTitle(fPtr->win, "");
176 WMCreateEventHandler(WMWidgetView(fPtr->win), StructureNotifyMask,
177 handleEvents, fPtr);
178 WMSetWindowMinSize(fPtr->win, PWIDTH, PHEIGHT);
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 WMSetBrowserDoubleAction(fPtr->browser, browserDClick, fPtr);
204 WMMoveWidget(fPtr->browser, 7, 72);
205 WMHangData(fPtr->browser, fPtr);
207 fPtr->nameLabel = WMCreateLabel(fPtr->win);
208 WMMoveWidget(fPtr->nameLabel, 7, 282);
209 WMResizeWidget(fPtr->nameLabel, 55, 14);
210 WMSetLabelText(fPtr->nameLabel, "Name:");
212 fPtr->fileField = WMCreateTextField(fPtr->win);
213 WMMoveWidget(fPtr->fileField, 60, 278);
214 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
215 WMAddNotificationObserver(textEditedObserver, fPtr,
216 WMTextDidEndEditingNotification,
217 fPtr->fileField);
218 WMAddNotificationObserver(textChangedObserver, fPtr,
219 WMTextDidChangeNotification,
220 fPtr->fileField);
222 fPtr->okButton = WMCreateCommandButton(fPtr->win);
223 WMMoveWidget(fPtr->okButton, 230, 325);
224 WMResizeWidget(fPtr->okButton, 80, 28);
225 WMSetButtonText(fPtr->okButton, "OK");
226 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
227 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
228 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
229 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
231 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
232 WMMoveWidget(fPtr->cancelButton, 140, 325);
233 WMResizeWidget(fPtr->cancelButton, 80, 28);
234 WMSetButtonText(fPtr->cancelButton, "Cancel");
235 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
237 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
238 WMMoveWidget(fPtr->homeButton, 55, 325);
239 WMResizeWidget(fPtr->homeButton, 28, 28);
240 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
241 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
242 WMSetButtonAltImage(fPtr->homeButton, scrPtr->altHomeIcon);
243 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
245 WMRealizeWidget(fPtr->win);
246 WMMapSubwidgets(fPtr->win);
248 WMSetFocusToWidget(fPtr->fileField);
249 WMSetTextFieldCursorPosition(fPtr->fileField, 0);
251 WMLoadBrowserColumnZero(fPtr->browser);
253 fPtr->flags.canChooseFiles = 1;
254 fPtr->flags.canChooseDirectories = 1;
255 fPtr->flags.autoCompletion = 1;
257 return fPtr;
261 WMOpenPanel*
262 WMGetOpenPanel(WMScreen *scrPtr)
264 WMFilePanel *panel;
266 if (scrPtr->sharedOpenPanel)
267 return scrPtr->sharedOpenPanel;
269 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
270 panel->flags.fileMustExist = 1;
271 panel->flags.panelType = WP_OPEN;
273 scrPtr->sharedOpenPanel = panel;
275 return panel;
279 WMSavePanel*
280 WMGetSavePanel(WMScreen *scrPtr)
282 WMFilePanel *panel;
284 if (scrPtr->sharedSavePanel)
285 return scrPtr->sharedSavePanel;
287 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
288 panel->flags.fileMustExist = 0;
289 panel->flags.panelType = WP_SAVE;
291 scrPtr->sharedSavePanel = panel;
293 return panel;
297 void
298 WMFreeFilePanel(WMFilePanel *panel)
300 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
301 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
303 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
304 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
306 WMRemoveNotificationObserver(panel);
307 WMUnmapWidget(panel->win);
308 WMDestroyWidget(panel->win);
309 free(panel);
314 WMRunModalFilePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
315 char *path, char *name, char **fileTypes)
317 WMScreen *scr = WMWidgetScreen(panel->win);
318 XEvent event;
320 if (name && !owner) {
321 WMSetWindowTitle(panel->win, name);
324 WMChangePanelOwner(panel->win, owner);
326 WMSetFilePanelDirectory(panel, path);
328 panel->flags.done = 0;
329 switch(panel->flags.panelType) {
330 case WP_OPEN:
331 if (fileTypes)
332 panel->flags.filtered = 1;
333 panel->fileTypes = fileTypes;
334 if (name == NULL)
335 name = "Open";
336 break;
337 case WP_SAVE:
338 panel->fileTypes = NULL;
339 panel->flags.filtered = 0;
340 if (name == NULL)
341 name = "Save";
342 break;
343 default:
344 break;
347 WMSetWindowUPosition(panel->win,
348 (scr->rootView->size.width - WMWidgetWidth(panel->win))/2,
349 (scr->rootView->size.height - WMWidgetHeight(panel->win))/2);
350 WMSetLabelText(panel->titleLabel, name);
352 scr->modalView = W_VIEW(panel->win);
353 WMMapWidget(panel->win);
355 scr->modal = 1;
356 while (!panel->flags.done) {
357 WMNextEvent(scr->display, &event);
358 WMHandleEvent(&event);
360 scr->modal = 0;
362 /* Must withdraw window because the next time we map
363 * it, it might have a different transient owner.
365 WMCloseWindow(panel->win);
367 return (panel->flags.canceled ? False : True);
373 void
374 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
376 WMList *list;
377 WMListItem *item;
378 int col;
379 char *rest;
381 rest = WMSetBrowserPath(panel->browser, path);
382 if (strcmp(path, "/")==0)
383 rest = NULL;
385 col = WMGetBrowserSelectedColumn(panel->browser);
386 list = WMGetBrowserListInColumn(panel->browser, col);
387 if (list && (item = WMGetListSelectedItem(list))) {
388 if (item->isBranch) {
389 WMSetTextFieldText(panel->fileField, rest);
390 } else {
391 WMSetTextFieldText(panel->fileField, item->text);
393 } else {
394 WMSetTextFieldText(panel->fileField, rest);
399 void
400 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
402 panel->flags.canChooseDirectories = flag;
405 void
406 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
408 panel->flags.canChooseFiles = flag;
412 void
413 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
415 panel->flags.autoCompletion = flag;
419 char*
420 WMGetFilePanelFileName(WMFilePanel *panel)
422 return getCurrentFileName(panel);
426 void
427 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
429 WMView *v;
431 panel->accessoryView = view;
433 v = WMWidgetView(panel->win);
435 W_ReparentView(view, v, 0, 0);
437 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
441 WMView*
442 WMGetFilePanelAccessoryView(WMFilePanel *panel)
444 return panel->accessoryView;
448 static char*
449 get_name_from_path(char *path)
451 int size;
453 assert(path!=NULL);
455 size = strlen(path);
457 /* remove trailing / */
458 while (size > 0 && path[size-1]=='/')
459 size--;
460 /* directory was root */
461 if (size == 0)
462 return wstrdup("/");
464 while (size > 0 && path[size-1] != '/')
465 size--;
467 return wstrdup(&(path[size]));
471 static int
472 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
474 return True;
478 static void
479 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
481 WMBrowser *bPtr = panel->browser;
482 struct dirent *dentry;
483 DIR *dir;
484 struct stat stat_buf;
485 char pbuf[PATH_MAX+16];
487 assert(column >= 0);
488 assert(path != NULL);
490 /* put directory name in the title */
491 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
493 dir = opendir(path);
495 if (!dir) {
496 #ifdef VERBOSE
497 printf("WINGs: could not open directory %s\n", path);
498 #endif
499 return;
502 /* list contents in the column */
503 while ((dentry = readdir(dir))) {
504 if (strcmp(dentry->d_name, ".")==0 ||
505 strcmp(dentry->d_name, "..")==0)
506 continue;
508 strcpy(pbuf, path);
509 if (strcmp(path, "/")!=0)
510 strcat(pbuf, "/");
511 strcat(pbuf, dentry->d_name);
513 if (stat(pbuf, &stat_buf)!=0) {
514 #ifdef VERBOSE
515 printf("WINGs: could not stat %s\n", pbuf);
516 #endif
517 continue;
518 } else {
519 int isDirectory;
521 isDirectory = S_ISDIR(stat_buf.st_mode);
523 if (filterFileName(panel, dentry->d_name, isDirectory))
524 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
525 isDirectory);
529 closedir(dir);
533 static void
534 fillColumn(WMBrowser *bPtr, int column)
536 char *path;
537 WMFilePanel *panel;
539 if (column > 0) {
540 path = WMGetBrowserPathToColumn(bPtr, column-1);
541 } else {
542 path = wstrdup("/");
545 panel = WMGetHangedData(bPtr);
546 listDirectoryOnColumn(panel, column, path);
547 free(path);
551 static void
552 browserDClick(WMBrowser *bPtr, WMFilePanel *panel)
554 WMPerformButtonClick(panel->okButton);
557 static void
558 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
560 int col = WMGetBrowserSelectedColumn(bPtr);
561 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
563 panel->flags.ignoreTextChangeNotification = 1;
565 if (!item || item->isBranch)
566 WMSetTextFieldText(panel->fileField, NULL);
567 else {
568 WMSetTextFieldText(panel->fileField, item->text);
571 panel->flags.ignoreTextChangeNotification = 0;
575 static void
576 goHome(WMButton *bPtr, WMFilePanel *panel)
578 char *home;
580 /* home is statically allocated. Don't free it! */
581 home = wgethomedir();
582 if (!home)
583 return;
585 WMSetFilePanelDirectory(panel, home);
589 static void
590 handleEvents(XEvent *event, void *data)
592 W_FilePanel *pPtr = (W_FilePanel*)data;
593 W_View *view = WMWidgetView(pPtr->win);
595 if (event->type == ConfigureNotify) {
596 if (event->xconfigure.width != view->size.width
597 || event->xconfigure.height != view->size.height) {
598 unsigned int newWidth = event->xconfigure.width;
599 unsigned int newHeight = event->xconfigure.height;
600 int newColumnCount;
602 W_ResizeView(view, newWidth, newHeight);
603 WMResizeWidget(pPtr->line, newWidth, 2);
604 WMResizeWidget(pPtr->browser, newWidth-14,
605 newHeight-(PHEIGHT-200));
606 WMResizeWidget(pPtr->fileField, newWidth-60-10, 24);
607 WMMoveWidget(pPtr->nameLabel, 7, newHeight-(PHEIGHT-282));
608 WMMoveWidget(pPtr->fileField, 60, newHeight-(PHEIGHT-278));
609 WMMoveWidget(pPtr->okButton, newWidth-(PWIDTH-230),
610 newHeight-(PHEIGHT-325));
611 WMMoveWidget(pPtr->cancelButton, newWidth-(PWIDTH-140),
612 newHeight-(PHEIGHT-325));
613 WMMoveWidget(pPtr->homeButton, 55, newHeight-(PHEIGHT-325));
615 newColumnCount = (newWidth - 14) / 140;
616 WMSetBrowserMaxVisibleColumns(pPtr->browser, newColumnCount);
622 static char*
623 getCurrentFileName(WMFilePanel *panel)
625 char *path;
626 char *file;
627 char *tmp;
628 int len;
630 path = WMGetBrowserPath(panel->browser);
632 len = strlen(path);
633 if (path[len-1]=='/') {
634 file = WMGetTextFieldText(panel->fileField);
635 tmp = wmalloc(strlen(path)+strlen(file)+8);
636 if (file[0]!='/') {
637 strcpy(tmp, path);
638 strcat(tmp, file);
639 } else
640 strcpy(tmp, file);
642 free(file);
643 free(path);
644 return tmp;
645 } else {
646 return path;
652 static Bool
653 validOpenFile(WMFilePanel *panel)
655 WMListItem *item;
656 int col, haveFile = 0;
657 char *file = WMGetTextFieldText(panel->fileField);
659 if (file[0] != '\0')
660 haveFile = 1;
661 free(file);
663 col = WMGetBrowserSelectedColumn(panel->browser);
664 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
665 if (item) {
666 if (item->isBranch && !panel->flags.canChooseDirectories && !haveFile)
667 return False;
668 else if (!item->isBranch && !panel->flags.canChooseFiles)
669 return False;
670 else
671 return True;
672 } else {
673 /* we compute for / here */
674 if (!panel->flags.canChooseDirectories && !haveFile)
675 return False;
676 else
677 return True;
679 return True;
684 static void
685 buttonClick(WMButton *bPtr, WMFilePanel *panel)
687 WMRange range;
689 if (bPtr == panel->okButton) {
690 if (!validOpenFile(panel))
691 return;
692 if (panel->flags.fileMustExist) {
693 char *file;
695 file = getCurrentFileName(panel);
696 if (access(file, F_OK)!=0) {
697 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
698 "Error", "File does not exist.",
699 "Ok", NULL, NULL);
700 free(file);
701 return;
703 free(file);
705 panel->flags.canceled = 0;
706 } else
707 panel->flags.canceled = 1;
709 range.count = range.position = 0;
710 WMSelectTextFieldRange(panel->fileField, range);
711 panel->flags.done = 1;