14 typedef struct W_FilePanel
{
26 WMButton
*cancelButton
;
29 WMButton
*trashcanButton
;
31 WMView
*accessoryView
;
33 WMTextField
*fileField
;
38 unsigned int canExit
:1;
39 unsigned int canceled
:1; /* clicked on cancel */
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;
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
,
67 static void deleteFile();
71 static void buttonClick();
73 static char *getCurrentFileName(WMFilePanel
*panel
);
75 static void handleEvents(XEvent
*event
, void *data
);
79 static WMBrowserDelegate browserDelegate
= {
81 fillColumn
, /* createRowsForColumn */
82 NULL
, /* titleOfColumn */
89 closestListItem(WMList
*list
, char *text
, Bool exact
)
91 WMListItem
*item
= WMGetListItem(list
, 0);
93 int len
= strlen(text
);
99 if (strlen(item
->text
) >= len
&&
100 ((exact
&& strcmp(item
->text
, text
)==0) ||
101 (!exact
&& strncmp(item
->text
, text
, len
)==0))) {
104 item
= item
->nextPtr
;
112 textChangedObserver(void *observerData
, WMNotification
*notification
)
114 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
117 int col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
120 if (!(list
= WMGetBrowserListInColumn(panel
->browser
, col
)))
123 text
= WMGetTextFieldText(panel
->fileField
);
124 textEvent
= (int)WMGetNotificationClientData(notification
);
126 if (panel
->flags
.autoCompletion
&& textEvent
!=WMDeleteTextEvent
)
127 i
= closestListItem(list
, text
, False
);
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
) {
142 WMInsertTextFieldText(panel
->fileField
, &item
->text
[textLen
],
144 WMSetTextFieldCursorPosition(panel
->fileField
, itemTextLen
);
145 range
.position
= textLen
;
146 range
.count
= itemTextLen
- textLen
;
147 WMSelectTextFieldRange(panel
->fileField
, range
);
156 textEditedObserver(void *observerData
, WMNotification
*notification
)
158 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
160 if ((int)WMGetNotificationClientData(notification
)==WMReturnTextMovement
) {
161 WMPerformButtonClick(panel
->okButton
);
168 makeFilePanel(WMScreen
*scrPtr
, char *name
, char *title
)
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
,
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
,
223 WMAddNotificationObserver(textChangedObserver
, fPtr
,
224 WMTextDidChangeNotification
,
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
);
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;
277 WMGetOpenPanel(WMScreen
*scrPtr
)
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
;
295 WMGetSavePanel(WMScreen
*scrPtr
)
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
;
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
);
329 WMRunModalFilePanelForDirectory(WMFilePanel
*panel
, WMWindow
*owner
,
330 char *path
, char *name
, char **fileTypes
)
332 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
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
) {
347 panel
->flags
.filtered
= 1;
348 panel
->fileTypes
= fileTypes
;
353 panel
->fileTypes
= NULL
;
354 panel
->flags
.filtered
= 0;
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
);
371 while (!panel
->flags
.done
) {
372 WMNextEvent(scr
->display
, &event
);
373 WMHandleEvent(&event
);
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
);
389 WMSetFilePanelDirectory(WMFilePanel
*panel
, char *path
)
396 rest
= WMSetBrowserPath(panel
->browser
, path
);
397 if (strcmp(path
, "/")==0)
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
);
406 WMSetTextFieldText(panel
->fileField
, item
->text
);
409 WMSetTextFieldText(panel
->fileField
, rest
);
415 WMSetFilePanelCanChooseDirectories(WMFilePanel
*panel
, Bool flag
)
417 panel
->flags
.canChooseDirectories
= flag
;
421 WMSetFilePanelCanChooseFiles(WMFilePanel
*panel
, Bool flag
)
423 panel
->flags
.canChooseFiles
= flag
;
428 WMSetFilePanelAutoCompletion(WMFilePanel
*panel
, Bool flag
)
430 panel
->flags
.autoCompletion
= flag
;
435 WMGetFilePanelFileName(WMFilePanel
*panel
)
437 return getCurrentFileName(panel
);
442 WMSetFilePanelAccessoryView(WMFilePanel
*panel
, WMView
*view
)
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);
457 WMGetFilePanelAccessoryView(WMFilePanel
*panel
)
459 return panel
->accessoryView
;
464 get_name_from_path(char *path
)
472 /* remove trailing / */
473 while (size
> 0 && path
[size
-1]=='/')
475 /* directory was root */
479 while (size
> 0 && path
[size
-1] != '/')
482 return wstrdup(&(path
[size
]));
487 filterFileName(WMFilePanel
*panel
, char *file
, Bool isDirectory
)
494 listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
)
496 WMBrowser
*bPtr
= panel
->browser
;
497 struct dirent
*dentry
;
499 struct stat stat_buf
;
500 char pbuf
[PATH_MAX
+16];
503 assert(path
!= NULL
);
505 /* put directory name in the title */
506 WMSetBrowserColumnTitle(bPtr
, column
, get_name_from_path(path
));
512 printf("WINGs: could not open directory %s\n", path
);
517 /* list contents in the column */
518 while ((dentry
= readdir(dir
))) {
519 if (strcmp(dentry
->d_name
, ".")==0 ||
520 strcmp(dentry
->d_name
, "..")==0)
524 if (strcmp(path
, "/")!=0)
526 strcat(pbuf
, dentry
->d_name
);
528 if (stat(pbuf
, &stat_buf
)!=0) {
530 printf("WINGs: could not stat %s\n", pbuf
);
536 isDirectory
= S_ISDIR(stat_buf
.st_mode
);
538 if (filterFileName(panel
, dentry
->d_name
, isDirectory
))
539 WMAddSortedBrowserItem(bPtr
, column
, dentry
->d_name
,
549 fillColumn(WMBrowserDelegate
*self
, WMBrowser
*bPtr
, int column
, WMList
*list
)
555 path
= WMGetBrowserPathToColumn(bPtr
, column
-1);
560 panel
= WMGetHangedData(bPtr
);
561 listDirectoryOnColumn(panel
, column
, path
);
567 browserDClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
569 WMPerformButtonClick(panel
->okButton
);
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
);
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);
591 deleteFile(WMButton
*bPre
, WMFilePanel
*panel
)
597 WMFilePanel
*deletePanel
;
599 file
= getCurrentFileName(panel
);
600 if (file
[strlen(file
)-1] == '/') {
601 ERROR_PANEL("%s is a directory.");
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
)) {
611 if (rem_stat
= remove(file
)) {
614 ERROR_PANEL("%s is a directory.");
617 ERROR_PANEL("%s does not exist.");
620 ERROR_PANEL("Has no right to access %s.");
623 ERROR_PANEL("Insufficient kernel memory was available.");
626 ERROR_PANEL("%s refers to a file on a read-only filesystem.");
629 ERROR_PANEL("Can not delete %s.");
634 char *s
= strrchr(file
,'/');
636 WMSetFilePanelDirectory(panel
, file
);
644 goHome(WMButton
*bPtr
, WMFilePanel
*panel
)
648 /* home is statically allocated. Don't free it! */
649 home
= wgethomedir();
653 WMSetFilePanelDirectory(panel
, home
);
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
;
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
);
692 getCurrentFileName(WMFilePanel
*panel
)
699 path
= WMGetBrowserPath(panel
->browser
);
702 if (path
[len
-1]=='/') {
703 file
= WMGetTextFieldText(panel
->fileField
);
704 tmp
= wmalloc(strlen(path
)+strlen(file
)+8);
722 validOpenFile(WMFilePanel
*panel
)
725 int col
, haveFile
= 0;
726 char *file
= WMGetTextFieldText(panel
->fileField
);
732 col
= WMGetBrowserSelectedColumn(panel
->browser
);
733 item
= WMGetBrowserSelectedItemInColumn(panel
->browser
, col
);
735 if (item
->isBranch
&& !panel
->flags
.canChooseDirectories
&& !haveFile
)
737 else if (!item
->isBranch
&& !panel
->flags
.canChooseFiles
)
742 /* we compute for / here */
743 if (!panel
->flags
.canChooseDirectories
&& !haveFile
)
754 buttonClick(WMButton
*bPtr
, WMFilePanel
*panel
)
758 if (bPtr
== panel
->okButton
) {
759 if (!validOpenFile(panel
))
761 if (panel
->flags
.fileMustExist
) {
764 file
= getCurrentFileName(panel
);
765 if (access(file
, F_OK
)!=0) {
766 WMRunAlertPanel(WMWidgetScreen(panel
->win
), panel
->win
,
767 "Error", "File does not exist.",
774 panel
->flags
.canceled
= 0;
776 panel
->flags
.canceled
= 1;
778 range
.count
= range
.position
= 0;
779 WMSelectTextFieldRange(panel
->fileField
, range
);
780 panel
->flags
.done
= 1;