added color editing and title justification to appearance section
[wmaker-crm.git] / WINGs / wfilepanel.c
blobf4ca89e695647ff5e742acb84b4cf3a31ef94a28
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;
48 } flags;
49 } W_FilePanel;
52 /* Type of panel */
53 #define WP_OPEN 0
54 #define WP_SAVE 1
56 #define PWIDTH 320
57 #define PHEIGHT 360
59 static void listDirectoryOnColumn(WMFilePanel *panel, int column, char *path);
60 static void browserClick();
61 static void browserDClick();
63 static void fillColumn(WMBrowserDelegate *self, WMBrowser *bPtr, int column,
64 WMList *list);
66 static void goHome();
68 static void buttonClick();
70 static char *getCurrentFileName(WMFilePanel *panel);
72 static void handleEvents(XEvent *event, void *data);
76 static WMBrowserDelegate browserDelegate = {
77 NULL, /* data */
78 fillColumn, /* createRowsForColumn */
79 NULL, /* titleOfColumn */
80 NULL, /* didScroll */
81 NULL /* willScroll */
85 static int
86 closestListItem(WMList *list, char *text, Bool exact)
88 WMListItem *item = WMGetListItem(list, 0);
89 int i = 0;
90 int len = strlen(text);
92 if (len==0)
93 return -1;
95 while (item) {
96 if (strlen(item->text) >= len &&
97 ((exact && strcmp(item->text, text)==0) ||
98 (!exact && strncmp(item->text, text, len)==0))) {
99 return i;
101 item = item->nextPtr;
102 i++;
104 return -1;
108 static void
109 textChangedObserver(void *observerData, WMNotification *notification)
111 W_FilePanel *panel = (W_FilePanel*)observerData;
112 char *text;
113 WMList *list;
114 int col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
115 int i, textEvent;
117 if (!(list = WMGetBrowserListInColumn(panel->browser, col)))
118 return;
120 text = WMGetTextFieldText(panel->fileField);
121 textEvent = (int)WMGetNotificationClientData(notification);
123 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
124 i = closestListItem(list, text, False);
125 else
126 i = closestListItem(list, text, True);
128 WMSelectListItem(list, i);
129 if (i>=0 && panel->flags.autoCompletion) {
130 WMListItem *item = WMGetListItem(list, i);
131 int textLen = strlen(text), itemTextLen = strlen(item->text);
132 int visibleItems = WMWidgetHeight(list)/WMGetListItemHeight(list);
134 WMSetListPosition(list, i - visibleItems/2);
136 if (textEvent!=WMDeleteTextEvent) {
137 WMRange range;
139 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
140 textLen);
141 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
142 range.position = textLen;
143 range.count = itemTextLen - textLen;
144 WMSelectTextFieldRange(panel->fileField, range);
148 free(text);
152 static void
153 textEditedObserver(void *observerData, WMNotification *notification)
155 W_FilePanel *panel = (W_FilePanel*)observerData;
157 if ((int)WMGetNotificationClientData(notification)==WMReturnTextMovement) {
158 WMPerformButtonClick(panel->okButton);
164 static WMFilePanel*
165 makeFilePanel(WMScreen *scrPtr, char *name, char *title)
167 WMFilePanel *fPtr;
168 WMFont *largeFont;
170 fPtr = wmalloc(sizeof(WMFilePanel));
171 memset(fPtr, 0, sizeof(WMFilePanel));
173 fPtr->win = WMCreateWindowWithStyle(scrPtr, name, WMTitledWindowMask
174 |WMResizableWindowMask);
175 WMResizeWidget(fPtr->win, PWIDTH, PHEIGHT);
176 WMSetWindowTitle(fPtr->win, "");
178 WMCreateEventHandler(WMWidgetView(fPtr->win), StructureNotifyMask,
179 handleEvents, fPtr);
180 WMSetWindowMinSize(fPtr->win, PWIDTH, PHEIGHT);
183 fPtr->iconLabel = WMCreateLabel(fPtr->win);
184 WMResizeWidget(fPtr->iconLabel, 64, 64);
185 WMMoveWidget(fPtr->iconLabel, 0, 0);
186 WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
187 WMSetLabelImage(fPtr->iconLabel, scrPtr->applicationIcon);
189 fPtr->titleLabel = WMCreateLabel(fPtr->win);
190 WMResizeWidget(fPtr->titleLabel, PWIDTH-64, 64);
191 WMMoveWidget(fPtr->titleLabel, 64, 0);
192 largeFont = WMBoldSystemFontOfSize(scrPtr, 24);
193 WMSetLabelFont(fPtr->titleLabel, largeFont);
194 WMReleaseFont(largeFont);
195 WMSetLabelText(fPtr->titleLabel, title);
197 fPtr->line = WMCreateFrame(fPtr->win);
198 WMMoveWidget(fPtr->line, 0, 64);
199 WMResizeWidget(fPtr->line, PWIDTH, 2);
200 WMSetFrameRelief(fPtr->line, WRGroove);
202 fPtr->browser = WMCreateBrowser(fPtr->win);
203 WMSetBrowserDelegate(fPtr->browser, &browserDelegate);
204 WMSetBrowserAction(fPtr->browser, browserClick, fPtr);
205 WMSetBrowserDoubleAction(fPtr->browser, browserDClick, fPtr);
206 WMMoveWidget(fPtr->browser, 7, 72);
207 WMHangData(fPtr->browser, fPtr);
209 fPtr->nameLabel = WMCreateLabel(fPtr->win);
210 WMMoveWidget(fPtr->nameLabel, 7, 282);
211 WMResizeWidget(fPtr->nameLabel, 55, 14);
212 WMSetLabelText(fPtr->nameLabel, "Name:");
214 fPtr->fileField = WMCreateTextField(fPtr->win);
215 WMMoveWidget(fPtr->fileField, 60, 278);
216 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
217 WMAddNotificationObserver(textEditedObserver, fPtr,
218 WMTextDidEndEditingNotification,
219 fPtr->fileField);
220 WMAddNotificationObserver(textChangedObserver, fPtr,
221 WMTextDidChangeNotification,
222 fPtr->fileField);
224 fPtr->okButton = WMCreateCommandButton(fPtr->win);
225 WMMoveWidget(fPtr->okButton, 230, 325);
226 WMResizeWidget(fPtr->okButton, 80, 28);
227 WMSetButtonText(fPtr->okButton, "OK");
228 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
229 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
230 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
231 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
233 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
234 WMMoveWidget(fPtr->cancelButton, 140, 325);
235 WMResizeWidget(fPtr->cancelButton, 80, 28);
236 WMSetButtonText(fPtr->cancelButton, "Cancel");
237 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
239 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
240 WMMoveWidget(fPtr->homeButton, 55, 325);
241 WMResizeWidget(fPtr->homeButton, 28, 28);
242 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
243 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
244 WMSetButtonAltImage(fPtr->homeButton, scrPtr->altHomeIcon);
245 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
247 WMRealizeWidget(fPtr->win);
248 WMMapSubwidgets(fPtr->win);
250 WMSetFocusToWidget(fPtr->fileField);
251 WMSetTextFieldCursorPosition(fPtr->fileField, 0);
253 WMLoadBrowserColumnZero(fPtr->browser);
255 fPtr->flags.canChooseFiles = 1;
256 fPtr->flags.canChooseDirectories = 1;
257 fPtr->flags.autoCompletion = 1;
259 return fPtr;
263 WMOpenPanel*
264 WMGetOpenPanel(WMScreen *scrPtr)
266 WMFilePanel *panel;
268 if (scrPtr->sharedOpenPanel)
269 return scrPtr->sharedOpenPanel;
271 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
272 panel->flags.fileMustExist = 1;
273 panel->flags.panelType = WP_OPEN;
275 scrPtr->sharedOpenPanel = panel;
277 return panel;
281 WMSavePanel*
282 WMGetSavePanel(WMScreen *scrPtr)
284 WMFilePanel *panel;
286 if (scrPtr->sharedSavePanel)
287 return scrPtr->sharedSavePanel;
289 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
290 panel->flags.fileMustExist = 0;
291 panel->flags.panelType = WP_SAVE;
293 scrPtr->sharedSavePanel = panel;
295 return panel;
299 void
300 WMFreeFilePanel(WMFilePanel *panel)
302 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
303 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
305 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
306 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
308 WMRemoveNotificationObserver(panel);
309 WMUnmapWidget(panel->win);
310 WMDestroyWidget(panel->win);
311 free(panel);
316 WMRunModalFilePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
317 char *path, char *name, char **fileTypes)
319 WMScreen *scr = WMWidgetScreen(panel->win);
320 XEvent event;
322 if (name && !owner) {
323 WMSetWindowTitle(panel->win, name);
326 WMChangePanelOwner(panel->win, owner);
328 WMSetFilePanelDirectory(panel, path);
330 panel->flags.done = 0;
331 switch(panel->flags.panelType) {
332 case WP_OPEN:
333 if (fileTypes)
334 panel->flags.filtered = 1;
335 panel->fileTypes = fileTypes;
336 if (name == NULL)
337 name = "Open";
338 break;
339 case WP_SAVE:
340 panel->fileTypes = NULL;
341 panel->flags.filtered = 0;
342 if (name == NULL)
343 name = "Save";
344 break;
345 default:
346 break;
349 WMSetWindowUPosition(panel->win,
350 (scr->rootView->size.width - WMWidgetWidth(panel->win))/2,
351 (scr->rootView->size.height - WMWidgetHeight(panel->win))/2);
352 WMSetLabelText(panel->titleLabel, name);
354 scr->modalView = W_VIEW(panel->win);
355 WMMapWidget(panel->win);
357 scr->modal = 1;
358 while (!panel->flags.done) {
359 WMNextEvent(scr->display, &event);
360 WMHandleEvent(&event);
362 scr->modal = 0;
364 /* Must withdraw window because the next time we map
365 * it, it might have a different transient owner.
367 WMCloseWindow(panel->win);
369 return (panel->flags.canceled ? False : True);
375 void
376 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
378 WMList *list;
379 WMListItem *item;
380 int col;
381 char *rest;
383 rest = WMSetBrowserPath(panel->browser, path);
384 if (strcmp(path, "/")==0)
385 rest = NULL;
387 col = WMGetBrowserSelectedColumn(panel->browser);
388 list = WMGetBrowserListInColumn(panel->browser, col);
389 if (list && (item = WMGetListSelectedItem(list))) {
390 if (item->isBranch) {
391 WMSetTextFieldText(panel->fileField, rest);
392 } else {
393 WMSetTextFieldText(panel->fileField, item->text);
395 } else {
396 WMSetTextFieldText(panel->fileField, rest);
401 void
402 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
404 panel->flags.canChooseDirectories = flag;
407 void
408 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
410 panel->flags.canChooseFiles = flag;
414 void
415 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
417 panel->flags.autoCompletion = flag;
421 char*
422 WMGetFilePanelFileName(WMFilePanel *panel)
424 return getCurrentFileName(panel);
428 void
429 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
431 WMView *v;
433 panel->accessoryView = view;
435 v = WMWidgetView(panel->win);
437 W_ReparentView(view, v, 0, 0);
439 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
443 WMView*
444 WMGetFilePanelAccessoryView(WMFilePanel *panel)
446 return panel->accessoryView;
450 static char*
451 get_name_from_path(char *path)
453 int size;
455 assert(path!=NULL);
457 size = strlen(path);
459 /* remove trailing / */
460 while (size > 0 && path[size-1]=='/')
461 size--;
462 /* directory was root */
463 if (size == 0)
464 return wstrdup("/");
466 while (size > 0 && path[size-1] != '/')
467 size--;
469 return wstrdup(&(path[size]));
473 static int
474 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
476 return True;
480 static void
481 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
483 WMBrowser *bPtr = panel->browser;
484 struct dirent *dentry;
485 DIR *dir;
486 struct stat stat_buf;
487 char pbuf[PATH_MAX+16];
489 assert(column >= 0);
490 assert(path != NULL);
492 /* put directory name in the title */
493 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
495 dir = opendir(path);
497 if (!dir) {
498 #ifdef VERBOSE
499 printf("WINGs: could not open directory %s\n", path);
500 #endif
501 return;
504 /* list contents in the column */
505 while ((dentry = readdir(dir))) {
506 if (strcmp(dentry->d_name, ".")==0 ||
507 strcmp(dentry->d_name, "..")==0)
508 continue;
510 strcpy(pbuf, path);
511 if (strcmp(path, "/")!=0)
512 strcat(pbuf, "/");
513 strcat(pbuf, dentry->d_name);
515 if (stat(pbuf, &stat_buf)!=0) {
516 #ifdef VERBOSE
517 printf("WINGs: could not stat %s\n", pbuf);
518 #endif
519 continue;
520 } else {
521 int isDirectory;
523 isDirectory = S_ISDIR(stat_buf.st_mode);
525 if (filterFileName(panel, dentry->d_name, isDirectory))
526 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
527 isDirectory);
531 closedir(dir);
535 static void
536 fillColumn(WMBrowserDelegate *self, WMBrowser *bPtr, int column, WMList *list)
538 char *path;
539 WMFilePanel *panel;
541 if (column > 0) {
542 path = WMGetBrowserPathToColumn(bPtr, column-1);
543 } else {
544 path = wstrdup("/");
547 panel = WMGetHangedData(bPtr);
548 listDirectoryOnColumn(panel, column, path);
549 free(path);
553 static void
554 browserDClick(WMBrowser *bPtr, WMFilePanel *panel)
556 WMPerformButtonClick(panel->okButton);
559 static void
560 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
562 int col = WMGetBrowserSelectedColumn(bPtr);
563 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
565 if (!item || item->isBranch)
566 WMSetTextFieldText(panel->fileField, NULL);
567 else {
568 WMSetTextFieldText(panel->fileField, item->text);
573 static void
574 goHome(WMButton *bPtr, WMFilePanel *panel)
576 char *home;
578 /* home is statically allocated. Don't free it! */
579 home = wgethomedir();
580 if (!home)
581 return;
583 WMSetFilePanelDirectory(panel, home);
587 static void
588 handleEvents(XEvent *event, void *data)
590 W_FilePanel *pPtr = (W_FilePanel*)data;
591 W_View *view = WMWidgetView(pPtr->win);
593 if (event->type == ConfigureNotify) {
594 if (event->xconfigure.width != view->size.width
595 || event->xconfigure.height != view->size.height) {
596 unsigned int newWidth = event->xconfigure.width;
597 unsigned int newHeight = event->xconfigure.height;
598 int newColumnCount;
600 W_ResizeView(view, newWidth, newHeight);
601 WMResizeWidget(pPtr->line, newWidth, 2);
602 WMResizeWidget(pPtr->browser, newWidth-14,
603 newHeight-(PHEIGHT-200));
604 WMResizeWidget(pPtr->fileField, newWidth-60-10, 24);
605 WMMoveWidget(pPtr->nameLabel, 7, newHeight-(PHEIGHT-282));
606 WMMoveWidget(pPtr->fileField, 60, newHeight-(PHEIGHT-278));
607 WMMoveWidget(pPtr->okButton, newWidth-(PWIDTH-230),
608 newHeight-(PHEIGHT-325));
609 WMMoveWidget(pPtr->cancelButton, newWidth-(PWIDTH-140),
610 newHeight-(PHEIGHT-325));
611 WMMoveWidget(pPtr->homeButton, 55, newHeight-(PHEIGHT-325));
613 newColumnCount = (newWidth - 14) / 140;
614 WMSetBrowserMaxVisibleColumns(pPtr->browser, newColumnCount);
620 static char*
621 getCurrentFileName(WMFilePanel *panel)
623 char *path;
624 char *file;
625 char *tmp;
626 int len;
628 path = WMGetBrowserPath(panel->browser);
630 len = strlen(path);
631 if (path[len-1]=='/') {
632 file = WMGetTextFieldText(panel->fileField);
633 tmp = wmalloc(strlen(path)+strlen(file)+8);
634 if (file[0]!='/') {
635 strcpy(tmp, path);
636 strcat(tmp, file);
637 } else
638 strcpy(tmp, file);
640 free(file);
641 free(path);
642 return tmp;
643 } else {
644 return path;
650 static Bool
651 validOpenFile(WMFilePanel *panel)
653 WMListItem *item;
654 int col, haveFile = 0;
655 char *file = WMGetTextFieldText(panel->fileField);
657 if (file[0] != '\0')
658 haveFile = 1;
659 free(file);
661 col = WMGetBrowserSelectedColumn(panel->browser);
662 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
663 if (item) {
664 if (item->isBranch && !panel->flags.canChooseDirectories && !haveFile)
665 return False;
666 else if (!item->isBranch && !panel->flags.canChooseFiles)
667 return False;
668 else
669 return True;
670 } else {
671 /* we compute for / here */
672 if (!panel->flags.canChooseDirectories && !haveFile)
673 return False;
674 else
675 return True;
677 return True;
682 static void
683 buttonClick(WMButton *bPtr, WMFilePanel *panel)
685 WMRange range;
687 if (bPtr == panel->okButton) {
688 if (!validOpenFile(panel))
689 return;
690 if (panel->flags.fileMustExist) {
691 char *file;
693 file = getCurrentFileName(panel);
694 if (access(file, F_OK)!=0) {
695 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
696 "Error", "File does not exist.",
697 "Ok", NULL, NULL);
698 free(file);
699 return;
701 free(file);
703 panel->flags.canceled = 0;
704 } else
705 panel->flags.canceled = 1;
707 range.count = range.position = 0;
708 WMSelectTextFieldRange(panel->fileField, range);
709 panel->flags.done = 1;