14 typedef struct W_FilePanel
{
26 WMButton
*cancelButton
;
30 WMView
*accessoryView
;
32 WMTextField
*fileField
;
37 unsigned int canExit
:1;
38 unsigned int canceled
:1; /* clicked on cancel */
40 unsigned int filtered
:1;
41 unsigned int canChooseFiles
:1;
42 unsigned int canChooseDirectories
:1;
43 unsigned int autoCompletion
:1;
44 unsigned int showAllFiles
:1;
45 unsigned int canFreeFileTypes
:1;
46 unsigned int fileMustExist
:1;
47 unsigned int panelType
:1;
50 unsigned int ignoreTextChangeNotification
:1;
62 static void listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
);
63 static void browserClick();
64 static void browserDClick();
66 static void fillColumn(WMBrowser
*bPtr
, int column
);
70 static void buttonClick();
72 static char *getCurrentFileName(WMFilePanel
*panel
);
74 static void handleEvents(XEvent
*event
, void *data
);
78 closestListItem(WMList
*list
, char *text
, Bool exact
)
80 WMListItem
*item
= WMGetListItem(list
, 0);
82 int len
= strlen(text
);
88 if (strlen(item
->text
) >= len
&&
89 ((exact
&& strcmp(item
->text
, text
)==0) ||
90 (!exact
&& strncmp(item
->text
, text
, len
)==0))) {
101 textChangedObserver(void *observerData
, WMNotification
*notification
)
103 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
106 int col
= WMGetBrowserNumberOfColumns(panel
->browser
) - 1;
109 if (panel
->flags
.ignoreTextChangeNotification
)
112 if (!(list
= WMGetBrowserListInColumn(panel
->browser
, col
)))
115 text
= WMGetTextFieldText(panel
->fileField
);
116 textEvent
= (int)WMGetNotificationClientData(notification
);
118 if (panel
->flags
.autoCompletion
&& textEvent
!=WMDeleteTextEvent
)
119 i
= closestListItem(list
, text
, False
);
121 i
= closestListItem(list
, text
, True
);
123 WMSelectListItem(list
, i
);
124 if (i
>=0 && panel
->flags
.autoCompletion
) {
125 WMListItem
*item
= WMGetListItem(list
, i
);
126 int textLen
= strlen(text
), itemTextLen
= strlen(item
->text
);
127 int visibleItems
= WMWidgetHeight(list
)/WMGetListItemHeight(list
);
129 WMSetListPosition(list
, i
- visibleItems
/2);
131 if (textEvent
!=WMDeleteTextEvent
) {
134 panel
->flags
.ignoreTextChangeNotification
= 1;
135 WMInsertTextFieldText(panel
->fileField
, &item
->text
[textLen
],
137 panel
->flags
.ignoreTextChangeNotification
= 0;
139 WMSetTextFieldCursorPosition(panel
->fileField
, itemTextLen
);
140 range
.position
= textLen
;
141 range
.count
= itemTextLen
- textLen
;
142 WMSelectTextFieldRange(panel
->fileField
, range
);
151 textEditedObserver(void *observerData
, WMNotification
*notification
)
153 W_FilePanel
*panel
= (W_FilePanel
*)observerData
;
155 if ((int)WMGetNotificationClientData(notification
)==WMReturnTextMovement
) {
156 WMPerformButtonClick(panel
->okButton
);
163 makeFilePanel(WMScreen
*scrPtr
, char *name
, char *title
)
168 fPtr
= wmalloc(sizeof(WMFilePanel
));
169 memset(fPtr
, 0, sizeof(WMFilePanel
));
171 fPtr
->win
= WMCreateWindowWithStyle(scrPtr
, name
, WMTitledWindowMask
172 |WMResizableWindowMask
);
173 WMResizeWidget(fPtr
->win
, PWIDTH
, PHEIGHT
);
174 WMSetWindowTitle(fPtr
->win
, "");
176 WMCreateEventHandler(WMWidgetView(fPtr
->win
), StructureNotifyMask
,
178 WMSetWindowMinSize(fPtr
->win
, PWIDTH
, PHEIGHT
);
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 WMSetBrowserDoubleAction(fPtr
->browser
, browserDClick
, fPtr
);
204 WMMoveWidget(fPtr
->browser
, 7, 72);
205 WMHangData(fPtr
->browser
, fPtr
);
207 fPtr
->nameLabel
= WMCreateLabel(fPtr
->win
);
208 WMMoveWidget(fPtr
->nameLabel
, 7, 282);
209 WMResizeWidget(fPtr
->nameLabel
, 55, 14);
210 WMSetLabelText(fPtr
->nameLabel
, "Name:");
212 fPtr
->fileField
= WMCreateTextField(fPtr
->win
);
213 WMMoveWidget(fPtr
->fileField
, 60, 278);
214 WMResizeWidget(fPtr
->fileField
, PWIDTH
-60-10, 24);
215 WMAddNotificationObserver(textEditedObserver
, fPtr
,
216 WMTextDidEndEditingNotification
,
218 WMAddNotificationObserver(textChangedObserver
, fPtr
,
219 WMTextDidChangeNotification
,
222 fPtr
->okButton
= WMCreateCommandButton(fPtr
->win
);
223 WMMoveWidget(fPtr
->okButton
, 230, 325);
224 WMResizeWidget(fPtr
->okButton
, 80, 28);
225 WMSetButtonText(fPtr
->okButton
, "OK");
226 WMSetButtonImage(fPtr
->okButton
, scrPtr
->buttonArrow
);
227 WMSetButtonAltImage(fPtr
->okButton
, scrPtr
->pushedButtonArrow
);
228 WMSetButtonImagePosition(fPtr
->okButton
, WIPRight
);
229 WMSetButtonAction(fPtr
->okButton
, buttonClick
, fPtr
);
231 fPtr
->cancelButton
= WMCreateCommandButton(fPtr
->win
);
232 WMMoveWidget(fPtr
->cancelButton
, 140, 325);
233 WMResizeWidget(fPtr
->cancelButton
, 80, 28);
234 WMSetButtonText(fPtr
->cancelButton
, "Cancel");
235 WMSetButtonAction(fPtr
->cancelButton
, buttonClick
, fPtr
);
237 fPtr
->homeButton
= WMCreateCommandButton(fPtr
->win
);
238 WMMoveWidget(fPtr
->homeButton
, 55, 325);
239 WMResizeWidget(fPtr
->homeButton
, 28, 28);
240 WMSetButtonImagePosition(fPtr
->homeButton
, WIPImageOnly
);
241 WMSetButtonImage(fPtr
->homeButton
, scrPtr
->homeIcon
);
242 WMSetButtonAltImage(fPtr
->homeButton
, scrPtr
->altHomeIcon
);
243 WMSetButtonAction(fPtr
->homeButton
, goHome
, fPtr
);
245 WMRealizeWidget(fPtr
->win
);
246 WMMapSubwidgets(fPtr
->win
);
248 WMSetFocusToWidget(fPtr
->fileField
);
249 WMSetTextFieldCursorPosition(fPtr
->fileField
, 0);
251 WMLoadBrowserColumnZero(fPtr
->browser
);
253 fPtr
->flags
.canChooseFiles
= 1;
254 fPtr
->flags
.canChooseDirectories
= 1;
255 fPtr
->flags
.autoCompletion
= 1;
262 WMGetOpenPanel(WMScreen
*scrPtr
)
266 if (scrPtr
->sharedOpenPanel
)
267 return scrPtr
->sharedOpenPanel
;
269 panel
= makeFilePanel(scrPtr
, "openFilePanel", "Open");
270 panel
->flags
.fileMustExist
= 1;
271 panel
->flags
.panelType
= WP_OPEN
;
273 scrPtr
->sharedOpenPanel
= panel
;
280 WMGetSavePanel(WMScreen
*scrPtr
)
284 if (scrPtr
->sharedSavePanel
)
285 return scrPtr
->sharedSavePanel
;
287 panel
= makeFilePanel(scrPtr
, "saveFilePanel", "Save");
288 panel
->flags
.fileMustExist
= 0;
289 panel
->flags
.panelType
= WP_SAVE
;
291 scrPtr
->sharedSavePanel
= panel
;
298 WMFreeFilePanel(WMFilePanel
*panel
)
300 if (panel
== WMWidgetScreen(panel
->win
)->sharedSavePanel
) {
301 WMWidgetScreen(panel
->win
)->sharedSavePanel
= NULL
;
303 if (panel
== WMWidgetScreen(panel
->win
)->sharedOpenPanel
) {
304 WMWidgetScreen(panel
->win
)->sharedOpenPanel
= NULL
;
306 WMRemoveNotificationObserver(panel
);
307 WMUnmapWidget(panel
->win
);
308 WMDestroyWidget(panel
->win
);
314 WMRunModalFilePanelForDirectory(WMFilePanel
*panel
, WMWindow
*owner
,
315 char *path
, char *name
, char **fileTypes
)
317 WMScreen
*scr
= WMWidgetScreen(panel
->win
);
320 if (name
&& !owner
) {
321 WMSetWindowTitle(panel
->win
, name
);
324 WMChangePanelOwner(panel
->win
, owner
);
326 WMSetFilePanelDirectory(panel
, path
);
328 panel
->flags
.done
= 0;
329 switch(panel
->flags
.panelType
) {
332 panel
->flags
.filtered
= 1;
333 panel
->fileTypes
= fileTypes
;
338 panel
->fileTypes
= NULL
;
339 panel
->flags
.filtered
= 0;
347 WMSetWindowUPosition(panel
->win
,
348 (scr
->rootView
->size
.width
- WMWidgetWidth(panel
->win
))/2,
349 (scr
->rootView
->size
.height
- WMWidgetHeight(panel
->win
))/2);
350 WMSetLabelText(panel
->titleLabel
, name
);
352 scr
->modalView
= W_VIEW(panel
->win
);
353 WMMapWidget(panel
->win
);
356 while (!panel
->flags
.done
) {
357 WMNextEvent(scr
->display
, &event
);
358 WMHandleEvent(&event
);
362 /* Must withdraw window because the next time we map
363 * it, it might have a different transient owner.
365 WMCloseWindow(panel
->win
);
367 return (panel
->flags
.canceled
? False
: True
);
374 WMSetFilePanelDirectory(WMFilePanel
*panel
, char *path
)
381 rest
= WMSetBrowserPath(panel
->browser
, path
);
382 if (strcmp(path
, "/")==0)
385 col
= WMGetBrowserSelectedColumn(panel
->browser
);
386 list
= WMGetBrowserListInColumn(panel
->browser
, col
);
387 if (list
&& (item
= WMGetListSelectedItem(list
))) {
388 if (item
->isBranch
) {
389 WMSetTextFieldText(panel
->fileField
, rest
);
391 WMSetTextFieldText(panel
->fileField
, item
->text
);
394 WMSetTextFieldText(panel
->fileField
, rest
);
400 WMSetFilePanelCanChooseDirectories(WMFilePanel
*panel
, Bool flag
)
402 panel
->flags
.canChooseDirectories
= flag
;
406 WMSetFilePanelCanChooseFiles(WMFilePanel
*panel
, Bool flag
)
408 panel
->flags
.canChooseFiles
= flag
;
413 WMSetFilePanelAutoCompletion(WMFilePanel
*panel
, Bool flag
)
415 panel
->flags
.autoCompletion
= flag
;
420 WMGetFilePanelFileName(WMFilePanel
*panel
)
422 return getCurrentFileName(panel
);
427 WMSetFilePanelAccessoryView(WMFilePanel
*panel
, WMView
*view
)
431 panel
->accessoryView
= view
;
433 v
= WMWidgetView(panel
->win
);
435 W_ReparentView(view
, v
, 0, 0);
437 W_MoveView(view
, (v
->size
.width
- v
->size
.width
)/2, 300);
442 WMGetFilePanelAccessoryView(WMFilePanel
*panel
)
444 return panel
->accessoryView
;
449 get_name_from_path(char *path
)
457 /* remove trailing / */
458 while (size
> 0 && path
[size
-1]=='/')
460 /* directory was root */
464 while (size
> 0 && path
[size
-1] != '/')
467 return wstrdup(&(path
[size
]));
472 filterFileName(WMFilePanel
*panel
, char *file
, Bool isDirectory
)
479 listDirectoryOnColumn(WMFilePanel
*panel
, int column
, char *path
)
481 WMBrowser
*bPtr
= panel
->browser
;
482 struct dirent
*dentry
;
484 struct stat stat_buf
;
485 char pbuf
[PATH_MAX
+16];
488 assert(path
!= NULL
);
490 /* put directory name in the title */
491 WMSetBrowserColumnTitle(bPtr
, column
, get_name_from_path(path
));
497 printf("WINGs: could not open directory %s\n", path
);
502 /* list contents in the column */
503 while ((dentry
= readdir(dir
))) {
504 if (strcmp(dentry
->d_name
, ".")==0 ||
505 strcmp(dentry
->d_name
, "..")==0)
509 if (strcmp(path
, "/")!=0)
511 strcat(pbuf
, dentry
->d_name
);
513 if (stat(pbuf
, &stat_buf
)!=0) {
515 printf("WINGs: could not stat %s\n", pbuf
);
521 isDirectory
= S_ISDIR(stat_buf
.st_mode
);
523 if (filterFileName(panel
, dentry
->d_name
, isDirectory
))
524 WMAddSortedBrowserItem(bPtr
, column
, dentry
->d_name
,
534 fillColumn(WMBrowser
*bPtr
, int column
)
540 path
= WMGetBrowserPathToColumn(bPtr
, column
-1);
545 panel
= WMGetHangedData(bPtr
);
546 listDirectoryOnColumn(panel
, column
, path
);
552 browserDClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
554 WMPerformButtonClick(panel
->okButton
);
558 browserClick(WMBrowser
*bPtr
, WMFilePanel
*panel
)
560 int col
= WMGetBrowserSelectedColumn(bPtr
);
561 WMListItem
*item
= WMGetBrowserSelectedItemInColumn(bPtr
, col
);
563 panel
->flags
.ignoreTextChangeNotification
= 1;
565 if (!item
|| item
->isBranch
)
566 WMSetTextFieldText(panel
->fileField
, NULL
);
568 WMSetTextFieldText(panel
->fileField
, item
->text
);
571 panel
->flags
.ignoreTextChangeNotification
= 0;
576 goHome(WMButton
*bPtr
, WMFilePanel
*panel
)
580 /* home is statically allocated. Don't free it! */
581 home
= wgethomedir();
585 WMSetFilePanelDirectory(panel
, home
);
590 handleEvents(XEvent
*event
, void *data
)
592 W_FilePanel
*pPtr
= (W_FilePanel
*)data
;
593 W_View
*view
= WMWidgetView(pPtr
->win
);
595 if (event
->type
== ConfigureNotify
) {
596 if (event
->xconfigure
.width
!= view
->size
.width
597 || event
->xconfigure
.height
!= view
->size
.height
) {
598 unsigned int newWidth
= event
->xconfigure
.width
;
599 unsigned int newHeight
= event
->xconfigure
.height
;
602 W_ResizeView(view
, newWidth
, newHeight
);
603 WMResizeWidget(pPtr
->line
, newWidth
, 2);
604 WMResizeWidget(pPtr
->browser
, newWidth
-14,
605 newHeight
-(PHEIGHT
-200));
606 WMResizeWidget(pPtr
->fileField
, newWidth
-60-10, 24);
607 WMMoveWidget(pPtr
->nameLabel
, 7, newHeight
-(PHEIGHT
-282));
608 WMMoveWidget(pPtr
->fileField
, 60, newHeight
-(PHEIGHT
-278));
609 WMMoveWidget(pPtr
->okButton
, newWidth
-(PWIDTH
-230),
610 newHeight
-(PHEIGHT
-325));
611 WMMoveWidget(pPtr
->cancelButton
, newWidth
-(PWIDTH
-140),
612 newHeight
-(PHEIGHT
-325));
613 WMMoveWidget(pPtr
->homeButton
, 55, newHeight
-(PHEIGHT
-325));
615 newColumnCount
= (newWidth
- 14) / 140;
616 WMSetBrowserMaxVisibleColumns(pPtr
->browser
, newColumnCount
);
623 getCurrentFileName(WMFilePanel
*panel
)
630 path
= WMGetBrowserPath(panel
->browser
);
633 if (path
[len
-1]=='/') {
634 file
= WMGetTextFieldText(panel
->fileField
);
635 tmp
= wmalloc(strlen(path
)+strlen(file
)+8);
653 validOpenFile(WMFilePanel
*panel
)
656 int col
, haveFile
= 0;
657 char *file
= WMGetTextFieldText(panel
->fileField
);
663 col
= WMGetBrowserSelectedColumn(panel
->browser
);
664 item
= WMGetBrowserSelectedItemInColumn(panel
->browser
, col
);
666 if (item
->isBranch
&& !panel
->flags
.canChooseDirectories
&& !haveFile
)
668 else if (!item
->isBranch
&& !panel
->flags
.canChooseFiles
)
673 /* we compute for / here */
674 if (!panel
->flags
.canChooseDirectories
&& !haveFile
)
685 buttonClick(WMButton
*bPtr
, WMFilePanel
*panel
)
689 if (bPtr
== panel
->okButton
) {
690 if (!validOpenFile(panel
))
692 if (panel
->flags
.fileMustExist
) {
695 file
= getCurrentFileName(panel
);
696 if (access(file
, F_OK
)!=0) {
697 WMRunAlertPanel(WMWidgetScreen(panel
->win
), panel
->win
,
698 "Error", "File does not exist.",
705 panel
->flags
.canceled
= 0;
707 panel
->flags
.canceled
= 1;
709 range
.count
= range
.position
= 0;
710 WMSelectTextFieldRange(panel
->fileField
, range
);
711 panel
->flags
.done
= 1;