Delete button in file panel.
[wmaker-crm.git] / WINGs / wfilepanel.c
blob4c5970ddbf7775a083810ea8f119a926f92133bb
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;
29 WMButton *trashcanButton;
31 WMView *accessoryView;
33 WMTextField *fileField;
35 char **fileTypes;
37 struct {
38 unsigned int canExit:1;
39 unsigned int canceled:1; /* clicked on cancel */
40 unsigned int done:1;
41 unsigned int filtered:1;
42 unsigned int canChooseFiles:1;
43 unsigned int canChooseDirectories:1;
44 unsigned int autoCompletion:1;
45 unsigned int showAllFiles:1;
46 unsigned int canFreeFileTypes:1;
47 unsigned int fileMustExist:1;
48 unsigned int panelType:1;
49 } flags;
50 } W_FilePanel;
53 /* Type of panel */
54 #define WP_OPEN 0
55 #define WP_SAVE 1
57 #define PWIDTH 320
58 #define PHEIGHT 360
60 static void listDirectoryOnColumn(WMFilePanel *panel, int column, char *path);
61 static void browserClick();
62 static void browserDClick();
64 static void fillColumn(WMBrowserDelegate *self, WMBrowser *bPtr, int column,
65 WMList *list);
67 static void deleteFile();
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 (!(list = WMGetBrowserListInColumn(panel->browser, col)))
121 return;
123 text = WMGetTextFieldText(panel->fileField);
124 textEvent = (int)WMGetNotificationClientData(notification);
126 if (panel->flags.autoCompletion && textEvent!=WMDeleteTextEvent)
127 i = closestListItem(list, text, False);
128 else
129 i = closestListItem(list, text, True);
131 WMSelectListItem(list, i);
132 if (i>=0 && panel->flags.autoCompletion) {
133 WMListItem *item = WMGetListItem(list, i);
134 int textLen = strlen(text), itemTextLen = strlen(item->text);
135 int visibleItems = WMWidgetHeight(list)/WMGetListItemHeight(list);
137 WMSetListPosition(list, i - visibleItems/2);
139 if (textEvent!=WMDeleteTextEvent) {
140 WMRange range;
142 WMInsertTextFieldText(panel->fileField, &item->text[textLen],
143 textLen);
144 WMSetTextFieldCursorPosition(panel->fileField, itemTextLen);
145 range.position = textLen;
146 range.count = itemTextLen - textLen;
147 WMSelectTextFieldRange(panel->fileField, range);
151 free(text);
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 WMSetBrowserDelegate(fPtr->browser, &browserDelegate);
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 fPtr->trashcanButton = WMCreateCommandButton(fPtr->win);
251 WMMoveWidget(fPtr->trashcanButton, 25, 325);
252 WMResizeWidget(fPtr->trashcanButton, 28, 28);
253 WMSetButtonImagePosition(fPtr->trashcanButton, WIPImageOnly);
254 WMSetButtonImage(fPtr->trashcanButton, scrPtr->trashcanIcon);
255 WMSetButtonAltImage(fPtr->trashcanButton, scrPtr->altTrashcanIcon);
257 WMSetButtonAction(fPtr->trashcanButton, deleteFile, fPtr);
258 /*xxxxxxxxxxxx***/
260 WMRealizeWidget(fPtr->win);
261 WMMapSubwidgets(fPtr->win);
263 WMSetFocusToWidget(fPtr->fileField);
264 WMSetTextFieldCursorPosition(fPtr->fileField, 0);
266 WMLoadBrowserColumnZero(fPtr->browser);
268 fPtr->flags.canChooseFiles = 1;
269 fPtr->flags.canChooseDirectories = 1;
270 fPtr->flags.autoCompletion = 1;
272 return fPtr;
276 WMOpenPanel*
277 WMGetOpenPanel(WMScreen *scrPtr)
279 WMFilePanel *panel;
281 if (scrPtr->sharedOpenPanel)
282 return scrPtr->sharedOpenPanel;
284 panel = makeFilePanel(scrPtr, "openFilePanel", "Open");
285 panel->flags.fileMustExist = 1;
286 panel->flags.panelType = WP_OPEN;
288 scrPtr->sharedOpenPanel = panel;
290 return panel;
294 WMSavePanel*
295 WMGetSavePanel(WMScreen *scrPtr)
297 WMFilePanel *panel;
299 if (scrPtr->sharedSavePanel)
300 return scrPtr->sharedSavePanel;
302 panel = makeFilePanel(scrPtr, "saveFilePanel", "Save");
303 panel->flags.fileMustExist = 0;
304 panel->flags.panelType = WP_SAVE;
306 scrPtr->sharedSavePanel = panel;
308 return panel;
312 void
313 WMFreeFilePanel(WMFilePanel *panel)
315 if (panel == WMWidgetScreen(panel->win)->sharedSavePanel) {
316 WMWidgetScreen(panel->win)->sharedSavePanel = NULL;
318 if (panel == WMWidgetScreen(panel->win)->sharedOpenPanel) {
319 WMWidgetScreen(panel->win)->sharedOpenPanel = NULL;
321 WMRemoveNotificationObserver(panel);
322 WMUnmapWidget(panel->win);
323 WMDestroyWidget(panel->win);
324 free(panel);
329 WMRunModalFilePanelForDirectory(WMFilePanel *panel, WMWindow *owner,
330 char *path, char *name, char **fileTypes)
332 WMScreen *scr = WMWidgetScreen(panel->win);
333 XEvent event;
335 if (name && !owner) {
336 WMSetWindowTitle(panel->win, name);
339 WMChangePanelOwner(panel->win, owner);
341 WMSetFilePanelDirectory(panel, path);
343 panel->flags.done = 0;
344 switch(panel->flags.panelType) {
345 case WP_OPEN:
346 if (fileTypes)
347 panel->flags.filtered = 1;
348 panel->fileTypes = fileTypes;
349 if (name == NULL)
350 name = "Open";
351 break;
352 case WP_SAVE:
353 panel->fileTypes = NULL;
354 panel->flags.filtered = 0;
355 if (name == NULL)
356 name = "Save";
357 break;
358 default:
359 break;
362 WMSetWindowUPosition(panel->win,
363 (scr->rootView->size.width - WMWidgetWidth(panel->win))/2,
364 (scr->rootView->size.height - WMWidgetHeight(panel->win))/2);
365 WMSetLabelText(panel->titleLabel, name);
367 scr->modalView = W_VIEW(panel->win);
368 WMMapWidget(panel->win);
370 scr->modal = 1;
371 while (!panel->flags.done) {
372 WMNextEvent(scr->display, &event);
373 WMHandleEvent(&event);
375 scr->modal = 0;
377 /* Must withdraw window because the next time we map
378 * it, it might have a different transient owner.
380 WMCloseWindow(panel->win);
382 return (panel->flags.canceled ? False : True);
388 void
389 WMSetFilePanelDirectory(WMFilePanel *panel, char *path)
391 WMList *list;
392 WMListItem *item;
393 int col;
394 char *rest;
396 rest = WMSetBrowserPath(panel->browser, path);
397 if (strcmp(path, "/")==0)
398 rest = NULL;
400 col = WMGetBrowserSelectedColumn(panel->browser);
401 list = WMGetBrowserListInColumn(panel->browser, col);
402 if (list && (item = WMGetListSelectedItem(list))) {
403 if (item->isBranch) {
404 WMSetTextFieldText(panel->fileField, rest);
405 } else {
406 WMSetTextFieldText(panel->fileField, item->text);
408 } else {
409 WMSetTextFieldText(panel->fileField, rest);
414 void
415 WMSetFilePanelCanChooseDirectories(WMFilePanel *panel, Bool flag)
417 panel->flags.canChooseDirectories = flag;
420 void
421 WMSetFilePanelCanChooseFiles(WMFilePanel *panel, Bool flag)
423 panel->flags.canChooseFiles = flag;
427 void
428 WMSetFilePanelAutoCompletion(WMFilePanel *panel, Bool flag)
430 panel->flags.autoCompletion = flag;
434 char*
435 WMGetFilePanelFileName(WMFilePanel *panel)
437 return getCurrentFileName(panel);
441 void
442 WMSetFilePanelAccessoryView(WMFilePanel *panel, WMView *view)
444 WMView *v;
446 panel->accessoryView = view;
448 v = WMWidgetView(panel->win);
450 W_ReparentView(view, v, 0, 0);
452 W_MoveView(view, (v->size.width - v->size.width)/2, 300);
456 WMView*
457 WMGetFilePanelAccessoryView(WMFilePanel *panel)
459 return panel->accessoryView;
463 static char*
464 get_name_from_path(char *path)
466 int size;
468 assert(path!=NULL);
470 size = strlen(path);
472 /* remove trailing / */
473 while (size > 0 && path[size-1]=='/')
474 size--;
475 /* directory was root */
476 if (size == 0)
477 return wstrdup("/");
479 while (size > 0 && path[size-1] != '/')
480 size--;
482 return wstrdup(&(path[size]));
486 static int
487 filterFileName(WMFilePanel *panel, char *file, Bool isDirectory)
489 return True;
493 static void
494 listDirectoryOnColumn(WMFilePanel *panel, int column, char *path)
496 WMBrowser *bPtr = panel->browser;
497 struct dirent *dentry;
498 DIR *dir;
499 struct stat stat_buf;
500 char pbuf[PATH_MAX+16];
502 assert(column >= 0);
503 assert(path != NULL);
505 /* put directory name in the title */
506 WMSetBrowserColumnTitle(bPtr, column, get_name_from_path(path));
508 dir = opendir(path);
510 if (!dir) {
511 #ifdef VERBOSE
512 printf("WINGs: could not open directory %s\n", path);
513 #endif
514 return;
517 /* list contents in the column */
518 while ((dentry = readdir(dir))) {
519 if (strcmp(dentry->d_name, ".")==0 ||
520 strcmp(dentry->d_name, "..")==0)
521 continue;
523 strcpy(pbuf, path);
524 if (strcmp(path, "/")!=0)
525 strcat(pbuf, "/");
526 strcat(pbuf, dentry->d_name);
528 if (stat(pbuf, &stat_buf)!=0) {
529 #ifdef VERBOSE
530 printf("WINGs: could not stat %s\n", pbuf);
531 #endif
532 continue;
533 } else {
534 int isDirectory;
536 isDirectory = S_ISDIR(stat_buf.st_mode);
538 if (filterFileName(panel, dentry->d_name, isDirectory))
539 WMAddSortedBrowserItem(bPtr, column, dentry->d_name,
540 isDirectory);
544 closedir(dir);
548 static void
549 fillColumn(WMBrowserDelegate *self, WMBrowser *bPtr, int column, WMList *list)
551 char *path;
552 WMFilePanel *panel;
554 if (column > 0) {
555 path = WMGetBrowserPathToColumn(bPtr, column-1);
556 } else {
557 path = wstrdup("/");
560 panel = WMGetHangedData(bPtr);
561 listDirectoryOnColumn(panel, column, path);
562 free(path);
566 static void
567 browserDClick(WMBrowser *bPtr, WMFilePanel *panel)
569 WMPerformButtonClick(panel->okButton);
572 static void
573 browserClick(WMBrowser *bPtr, WMFilePanel *panel)
575 int col = WMGetBrowserSelectedColumn(bPtr);
576 WMListItem *item = WMGetBrowserSelectedItemInColumn(bPtr, col);
578 if (!item || item->isBranch)
579 WMSetTextFieldText(panel->fileField, NULL);
580 else {
581 WMSetTextFieldText(panel->fileField, item->text);
585 #define ERROR_PANEL(s) err_str = wmalloc(strlen(file)+strlen(s)); \
586 sprintf(err_str, s, file); \
587 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win, \
588 "Error", err_str, "OK", NULL, NULL);
590 static void
591 deleteFile(WMButton *bPre, WMFilePanel *panel)
593 char *file;
594 char *buffer;
595 char *err_str;
597 WMFilePanel *deletePanel;
599 file = getCurrentFileName(panel);
600 if (file[strlen(file)-1] == '/') {
601 ERROR_PANEL("%s is a directory.");
602 free(err_str);
603 free(file);
604 return;
606 buffer = wmalloc(strlen(file)+15);
607 sprintf(buffer,"Delete file %s ?\x0",file);
608 if (!WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
609 "Warning", buffer, "OK", "Cancel", NULL)) {
610 int rem_stat;
611 if (rem_stat = remove(file)) {
612 switch (errno) {
613 case EISDIR:
614 ERROR_PANEL("%s is a directory.");
615 break;
616 case ENOENT:
617 ERROR_PANEL("%s does not exist.");
618 break;
619 case EACCES:
620 ERROR_PANEL("Has no right to access %s.");
621 break;
622 case ENOMEM:
623 ERROR_PANEL("Insufficient kernel memory was available.");
624 break;
625 case EROFS:
626 ERROR_PANEL("%s refers to a file on a read-only filesystem.");
627 break;
628 default:
629 ERROR_PANEL("Can not delete %s.");
631 free(err_str);
633 else {
634 char *s = strrchr(file,'/');
635 if (s) s[1] = 0;
636 WMSetFilePanelDirectory(panel, file);
639 free(buffer);
640 free(file);
643 static void
644 goHome(WMButton *bPtr, WMFilePanel *panel)
646 char *home;
648 /* home is statically allocated. Don't free it! */
649 home = wgethomedir();
650 if (!home)
651 return;
653 WMSetFilePanelDirectory(panel, home);
657 static void
658 handleEvents(XEvent *event, void *data)
660 W_FilePanel *pPtr = (W_FilePanel*)data;
661 W_View *view = WMWidgetView(pPtr->win);
663 if (event->type == ConfigureNotify) {
664 if (event->xconfigure.width != view->size.width
665 || event->xconfigure.height != view->size.height) {
666 unsigned int newWidth = event->xconfigure.width;
667 unsigned int newHeight = event->xconfigure.height;
668 int newColumnCount;
670 W_ResizeView(view, newWidth, newHeight);
671 WMResizeWidget(pPtr->line, newWidth, 2);
672 WMResizeWidget(pPtr->browser, newWidth-14,
673 newHeight-(PHEIGHT-200));
674 WMResizeWidget(pPtr->fileField, newWidth-60-10, 24);
675 WMMoveWidget(pPtr->nameLabel, 7, newHeight-(PHEIGHT-282));
676 WMMoveWidget(pPtr->fileField, 60, newHeight-(PHEIGHT-278));
677 WMMoveWidget(pPtr->okButton, newWidth-(PWIDTH-230),
678 newHeight-(PHEIGHT-325));
679 WMMoveWidget(pPtr->cancelButton, newWidth-(PWIDTH-140),
680 newHeight-(PHEIGHT-325));
681 WMMoveWidget(pPtr->homeButton, 55, newHeight-(PHEIGHT-325));
682 WMMoveWidget(pPtr->trashcanButton, 25, newHeight-(PHEIGHT-325));
684 newColumnCount = (newWidth - 14) / 140;
685 WMSetBrowserMaxVisibleColumns(pPtr->browser, newColumnCount);
691 static char*
692 getCurrentFileName(WMFilePanel *panel)
694 char *path;
695 char *file;
696 char *tmp;
697 int len;
699 path = WMGetBrowserPath(panel->browser);
701 len = strlen(path);
702 if (path[len-1]=='/') {
703 file = WMGetTextFieldText(panel->fileField);
704 tmp = wmalloc(strlen(path)+strlen(file)+8);
705 if (file[0]!='/') {
706 strcpy(tmp, path);
707 strcat(tmp, file);
708 } else
709 strcpy(tmp, file);
711 free(file);
712 free(path);
713 return tmp;
714 } else {
715 return path;
721 static Bool
722 validOpenFile(WMFilePanel *panel)
724 WMListItem *item;
725 int col, haveFile = 0;
726 char *file = WMGetTextFieldText(panel->fileField);
728 if (file[0] != '\0')
729 haveFile = 1;
730 free(file);
732 col = WMGetBrowserSelectedColumn(panel->browser);
733 item = WMGetBrowserSelectedItemInColumn(panel->browser, col);
734 if (item) {
735 if (item->isBranch && !panel->flags.canChooseDirectories && !haveFile)
736 return False;
737 else if (!item->isBranch && !panel->flags.canChooseFiles)
738 return False;
739 else
740 return True;
741 } else {
742 /* we compute for / here */
743 if (!panel->flags.canChooseDirectories && !haveFile)
744 return False;
745 else
746 return True;
748 return True;
753 static void
754 buttonClick(WMButton *bPtr, WMFilePanel *panel)
756 WMRange range;
758 if (bPtr == panel->okButton) {
759 if (!validOpenFile(panel))
760 return;
761 if (panel->flags.fileMustExist) {
762 char *file;
764 file = getCurrentFileName(panel);
765 if (access(file, F_OK)!=0) {
766 WMRunAlertPanel(WMWidgetScreen(panel->win), panel->win,
767 "Error", "File does not exist.",
768 "Ok", NULL, NULL);
769 free(file);
770 return;
772 free(file);
774 panel->flags.canceled = 0;
775 } else
776 panel->flags.canceled = 1;
778 range.count = range.position = 0;
779 WMSelectTextFieldRange(panel->fileField, range);
780 panel->flags.done = 1;