15 typedef struct W_FilePanel
{
27 WMButton
*cancelButton
;
30 WMButton
*trashcanButton
;
31 WMButton
*disketteButton
;
33 WMView
*accessoryView
;
35 WMTextField
*fileField
;
40 unsigned int canExit
:1;
41 unsigned int canceled
:1; /* clicked on cancel */
43 unsigned int filtered
:1;
44 unsigned int canChooseFiles
:1;
45 unsigned int canChooseDirectories
:1;
46 unsigned int autoCompletion
:1;
47 unsigned int showAllFiles
:1;
48 unsigned int canFreeFileTypes
:1;
49 unsigned int fileMustExist
:1;
50 unsigned int panelType
:1;
62 static void listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
);
63 static void browserClick();
64 static void browserDClick();
66 static void fillColumn(WMBrowserDelegate
*self
, WMBrowser
*bPtr
, int column
,
69 static void deleteFile();
73 static void goFloppy();
75 static void buttonClick();
77 static char *getCurrentFileName(WMFilePanel
*panel
);
79 static void handleEvents(XEvent
*event
, void *data
);
83 static WMBrowserDelegate browserDelegate
= {
85 fillColumn
, /* createRowsForColumn */
86 NULL
, /* titleOfColumn */
93 closestListItem(WMList
*list
, char *text
, Bool exact
)
95 WMListItem
*item
= WMGetListItem(list
, 0);
97 int len
= strlen(text
);
103 if (strlen(item
->text
) >= len
&&
104 ((exact
&& strcmp(item
->text
, text
)==0) ||
105 (!exact
&& strncmp(item
->text
, text
, len
)==0))) {
108 item
= item
->nextPtr
;
116 textChangedObserver(void *observerData
, WMNotification
*notification
)
118 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
121 int col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
124 if (!(list
= WMGetBrowserListInColumn(panel
->browser
, col
)))
127 text
= WMGetTextFieldText(panel
->fileField
);
128 textEvent
= (int)WMGetNotificationClientData(notification
);
130 if (panel
->flags
.autoCompletion
&& textEvent
!=WMDeleteTextEvent
)
131 i
= closestListItem(list
, text
, False
);
133 i
= closestListItem(list
, text
, True
);
135 WMSelectListItem(list
, i
);
136 if (i
>=0 && panel
->flags
.autoCompletion
) {
137 WMListItem
*item
= WMGetListItem(list
, i
);
138 int textLen
= strlen(text
), itemTextLen
= strlen(item
->text
);
139 int visibleItems
= WMWidgetHeight(list
)/WMGetListItemHeight(list
);
141 WMSetListPosition(list
, i
- visibleItems
/2);
143 if (textEvent
!=WMDeleteTextEvent
) {
146 WMInsertTextFieldText(panel
->fileField
, &item
->text
[textLen
],
148 WMSetTextFieldCursorPosition(panel
->fileField
, itemTextLen
);
149 range
.position
= textLen
;
150 range
.count
= itemTextLen
- textLen
;
151 WMSelectTextFieldRange(panel
->fileField
, range
);
160 textEditedObserver(void *observerData
, WMNotification
*notification
)
162 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
164 if ((int)WMGetNotificationClientData(notification
)==WMReturnTextMovement
) {
165 WMPerformButtonClick(panel
->okButton
);
172 makeFilePanel(WMScreen
*scrPtr
, char *name
, char *title
)
177 fPtr
= wmalloc(sizeof(WMFilePanel
));
178 memset(fPtr
, 0, sizeof(WMFilePanel
));
180 fPtr
->win
= WMCreateWindowWithStyle(scrPtr
, name
, WMTitledWindowMask
181 |WMResizableWindowMask
);
182 WMResizeWidget(fPtr
->win
, PWIDTH
, PHEIGHT
);
183 WMSetWindowTitle(fPtr
->win
, "");
185 WMCreateEventHandler(WMWidgetView(fPtr
->win
), StructureNotifyMask
,
187 WMSetWindowMinSize(fPtr
->win
, PWIDTH
, PHEIGHT
);
190 fPtr
->iconLabel
= WMCreateLabel(fPtr
->win
);
191 WMResizeWidget(fPtr
->iconLabel
, 64, 64);
192 WMMoveWidget(fPtr
->iconLabel
, 0, 0);
193 WMSetLabelImagePosition(fPtr
->iconLabel
, WIPImageOnly
);
194 WMSetLabelImage(fPtr
->iconLabel
, scrPtr
->applicationIcon
);
196 fPtr
->titleLabel
= WMCreateLabel(fPtr
->win
);
197 WMResizeWidget(fPtr
->titleLabel
, PWIDTH
-64, 64);
198 WMMoveWidget(fPtr
->titleLabel
, 64, 0);
199 largeFont
= WMBoldSystemFontOfSize(scrPtr
, 24);
200 WMSetLabelFont(fPtr
->titleLabel
, largeFont
);
201 WMReleaseFont(largeFont
);
202 WMSetLabelText(fPtr
->titleLabel
, title
);
204 fPtr
->line
= WMCreateFrame(fPtr
->win
);
205 WMMoveWidget(fPtr
->line
, 0, 64);
206 WMResizeWidget(fPtr
->line
, PWIDTH
, 2);
207 WMSetFrameRelief(fPtr
->line
, WRGroove
);
209 fPtr
->browser
= WMCreateBrowser(fPtr
->win
);
210 WMSetBrowserDelegate(fPtr
->browser
, &browserDelegate
);
211 WMSetBrowserAction(fPtr
->browser
, browserClick
, fPtr
);
212 WMSetBrowserDoubleAction(fPtr
->browser
, browserDClick
, fPtr
);
213 WMMoveWidget(fPtr
->browser
, 7, 72);
214 WMHangData(fPtr
->browser
, fPtr
);
216 fPtr
->nameLabel
= WMCreateLabel(fPtr
->win
);
217 WMMoveWidget(fPtr
->nameLabel
, 7, 282);
218 WMResizeWidget(fPtr
->nameLabel
, 55, 14);
219 WMSetLabelText(fPtr
->nameLabel
, "Name:");
221 fPtr
->fileField
= WMCreateTextField(fPtr
->win
);
222 WMMoveWidget(fPtr
->fileField
, 60, 278);
223 WMResizeWidget(fPtr
->fileField
, PWIDTH
-60-10, 24);
224 WMAddNotificationObserver(textEditedObserver
, fPtr
,
225 WMTextDidEndEditingNotification
,
227 WMAddNotificationObserver(textChangedObserver
, fPtr
,
228 WMTextDidChangeNotification
,
231 fPtr
->okButton
= WMCreateCommandButton(fPtr
->win
);
232 WMMoveWidget(fPtr
->okButton
, 230, 325);
233 WMResizeWidget(fPtr
->okButton
, 80, 28);
234 WMSetButtonText(fPtr
->okButton
, "OK");
235 WMSetButtonImage(fPtr
->okButton
, scrPtr
->buttonArrow
);
236 WMSetButtonAltImage(fPtr
->okButton
, scrPtr
->pushedButtonArrow
);
237 WMSetButtonImagePosition(fPtr
->okButton
, WIPRight
);
238 WMSetButtonAction(fPtr
->okButton
, buttonClick
, fPtr
);
240 fPtr
->cancelButton
= WMCreateCommandButton(fPtr
->win
);
241 WMMoveWidget(fPtr
->cancelButton
, 140, 325);
242 WMResizeWidget(fPtr
->cancelButton
, 80, 28);
243 WMSetButtonText(fPtr
->cancelButton
, "Cancel");
244 WMSetButtonAction(fPtr
->cancelButton
, buttonClick
, fPtr
);
246 fPtr
->homeButton
= WMCreateCommandButton(fPtr
->win
);
247 WMMoveWidget(fPtr
->homeButton
, 55, 325);
248 WMResizeWidget(fPtr
->homeButton
, 28, 28);
249 WMSetButtonImagePosition(fPtr
->homeButton
, WIPImageOnly
);
250 WMSetButtonImage(fPtr
->homeButton
, scrPtr
->homeIcon
);
251 WMSetButtonAltImage(fPtr
->homeButton
, scrPtr
->altHomeIcon
);
252 WMSetButtonAction(fPtr
->homeButton
, goHome
, fPtr
);
254 fPtr
->trashcanButton
= WMCreateCommandButton(fPtr
->win
);
255 WMMoveWidget(fPtr
->trashcanButton
, 25, 325);
256 WMResizeWidget(fPtr
->trashcanButton
, 28, 28);
257 WMSetButtonImagePosition(fPtr
->trashcanButton
, WIPImageOnly
);
258 WMSetButtonImage(fPtr
->trashcanButton
, scrPtr
->trashcanIcon
);
259 WMSetButtonAltImage(fPtr
->trashcanButton
, scrPtr
->altTrashcanIcon
);
260 WMSetButtonAction(fPtr
->trashcanButton
, deleteFile
, fPtr
);
262 fPtr
->disketteButton
= WMCreateCommandButton(fPtr
->win
);
263 WMMoveWidget(fPtr
->disketteButton
, 85, 325);
264 WMResizeWidget(fPtr
->disketteButton
, 28, 28);
265 WMSetButtonImagePosition(fPtr
->disketteButton
, WIPImageOnly
);
266 WMSetButtonImage(fPtr
->disketteButton
, scrPtr
->disketteIcon
);
267 WMSetButtonAltImage(fPtr
->disketteButton
, scrPtr
->altDisketteIcon
);
268 WMSetButtonAction(fPtr
->disketteButton
, goFloppy
, fPtr
);
272 WMRealizeWidget(fPtr
->win
);
273 WMMapSubwidgets(fPtr
->win
);
275 WMSetFocusToWidget(fPtr
->fileField
);
276 WMSetTextFieldCursorPosition(fPtr
->fileField
, 0);
278 WMLoadBrowserColumnZero(fPtr
->browser
);
280 fPtr
->flags
.canChooseFiles
= 1;
281 fPtr
->flags
.canChooseDirectories
= 1;
282 fPtr
->flags
.autoCompletion
= 1;
289 WMGetOpenPanel(WMScreen
*scrPtr
)
293 if (scrPtr
->sharedOpenPanel
)
294 return scrPtr
->sharedOpenPanel
;
296 panel
= makeFilePanel(scrPtr
, "openFilePanel", "Open");
297 panel
->flags
.fileMustExist
= 1;
298 panel
->flags
.panelType
= WP_OPEN
;
300 scrPtr
->sharedOpenPanel
= panel
;
307 WMGetSavePanel(WMScreen
*scrPtr
)
311 if (scrPtr
->sharedSavePanel
)
312 return scrPtr
->sharedSavePanel
;
314 panel
= makeFilePanel(scrPtr
, "saveFilePanel", "Save");
315 panel
->flags
.fileMustExist
= 0;
316 panel
->flags
.panelType
= WP_SAVE
;
318 scrPtr
->sharedSavePanel
= panel
;
325 WMFreeFilePanel(WMFilePanel
*panel
)
327 if (panel
== WMWidgetScreen(panel
->win
)->sharedSavePanel
) {
328 WMWidgetScreen(panel
->win
)->sharedSavePanel
= NULL
;
330 if (panel
== WMWidgetScreen(panel
->win
)->sharedOpenPanel
) {
331 WMWidgetScreen(panel
->win
)->sharedOpenPanel
= NULL
;
333 WMRemoveNotificationObserver(panel
);
334 WMUnmapWidget(panel
->win
);
335 WMDestroyWidget(panel
->win
);
341 WMRunModalFilePanelForDirectory(WMFilePanel
*panel
, WMWindow
*owner
,
342 char *path
, char *name
, char **fileTypes
)
344 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
347 if (name
&& !owner
) {
348 WMSetWindowTitle(panel
->win
, name
);
351 WMChangePanelOwner(panel
->win
, owner
);
353 WMSetFilePanelDirectory(panel
, path
);
355 panel
->flags
.done
= 0;
356 switch(panel
->flags
.panelType
) {
359 panel
->flags
.filtered
= 1;
360 panel
->fileTypes
= fileTypes
;
365 panel
->fileTypes
= NULL
;
366 panel
->flags
.filtered
= 0;
374 WMSetWindowUPosition(panel
->win
,
375 (scr
->rootView
->size
.width
- WMWidgetWidth(panel
->win
))/2,
376 (scr
->rootView
->size
.height
- WMWidgetHeight(panel
->win
))/2);
377 WMSetLabelText(panel
->titleLabel
, name
);
379 scr
->modalView
= W_VIEW(panel
->win
);
380 WMMapWidget(panel
->win
);
383 while (!panel
->flags
.done
) {
384 WMNextEvent(scr
->display
, &event
);
385 WMHandleEvent(&event
);
389 /* Must withdraw window because the next time we map
390 * it, it might have a different transient owner.
392 WMCloseWindow(panel
->win
);
394 return (panel
->flags
.canceled
? False
: True
);
401 WMSetFilePanelDirectory(WMFilePanel
*panel
, char *path
)
408 rest
= WMSetBrowserPath(panel
->browser
, path
);
409 if (strcmp(path
, "/")==0)
412 col
= WMGetBrowserSelectedColumn(panel
->browser
);
413 list
= WMGetBrowserListInColumn(panel
->browser
, col
);
414 if (list
&& (item
= WMGetListSelectedItem(list
))) {
415 if (item
->isBranch
) {
416 WMSetTextFieldText(panel
->fileField
, rest
);
418 WMSetTextFieldText(panel
->fileField
, item
->text
);
421 WMSetTextFieldText(panel
->fileField
, rest
);
427 WMSetFilePanelCanChooseDirectories(WMFilePanel
*panel
, Bool flag
)
429 panel
->flags
.canChooseDirectories
= flag
;
433 WMSetFilePanelCanChooseFiles(WMFilePanel
*panel
, Bool flag
)
435 panel
->flags
.canChooseFiles
= flag
;
440 WMSetFilePanelAutoCompletion(WMFilePanel
*panel
, Bool flag
)
442 panel
->flags
.autoCompletion
= flag
;
447 WMGetFilePanelFileName(WMFilePanel
*panel
)
449 return getCurrentFileName(panel
);
454 WMSetFilePanelAccessoryView(WMFilePanel
*panel
, WMView
*view
)
458 panel
->accessoryView
= view
;
460 v
= WMWidgetView(panel
->win
);
462 W_ReparentView(view
, v
, 0, 0);
464 W_MoveView(view
, (v
->size
.width
- v
->size
.width
)/2, 300);
469 WMGetFilePanelAccessoryView(WMFilePanel
*panel
)
471 return panel
->accessoryView
;
476 get_name_from_path(char *path
)
484 /* remove trailing / */
485 while (size
> 0 && path
[size
-1]=='/')
487 /* directory was root */
491 while (size
> 0 && path
[size
-1] != '/')
494 return wstrdup(&(path
[size
]));
499 filterFileName(WMFilePanel
*panel
, char *file
, Bool isDirectory
)
506 listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
)
508 WMBrowser
*bPtr
= panel
->browser
;
509 struct dirent
*dentry
;
511 struct stat stat_buf
;
512 char pbuf
[PATH_MAX
+16];
515 assert(path
!= NULL
);
517 /* put directory name in the title */
518 WMSetBrowserColumnTitle(bPtr
, column
, get_name_from_path(path
));
524 printf("WINGs: could not open directory %s\n", path
);
529 /* list contents in the column */
530 while ((dentry
= readdir(dir
))) {
531 if (strcmp(dentry
->d_name
, ".")==0 ||
532 strcmp(dentry
->d_name
, "..")==0)
536 if (strcmp(path
, "/")!=0)
538 strcat(pbuf
, dentry
->d_name
);
540 if (stat(pbuf
, &stat_buf
)!=0) {
542 printf("WINGs: could not stat %s\n", pbuf
);
548 isDirectory
= S_ISDIR(stat_buf
.st_mode
);
550 if (filterFileName(panel
, dentry
->d_name
, isDirectory
))
551 WMAddSortedBrowserItem(bPtr
, column
, dentry
->d_name
,
561 fillColumn(WMBrowserDelegate
*self
, WMBrowser
*bPtr
, int column
, WMList
*list
)
567 path
= WMGetBrowserPathToColumn(bPtr
, column
-1);
572 panel
= WMGetHangedData(bPtr
);
573 listDirectoryOnColumn(panel
, column
, path
);
579 browserDClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
581 WMPerformButtonClick(panel
->okButton
);
585 browserClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
587 int col
= WMGetBrowserSelectedColumn(bPtr
);
588 WMListItem
*item
= WMGetBrowserSelectedItemInColumn(bPtr
, col
);
590 if (!item
|| item
->isBranch
)
591 WMSetTextFieldText(panel
->fileField
, NULL
);
593 WMSetTextFieldText(panel
->fileField
, item
->text
);
599 showError(WMScreen
*scr
, WMWindow
*owner
, char *s
, char *file
)
604 errStr
= wmalloc(strlen(file
)+strlen(s
));
605 sprintf(errStr
, s
, file
);
609 WMRunAlertPanel(scr
, owner
, "Error", errStr
, "OK", NULL
, NULL
);
615 deleteFile(WMButton
*bPre
, WMFilePanel
*panel
)
617 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
621 file
= getCurrentFileName(panel
);
622 if (file
[strlen(file
)-1] == '/') {
623 showError(scr
, panel
->win
, "%s is a directory.", file
);
627 buffer
= wmalloc(strlen(file
)+16);
628 sprintf(buffer
,"Delete file %s ?",file
);
629 if (!WMRunAlertPanel(WMWidgetScreen(panel
->win
), panel
->win
,
630 "Warning", buffer
, "OK", "Cancel", NULL
)) {
631 if (remove(file
) != 0) {
634 showError(scr
, panel
->win
, "'%s' is a directory.", file
);
637 showError(scr
, panel
->win
, "'%s' does not exist.", file
);
640 showError(scr
, panel
->win
, "Permission denied.", NULL
);
643 showError(scr
, panel
->win
,
644 "Insufficient memory available.", NULL
);
647 showError(scr
, panel
->win
,
648 "'%s' is on a read-only filesystem.", file
);
651 showError(scr
, panel
->win
, "Can not delete '%s'.", file
);
655 char *s
= strrchr(file
, '/');
657 WMSetFilePanelDirectory(panel
, file
);
666 goFloppy(WMButton
*bPtr
, WMFilePanel
*panel
)
668 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
669 struct stat filestat
;
671 if (stat("/floppy", &filestat
)) {
672 showError(scr
, panel
->win
, "An error occured browsing /floppy.", NULL
);
674 } else if (!S_ISDIR(filestat
.st_mode
)) {
675 showError(scr
, panel
->win
, "/floppy is not a directory.", NULL
);
679 WMSetFilePanelDirectory(panel
, "/floppy/");
683 goHome(WMButton
*bPtr
, WMFilePanel
*panel
)
687 /* home is statically allocated. Don't free it! */
688 home
= wgethomedir();
692 WMSetFilePanelDirectory(panel
, home
);
697 handleEvents(XEvent
*event
, void *data
)
699 W_FilePanel
*pPtr
= (W_FilePanel
*)data
;
700 W_View
*view
= WMWidgetView(pPtr
->win
);
702 if (event
->type
== ConfigureNotify
) {
703 if (event
->xconfigure
.width
!= view
->size
.width
704 || event
->xconfigure
.height
!= view
->size
.height
) {
705 unsigned int newWidth
= event
->xconfigure
.width
;
706 unsigned int newHeight
= event
->xconfigure
.height
;
709 W_ResizeView(view
, newWidth
, newHeight
);
710 WMResizeWidget(pPtr
->line
, newWidth
, 2);
711 WMResizeWidget(pPtr
->browser
, newWidth
-14,
712 newHeight
-(PHEIGHT
-200));
713 WMResizeWidget(pPtr
->fileField
, newWidth
-60-10, 24);
714 WMMoveWidget(pPtr
->nameLabel
, 7, newHeight
-(PHEIGHT
-282));
715 WMMoveWidget(pPtr
->fileField
, 60, newHeight
-(PHEIGHT
-278));
716 WMMoveWidget(pPtr
->okButton
, newWidth
-(PWIDTH
-230),
717 newHeight
-(PHEIGHT
-325));
718 WMMoveWidget(pPtr
->cancelButton
, newWidth
-(PWIDTH
-140),
719 newHeight
-(PHEIGHT
-325));
720 WMMoveWidget(pPtr
->homeButton
, 55, newHeight
-(PHEIGHT
-325));
721 WMMoveWidget(pPtr
->trashcanButton
, 25, newHeight
-(PHEIGHT
-325));
723 newColumnCount
= (newWidth
- 14) / 140;
724 WMSetBrowserMaxVisibleColumns(pPtr
->browser
, newColumnCount
);
731 getCurrentFileName(WMFilePanel
*panel
)
738 path
= WMGetBrowserPath(panel
->browser
);
741 if (path
[len
-1]=='/') {
742 file
= WMGetTextFieldText(panel
->fileField
);
743 tmp
= wmalloc(strlen(path
)+strlen(file
)+8);
761 validOpenFile(WMFilePanel
*panel
)
764 int col
, haveFile
= 0;
765 char *file
= WMGetTextFieldText(panel
->fileField
);
771 col
= WMGetBrowserSelectedColumn(panel
->browser
);
772 item
= WMGetBrowserSelectedItemInColumn(panel
->browser
, col
);
774 if (item
->isBranch
&& !panel
->flags
.canChooseDirectories
&& !haveFile
)
776 else if (!item
->isBranch
&& !panel
->flags
.canChooseFiles
)
781 /* we compute for / here */
782 if (!panel
->flags
.canChooseDirectories
&& !haveFile
)
793 buttonClick(WMButton
*bPtr
, WMFilePanel
*panel
)
797 if (bPtr
== panel
->okButton
) {
798 if (!validOpenFile(panel
))
800 if (panel
->flags
.fileMustExist
) {
803 file
= getCurrentFileName(panel
);
804 if (access(file
, F_OK
)!=0) {
805 WMRunAlertPanel(WMWidgetScreen(panel
->win
), panel
->win
,
806 "Error", "File does not exist.",
813 panel
->flags
.canceled
= 0;
815 panel
->flags
.canceled
= 1;
817 range
.count
= range
.position
= 0;
818 WMSelectTextFieldRange(panel
->fileField
, range
);
819 panel
->flags
.done
= 1;