17 typedef struct W_FilePanel
{
29 WMButton
*cancelButton
;
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;
57 static void listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
);
58 static void browserClick();
60 static void fillColumn(WMBrowser
*bPtr
, int column
);
64 static void buttonClick();
66 static char *getCurrentFileName(WMFilePanel
*panel
);
71 closestListItem(WMList
*list
, char *text
, Bool exact
)
73 WMListItem
*item
= WMGetListItem(list
, 0);
75 int len
= strlen(text
);
81 if (strlen(item
->text
) >= len
&&
82 ((exact
&& strcmp(item
->text
, text
)==0) ||
83 (!exact
&& strncmp(item
->text
, text
, len
)==0))) {
94 textChangedObserver(void *observerData
, WMNotification
*notification
)
96 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
99 int col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
100 int i
, j
, textEvent
= (int)WMGetNotificationClientData(notification
);
102 list
= WMGetBrowserListInColumn(panel
->browser
, col
);
105 text
= WMGetTextFieldText(panel
->fileField
);
107 if (panel
->flags
.autoCompletion
&& textEvent
!=WMDeleteTextEvent
)
108 i
= closestListItem(list
, text
, False
);
110 i
= closestListItem(list
, text
, True
);
112 WMSelectListItem(list
, i
);
114 WMListItem
*item
= WMGetListItem(list
, i
);
115 int textLen
= strlen(text
), itemTextLen
= strlen(item
->text
);
117 WMSetListPosition(list
, i
);
119 if (panel
->flags
.autoCompletion
) {
120 /* Be careful with the '<' condition below. Changing it can drop
121 * you into an endless loop. We call here, inside a function
122 * called from a notification observer, to a function that
123 * generates notification events. So make sure we do not call it
124 * recursively for ever.
125 * '<' means we only update text field if it is really not yet
128 if (textLen
< itemTextLen
&& textEvent
!=WMDeleteTextEvent
) {
131 WMInsertTextFieldText(panel
->fileField
, &item
->text
[textLen
],
133 WMSetTextFieldCursorPosition(panel
->fileField
, itemTextLen
);
134 range
.position
= textLen
;
135 range
.count
= itemTextLen
- textLen
;
136 WMSelectTextFieldRange(panel
->fileField
, range
);
139 text1
= WMGetTextFieldText(panel
->fileField
);
140 j
= closestListItem(list
, text1
, True
);
142 WMSelectListItem(list
, j
);
144 WMSetListPosition(list
, j
);
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 fPtr
->iconLabel
= WMCreateLabel(fPtr
->win
);
182 WMResizeWidget(fPtr
->iconLabel
, 64, 64);
183 WMMoveWidget(fPtr
->iconLabel
, 0, 0);
184 WMSetLabelImagePosition(fPtr
->iconLabel
, WIPImageOnly
);
185 WMSetLabelImage(fPtr
->iconLabel
, scrPtr
->applicationIcon
);
187 fPtr
->titleLabel
= WMCreateLabel(fPtr
->win
);
188 WMResizeWidget(fPtr
->titleLabel
, PWIDTH
-64, 64);
189 WMMoveWidget(fPtr
->titleLabel
, 64, 0);
190 largeFont
= WMBoldSystemFontOfSize(scrPtr
, 24);
191 WMSetLabelFont(fPtr
->titleLabel
, largeFont
);
192 WMReleaseFont(largeFont
);
193 WMSetLabelText(fPtr
->titleLabel
, title
);
195 fPtr
->line
= WMCreateFrame(fPtr
->win
);
196 WMMoveWidget(fPtr
->line
, 0, 64);
197 WMResizeWidget(fPtr
->line
, PWIDTH
, 2);
198 WMSetFrameRelief(fPtr
->line
, WRGroove
);
200 fPtr
->browser
= WMCreateBrowser(fPtr
->win
);
201 WMSetBrowserFillColumnProc(fPtr
->browser
, fillColumn
);
202 WMSetBrowserAction(fPtr
->browser
, browserClick
, fPtr
);
203 WMMoveWidget(fPtr
->browser
, 7, 72);
204 WMHangData(fPtr
->browser
, fPtr
);
206 fPtr
->nameLabel
= WMCreateLabel(fPtr
->win
);
207 WMMoveWidget(fPtr
->nameLabel
, 7, 282);
208 WMResizeWidget(fPtr
->nameLabel
, 55, 14);
209 WMSetLabelText(fPtr
->nameLabel
, "Name:");
211 fPtr
->fileField
= WMCreateTextField(fPtr
->win
);
212 WMMoveWidget(fPtr
->fileField
, 60, 278);
213 WMResizeWidget(fPtr
->fileField
, PWIDTH
-60-10, 24);
214 WMAddNotificationObserver(textEditedObserver
, fPtr
,
215 WMTextDidEndEditingNotification
,
217 WMAddNotificationObserver(textChangedObserver
, fPtr
,
218 WMTextDidChangeNotification
,
221 fPtr
->okButton
= WMCreateCommandButton(fPtr
->win
);
222 WMMoveWidget(fPtr
->okButton
, 230, 325);
223 WMResizeWidget(fPtr
->okButton
, 80, 28);
224 WMSetButtonText(fPtr
->okButton
, "OK");
225 WMSetButtonImage(fPtr
->okButton
, scrPtr
->buttonArrow
);
226 WMSetButtonAltImage(fPtr
->okButton
, scrPtr
->pushedButtonArrow
);
227 WMSetButtonImagePosition(fPtr
->okButton
, WIPRight
);
228 WMSetButtonAction(fPtr
->okButton
, buttonClick
, fPtr
);
230 fPtr
->cancelButton
= WMCreateCommandButton(fPtr
->win
);
231 WMMoveWidget(fPtr
->cancelButton
, 140, 325);
232 WMResizeWidget(fPtr
->cancelButton
, 80, 28);
233 WMSetButtonText(fPtr
->cancelButton
, "Cancel");
234 WMSetButtonAction(fPtr
->cancelButton
, buttonClick
, fPtr
);
236 fPtr
->homeButton
= WMCreateCommandButton(fPtr
->win
);
237 WMMoveWidget(fPtr
->homeButton
, 55, 325);
238 WMResizeWidget(fPtr
->homeButton
, 28, 28);
239 WMSetButtonImagePosition(fPtr
->homeButton
, WIPImageOnly
);
240 WMSetButtonImage(fPtr
->homeButton
, scrPtr
->homeIcon
);
241 WMSetButtonAltImage(fPtr
->homeButton
, scrPtr
->homeAltIcon
);
242 WMSetButtonAction(fPtr
->homeButton
, goHome
, fPtr
);
244 WMRealizeWidget(fPtr
->win
);
245 WMMapSubwidgets(fPtr
->win
);
247 WMLoadBrowserColumnZero(fPtr
->browser
);
249 fPtr
->flags
.canChooseFiles
= 1;
250 fPtr
->flags
.canChooseDirectories
= 1;
251 fPtr
->flags
.autoCompletion
= 1;
258 WMGetOpenPanel(WMScreen
*scrPtr
)
262 if (scrPtr
->sharedOpenPanel
)
263 return scrPtr
->sharedOpenPanel
;
265 panel
= makeFilePanel(scrPtr
, "openFilePanel", "Open");
266 panel
->flags
.fileMustExist
= 1;
268 scrPtr
->sharedOpenPanel
= panel
;
275 WMGetSavePanel(WMScreen
*scrPtr
)
279 if (scrPtr
->sharedSavePanel
)
280 return scrPtr
->sharedSavePanel
;
282 panel
= makeFilePanel(scrPtr
, "saveFilePanel", "Save");
283 panel
->flags
.fileMustExist
= 0;
285 scrPtr
->sharedSavePanel
= panel
;
292 WMFreeFilePanel(WMFilePanel
*panel
)
294 if (panel
== WMWidgetScreen(panel
->win
)->sharedSavePanel
) {
295 WMWidgetScreen(panel
->win
)->sharedSavePanel
= NULL
;
297 if (panel
== WMWidgetScreen(panel
->win
)->sharedOpenPanel
) {
298 WMWidgetScreen(panel
->win
)->sharedOpenPanel
= NULL
;
300 WMRemoveNotificationObserver(panel
);
301 WMUnmapWidget(panel
->win
);
302 WMDestroyWidget(panel
->win
);
308 WMRunModalSavePanelForDirectory(WMFilePanel
*panel
, WMWindow
*owner
,
309 char *path
, char *name
)
311 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
314 WMChangePanelOwner(panel
->win
, owner
);
316 WMSetFilePanelDirectory(panel
, path
);
318 panel
->flags
.done
= 0;
319 panel
->fileTypes
= NULL
;
321 panel
->flags
.filtered
= 0;
323 WMMapWidget(panel
->win
);
325 while (!panel
->flags
.done
) {
326 WMNextEvent(scr
->display
, &event
);
327 WMHandleEvent(&event
);
330 /* Must withdraw window because the next time we map
331 * it, it might have a different transient owner.
333 WMCloseWindow(panel
->win
);
335 return (panel
->flags
.canceled
? False
: True
);
341 WMRunModalOpenPanelForDirectory(WMFilePanel
*panel
, WMWindow
*owner
,
342 char *path
, char *name
, char **fileTypes
)
344 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
347 WMChangePanelOwner(panel
->win
, owner
);
349 WMSetFilePanelDirectory(panel
, path
);
351 panel
->flags
.done
= 0;
354 panel
->flags
.filtered
= 1;
355 panel
->fileTypes
= fileTypes
;
357 WMMapWidget(panel
->win
);
359 while (!panel
->flags
.done
) {
360 WMNextEvent(scr
->display
, &event
);
361 WMHandleEvent(&event
);
364 WMCloseWindow(panel
->win
);
366 return (panel
->flags
.canceled
? False
: True
);
371 WMSetFilePanelDirectory(WMFilePanel
*panel
, char *path
)
377 WMSetBrowserPath(panel
->browser
, path
);
378 col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
379 list
= WMGetBrowserListInColumn(panel
->browser
, col
);
380 if (list
&& (item
= WMGetListSelectedItem(list
))) {
381 if (item
->isBranch
) {
382 WMSetTextFieldText(panel
->fileField
, NULL
);
384 WMSetTextFieldText(panel
->fileField
, item
->text
);
387 WMSetTextFieldText(panel
->fileField
, path
);
393 WMSetFilePanelCanChooseDirectories(WMFilePanel
*panel
, Bool flag
)
395 panel
->flags
.canChooseDirectories
= flag
;
399 WMSetFilePanelCanChooseFiles(WMFilePanel
*panel
, Bool flag
)
401 panel
->flags
.canChooseFiles
= flag
;
406 WMSetFilePanelAutoCompletion(WMFilePanel
*panel
, Bool flag
)
408 panel
->flags
.autoCompletion
= flag
;
413 WMGetFilePanelFileName(WMFilePanel
*panel
)
415 return getCurrentFileName(panel
);
420 WMSetFilePanelAccessoryView(WMFilePanel
*panel
, WMView
*view
)
424 panel
->accessoryView
= view
;
426 v
= WMWidgetView(panel
->win
);
428 W_ReparentView(view
, v
);
430 W_MoveView(view
, (v
->size
.width
- v
->size
.width
)/2, 300);
435 WMGetFilePanelAccessoryView(WMFilePanel
*panel
)
437 return panel
->accessoryView
;
442 get_name_from_path(char *path
)
450 /* remove trailing / */
451 while (size
> 0 && path
[size
-1]=='/')
453 /* directory was root */
457 while (size
> 0 && path
[size
-1] != '/')
460 return wstrdup(&(path
[size
]));
465 filterFileName(WMFilePanel
*panel
, char *file
, Bool isDirectory
)
472 listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
)
474 WMBrowser
*bPtr
= panel
->browser
;
475 struct dirent
*dentry
;
477 struct stat stat_buf
;
478 char pbuf
[PATH_MAX
+16];
481 assert(path
!= NULL
);
483 /* put directory name in the title */
484 WMSetBrowserColumnTitle(bPtr
, column
, get_name_from_path(path
));
490 printf("WINGs: could not open directory %s\n", path
);
495 /* list contents in the column */
496 while ((dentry
= readdir(dir
))) {
497 if (strcmp(dentry
->d_name
, ".")==0 ||
498 strcmp(dentry
->d_name
, "..")==0)
502 if (strcmp(path
, "/")!=0)
504 strcat(pbuf
, dentry
->d_name
);
506 if (stat(pbuf
, &stat_buf
)!=0) {
508 printf("WINGs: could not stat %s\n", pbuf
);
514 isDirectory
= S_ISDIR(stat_buf
.st_mode
);
516 if (filterFileName(panel
, dentry
->d_name
, isDirectory
))
517 WMAddSortedBrowserItem(bPtr
, column
, dentry
->d_name
,
527 fillColumn(WMBrowser
*bPtr
, int column
)
533 path
= WMGetBrowserPathToColumn(bPtr
, column
-1);
538 panel
= WMGetHangedData(bPtr
);
539 listDirectoryOnColumn(panel
, column
, path
);
546 browserClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
548 int col
= WMGetBrowserSelectedColumn(bPtr
);
549 WMListItem
*item
= WMGetBrowserSelectedItemInColumn(bPtr
, col
);
551 if (!item
|| item
->isBranch
)
552 WMSetTextFieldText(panel
->fileField
, NULL
);
554 WMSetTextFieldText(panel
->fileField
, item
->text
);
560 goHome(WMButton
*bPtr
, WMFilePanel
*panel
)
564 /* home is statically allocated. Don't free it! */
565 home
= wgethomedir();
569 WMSetFilePanelDirectory(panel
, home
);
574 getCurrentFileName(WMFilePanel
*panel
)
581 path
= WMGetBrowserPath(panel
->browser
);
584 if (path
[len
-1]=='/') {
585 file
= WMGetTextFieldText(panel
->fileField
);
586 tmp
= wmalloc(strlen(path
)+strlen(file
)+8);
604 validOpenFile(WMFilePanel
*panel
)
609 /*col = WMGetBrowserSelectedColumn(panel->browser);*/
610 col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
611 item
= WMGetBrowserSelectedItemInColumn(panel
->browser
, col
);
613 if (item
->isBranch
&& !panel
->flags
.canChooseDirectories
)
615 else if (!item
->isBranch
&& !panel
->flags
.canChooseFiles
)
620 if (!panel
->flags
.canChooseDirectories
)
629 buttonClick(WMButton
*bPtr
, WMFilePanel
*panel
)
631 if (bPtr
== panel
->okButton
) {
632 if (panel
->flags
.fileMustExist
) {
634 if (!validOpenFile(panel
))
637 file
= getCurrentFileName(panel
);
638 if (access(file
, F_OK
)!=0) {
639 WMRunAlertPanel(WMWidgetScreen(panel
->win
), panel
->win
,
640 "Error", "File does not exist.",
647 panel
->flags
.canceled
= 0;
649 panel
->flags
.canceled
= 1;
651 panel
->flags
.done
= 1;