Update for 0.51.0
[wmaker-crm.git] / WINGs / wfilepanel.c
blob43aa0d9c316d4da9068be545a4f68ef302756423
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 showAllFiles:1;
47 unsigned int canFreeFileTypes:1;
48 unsigned int fileMustExist:1;
49 } flags;
50 } W_FilePanel;
53 #define PWIDTH 320
54 #define PHEIGHT 360
56 static void listDirectoryOnColumn(WMFilePanel *panel, int column, char *path);
57 static void browserClick();
59 static void fillColumn(WMBrowser *bPtr, int column);
61 static void goHome();
63 static void buttonClick();
65 static char *getCurrentFileName(WMFilePanel *panel);
67 static int
68 closestListItem(WMList *list, char *text)
70 WMListItem *item = WMGetListItem(list, 0);
71 int i = 0;
72 int len = strlen(text);
74 if (len==0)
75 return -1;
77 while (item) {
78 if (strlen(item->text) >= len && strncmp(item->text, text, len)==0) {
79 return i;
81 item = item->nextPtr;
82 i++;
84 return -1;
88 static void
89 textChangedObserver(void *observerData, WMNotification *notification)
91 W_FilePanel *panel = (W_FilePanel*)observerData;
92 char *text;
93 WMList *list;
94 int col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
95 int i;
97 list = WMGetBrowserListInColumn(panel->browser, col);
98 if (!list)
99 return;
100 text = WMGetTextFieldText(panel->fileField);
102 i = closestListItem(list, text);
103 WMSelectListItem(list, i);
104 if (i>=0)
105 WMSetListPosition(list, i);
107 free(text);
111 static void
112 textEditedObserver(void *observerData, WMNotification *notification)
114 W_FilePanel *panel = (W_FilePanel*)observerData;
116 if ((int)WMGetNotificationClientData(notification)==WMReturnTextMovement) {
117 WMPerformButtonClick(panel->okButton);
123 static WMFilePanel*
124 makeFilePanel(WMScreen *scrPtr, char *name, char *title)
126 WMFilePanel *fPtr;
127 WMFont *largeFont;
129 fPtr = wmalloc(sizeof(WMFilePanel));
130 memset(fPtr, 0, sizeof(WMFilePanel));
132 fPtr->win = WMCreateWindowWithStyle(scrPtr, name, WMTitledWindowMask
133 |WMResizableWindowMask);
134 WMResizeWidget(fPtr->win, PWIDTH, PHEIGHT);
135 WMSetWindowTitle(fPtr->win, "");
137 fPtr->iconLabel = WMCreateLabel(fPtr->win);
138 WMResizeWidget(fPtr->iconLabel, 64, 64);
139 WMMoveWidget(fPtr->iconLabel, 0, 0);
140 WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
141 WMSetLabelImage(fPtr->iconLabel, scrPtr->applicationIcon);
143 fPtr->titleLabel = WMCreateLabel(fPtr->win);
144 WMResizeWidget(fPtr->titleLabel, PWIDTH-64, 64);
145 WMMoveWidget(fPtr->titleLabel, 64, 0);
146 largeFont = WMBoldSystemFontOfSize(scrPtr, 24);
147 WMSetLabelFont(fPtr->titleLabel, largeFont);
148 WMReleaseFont(largeFont);
149 WMSetLabelText(fPtr->titleLabel, title);
151 fPtr->line = WMCreateFrame(fPtr->win);
152 WMMoveWidget(fPtr->line, 0, 64);
153 WMResizeWidget(fPtr->line, PWIDTH, 2);
154 WMSetFrameRelief(fPtr->line, WRGroove);
156 fPtr->browser = WMCreateBrowser(fPtr->win);
157 WMSetBrowserFillColumnProc(fPtr->browser, fillColumn);
158 WMSetBrowserAction(fPtr->browser, browserClick, fPtr);
159 WMMoveWidget(fPtr->browser, 7, 72);
160 WMHangData(fPtr->browser, fPtr);
162 fPtr->nameLabel = WMCreateLabel(fPtr->win);
163 WMMoveWidget(fPtr->nameLabel, 7, 282);
164 WMResizeWidget(fPtr->nameLabel, 55, 14);
165 WMSetLabelText(fPtr->nameLabel, "Name:");
167 fPtr->fileField = WMCreateTextField(fPtr->win);
168 WMMoveWidget(fPtr->fileField, 60, 278);
169 WMResizeWidget(fPtr->fileField, PWIDTH-60-10, 24);
170 WMAddNotificationObserver(textEditedObserver, fPtr,
171 WMTextDidEndEditingNotification,
172 fPtr->fileField);
173 WMAddNotificationObserver(textChangedObserver, fPtr,
174 WMTextDidChangeNotification,
175 fPtr->fileField);
177 fPtr->okButton = WMCreateCommandButton(fPtr->win);
178 WMMoveWidget(fPtr->okButton, 230, 325);
179 WMResizeWidget(fPtr->okButton, 80, 28);
180 WMSetButtonText(fPtr->okButton, "OK");
181 WMSetButtonImage(fPtr->okButton, scrPtr->buttonArrow);
182 WMSetButtonAltImage(fPtr->okButton, scrPtr->pushedButtonArrow);
183 WMSetButtonImagePosition(fPtr->okButton, WIPRight);
184 WMSetButtonAction(fPtr->okButton, buttonClick, fPtr);
186 fPtr->cancelButton = WMCreateCommandButton(fPtr->win);
187 WMMoveWidget(fPtr->cancelButton, 140, 325);
188 WMResizeWidget(fPtr->cancelButton, 80, 28);
189 WMSetButtonText(fPtr->cancelButton, "Cancel");
190 WMSetButtonAction(fPtr->cancelButton, buttonClick, fPtr);
192 fPtr->homeButton = WMCreateCommandButton(fPtr->win);
193 WMMoveWidget(fPtr->homeButton, 55, 325);
194 WMResizeWidget(fPtr->homeButton, 28, 28);
195 WMSetButtonImagePosition(fPtr->homeButton, WIPImageOnly);
196 WMSetButtonImage(fPtr->homeButton, scrPtr->homeIcon);
197 WMSetButtonAltImage(fPtr->homeButton, scrPtr->homeAltIcon);
198 WMSetButtonAction(fPtr->homeButton, goHome, fPtr);
200 WMRealizeWidget(fPtr->win);
201 WMMapSubwidgets(fPtr->win);
203 WMLoadBrowserColumnZero(fPtr->browser);
205 fPtr->flags.canChooseFiles = 1;
206 fPtr->flags.canChooseDirectories = 1;
208 return fPtr;
212 WMOpenPanel*
213 WMGetOpenPanel(WMScreen *scrPtr)
215 WMFilePanel *panel;
217 if (scrPtr->sharedOpenPanel)
218 return scrPtr->sharedOpenPanel;
220 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
221 panel->flags.fileMustExist = 1;
223 scrPtr->sharedOpenPanel = panel;
225 return panel;
229 WMSavePanel*
230 WMGetSavePanel(WMScreen *scrPtr)
232 WMFilePanel *panel;
234 if (scrPtr->sharedSavePanel)
235 return scrPtr->sharedSavePanel;
237 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
238 panel->flags.fileMustExist = 0;
240 scrPtr->sharedSavePanel = panel;
242 return panel;
246 void
247 WMFreeFilePanel(WMFilePanel *panel)
249 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
250 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
252 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
253 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
255 WMRemoveNotificationObserver(panel);
256 WMUnmapWidget(panel->win);
257 WMDestroyWidget(panel->win);
258 free(panel);
263 WMRunModalSavePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
264 char *path, char *name)
266 WMScreen *scr = WMWidgetScreen(panel->win);
267 XEvent event;
269 WMChangePanelOwner(panel->win, owner);
271 WMSetFilePanelDirectory(panel, path);
273 panel->flags.done = 0;
274 panel->fileTypes = NULL;
276 panel->flags.filtered = 0;
278 WMMapWidget(panel->win);
280 while (!panel->flags.done) {
281 WMNextEvent(scr->display, &event);
282 WMHandleEvent(&event);
285 /* Must withdraw window because the next time we map
286 * it, it might have a different transient owner.
288 WMCloseWindow(panel->win);
290 return (panel->flags.canceled ? False : True);
296 WMRunModalOpenPanelForDirectory(WMFilePanel *panel, WMWindow *owner,
297 char *path, char *name, char **fileTypes)
299 WMScreen *scr = WMWidgetScreen(panel->win);
300 XEvent event;
302 WMChangePanelOwner(panel->win, owner);
304 WMSetFilePanelDirectory(panel, path);
306 panel->flags.done = 0;
308 if (fileTypes)
309 panel->flags.filtered = 1;
310 panel->fileTypes = fileTypes;
312 WMMapWidget(panel->win);
314 while (!panel->flags.done) {
315 WMNextEvent(scr->display, &event);
316 WMHandleEvent(&event);
319 WMCloseWindow(panel->win);
321 return (panel->flags.canceled ? False : True);
325 void
326 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
328 WMList *list;
329 WMListItem *item;
330 int col;
332 WMSetBrowserPath(panel->browser, path);
333 col = WMGetBrowserNumberOfColumns(panel->browser) - 1;
334 list = WMGetBrowserListInColumn(panel->browser, col);
335 if (list && (item = WMGetListSelectedItem(list))) {
336 if (item->isBranch) {
337 WMSetTextFieldText(panel->fileField, NULL);
338 } else {
339 WMSetTextFieldText(panel->fileField, item->text);
341 } else {
342 WMSetTextFieldText(panel->fileField, path);
347 void
348 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, int flag)
350 panel->flags.canChooseDirectories = flag;
353 void
354 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, int flag)
356 panel->flags.canChooseFiles = flag;
360 char*
361 WMGetFilePanelFileName(WMFilePanel *panel)
363 return getCurrentFileName(panel);
367 void
368 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
370 WMView *v;
372 panel->accessoryView = view;
374 v = WMWidgetView(panel->win);
376 W_ReparentView(view, v);
378 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
382 WMView*
383 WMGetFilePanelAccessoryView(WMFilePanel *panel)
385 return panel->accessoryView;
389 static char*
390 get_name_from_path(char *path)
392 int size;
394 assert(path!=NULL);
396 size = strlen(path);
398 /* remove trailing / */
399 while (size > 0 && path[size-1]=='/')
400 size--;
401 /* directory was root */
402 if (size == 0)
403 return wstrdup("/");
405 while (size > 0 && path[size-1] != '/')
406 size--;
408 return wstrdup(&(path[size]));
412 static int
413 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
415 return True;
419 static void
420 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
422 WMBrowser *bPtr = panel->browser;
423 struct dirent *dentry;
424 DIR *dir;
425 struct stat stat_buf;
426 char pbuf[PATH_MAX+16];
428 assert(column >= 0);
429 assert(path != NULL);
431 /* put directory name in the title */
432 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
434 dir = opendir(path);
436 if (!dir) {
437 #ifdef VERBOSE
438 printf("WINGs: could not open directory %s\n", path);
439 #endif
440 return;
443 /* list contents in the column */
444 while ((dentry = readdir(dir))) {
445 if (strcmp(dentry->d_name, ".")==0 ||
446 strcmp(dentry->d_name, "..")==0)
447 continue;
449 strcpy(pbuf, path);
450 if (strcmp(path, "/")!=0)
451 strcat(pbuf, "/");
452 strcat(pbuf, dentry->d_name);
454 if (stat(pbuf, &stat_buf)!=0) {
455 #ifdef VERBOSE
456 printf("WINGs: could not stat %s\n", pbuf);
457 #endif
458 continue;
459 } else {
460 int isDirectory;
462 isDirectory = S_ISDIR(stat_buf.st_mode);
464 if (filterFileName(panel, dentry->d_name, isDirectory))
465 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
466 isDirectory);
470 closedir(dir);
474 static void
475 fillColumn(WMBrowser *bPtr, int column)
477 char *path;
478 WMFilePanel *panel;
480 if (column > 0) {
481 path = WMGetBrowserPathToColumn(bPtr, column-1);
482 } else {
483 path = wstrdup("/");
486 panel = WMGetHangedData(bPtr);
487 listDirectoryOnColumn(panel, column, path);
488 free(path);
493 static void
494 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
496 int col = WMGetBrowserSelectedColumn(bPtr);
497 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
499 if (!item || item->isBranch)
500 WMSetTextFieldText(panel->fileField, NULL);
501 else {
502 WMSetTextFieldText(panel->fileField, item->text);
507 static void
508 goHome(WMButton *bPtr, WMFilePanel *panel)
510 char *home;
512 /* home is statically allocated. Don't free it! */
513 home = wgethomedir();
514 if (!home)
515 return;
517 WMSetFilePanelDirectory(panel, home);
521 static char*
522 getCurrentFileName(WMFilePanel *panel)
524 char *path;
525 char *file;
526 char *tmp;
527 int len;
529 path = WMGetBrowserPath(panel->browser);
531 len = strlen(path);
532 if (path[len-1]=='/') {
533 file = WMGetTextFieldText(panel->fileField);
534 tmp = wmalloc(strlen(path)+strlen(file)+8);
535 if (file[0]!='/') {
536 strcpy(tmp, path);
537 strcat(tmp, file);
538 } else
539 strcpy(tmp, file);
541 free(file);
542 free(path);
543 return tmp;
544 } else {
545 return path;
551 static Bool
552 validOpenFile(WMFilePanel *panel)
554 WMListItem *item;
555 int col;
557 col = WMGetBrowserSelectedColumn(panel->browser);
558 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
559 if (item) {
560 if (item->isBranch && !panel->flags.canChooseDirectories)
561 return False;
562 else if (!item->isBranch && !panel->flags.canChooseFiles)
563 return False;
565 return True;
570 static void
571 buttonClick(WMButton *bPtr, WMFilePanel *panel)
573 if (bPtr == panel->okButton) {
574 if (panel->flags.fileMustExist) {
575 char *file;
576 if (!validOpenFile(panel))
577 return;
579 file = getCurrentFileName(panel);
580 if (access(file, F_OK)!=0) {
581 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
582 "Error", "File does not exist.",
583 "Ok", NULL, NULL);
584 free(file);
585 return;
587 free(file);
589 panel->flags.canceled = 0;
590 } else
591 panel->flags.canceled = 1;
593 panel->flags.done = 1;