2 * Copyright 2021 Olaf Wintermann
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
23 //define FSB_ENABLE_DETAIL
42 #include <Xm/DropDown.h>
44 #ifdef FSB_ENABLE_DETAIL
48 #define WIDGET_SPACING 5
49 #define WINDOW_SPACING 8
51 #define BUTTON_EXTRA_SPACE 4
53 #define DATE_FORMAT_SAME_YEAR "%b %d %H:%M"
54 #define DATE_FORMAT_OTHER_YEAR "%b %d %Y"
56 #define KB_SUFFIX "KiB"
57 #define MB_SUFFIX "MiB"
58 #define GB_SUFFIX "GiB"
59 #define TB_SUFFIX "TiB"
61 #define FSB_ERROR_TITLE "Error"
62 #define FSB_ERROR_CHAR "Character '/' is not allowed in file names"
63 #define FSB_ERROR_RENAME "Cannot rename file: %s"
64 #define FSB_ERROR_DELETE "Cannot delete file: %s"
65 #define FSB_ERROR_CREATE_FOLDER "Cannot create folder: %s"
66 #define FSB_ERROR_OPEN_DIR "Cannot open directory: %s"
68 #define FSB_DETAIL_HEADINGS "Name|Size|Last Modified"
70 static void fsb_class_init(void);
71 static void fsb_class_part_init (WidgetClass wc);
72 static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args);
73 static void fsb_resize(Widget widget);
74 static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes);
75 static void fsb_destroy(Widget widget);
76 static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
77 static Boolean fsb_acceptfocus(Widget widget, Time *time);
79 static void fsb_insert_child(Widget child);
81 static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb);
83 static int FSBGlobFilter(const char *a, const char *b);
85 static void FSBUpdateTitle(Widget w);
87 static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
89 static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg);
91 static void FSBRename(XnFileSelectionBox fsb, const char *path);
92 static void FSBDelete(XnFileSelectionBox fsb, const char *path);
94 static void FSBSelectItem(XnFileSelectionBox fsb, const char *item);
96 static char* set_selected_path(XnFileSelectionBox data, XmString item);
98 static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd);
100 static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback);
102 static void FileListUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData);
103 static void FileListSelect(Widget fsb, Widget view, const char *item);
104 static void FileListCleanup(Widget fsb, Widget view, void *userData);
105 static void FileListDestroy(Widget fsb, Widget view, void *userData);
107 static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb);
108 static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb);
110 static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count);
112 #ifdef FSB_ENABLE_DETAIL
113 static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData);
114 static void FileListDetailSelect(Widget fsb, Widget view, const char *item);
115 static void FileListDetailCleanup(Widget fsb, Widget view, void *userData);
116 static void FileListDetailDestroy(Widget fsb, Widget view, void *userData);
117 static void FileListDetailAdjustColWidth(Widget grid);
118 static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth);
121 static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u);
123 static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u);
125 static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value);
127 static void CreateUI(XnFileSelectionBox w);
128 static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc createProc, void *userData, Boolean useDirList);
129 static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex);
130 static void SelectView(XnFileSelectionBox f, int view);
132 static char* FSBDialogTitle(Widget w);
134 static FSBViewWidgets CreateListView(Widget fsb, ArgList args, int n, void *userData);
135 static FSBViewWidgets CreateDetailView(Widget fsb, ArgList args, int n, void *userData);
137 static const char* GetHomeDir(void);
139 static char* ConcatPath(const char *parent, const char *name);
140 static char* FileName(char *path);
141 static char* ParentPath(const char *path);
142 //static int CheckFileName(const char *name);
144 static int filedialog_update_dir(XnFileSelectionBox data, const char *path);
145 static void filedialog_cleanup_filedata(XnFileSelectionBox data);
147 static void pathbar_resize(Widget w, PathBar *p, XtPointer d);
149 static XtResource resources[] = {
150 {XmNokCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.okCallback), XmRCallback, NULL},
151 {XmNcancelCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.cancelCallback), XmRCallback, NULL},
152 {XnNwidgetSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.widgetSpacing), XmRImmediate, (XtPointer)WIDGET_SPACING},
153 {XnNwindowSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.windowSpacing), XmRImmediate, (XtPointer)WINDOW_SPACING},
154 {XnNfsbType, XnCfsbType, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.type), XmRImmediate, (XtPointer)FILEDIALOG_OPEN},
155 {XnNshowHidden, XnCshowHidden, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHidden), XmRImmediate, (XtPointer)False},
156 {XnNshowHiddenButton, XnCshowHiddenButton, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHiddenButton), XmRImmediate, (XtPointer)True},
157 {XnNshowViewMenu, XnCshowViewMenu, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showViewMenu), XmRImmediate, (XtPointer)False},
158 {XnNselectedView, XnCselectedView, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.selectedview), XmRImmediate, (XtPointer)0},
160 {XnNdirectory, XnCdirectory, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.currentPath), XmRString, NULL},
161 {XnNselectedPath, XnCselectedPath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.selectedPath), XmRString, NULL},
162 {XnNhomePath, XnChomePath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.homePath), XmRString, NULL},
164 {XnNfilter,XnCfilter,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.filterStr), XmRString, "*"},
165 {XnNfilterFunc,XnCfilterFunc,XmRFunction,sizeof(FSBFilterFunc),XtOffset(XnFileSelectionBox, fsb.filterFunc), XmRFunction, NULL},
167 {XnNlabelListView,XnClabelListView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelListView), XmRString, "List"},
168 {XnNlabelDetailView,XnClabelDetailView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDetailView), XmRString, "Detail"},
169 {XnNlabelOpenFileTitle,XnClabelOpenFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpenFileTitle), XmRString, "Open File"},
170 {XnNlabelSaveFileTitle,XnClabelSaveFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSaveFileTitle), XmRString, "Save File"},
171 {XnNlabelDirUp,XnClabelDirUp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirUp), XmRString, "Dir Up"},
172 {XnNlabelHome,XnClabelHome,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHome), XmRString, "Home"},
173 {XnNlabelNewFolder,XnClabelNewFolder,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFolder), XmRString, "New Folder"},
174 {XnNlabelFilterButton,XnClabelFilterButton,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFilterButton), XmRString, "Filter"},
175 {XnNlabelShowHiddenFiles,XnClabelShowHiddenFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelShowHiddenFiles), XmRString, "Show hiden files"},
176 {XnNlabelDirectories,XnClabelDirectories,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectories), XmRString, "Directories"},
177 {XnNlabelFiles,XnClabelFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFiles), XmRString, "Files"},
178 {XnNlabelRename,XnClabelRename,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelRename), XmRString, "Rename"},
179 {XnNlabelDelete,XnClabelDelete,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDelete), XmRString, "Delete"},
180 {XnNlabelOpen,XnClabelOpen,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpen), XmRString, "Open"},
181 {XnNlabelSave,XnClabelSave,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSave), XmRString, "Save"},
182 {XnNlabelOk,XnClabelOk,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOk), XmRString, "OK"},
183 {XnNlabelCancel,XnClabelCancel,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelCancel), XmRString, "Cancel"},
184 {XnNlabelHelp,XnClabelHelp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHelp), XmRString, "Help"},
185 {XnNlabelFileName,XnClabelFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFileName), XmRString, "New File Name"},
186 {XnNlabelDirectoryName,XnClabelDirectoryName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectoryName), XmRString, "Directory name:"},
187 {XnNlabelNewFileName,XnClabelNewFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFileName), XmRString, "New file name:"},
188 {XnNlabelDeleteFile,XnClabelDeleteFile,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDeleteFile), XmRString, "Delete file '%s'?"},
189 {XnNdetailHeadings,XnCdetailHeadings,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.detailHeadings), XmRString,FSB_DETAIL_HEADINGS},
190 {XnNdateFormatSameYear,XnCdateFormatSameYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatSameYear), XmRString,DATE_FORMAT_SAME_YEAR},
191 {XnNdateFormatOtherYear,XnNdateFormatOtherYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatOtherYear), XmRString,DATE_FORMAT_OTHER_YEAR},
192 {XnNsuffixBytes,XnCsuffixBytes,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixBytes), XmRString,"bytes"},
193 {XnNsuffixKB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixKB), XmRString,KB_SUFFIX},
194 {XnNsuffixMB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixMB), XmRString,MB_SUFFIX},
195 {XnNsuffixGB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixGB), XmRString,GB_SUFFIX},
196 {XnNsuffixTB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixTB), XmRString,TB_SUFFIX},
198 {XnNerrorTitle,XnCerrorTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorTitle), XmRString,FSB_ERROR_TITLE},
199 {XnNerrorIllegalChar,XnCerrorIllegalChar,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorIllegalChar), XmRString,FSB_ERROR_CHAR},
200 {XnNerrorRename,XnCerrorRename,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorRename), XmRString,FSB_ERROR_RENAME},
201 {XnNerrorCreateFolder,XnCerrorCreateFolder,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorFolder), XmRString,FSB_ERROR_CREATE_FOLDER},
202 {XnNerrorDelete,XnCerrorDelete,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorDelete), XmRString,FSB_ERROR_DELETE},
203 {XnNerrorOpenDir,XnCerrorOpenDir,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorOpenDir), XmRString,FSB_ERROR_OPEN_DIR}
206 static XtActionsRec actionslist[] = {
207 {"focusIn", FocusInAP},
212 static char defaultTranslations[] = "<FocusIn>: focusIn()";
214 static XtResource constraints[] = {};
216 FSBClassRec fsbWidgetClassRec = {
219 (WidgetClass)&xmFormClassRec,
220 "XnFSB", // class_name
221 sizeof(FSBRec), // widget_size
222 fsb_class_init, // class_initialize
223 fsb_class_part_init, // class_part_initialize
224 FALSE, // class_inited
225 fsb_init, // initialize
226 NULL, // initialize_hook
227 fsb_realize, // realize
228 actionslist, // actions
229 XtNumber(actionslist), // num_actions
230 resources, // resources
231 XtNumber(resources), // num_resources
232 NULLQUARK, // xrm_class
233 True, // compress_motion
234 True, // compress_exposure
235 True, // compress_enterleave
236 False, // visible_interest
237 fsb_destroy, // destroy
238 fsb_resize, // resize
239 XtInheritExpose, // expose
240 fsb_set_values, // set_values
241 NULL, // set_values_hook
242 XtInheritSetValuesAlmost, // set_values_almost
243 NULL, // get_values_hook
244 fsb_acceptfocus, // accept_focus
245 XtVersion, // version
246 NULL, // callback_offsets
247 defaultTranslations, // tm_table
248 XtInheritQueryGeometry, // query_geometry
249 XtInheritDisplayAccelerator, // display_accelerator
254 XtInheritGeometryManager, // geometry_manager
255 XtInheritChangeManaged, // change_managed
256 fsb_insert_child, // insert_child
257 XtInheritDeleteChild, // delete_child
262 constraints, // resources
263 XtNumber(constraints), // num_resources
264 sizeof(XmFormConstraintRec), // constraint_size
272 XtInheritTranslations, // translations
273 NULL, // syn_resources
274 0, // num_syn_resources
275 NULL, // syn_constraint_resources
276 0, // num_syn_constraint_resources
277 XmInheritParentProcess, // parent_process
284 XmInheritFocusMovedProc,
297 WidgetClass xnFsbWidgetClass = (WidgetClass)&fsbWidgetClassRec;
300 static void fsb_class_init(void) {
304 static void fsb_class_part_init (WidgetClass wc) {
305 FSBClassRec *fsbClass = (FSBClassRec*)wc;
306 XmFormClassRec *formClass = (XmFormClassRec*)xmFormWidgetClass;
308 fsbClass->constraint_class.initialize = formClass->constraint_class.initialize;
309 fsbClass->constraint_class.set_values = formClass->constraint_class.set_values;
313 #define STRDUP_RES(a) if(a) a = strdup(a)
314 #define XMS_STRDUP_RES(a) if(a) a = XmStringCopy(a)
316 static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args) {
317 XnFileSelectionBox fsb = (XnFileSelectionBox)neww;
318 (xmFormClassRec.core_class.initialize)(request, neww, args, num_args);
320 fsb->fsb.disable_set_values = 0;
322 STRDUP_RES(fsb->fsb.homePath);
323 STRDUP_RES(fsb->fsb.selectedPath);
324 STRDUP_RES(fsb->fsb.currentPath);
325 STRDUP_RES(fsb->fsb.filterStr);
326 STRDUP_RES(fsb->fsb.labelListView);
327 STRDUP_RES(fsb->fsb.labelDetailView);
328 STRDUP_RES(fsb->fsb.labelOpenFileTitle);
329 STRDUP_RES(fsb->fsb.labelSaveFileTitle);
330 XMS_STRDUP_RES(fsb->fsb.labelDirUp);
331 XMS_STRDUP_RES(fsb->fsb.labelHome);
332 XMS_STRDUP_RES(fsb->fsb.labelNewFolder);
333 XMS_STRDUP_RES(fsb->fsb.labelFilterButton);
334 XMS_STRDUP_RES(fsb->fsb.labelShowHiddenFiles);
335 XMS_STRDUP_RES(fsb->fsb.labelDirectories);
336 XMS_STRDUP_RES(fsb->fsb.labelFiles);
337 XMS_STRDUP_RES(fsb->fsb.labelRename);
338 XMS_STRDUP_RES(fsb->fsb.labelDelete);
339 XMS_STRDUP_RES(fsb->fsb.labelOpen);
340 XMS_STRDUP_RES(fsb->fsb.labelSave);
341 XMS_STRDUP_RES(fsb->fsb.labelCancel);
342 XMS_STRDUP_RES(fsb->fsb.labelHelp);
343 XMS_STRDUP_RES(fsb->fsb.labelFileName);
344 XMS_STRDUP_RES(fsb->fsb.labelDirectoryName);
345 XMS_STRDUP_RES(fsb->fsb.labelNewFileName);
346 STRDUP_RES(fsb->fsb.labelDeleteFile);
347 STRDUP_RES(fsb->fsb.detailHeadings);
348 STRDUP_RES(fsb->fsb.dateFormatSameYear);
349 STRDUP_RES(fsb->fsb.dateFormatOtherYear);
350 STRDUP_RES(fsb->fsb.suffixBytes);
351 STRDUP_RES(fsb->fsb.suffixKB);
352 STRDUP_RES(fsb->fsb.suffixMB);
353 STRDUP_RES(fsb->fsb.suffixGB);
354 STRDUP_RES(fsb->fsb.suffixTB);
355 STRDUP_RES(fsb->fsb.errorTitle);
356 STRDUP_RES(fsb->fsb.errorIllegalChar);
357 STRDUP_RES(fsb->fsb.errorRename);
358 STRDUP_RES(fsb->fsb.errorFolder);
359 STRDUP_RES(fsb->fsb.errorDelete);
360 STRDUP_RES(fsb->fsb.errorOpenDir);
362 CreateUI((XnFileSelectionBox)fsb);
364 XtAddCallback(neww, XmNmapCallback, fsb_mapcb, NULL);
367 #define STR_FREE(a) if(a) free(a)
368 #define XMSTR_FREE(a) if(a) XmStringFree(a)
370 static void fsb_destroy(Widget widget) {
371 XnFileSelectionBox w = (XnFileSelectionBox)widget;
374 for(int i=0;i<w->fsb.numviews;i++) {
375 FSBView v = w->fsb.view[i];
376 v.destroy(widget, v.widget, v.userData);
379 STR_FREE(w->fsb.homePath);
382 filedialog_cleanup_filedata(w);
383 STR_FREE(w->fsb.currentPath);
384 STR_FREE(w->fsb.selectedPath);
385 STR_FREE(w->fsb.filterStr);
387 PathBarDestroy(w->fsb.pathBar);
390 STR_FREE(w->fsb.labelListView);
391 STR_FREE(w->fsb.labelDetailView);
392 STR_FREE(w->fsb.labelOpenFileTitle);
393 STR_FREE(w->fsb.labelSaveFileTitle);
395 XMSTR_FREE(w->fsb.labelDirUp);
396 XMSTR_FREE(w->fsb.labelHome);
397 XMSTR_FREE(w->fsb.labelNewFolder);
398 XMSTR_FREE(w->fsb.labelFilterButton);
399 XMSTR_FREE(w->fsb.labelShowHiddenFiles);
400 XMSTR_FREE(w->fsb.labelDirectories);
401 XMSTR_FREE(w->fsb.labelFiles);
402 XMSTR_FREE(w->fsb.labelRename);
403 XMSTR_FREE(w->fsb.labelDelete);
404 XMSTR_FREE(w->fsb.labelOpen);
405 XMSTR_FREE(w->fsb.labelSave);
406 XMSTR_FREE(w->fsb.labelCancel);
407 XMSTR_FREE(w->fsb.labelHelp);
408 XMSTR_FREE(w->fsb.labelFileName);
409 XMSTR_FREE(w->fsb.labelDirectoryName);
410 XMSTR_FREE(w->fsb.labelNewFileName);
411 STR_FREE(w->fsb.labelDeleteFile);
412 STR_FREE(w->fsb.detailHeadings);
414 STR_FREE(w->fsb.dateFormatSameYear);
415 STR_FREE(w->fsb.dateFormatOtherYear);
416 STR_FREE(w->fsb.suffixBytes);
417 STR_FREE(w->fsb.suffixKB);
418 STR_FREE(w->fsb.suffixMB);
419 STR_FREE(w->fsb.suffixGB);
420 STR_FREE(w->fsb.suffixTB);
422 STR_FREE(w->fsb.errorTitle);
423 STR_FREE(w->fsb.errorIllegalChar);
424 STR_FREE(w->fsb.errorRename);
425 STR_FREE(w->fsb.errorFolder);
426 STR_FREE(w->fsb.errorDelete);
427 STR_FREE(w->fsb.errorOpenDir);
430 static void fsb_resize(Widget widget) {
431 XnFileSelectionBox w = (XnFileSelectionBox)widget;
432 (xmFormClassRec.core_class.resize)(widget);
434 #ifdef FSB_ENABLE_DETAIL
435 if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) {
436 FileListDetailAdjustColWidth(w->fsb.grid);
441 static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) {
442 XnFileSelectionBox w = (XnFileSelectionBox)widget;
443 (xmFormClassRec.core_class.realize)(widget, mask, attributes);
445 FSBView view = w->fsb.view[w->fsb.selectedview];
446 XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT);
448 #ifdef FSB_ENABLE_DETAIL
449 if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) {
450 FileListDetailAdjustColWidth(w->fsb.grid);
455 static void FSBUpdateTitle(Widget w) {
456 if(XtParent(w)->core.widget_class == xmDialogShellWidgetClass) {
457 char *title = FSBDialogTitle(w);
458 XtVaSetValues(XtParent(w), XmNtitle, title, NULL);
462 static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
465 XnFileSelectionBox o = (XnFileSelectionBox)old;
466 XnFileSelectionBox n = (XnFileSelectionBox)neww;
468 int setOkBtnLabel = 0;
469 int ismanaged = XtIsManaged(neww);
470 Dimension width, height;
472 width = n->core.width;
473 height = n->core.height;
475 n->fsb.pathBar->disableResize = True;
479 if(o->fsb.selectedview != n->fsb.selectedview) {
480 int selectedview = n->fsb.selectedview;
481 n->fsb.selectedview = o->fsb.selectedview;
482 SelectView(n, selectedview);
485 char *updateDir = NULL;
487 if(o->fsb.selectedPath != n->fsb.selectedPath) {
488 STR_FREE(o->fsb.selectedPath);
489 STRDUP_RES(n->fsb.selectedPath);
490 XmTextFieldSetString(n->fsb.name, FileName(n->fsb.selectedPath));
491 // also update current directory
492 updateDir = ParentPath(n->fsb.selectedPath);
495 if(o->fsb.currentPath != n->fsb.currentPath) {
496 STR_FREE(o->fsb.currentPath);
497 updateDir = strdup(n->fsb.currentPath);
498 n->fsb.currentPath = NULL;
501 if(o->fsb.filterStr != n->fsb.filterStr) {
502 STR_FREE(o->fsb.filterStr);
503 STRDUP_RES(n->fsb.filterStr);
504 XmTextFieldSetString(XmDropDownGetText(n->fsb.filter), n->fsb.filterStr);
506 filedialog_update_dir(n, NULL);
511 filedialog_update_dir(n, updateDir);
512 PathBarSetPath(n->fsb.pathBar, updateDir);
516 if(o->fsb.type != n->fsb.type) {
517 if(n->fsb.type == FILEDIALOG_OPEN) {
518 XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.separator, NULL);
519 XtUnmanageChild(n->fsb.name);
520 XtUnmanageChild(n->fsb.nameLabel);
522 XtManageChild(n->fsb.name);
523 XtManageChild(n->fsb.nameLabel);
524 XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.nameLabel, NULL);
526 FSBUpdateTitle(neww);
532 if(o->fsb.labelListView != n->fsb.labelListView) {
533 STR_FREE(o->fsb.labelListView);
534 STRDUP_RES(n->fsb.labelListView);
535 XmString label = XmStringCreateLocalized(n->fsb.labelListView);
536 XtVaSetValues(n->fsb.viewSelectorList, XmNlabelString, label, NULL);
539 if(o->fsb.labelDetailView != n->fsb.labelDetailView) {
540 STR_FREE(o->fsb.labelDetailView);
541 STRDUP_RES(n->fsb.labelDetailView);
542 XmString label = XmStringCreateLocalized(n->fsb.labelDetailView);
543 XtVaSetValues(n->fsb.viewSelectorDetail, XmNlabelString, label, NULL);
544 if(n->fsb.detailToggleButton) {
545 XtVaSetValues(n->fsb.detailToggleButton, XmNlabelString, label, NULL);
549 if(o->fsb.labelOpenFileTitle != n->fsb.labelOpenFileTitle) {
550 STR_FREE(o->fsb.labelOpenFileTitle);
551 STRDUP_RES(n->fsb.labelOpenFileTitle);
554 if(o->fsb.labelSaveFileTitle != n->fsb.labelSaveFileTitle) {
555 STR_FREE(o->fsb.labelSaveFileTitle);
556 STRDUP_RES(n->fsb.labelSaveFileTitle);
560 if(o->fsb.labelDirUp != n->fsb.labelDirUp) {
561 XMSTR_FREE(o->fsb.labelDirUp);
562 XMS_STRDUP_RES(n->fsb.labelDirUp);
563 XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelDirUp, NULL);
565 if(o->fsb.labelHome != n->fsb.labelHome) {
566 XMSTR_FREE(o->fsb.labelHome);
567 XMS_STRDUP_RES(n->fsb.labelHome);
568 XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelHome, NULL);
570 if(o->fsb.labelNewFolder != n->fsb.labelNewFolder) {
571 XMSTR_FREE(o->fsb.labelNewFolder);
572 XMS_STRDUP_RES(n->fsb.labelNewFolder);
573 XtVaSetValues(n->fsb.newFolder, XmNlabelString, n->fsb.labelNewFolder, NULL);
575 if(o->fsb.labelFilterButton != n->fsb.labelFilterButton) {
576 XMSTR_FREE(o->fsb.labelFilterButton);
577 XMS_STRDUP_RES(n->fsb.labelFilterButton);
578 XtVaSetValues(n->fsb.filterButton, XmNlabelString, n->fsb.labelFilterButton, NULL);
580 if(o->fsb.labelShowHiddenFiles != n->fsb.labelShowHiddenFiles) {
581 XMSTR_FREE(o->fsb.labelShowHiddenFiles);
582 XMS_STRDUP_RES(n->fsb.labelShowHiddenFiles);
583 XtVaSetValues(n->fsb.showHiddenButtonW, XmNlabelString, n->fsb.labelShowHiddenFiles, NULL);
585 if(o->fsb.labelDirectories != n->fsb.labelDirectories) {
586 XMSTR_FREE(o->fsb.labelDirectories);
587 XMS_STRDUP_RES(n->fsb.labelDirectories);
588 XtVaSetValues(n->fsb.lsDirLabel, XmNlabelString, n->fsb.labelDirectories, NULL);
590 if(o->fsb.labelFiles != n->fsb.labelFiles) {
591 XMSTR_FREE(o->fsb.labelFiles);
592 XMS_STRDUP_RES(n->fsb.labelFiles);
593 XtVaSetValues(n->fsb.lsFileLabel, XmNlabelString, n->fsb.labelFiles, NULL);
595 int recreateContextMenu = 0;
596 if(o->fsb.labelRename != n->fsb.labelRename) {
597 XMSTR_FREE(o->fsb.labelRename);
598 XMS_STRDUP_RES(n->fsb.labelRename);
599 recreateContextMenu = 1;
601 if(o->fsb.labelDelete != n->fsb.labelDelete) {
602 XMSTR_FREE(o->fsb.labelDelete);
603 XMS_STRDUP_RES(n->fsb.labelDelete);
604 recreateContextMenu = 1;
607 if(o->fsb.labelOpen != n->fsb.labelOpen) {
608 XMSTR_FREE(o->fsb.labelOpen);
609 XMS_STRDUP_RES(n->fsb.labelOpen);
612 if(o->fsb.labelSave != n->fsb.labelSave) {
613 XMSTR_FREE(o->fsb.labelSave);
614 XMS_STRDUP_RES(n->fsb.labelSave);
617 if(o->fsb.labelCancel != n->fsb.labelCancel) {
618 XMSTR_FREE(o->fsb.labelCancel);
619 XMS_STRDUP_RES(n->fsb.labelCancel);
620 XtVaSetValues(n->fsb.cancelBtn, XmNlabelString, n->fsb.labelCancel, NULL);
622 if(o->fsb.labelHelp != n->fsb.labelHelp) {
623 XMSTR_FREE(o->fsb.labelHelp);
624 XMS_STRDUP_RES(n->fsb.labelHelp);
625 XtVaSetValues(n->fsb.helpBtn, XmNlabelString, n->fsb.labelHelp, NULL);
627 if(o->fsb.labelFileName != n->fsb.labelFileName) {
628 XMSTR_FREE(o->fsb.labelFileName);
629 XMS_STRDUP_RES(n->fsb.labelFileName);
630 XtVaSetValues(n->fsb.nameLabel, XmNlabelString, n->fsb.labelFileName, NULL);
632 if(o->fsb.labelDirectoryName != n->fsb.labelDirectoryName) {
633 XMSTR_FREE(o->fsb.labelDirectoryName);
634 XMS_STRDUP_RES(n->fsb.labelDirectoryName);
636 if(o->fsb.labelNewFileName != n->fsb.labelNewFileName) {
637 XMSTR_FREE(o->fsb.labelNewFileName);
638 XMS_STRDUP_RES(n->fsb.labelNewFileName);
641 if(o->fsb.labelDeleteFile != n->fsb.labelDeleteFile) {
642 STR_FREE(o->fsb.labelDeleteFile);
643 STRDUP_RES(n->fsb.labelDeleteFile);
645 #ifdef FSB_ENABLE_DETAIL
646 if(o->fsb.detailHeadings != n->fsb.detailHeadings) {
647 STR_FREE(o->fsb.detailHeadings);
648 STRDUP_RES(n->fsb.detailHeadings);
649 XtVaSetValues(n->fsb.grid, XmNsimpleHeadings, n->fsb.detailHeadings, NULL);
652 if(o->fsb.dateFormatSameYear != n->fsb.dateFormatSameYear) {
653 STR_FREE(o->fsb.dateFormatSameYear);
654 STRDUP_RES(n->fsb.dateFormatSameYear);
656 if(o->fsb.dateFormatOtherYear != n->fsb.dateFormatOtherYear) {
657 STR_FREE(o->fsb.dateFormatOtherYear);
658 STRDUP_RES(n->fsb.dateFormatOtherYear);
660 if(o->fsb.suffixBytes != n->fsb.suffixBytes) {
661 STR_FREE(o->fsb.suffixBytes);
662 STRDUP_RES(n->fsb.suffixBytes);
664 if(o->fsb.suffixMB != n->fsb.suffixMB) {
665 STR_FREE(o->fsb.suffixMB);
666 STRDUP_RES(n->fsb.suffixMB);
668 if(o->fsb.suffixGB != n->fsb.suffixGB) {
669 STR_FREE(o->fsb.suffixGB);
670 STRDUP_RES(n->fsb.suffixGB);
672 if(o->fsb.suffixTB != n->fsb.suffixTB) {
673 STR_FREE(o->fsb.suffixTB);
674 STRDUP_RES(n->fsb.suffixTB);
676 if(o->fsb.errorTitle != n->fsb.errorTitle) {
677 STR_FREE(o->fsb.errorTitle);
678 STRDUP_RES(n->fsb.errorTitle);
680 if(o->fsb.errorIllegalChar != n->fsb.errorIllegalChar) {
681 STR_FREE(o->fsb.errorIllegalChar);
682 STRDUP_RES(n->fsb.errorIllegalChar);
684 if(o->fsb.errorRename != n->fsb.errorRename) {
685 STR_FREE(o->fsb.errorRename);
686 STRDUP_RES(n->fsb.errorRename);
688 if(o->fsb.errorFolder != n->fsb.errorFolder) {
689 STR_FREE(o->fsb.errorFolder);
690 STRDUP_RES(n->fsb.errorFolder);
692 if(o->fsb.errorDelete != n->fsb.errorDelete) {
693 STR_FREE(o->fsb.errorDelete);
694 STRDUP_RES(n->fsb.errorDelete);
696 if(o->fsb.errorOpenDir != n->fsb.errorOpenDir) {
697 STR_FREE(o->fsb.errorOpenDir);
698 STRDUP_RES(n->fsb.errorOpenDir);
702 FSBUpdateTitle(neww);
704 if(recreateContextMenu) {
705 XtDestroyWidget(n->fsb.listContextMenu);
706 XtDestroyWidget(n->fsb.gridContextMenu);
707 n->fsb.listContextMenu = CreateContextMenu(n, n->fsb.filelist, FileContextMenuCB);
708 n->fsb.gridContextMenu = CreateContextMenu(n, n->fsb.grid, FileContextMenuCB);
711 XtVaSetValues(n->fsb.okBtn, XmNlabelString, n->fsb.type == FILEDIALOG_OPEN ? n->fsb.labelOpen : n->fsb.labelSave, NULL);
714 if(!ismanaged && !n->fsb.disable_set_values) {
715 n->fsb.disable_set_values = 1;
716 XtVaSetValues(neww, XmNwidth, width, XmNheight, height, NULL);
717 n->fsb.disable_set_values = 0;
720 n->fsb.pathBar->disableResize = False;
725 FSBSelectItem(n, FileName(n->fsb.selectedPath));
729 Boolean fr = (xmFormClassRec.core_class.set_values)(old, request, neww, args, num_args);
733 static void fsb_insert_child(Widget child) {
734 XnFileSelectionBox p = (XnFileSelectionBox)XtParent(child);
735 (xmFormClassRec.composite_class.insert_child)(child);
737 if(!p->fsb.gui_created) {
741 // custom child widget insert
743 XmNbottomAttachment, XmATTACH_WIDGET,
744 XmNbottomWidget, p->fsb.bottom_widget,
745 XmNbottomOffset, p->fsb.widgetSpacing,
746 XmNleftAttachment, XmATTACH_FORM,
747 XmNleftOffset, p->fsb.windowSpacing,
748 XmNrightAttachment, XmATTACH_FORM,
749 XmNrightAttachment, XmATTACH_FORM,
750 XmNrightOffset, p->fsb.windowSpacing,
754 XtVaSetValues(p->fsb.listform,
755 XmNbottomWidget, child,
759 p->fsb.workarea = child;
762 Boolean fsb_acceptfocus(Widget widget, Time *time) {
766 static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb) {
767 XnFileSelectionBox w = (XnFileSelectionBox)widget;
768 pathbar_resize(w->fsb.pathBar->widget, w->fsb.pathBar, NULL);
770 if(w->fsb.type == FILEDIALOG_OPEN) {
771 FSBView view = w->fsb.view[w->fsb.selectedview];
772 XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT);
774 XmProcessTraversal(w->fsb.name, XmTRAVERSE_CURRENT);
778 if(w->fsb.selectedPath) {
779 FSBSelectItem(w, FileName(w->fsb.selectedPath));
783 static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
787 static int apply_filter(XnFileSelectionBox w, const char *pattern, const char *string) {
788 if(!pattern) return 0;
790 FSBFilterFunc func = w->fsb.filterFunc ? w->fsb.filterFunc : FSBGlobFilter;
791 return func(pattern, string);
794 static int FSBGlobFilter(const char *a, const char *b) {
795 return fnmatch(a, b, 0);
799 static void errCB(Widget w, XtPointer d, XtPointer cbs) {
803 static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg) {
807 XmString titleStr = XmStringCreateLocalized((char*)title);
808 XmString msg = XmStringCreateLocalized((char*)errmsg);
810 XtSetArg(args[n], XmNdialogTitle, titleStr); n++;
811 XtSetArg(args[n], XmNselectionLabelString, msg); n++;
812 XtSetArg(args[n], XmNokLabelString, w->fsb.labelOk); n++;
813 XtSetArg(args[n], XmNcancelLabelString, w->fsb.labelCancel); n++;
815 Widget dialog = XmCreatePromptDialog ((Widget)w, "NewFolderPrompt", args, n);
817 Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
818 XtUnmanageChild(help);
819 Widget cancel = XmSelectionBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON);
820 XtUnmanageChild(cancel);
821 Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
822 XtUnmanageChild(text);
824 XtAddCallback(dialog, XmNokCallback, errCB, NULL);
826 XtManageChild(dialog);
828 XmStringFree(titleStr);
832 static void rename_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) {
833 XnFileSelectionBox fsb = NULL;
834 XtVaGetValues(w, XmNuserData, &fsb, NULL);
836 char *fileName = NULL;
837 XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &fileName);
839 // make sure the new file name doesn't contain a path separator
840 if(strchr(fileName, '/')) {
841 ErrDialog(fsb, fsb->fsb.errorTitle, fsb->fsb.errorIllegalChar);
846 char *parentPath = ParentPath(path);
847 char *newPath = ConcatPath(parentPath, fileName);
849 if(rename(path, newPath)) {
851 snprintf(errmsg, 256, fsb->fsb.errorRename, strerror(errno));
852 ErrDialog(fsb, fsb->fsb.errorTitle, errmsg);
854 filedialog_update_dir(fsb, parentPath);
860 XtDestroyWidget(XtParent(w));
863 static void selectionbox_cancel(Widget w, XtPointer data, XtPointer d) {
864 XtDestroyWidget(XtParent(w));
867 static void FSBRename(XnFileSelectionBox fsb, const char *path) {
870 Widget w = (Widget)fsb;
872 char *name = FileName((char*)path);
874 XmString filename = XmStringCreateLocalized(name);
875 XtSetArg(args[n], XmNselectionLabelString,fsb->fsb.labelNewFileName); n++;
876 XtSetArg(args[n], XmNtextString, filename); n++;
877 XtSetArg(args[n], XmNuserData, fsb); n++;
878 XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelRename); n++;
879 XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++;
880 XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++;
881 Widget dialog = XmCreatePromptDialog (w, "RenameFilePrompt", args, n);
883 Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
884 XtUnmanageChild(help);
886 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)rename_file_cb, (char*)path);
887 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL);
889 XmStringFree(filename);
890 XtManageChild(dialog);
893 static void delete_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) {
894 XnFileSelectionBox fsb = NULL;
895 XtVaGetValues(w, XmNuserData, &fsb, NULL);
899 snprintf(errmsg, 256, fsb->fsb.errorDelete, strerror(errno));
900 ErrDialog(fsb, fsb->fsb.errorTitle, errmsg);
902 char *parentPath = ParentPath(path);
903 filedialog_update_dir(fsb, parentPath);
907 XtDestroyWidget(XtParent(w));
910 static void FSBDelete(XnFileSelectionBox fsb, const char *path) {
913 Widget w = (Widget)fsb;
915 char *name = FileName((char*)path);
916 size_t len = strlen(name);
917 size_t msglen = len + strlen(fsb->fsb.labelDeleteFile) + 4;
918 char *msg = malloc(msglen);
919 snprintf(msg, msglen, fsb->fsb.labelDeleteFile, name);
921 XmString prompt = XmStringCreateLocalized(msg);
922 XtSetArg(args[n], XmNselectionLabelString, prompt); n++;
923 XtSetArg(args[n], XmNuserData, fsb); n++;
924 XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelDelete); n++;
925 XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++;
926 XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++;
927 Widget dialog = XmCreatePromptDialog (w, "DeleteFilePrompt", args, n);
929 Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
930 XtUnmanageChild(help);
931 Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
932 XtUnmanageChild(text);
934 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)delete_file_cb, (char*)path);
935 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL);
938 XmStringFree(prompt);
939 XtManageChild(dialog);
942 static void FSBSelectItem(XnFileSelectionBox fsb, const char *item) {
943 FSBView view = fsb->fsb.view[fsb->fsb.selectedview];
945 view.select((Widget)fsb, view.widget, item);
949 static char* set_selected_path(XnFileSelectionBox data, XmString item)
952 XmStringGetLtoR(item, XmFONTLIST_DEFAULT_TAG, &name);
956 char *path = ConcatPath(data->fsb.currentPath, name);
959 if(data->fsb.selectedPath) {
960 free(data->fsb.selectedPath);
962 data->fsb.selectedPath = path;
969 static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd) {
970 intptr_t i = (intptr_t)index;
971 Widget parent = XtParent(item);
972 XnFileSelectionBox fsb = NULL;
973 XtVaGetValues(parent, XmNuserData, &fsb, NULL);
975 const char *path = fsb->fsb.selectedPath;
978 FSBRename(fsb, path);
980 FSBDelete(fsb, path);
985 static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback) {
986 return XmVaCreateSimplePopupMenu(
987 parent, "popup", callback, XmNpopupEnabled, XmPOPUP_AUTOMATIC,
989 XmVaPUSHBUTTON, fsb->fsb.labelRename, 'R', NULL, NULL,
990 XmVaPUSHBUTTON, fsb->fsb.labelDelete, 'D', NULL, NULL,
995 static void FileListUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) {
996 XnFileSelectionBox data = userData;
997 FileListWidgetAdd(data, data->fsb.filelist, data->fsb.showHidden, filter, files, filecount);
1000 static void FileListSelect(Widget fsb, Widget view, const char *item) {
1001 XnFileSelectionBox w = (XnFileSelectionBox)fsb;
1004 XmStringTable items = NULL;
1005 XtVaGetValues(w->fsb.filelist, XmNitemCount, &numItems, XmNitems, &items, NULL);
1007 for(int i=0;i<numItems;i++) {
1009 XmStringGetLtoR(items[i], XmFONTLIST_DEFAULT_TAG, &str);
1010 if(!strcmp(str, item)) {
1011 XmListSelectPos(w->fsb.filelist, i+1, False);
1018 static void FileListCleanup(Widget fsb, Widget view, void *userData) {
1019 XnFileSelectionBox data = userData;
1020 XmListDeleteAllItems(data->fsb.filelist);
1023 static void FileListDestroy(Widget fsb, Widget view, void *userData) {
1027 static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
1029 char *path = set_selected_path(data, cb->item);
1031 data->fsb.end = True;
1032 data->fsb.status = FILEDIALOG_OK;
1033 data->fsb.selIsDir = False;
1034 FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
1038 static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
1040 if(data->fsb.type == FILEDIALOG_SAVE) {
1042 XmStringGetLtoR(cb->item, XmFONTLIST_DEFAULT_TAG, &name);
1043 XmTextFieldSetString(data->fsb.name, name);
1046 char *path = set_selected_path(data, cb->item);
1048 data->fsb.selIsDir = False;
1054 static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count)
1057 XmStringTable items = calloc(count, sizeof(XmString));
1060 for(int j=0;j<count;j++) {
1061 FileElm *e = &ls[j];
1063 char *name = FileName(e->path);
1064 if((!showHidden && name[0] == '.') || apply_filter(fsb, filter, name)) {
1068 items[i] = XmStringCreateLocalized(name);
1071 XmListAddItems(w, items, i, 0);
1072 for(i=0;i<count;i++) {
1073 XmStringFree(items[i]);
1079 #ifdef FSB_ENABLE_DETAIL
1080 static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) {
1081 XnFileSelectionBox data = userData;
1082 FileListDetailAdd(data, data->fsb.grid, data->fsb.showHidden, filter, files, filecount, maxnamelen);
1087 * create file size string with kb/mb/gb/tb suffix
1089 static char* size_str(XnFileSelectionBox fsb, FileElm *f) {
1090 char *str = malloc(16);
1091 uint64_t size = f->size;
1093 if(f->isDirectory) {
1095 } else if(size < 0x400) {
1096 snprintf(str, 16, "%d %s", (int)size, fsb->fsb.suffixBytes);
1097 } else if(size < 0x100000) {
1098 float s = (float)size/0x400;
1099 int diff = (s*100 - (int)s*100);
1104 if(size < 0x2800 && diff != 0) {
1106 snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixKB);
1108 snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixKB);
1110 } else if(size < 0x40000000) {
1111 float s = (float)size/0x100000;
1112 int diff = (s*100 - (int)s*100);
1117 if(size < 0xa00000 && diff != 0) {
1119 snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixMB);
1121 snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixMB);
1123 } else if(size < 0x1000000000ULL) {
1124 float s = (float)size/0x40000000;
1125 int diff = (s*100 - (int)s*100);
1130 if(size < 0x280000000 && diff != 0) {
1132 snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixGB);
1134 snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixGB);
1138 float s = (float)size/0x40000000;
1139 int diff = (s*100 - (int)s*100);
1144 if(size < 0x280000000 && diff != 0) {
1146 snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixTB);
1148 snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixTB);
1154 static char* date_str(XnFileSelectionBox fsb, time_t tm) {
1157 time_t now = time(NULL);
1159 localtime_r(&tm, &t);
1160 localtime_r(&now, &n);
1162 char *str = malloc(24);
1163 if(t.tm_year == n.tm_year) {
1164 strftime(str, 24, fsb->fsb.dateFormatSameYear, &t);
1166 strftime(str, 24, fsb->fsb.dateFormatOtherYear, &t);
1171 #ifdef FSB_ENABLE_DETAIL
1172 static void FileListDetailAdjustColWidth(Widget grid) {
1173 XmLGridColumn column0 = XmLGridGetColumn(grid, XmCONTENT, 0);
1174 XmLGridColumn column1 = XmLGridGetColumn(grid, XmCONTENT, 1);
1175 XmLGridColumn column2 = XmLGridGetColumn(grid, XmCONTENT, 2);
1177 Dimension col0Width = XmLGridColumnWidthInPixels(column0);
1178 Dimension col1Width = XmLGridColumnWidthInPixels(column1);
1179 Dimension col2Width = XmLGridColumnWidthInPixels(column2);
1181 Dimension totalWidth = col0Width + col1Width + col2Width;
1183 Dimension gridWidth = 0;
1184 Dimension gridShadow = 0;
1185 XtVaGetValues(grid, XmNwidth, &gridWidth, XmNshadowThickness, &gridShadow, NULL);
1187 Dimension widthDiff = gridWidth - totalWidth - gridShadow - gridShadow;
1189 if(gridWidth > totalWidth) {
1191 XmNcolumnRangeStart, 0,
1192 XmNcolumnRangeEnd, 0,
1193 XmNcolumnWidth, col0Width + widthDiff - XmLGridVSBWidth(grid) - 2,
1194 XmNcolumnSizePolicy, XmCONSTANT,
1199 static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth)
1201 XmLGridAddRows(grid, XmCONTENT, 1, count);
1204 for(int i=0;i<count;i++) {
1205 FileElm *e = &ls[i];
1207 char *name = FileName(e->path);
1208 if((!showHidden && name[0] == '.') || (!e->isDirectory && apply_filter(fsb, filter, name))) {
1213 XmString str = XmStringCreateLocalized(name);
1217 XmNcellString, str, NULL);
1220 char *szbuf = size_str(fsb, e);
1221 str = XmStringCreateLocalized(szbuf);
1225 XmNcellString, str, NULL);
1229 char *datebuf = date_str(fsb, e->lastModified);
1230 str = XmStringCreateLocalized(datebuf);
1234 XmNcellString, str, NULL);
1238 XtVaSetValues(grid, XmNrow, row, XmNrowUserData, e, NULL);
1242 // remove unused rows
1244 XmLGridDeleteRows(grid, XmCONTENT, row, count-row);
1252 XmNcolumnRangeStart, 0,
1253 XmNcolumnRangeEnd, 0,
1254 XmNcolumnWidth, maxWidth,
1255 XmNcellAlignment, XmALIGNMENT_LEFT,
1256 XmNcolumnSizePolicy, XmVARIABLE,
1259 XmNcolumnRangeStart, 1,
1260 XmNcolumnRangeEnd, 1,
1262 XmNcellAlignment, XmALIGNMENT_LEFT,
1263 XmNcolumnSizePolicy, XmVARIABLE,
1266 XmNcolumnRangeStart, 2,
1267 XmNcolumnRangeEnd, 2,
1269 XmNcellAlignment, XmALIGNMENT_RIGHT,
1270 XmNcolumnSizePolicy, XmVARIABLE,
1273 FileListDetailAdjustColWidth(grid);
1276 static void FileListDetailSelect(Widget fsb, Widget view, const char *item) {
1277 XnFileSelectionBox w = (XnFileSelectionBox)fsb;
1280 XtVaGetValues(w->fsb.grid, XmNrows, &numRows, NULL);
1282 XmLGridColumn col = XmLGridGetColumn(w->fsb.grid, XmCONTENT, 0);
1283 for(int i=0;i<numRows;i++) {
1284 XmLGridRow row = XmLGridGetRow(w->fsb.grid, XmCONTENT, i);
1285 FileElm *elm = NULL;
1286 XtVaGetValues(w->fsb.grid, XmNrowPtr, row, XmNcolumnPtr, col, XmNrowUserData, &elm, NULL);
1288 if(!strcmp(item, FileName(elm->path))) {
1289 XmLGridSelectRow(w->fsb.grid, i, False);
1290 XmLGridFocusAndShowRow(w->fsb.grid, i+1);
1297 static void FileListDetailCleanup(Widget fsb, Widget view, void *userData) {
1298 XnFileSelectionBox data = userData;
1301 XtVaGetValues(data->fsb.grid, XmNrows, &rows, NULL);
1302 XmLGridDeleteRows(data->fsb.grid, XmCONTENT, 0, rows);
1305 static void FileListDetailDestroy(Widget fsb, Widget view, void *userData) {
1310 static void create_folder(Widget w, XnFileSelectionBox data, XmSelectionBoxCallbackStruct *cbs) {
1311 char *fileName = NULL;
1312 XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &fileName);
1314 char *newFolder = ConcatPath(data->fsb.currentPath ? data->fsb.currentPath : "", fileName);
1315 if(mkdir(newFolder, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
1317 snprintf(errmsg, 256, data->fsb.errorFolder, strerror(errno));
1318 ErrDialog(data, data->fsb.errorTitle, errmsg);
1320 char *p = strdup(data->fsb.currentPath);
1321 filedialog_update_dir(data, p);
1326 XtDestroyWidget(XtParent(w));
1329 static void new_folder_cancel(Widget w, XnFileSelectionBox data, XtPointer d) {
1330 XtDestroyWidget(XtParent(w));
1333 static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u)
1338 XtSetArg(args[n], XmNdialogTitle, data->fsb.labelNewFolder); n++;
1339 XtSetArg (args[n], XmNselectionLabelString, data->fsb.labelDirectoryName); n++;
1340 XtSetArg(args[n], XmNokLabelString, data->fsb.labelOk); n++;
1341 XtSetArg(args[n], XmNcancelLabelString, data->fsb.labelCancel); n++;
1342 Widget dialog = XmCreatePromptDialog (w, "NewFolderPrompt", args, n);
1344 Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
1345 XtUnmanageChild(help);
1347 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)create_folder, data);
1348 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)new_folder_cancel, data);
1350 XtManageChild(dialog);
1354 static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u) {
1355 const char *homePath = data->fsb.homePath ? data->fsb.homePath : GetHomeDir();
1356 filedialog_update_dir(data, homePath);
1357 PathBarSetPath(data->fsb.pathBar, homePath);
1367 static int file_cmp_field = 0;
1372 static int file_cmp_order = 1;
1374 static int filecmp(const void *f1, const void *f2)
1376 const FileElm *file1 = f1;
1377 const FileElm *file2 = f2;
1378 if(file1->isDirectory != file2->isDirectory) {
1379 return file1->isDirectory < file2->isDirectory;
1382 int cmp_field = file_cmp_field;
1383 int cmp_order = file_cmp_order;
1384 if(file1->isDirectory) {
1392 ret = strcmp(FileName(file1->path), FileName(file2->path));
1396 if(file1->size < file2->size) {
1398 } else if(file1->size == file2->size) {
1406 if(file1->lastModified < file2->lastModified) {
1408 } else if(file1->lastModified == file2->lastModified) {
1417 return ret * cmp_order;
1421 static void free_files(FileElm *ls, int count)
1423 for(int i=0;i<count;i++) {
1431 static void filedialog_cleanup_filedata(XnFileSelectionBox data)
1433 free_files(data->fsb.dirs, data->fsb.dircount);
1434 free_files(data->fsb.files, data->fsb.filecount);
1435 data->fsb.dirs = NULL;
1436 data->fsb.files = NULL;
1437 data->fsb.dircount = 0;
1438 data->fsb.filecount = 0;
1439 data->fsb.maxnamelen = 0;
1442 #define FILE_ARRAY_SIZE 1024
1444 static void file_array_add(FileElm **files, int *alloc, int *count, FileElm elm) {
1449 FileElm *newarray = realloc(*files, sizeof(FileElm) * a);
1460 static int filedialog_update_dir(XnFileSelectionBox data, const char *path)
1464 // try to check first, if we can open the path
1465 dir = opendir(path);
1468 snprintf(errmsg, 256, data->fsb.errorOpenDir, strerror(errno));
1470 ErrDialog(data, data->fsb.errorTitle, errmsg);
1475 FSBView view = data->fsb.view[data->fsb.selectedview];
1476 view.cleanup((Widget)data, view.widget, view.userData);
1478 if(view.useDirList) {
1479 XmListDeleteAllItems(data->fsb.dirlist);
1482 /* read dir and insert items */
1486 size_t maxNameLen = 0;
1488 FileElm *dirs = calloc(sizeof(FileElm), FILE_ARRAY_SIZE);
1489 FileElm *files = calloc(sizeof(FileElm), FILE_ARRAY_SIZE);
1490 int dirs_alloc = FILE_ARRAY_SIZE;
1491 int files_alloc = FILE_ARRAY_SIZE;
1493 filedialog_cleanup_filedata(data);
1495 /* dir reading complete - set the path textfield */
1496 XmTextFieldSetString(data->fsb.path, (char*)path);
1497 char *oldPath = data->fsb.currentPath;
1498 data->fsb.currentPath = strdup(path);
1502 path = data->fsb.currentPath;
1505 while((ent = readdir(dir)) != NULL) {
1506 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
1510 char *entpath = ConcatPath(path, ent->d_name);
1513 if(stat(entpath, &s)) {
1519 new_entry.path = entpath;
1520 new_entry.isDirectory = S_ISDIR(s.st_mode);
1521 new_entry.size = (uint64_t)s.st_size;
1522 new_entry.lastModified = s.st_mtime;
1524 size_t nameLen = strlen(ent->d_name);
1525 if(nameLen > maxNameLen) {
1526 maxNameLen = nameLen;
1529 if(new_entry.isDirectory) {
1530 file_array_add(&dirs, &dirs_alloc, &dircount, new_entry);
1532 file_array_add(&files, &files_alloc, &filecount, new_entry);
1537 data->fsb.dirs = dirs;
1538 data->fsb.files = files;
1539 data->fsb.dircount = dircount;
1540 data->fsb.filecount = filecount;
1541 data->fsb.maxnamelen = maxNameLen;
1544 qsort(dirs, dircount, sizeof(FileElm), filecmp);
1545 qsort(files, filecount, sizeof(FileElm), filecmp);
1548 Widget filterTF = XmDropDownGetText(data->fsb.filter);
1549 char *filter = XmTextFieldGetString(filterTF);
1550 char *filterStr = filter;
1551 if(!filter || strlen(filter) == 0) {
1555 if(view.useDirList) {
1556 FileListWidgetAdd(data, data->fsb.dirlist, data->fsb.showHidden, NULL, data->fsb.dirs, data->fsb.dircount);
1563 data->fsb.filecount,
1565 data->fsb.maxnamelen,
1574 data->fsb.filecount,
1576 data->fsb.maxnamelen,
1588 static void dirlist_activate(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
1590 char *path = set_selected_path(data, cb->item);
1592 if(!filedialog_update_dir(data, path)) {
1593 PathBarSetPath(data->fsb.pathBar, path);
1594 data->fsb.selIsDir = TRUE;
1599 static void dirlist_select(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
1601 char *path = set_selected_path(data, cb->item);
1603 data->fsb.selIsDir = TRUE;
1607 static void filedialog_enable_detailview(Widget w, XnFileSelectionBox data, XmToggleButtonCallbackStruct *tb) {
1608 SelectView(data, tb->set); // 0: list, 1: detail
1612 static void filedialog_setshowhidden(
1614 XnFileSelectionBox data,
1615 XmToggleButtonCallbackStruct *tb)
1617 data->fsb.showHidden = tb->set;
1618 filedialog_update_dir(data, NULL);
1621 static void filedialog_filter(Widget w, XnFileSelectionBox data, XtPointer c)
1623 filedialog_update_dir(data, NULL);
1626 static void filedialog_update_filter(Widget w, XnFileSelectionBox data, XtPointer c)
1628 filedialog_update_dir(data, NULL);
1632 static void filedialog_goup(Widget w, XnFileSelectionBox data, XtPointer d)
1634 char *newPath = ParentPath(data->fsb.currentPath);
1635 filedialog_update_dir(data, newPath);
1636 PathBarSetPath(data->fsb.pathBar, newPath);
1640 static void filedialog_ok(Widget w, XnFileSelectionBox data, XtPointer d)
1642 if(data->fsb.type == FILEDIALOG_SAVE) {
1643 char *newName = XmTextFieldGetString(data->fsb.name);
1645 if(strchr(newName, '/')) {
1646 ErrDialog(data, data->fsb.errorTitle, data->fsb.errorIllegalChar);
1651 if(strlen(newName) > 0) {
1652 char *selPath = ConcatPath(data->fsb.currentPath, newName);
1653 if(data->fsb.selectedPath) free(data->fsb.selectedPath);
1654 data->fsb.selectedPath = selPath;
1658 data->fsb.selIsDir = False;
1662 if(data->fsb.selectedPath) {
1663 if(!data->fsb.selIsDir) {
1664 data->fsb.status = FILEDIALOG_OK;
1665 data->fsb.end = True;
1666 FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
1671 static void filedialog_cancel(Widget w, XnFileSelectionBox data, XtPointer d)
1674 data->fsb.status = FILEDIALOG_CANCEL;
1675 FileSelectionCallback(data, data->fsb.cancelCallback, XmCR_CANCEL, data->fsb.currentPath);
1678 static void filedialog_help(Widget w, XnFileSelectionBox data, XtPointer d)
1680 FileSelectionCallback(data, data->manager.help_callback, XmCR_HELP, data->fsb.currentPath);
1683 static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value) {
1684 XmFileSelectionBoxCallbackStruct cbs;
1685 memset(&cbs, 0, sizeof(XmFileSelectionBoxCallbackStruct));
1687 char *dir = fsb->fsb.currentPath;
1688 size_t dirlen = dir ? strlen(dir) : 0;
1689 if(dir && dirlen > 0) {
1691 if(dir[dirlen-1] != '/') {
1692 // add a trailing / to the dir string
1693 dir2 = malloc(dirlen+2);
1694 memcpy(dir2, dir, dirlen);
1696 dir2[dirlen+1] = '\0';
1700 cbs.dir = XmStringCreateLocalized(dir);
1701 cbs.dir_length = dirlen;
1706 cbs.dir = XmStringCreateLocalized("");
1709 cbs.reason = reason;
1711 cbs.value = XmStringCreateLocalized((char*)value);
1712 cbs.length = strlen(value);
1714 XtCallCallbackList((Widget)fsb, cb, (XtPointer)&cbs);
1716 XmStringFree(cbs.dir);
1717 XmStringFree(cbs.value);
1720 static void CreateUI(XnFileSelectionBox w) {
1725 int widget_spacing = w->fsb.widgetSpacing;
1726 int window_spacing = w->fsb.windowSpacing;
1728 Widget form = (Widget)w;
1729 int type = w->fsb.type;
1731 XtVaSetValues((Widget)w, XmNautoUnmanage, False, NULL);
1733 /* upper part of the gui */
1736 XtSetArg(args[n], XmNlabelString, w->fsb.labelDirUp); n++;
1737 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1738 XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
1739 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1740 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
1741 XtSetArg(args[n], XmNresizable, True); n++;
1742 XtSetArg(args[n], XmNarrowDirection, XmARROW_UP); n++;
1743 w->fsb.dirUp = XmCreatePushButton(form, "DirUp", args, n);
1744 XtManageChild(w->fsb.dirUp);
1745 XtAddCallback(w->fsb.dirUp, XmNactivateCallback,
1746 (XtCallbackProc)filedialog_goup, w);
1750 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1751 XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
1752 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1753 XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
1754 XtSetArg(args[n], XmNshadowThickness, 0); n++;
1755 Widget viewframe = XmCreateForm(form, "vframe", args, n);
1756 XtManageChild(viewframe);
1758 w->fsb.viewMenu = XmCreatePulldownMenu(viewframe, "menu", NULL, 0);
1761 if(w->fsb.showViewMenu) {
1763 XtSetArg(args[n], XmNsubMenuId, w->fsb.viewMenu); n++;
1764 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1765 XtSetArg(args[n], XmNmarginHeight, 0); n++;
1766 XtSetArg(args[n], XmNmarginWidth, 0); n++;
1767 view = XmCreateOptionMenu(viewframe, "option_menu", args, n);
1768 XtManageChild(view);
1769 w->fsb.viewOption = view;
1770 w->fsb.detailToggleButton = NULL;
1773 str = XmStringCreateLocalized(w->fsb.labelDetailView);
1774 XtSetArg(args[n], XmNlabelString, str); n++;
1775 XtSetArg(args[n], XmNfillOnSelect, True); n++;
1776 XtSetArg(args[n], XmNindicatorOn, False); n++;
1777 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1778 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1779 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1780 if(w->fsb.selectedview == 1) {
1781 XtSetArg(args[n], XmNset, 1); n++;
1783 w->fsb.detailToggleButton = XmCreateToggleButton(viewframe, "ToggleDetailView", args, n);
1784 XtManageChild(w->fsb.detailToggleButton);
1785 view = w->fsb.detailToggleButton;
1789 w->fsb.detailToggleButton,
1790 XmNvalueChangedCallback,
1791 (XtCallbackProc)filedialog_enable_detailview,
1794 w->fsb.viewOption = NULL;
1798 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
1799 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1800 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1801 XtSetArg(args[n], XmNrightWidget, view); n++;
1802 XtSetArg(args[n], XmNmarginHeight, 0); n++;
1803 XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
1804 XtSetArg(args[n], XmNlabelString, w->fsb.labelNewFolder); n++;
1805 w->fsb.newFolder = XmCreatePushButton(viewframe, "NewFolder", args, n);
1806 XtManageChild(w->fsb.newFolder);
1809 XmNactivateCallback,
1810 (XtCallbackProc)FSBNewFolder,
1815 XtSetArg(args[n], XmNlabelString, w->fsb.labelHome); n++;
1816 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1817 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
1818 XtSetArg(args[n], XmNrightWidget, w->fsb.newFolder); n++;
1819 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1820 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1821 w->fsb.home = XmCreatePushButton(viewframe, "Home", args, n);
1822 XtManageChild(w->fsb.home);
1825 XmNactivateCallback,
1826 (XtCallbackProc)FSBHome,
1829 // match visual appearance of detailToggleButton with the other buttons
1830 if(w->fsb.detailToggleButton) {
1831 Dimension highlight, shadow;
1832 XtVaGetValues(w->fsb.newFolder, XmNshadowThickness, &shadow, XmNhighlightThickness, &highlight, NULL);
1833 XtVaSetValues(w->fsb.detailToggleButton, XmNshadowThickness, shadow, XmNhighlightThickness, highlight, NULL);
1838 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1839 XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
1840 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
1841 XtSetArg(args[n], XmNleftWidget, w->fsb.dirUp); n++;
1842 XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
1843 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
1844 XtSetArg(args[n], XmNrightWidget, viewframe); n++;
1845 XtSetArg(args[n], XmNrightOffset, widget_spacing); n++;
1846 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++;
1847 Widget pathBarFrame = XmCreateFrame(form, "pathbar_frame", args, n);
1848 XtManageChild(pathBarFrame);
1849 w->fsb.pathBar = CreatePathBar(pathBarFrame, args, 0);
1850 w->fsb.pathBar->updateDir = (updatedir_callback)filedialog_update_dir;
1851 w->fsb.pathBar->updateDirData = w;
1852 XtManageChild(w->fsb.pathBar->widget);
1853 w->fsb.path = XmCreateTextField(form, "textfield", args, 0);
1855 XtVaSetValues(w->fsb.dirUp, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL);
1856 if(!w->fsb.showViewMenu) {
1857 XtVaSetValues(viewframe, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL);
1861 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1862 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
1863 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
1864 XtSetArg(args[n], XmNtopWidget, pathBarFrame); n++;
1865 XtSetArg(args[n], XmNtopOffset, 2*widget_spacing); n++;
1866 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1867 XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
1868 w->fsb.filterForm = XmCreateForm(form, "filterform", args, n);
1869 XtManageChild(w->fsb.filterForm);
1872 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1873 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1874 XtSetArg(args[n], XmNlabelString, w->fsb.labelDirectories); n++;
1875 w->fsb.lsDirLabel = XmCreateLabel(w->fsb.filterForm, "labelDirs", args, n);
1876 XtManageChild(w->fsb.lsDirLabel);
1879 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1880 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
1881 XtSetArg(args[n], XmNleftPosition, 35); n++;
1882 XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
1883 XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++;
1884 w->fsb.lsFileLabel = XmCreateLabel(w->fsb.filterForm, "labelFiles", args, n);
1885 XtManageChild(w->fsb.lsFileLabel);
1887 if(w->fsb.showHiddenButton) {
1889 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1890 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1891 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1892 XtSetArg(args[n], XmNlabelString, w->fsb.labelShowHiddenFiles); n++;
1893 XtSetArg(args[n], XmNset, w->fsb.showHidden); n++;
1894 w->fsb.showHiddenButtonW = XmCreateToggleButton(w->fsb.filterForm, "showHidden", args, n);
1895 XtManageChild(w->fsb.showHiddenButtonW);
1896 XtAddCallback(w->fsb.showHiddenButtonW, XmNvalueChangedCallback,
1897 (XtCallbackProc)filedialog_setshowhidden, w);
1901 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1902 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1903 XtSetArg(args[n], XmNlabelString, w->fsb.labelFilterButton); n++;
1904 if(w->fsb.showHiddenButton) {
1905 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
1906 XtSetArg(args[n], XmNrightWidget, w->fsb.showHiddenButtonW); n++;
1908 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1910 w->fsb.filterButton = XmCreatePushButton(w->fsb.filterForm, "filedialog_filter", args, n);
1911 XtManageChild(w->fsb.filterButton);
1912 XtAddCallback(w->fsb.filterButton, XmNactivateCallback,
1913 (XtCallbackProc)filedialog_filter, w);
1916 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1917 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1918 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
1919 XtSetArg(args[n], XmNleftWidget, w->fsb.lsFileLabel); n++;
1920 XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
1921 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
1922 XtSetArg(args[n], XmNrightWidget, w->fsb.filterButton); n++;
1923 XtSetArg(args[n], XmNrightOffset, widget_spacing); n++;
1924 XtSetArg(args[n], XmNshowLabel, False); n++;
1925 XtSetArg(args[n], XmNuseTextField, True); n++;
1926 XtSetArg(args[n], XmNverify, False); n++;
1927 w->fsb.filter = XmCreateDropDown(w->fsb.filterForm, "filedialog_filter_textfield", args, n);
1928 XtManageChild(w->fsb.filter);
1929 XmTextFieldSetString(XmDropDownGetText(w->fsb.filter), w->fsb.filterStr);
1930 XtAddCallback(XmDropDownGetText(w->fsb.filter), XmNactivateCallback,
1931 (XtCallbackProc)filedialog_filter, w);
1932 XtAddCallback(w->fsb.filter, XmNupdateTextCallback,
1933 (XtCallbackProc)filedialog_update_filter, w);
1934 Widget filterList = XmDropDownGetList(w->fsb.filter);
1935 str = XmStringCreateSimple("*");
1936 XmListAddItem(filterList, str, 0);
1941 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1942 XtSetArg(args[n], XmNbottomOffset, window_spacing); n++;
1943 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1944 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
1945 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1946 XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
1947 XtSetArg(args[n], XmNtopOffset, widget_spacing * 2); n++;
1948 Widget buttons = XmCreateForm(form, "buttons", args, n);
1949 XtManageChild(buttons);
1952 str = type == FILEDIALOG_OPEN ? w->fsb.labelOpen : w->fsb.labelSave;
1953 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1954 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1955 XtSetArg(args[n], XmNlabelString, str); n++;
1956 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1957 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
1958 XtSetArg(args[n], XmNrightPosition, 14); n++;
1959 w->fsb.okBtn = XmCreatePushButton(buttons, "filedialog_open", args, n);
1960 XtManageChild(w->fsb.okBtn);
1962 XtAddCallback(w->fsb.okBtn, XmNactivateCallback,
1963 (XtCallbackProc)filedialog_ok, w);
1966 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1967 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1968 XtSetArg(args[n], XmNlabelString, w->fsb.labelHelp); n++;
1969 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1970 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
1971 XtSetArg(args[n], XmNleftPosition, 86); n++;
1972 w->fsb.helpBtn = XmCreatePushButton(buttons, "filedialog_help", args, n);
1973 XtManageChild(w->fsb.helpBtn);
1974 XtAddCallback(w->fsb.helpBtn, XmNactivateCallback,
1975 (XtCallbackProc)filedialog_help, w);
1978 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1979 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1980 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
1981 XtSetArg(args[n], XmNleftPosition, 43); n++;
1982 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
1983 XtSetArg(args[n], XmNrightPosition, 57); n++;
1984 XtSetArg(args[n], XmNlabelString, w->fsb.labelCancel); n++;
1985 w->fsb.cancelBtn = XmCreatePushButton(buttons, "filedialog_cancel", args, n);
1986 XtManageChild(w->fsb.cancelBtn);
1987 XtAddCallback(w->fsb.cancelBtn, XmNactivateCallback,
1988 (XtCallbackProc)filedialog_cancel, w);
1991 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
1992 XtSetArg(args[n], XmNbottomWidget, buttons); n++;
1993 XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
1994 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1995 XtSetArg(args[n], XmNleftOffset, 1); n++;
1996 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1997 XtSetArg(args[n], XmNrightOffset, 1); n++;
1998 w->fsb.separator = XmCreateSeparator(form, "ofd_separator", args, n);
1999 XtManageChild(w->fsb.separator);
2001 Widget bottomWidget = w->fsb.separator;
2004 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
2005 XtSetArg(args[n], XmNbottomWidget, w->fsb.separator); n++;
2006 XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
2007 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2008 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
2009 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
2010 XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
2011 w->fsb.name = XmCreateTextField(form, "textfield", args, n);
2012 XtAddCallback(w->fsb.name, XmNactivateCallback,
2013 (XtCallbackProc)filedialog_ok, w);
2016 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
2017 XtSetArg(args[n], XmNbottomWidget, w->fsb.name); n++;
2018 XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
2019 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2020 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
2021 XtSetArg(args[n], XmNlabelString, w->fsb.labelFileName); n++;
2022 w->fsb.nameLabel = XmCreateLabel(form, "label", args, n);
2024 if(type == FILEDIALOG_SAVE) {
2025 bottomWidget = w->fsb.nameLabel;
2026 XtManageChild(w->fsb.name);
2027 XtManageChild(w->fsb.nameLabel);
2029 w->fsb.bottom_widget = bottomWidget;
2033 // form for dir/file lists
2035 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2036 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
2037 XtSetArg(args[n], XmNtopWidget, w->fsb.filterForm); n++;
2038 XtSetArg(args[n], XmNtopOffset, widget_spacing); n++;
2039 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
2040 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
2041 XtSetArg(args[n], XmNbottomWidget, bottomWidget); n++;
2042 XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
2043 XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
2044 XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
2045 XtSetArg(args[n], XmNwidth, 580); n++;
2046 XtSetArg(args[n], XmNheight, 400); n++;
2047 w->fsb.listform = XmCreateForm(form, "fds_listform", args, n);
2052 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2053 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
2054 XtSetArg(args[n], XmNtopWidget, w->fsb.lsDirLabel); n++;
2055 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
2056 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
2057 XtSetArg(args[n], XmNrightPosition, 35); n++;
2058 w->fsb.dirlist = XmCreateScrolledList(w->fsb.listform, "dirlist", args, n);
2059 Dimension width, height;
2060 XtMakeResizeRequest(w->fsb.dirlist, 150, 200, &width, &height);
2061 XtManageChild(w->fsb.dirlist);
2065 XmNdefaultActionCallback,
2066 (XtCallbackProc)dirlist_activate,
2070 XmNbrowseSelectionCallback,
2071 (XtCallbackProc)dirlist_select,
2075 XnFileSelectionBoxAddView(
2077 w->fsb.labelListView,
2087 #ifdef FSB_ENABLE_DETAIL
2088 XnFileSelectionBoxAddView(
2090 w->fsb.labelDetailView,
2092 FileListDetailUpdate,
2093 FileListDetailSelect,
2094 FileListDetailCleanup,
2095 FileListDetailDestroy,
2102 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
2103 XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++;
2104 XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
2105 //XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
2106 //XtSetArg(args[n], XmNbottomWidget, w->fsb.filelist); n++;
2107 XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
2108 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
2109 XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++;
2110 w->fsb.lsFileLabel = XmCreateLabel(w->fsb.listform, "label", args, n);
2111 XtManageChild(w->fsb.lsFileLabel);
2114 XtManageChild(w->fsb.listform);
2116 int selview = w->fsb.selectedview;
2118 XtManageChild(w->fsb.view[selview].widget);
2120 w->fsb.selectedview = 0;
2124 if(w->fsb.selectedPath) {
2125 char *parentPath = ParentPath(w->fsb.selectedPath);
2126 filedialog_update_dir(w, parentPath);
2127 PathBarSetPath(w->fsb.pathBar, parentPath);
2130 if(w->fsb.type == FILEDIALOG_SAVE) {
2131 XmTextFieldSetString(w->fsb.name, FileName(w->fsb.selectedPath));
2135 const char *currentPath = w->fsb.currentPath;
2137 if(getcwd(cwd, PATH_MAX)) {
2140 currentPath = GetHomeDir();
2144 filedialog_update_dir(w, currentPath);
2145 PathBarSetPath(w->fsb.pathBar, w->fsb.currentPath);
2149 w->fsb.selectedview = selview;
2151 XtVaSetValues((Widget)w, XmNcancelButton, w->fsb.cancelBtn, NULL);
2153 w->fsb.gui_created = 1;
2156 static char* FSBDialogTitle(Widget widget) {
2157 XnFileSelectionBox w = (XnFileSelectionBox)widget;
2158 if(w->fsb.type == FILEDIALOG_OPEN) {
2159 return w->fsb.labelOpenFileTitle;
2161 return w->fsb.labelSaveFileTitle;
2165 static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc create, void *userData, Boolean useDirList) {
2169 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
2170 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
2172 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
2173 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
2174 XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++;
2175 XtSetArg(args[n], XmNleftOffset, w->fsb.widgetSpacing); n++;
2177 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
2178 XtSetArg(args[n], XmNtopOffset, w->fsb.widgetSpacing); n++;
2179 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2182 return create(w->fsb.listform, args, n, userData);
2186 typedef struct FSBViewSelection {
2187 XnFileSelectionBox fsb;
2191 static void SelectView(XnFileSelectionBox f, int view) {
2192 FSBView current = f->fsb.view[f->fsb.selectedview];
2193 FSBView newview = f->fsb.view[view];
2195 XtUnmanageChild(current.widget);
2196 if(newview.useDirList != current.useDirList) {
2197 if(current.useDirList) {
2198 XtUnmanageChild(f->fsb.listform);
2200 XtManageChild(f->fsb.listform);
2204 current.cleanup((Widget)f, current.widget, current.userData);
2205 XtManageChild(newview.widget);
2207 f->fsb.selectedview = view;
2209 filedialog_update_dir(f, NULL);
2210 XmProcessTraversal(newview.focus, XmTRAVERSE_CURRENT);
2213 static void SelectViewCallback(Widget w, FSBViewSelection *data, XtPointer u) {
2214 SelectView(data->fsb, data->index);
2217 static void SelectViewItemDestroy(Widget w, FSBViewSelection *data, XtPointer u) {
2221 static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex) {
2225 XmString label = XmStringCreateLocalized((char*)name);
2227 XtSetArg(args[n], XmNlabelString, label); n++;
2228 XtSetArg(args[1], XmNpositionIndex, w->fsb.selectedview == w->fsb.numviews ? 0 : w->fsb.numviews+1); n++;
2229 Widget item = XmCreatePushButton(w->fsb.viewMenu, "menuitem", args, n);
2231 if(viewIndex == 0) {
2232 w->fsb.viewSelectorList = item;
2233 } else if(viewIndex == 1) {
2234 w->fsb.viewSelectorDetail = item;
2237 XtManageChild(item);
2238 XmStringFree(label);
2240 FSBViewSelection *data = malloc(sizeof(FSBViewSelection));
2242 data->index = viewIndex;
2246 XmNactivateCallback,
2247 (XtCallbackProc)SelectViewCallback,
2252 (XtCallbackProc)SelectViewItemDestroy,
2256 static FSBViewWidgets CreateListView(Widget parent, ArgList args, int n, void *userData) {
2257 XnFileSelectionBox fsb = (XnFileSelectionBox)userData;
2259 XtSetArg(args[n], XmNshadowThickness, 0); n++;
2260 Widget frame = XmCreateFrame(parent, "filelistframe", args, n);
2262 fsb->fsb.filelist = XmCreateScrolledList(frame, "filelist", NULL, 0);
2263 XtManageChild(fsb->fsb.filelist);
2267 XmNdefaultActionCallback,
2268 (XtCallbackProc)FileListActivateCB,
2272 XmNbrowseSelectionCallback,
2273 (XtCallbackProc)FileListSelectCB,
2276 fsb->fsb.listContextMenu = CreateContextMenu(fsb, fsb->fsb.filelist, FileContextMenuCB);
2278 FSBViewWidgets widgets;
2279 widgets.view = frame;
2280 widgets.focus = fsb->fsb.filelist;
2284 #ifdef FSB_ENABLE_DETAIL
2285 static void set_path_from_row(XnFileSelectionBox data, int row) {
2286 FileElm *elm = NULL;
2287 XmLGridRow rowPtr = XmLGridGetRow(data->fsb.grid, XmCONTENT, row);
2288 XtVaGetValues(data->fsb.grid, XmNrowPtr, rowPtr, XmNrowUserData, &elm, NULL);
2290 fprintf(stderr, "error: no row data\n");
2294 char *path = strdup(elm->path);
2296 data->fsb.selIsDir = False;
2297 if(data->fsb.type == FILEDIALOG_SAVE) {
2298 XmTextFieldSetString(data->fsb.name, FileName(path));
2301 if(data->fsb.selectedPath) {
2302 free(data->fsb.selectedPath);
2304 data->fsb.selectedPath = path;
2307 static void grid_select(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
2308 set_path_from_row(data, cb->row);
2311 static void grid_activate(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
2312 set_path_from_row(data, cb->row);
2313 data->fsb.end = True;
2314 data->fsb.status = FILEDIALOG_OK;
2316 FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
2319 static void grid_key_pressed(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
2324 nchars = XLookupString(&cb->event->xkey, chars, 15, &keysym, NULL);
2326 if(nchars == 0) return;
2328 // if data->showHidden is 0, data->files contains more items than the grid
2329 // this means SelectedRow might not be the correct index for data->files
2330 // we have to count files manually and increase 'row', if the file
2331 // is actually displayed in the grid
2333 int selectedRow = XmLGridGetSelectedRow(w);
2337 for(int i=0;i<data->fsb.filecount;i++) {
2338 const char *name = FileName(data->fsb.files[i].path);
2339 if(!data->fsb.showHidden && name[0] == '.') continue;
2341 size_t namelen = strlen(name);
2343 size_t cmplen = namelen < nchars ? namelen : nchars;
2344 if(!memcmp(name, chars, cmplen)) {
2345 if(row <= selectedRow) {
2359 XmLGridSelectRow(w, match, True);
2360 XmLGridFocusAndShowRow(w, match+1);
2362 XBell(XtDisplay(w), 0);
2366 static void grid_header_clicked(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
2367 int new_cmp_field = 0;
2368 switch(cb->column) {
2383 if(new_cmp_field == file_cmp_field) {
2384 file_cmp_order = -file_cmp_order; // revert sort order
2386 file_cmp_field = new_cmp_field; // change file cmp order to new field
2390 int sort_type = file_cmp_order == 1 ? XmSORT_ASCENDING : XmSORT_DESCENDING;
2391 XmLGridSetSort(data->fsb.grid, file_cmp_field, sort_type);
2393 qsort(data->fsb.files, data->fsb.filecount, sizeof(FileElm), filecmp);
2396 filedialog_update_dir(data, NULL);
2399 static FSBViewWidgets CreateDetailView(Widget parent, ArgList args, int n, void *userData) {
2400 XnFileSelectionBox w = userData;
2402 XtSetArg(args[n], XmNshadowThickness, 0); n++;
2403 Widget gridcontainer = XmCreateFrame(parent, "gridcontainer", args, n);
2404 XtManageChild(gridcontainer);
2407 XtSetArg(args[n], XmNcolumns, 3); n++;
2408 XtSetArg(args[n], XmNheadingColumns, 0); n++;
2409 XtSetArg(args[n], XmNheadingRows, 1); n++;
2410 XtSetArg(args[n], XmNallowColumnResize, 1); n++;
2411 XtSetArg(args[n], XmNsimpleHeadings, w->fsb.detailHeadings); n++;
2412 XtSetArg(args[n], XmNhorizontalSizePolicy, XmCONSTANT); n++;
2414 w->fsb.grid = XmLCreateGrid(gridcontainer, "grid", args, n);
2415 XmLGridSetIgnoreModifyVerify(w->fsb.grid, True);
2416 XtManageChild(w->fsb.grid);
2420 XmNcellDefaults, True,
2421 XtVaTypedArg, XmNblankBackground, XmRString, "white", 6,
2422 XtVaTypedArg, XmNcellBackground, XmRString, "white", 6,
2425 XtAddCallback(w->fsb.grid, XmNselectCallback, (XtCallbackProc)grid_select, w);
2426 XtAddCallback(w->fsb.grid, XmNactivateCallback, (XtCallbackProc)grid_activate, w);
2427 XtAddCallback(w->fsb.grid, XmNheaderClickCallback, (XtCallbackProc)grid_header_clicked, w);
2428 XtAddCallback(w->fsb.grid, XmNgridKeyPressedCallback, (XtCallbackProc)grid_key_pressed, w);
2431 w->fsb.gridContextMenu = CreateContextMenu(w, w->fsb.grid, FileContextMenuCB);
2433 FSBViewWidgets widgets;
2434 widgets.view = gridcontainer;
2435 widgets.focus = w->fsb.grid;
2441 /* ------------------------------ Path Utils ------------------------------ */
2443 const char* GetHomeDir(void) {
2444 char *home = getenv("HOME");
2446 home = getenv("USERPROFILE");
2454 static char* ConcatPath(const char *parent, const char *name)
2456 size_t parentlen = strlen(parent);
2457 size_t namelen = strlen(name);
2459 size_t pathlen = parentlen + namelen + 2;
2460 char *path = malloc(pathlen);
2462 memcpy(path, parent, parentlen);
2463 if(parentlen > 0 && parent[parentlen-1] != '/') {
2464 path[parentlen] = '/';
2467 if(name[0] == '/') {
2471 memcpy(path+parentlen, name, namelen);
2472 path[parentlen+namelen] = '\0';
2476 static char* FileName(char *path) {
2482 while((c = path[i]) != 0) {
2491 char *name = path + si + p;
2493 name = path + osi + p;
2502 static char* ParentPath(const char *path) {
2503 char *name = FileName((char*)path);
2504 size_t namelen = strlen(name);
2505 size_t pathlen = strlen(path);
2506 size_t parentlen = pathlen - namelen;
2507 if(parentlen == 0) {
2510 char *parent = malloc(parentlen + 1);
2511 memcpy(parent, path, parentlen);
2512 parent[parentlen] = '\0';
2516 // unused at the moment, maybe reactivate if more illegal characters
2519 static int CheckFileName(const char *fileName) {
2520 size_t len = strlen(fileName);
2521 for(int i=0;i<len;i++) {
2522 if(fileName[i] == '/') {
2530 /* ------------------------------ PathBar ------------------------------ */
2532 static void pathbar_resize(Widget w, PathBar *p, XtPointer d)
2534 if(p->disableResize) return;
2536 Dimension width, height;
2537 XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
2540 XtVaGetValues(p->down, XmNwidth, &xoff, NULL);
2542 Dimension *segW = calloc(p->numSegments, sizeof(Dimension));
2544 Dimension maxHeight = 0;
2546 /* get width/height from all widgets */
2547 Dimension pathWidth = 0;
2548 for(int i=0;i<p->numSegments;i++) {
2550 Dimension segHeight;
2551 XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL);
2553 pathWidth += segWidth;
2554 if(segHeight > maxHeight) {
2555 maxHeight = segHeight;
2559 XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL);
2560 if(tfHeight > maxHeight) {
2561 maxHeight = tfHeight;
2564 Boolean arrows = False;
2565 if(pathWidth + xoff + 10 > width) {
2567 //pathWidth += p->lw + p->rw;
2570 /* calc max visible widgets */
2573 Dimension vis = p->lw+p->rw;
2574 for(int i=p->numSegments;i>0;i--) {
2575 Dimension segWidth = segW[i-1];
2576 if(vis + segWidth + xoff + 10 > width) {
2589 if(start + p->shift < 0) {
2592 p->shift = -leftShift;
2594 leftShift = -p->shift; /* negative shift */
2601 XtManageChild(p->left);
2602 XtManageChild(p->right);
2605 XtUnmanageChild(p->left);
2606 XtUnmanageChild(p->right);
2609 for(int i=0;i<p->numSegments;i++) {
2610 if(i >= start && i < p->numSegments - leftShift && !p->input) {
2611 XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
2613 XtManageChild(p->pathSegments[i]);
2615 XtUnmanageChild(p->pathSegments[i]);
2620 XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL);
2621 XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
2623 XtVaSetValues(p->down, XmNx, width-xoff, XmNheight, maxHeight, NULL);
2627 XtMakeResizeRequest(w, width, maxHeight, &rw, &rh);
2628 XtVaSetValues(p->textfield, XmNwidth, rw-xoff, XmNheight, rh, NULL);
2631 static void pathbar_input(Widget w, PathBar *p, XtPointer c)
2633 XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c;
2634 XEvent *xevent = cbs->event;
2636 if (cbs->reason == XmCR_INPUT) {
2637 if (xevent->xany.type == ButtonPress) {
2638 XtUnmanageChild(p->left);
2639 XtUnmanageChild(p->right);
2641 XtManageChild(p->textfield);
2644 XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT);
2646 pathbar_resize(p->widget, p, NULL);
2651 static void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c)
2654 XtUnmanageChild(p->textfield);
2655 pathbar_resize(p->widget, p, NULL);
2658 static void pathbar_pathinput(Widget w, PathBar *pb, XtPointer d)
2660 char *newpath = XmTextFieldGetString(pb->textfield);
2662 if(newpath[0] == '~') {
2663 char *p = newpath+1;
2664 char *cp = ConcatPath(GetHomeDir(), p);
2667 } else if(newpath[0] != '/') {
2669 if(!getcwd(cwd, sizeof(cwd))) {
2673 char *cp = ConcatPath(cwd, newpath);
2680 if(!pb->updateDir(pb->updateDirData, newpath)) {
2681 PathBarSetPath(pb, newpath);
2684 PathBarSetPath(pb, newpath);
2688 /* hide textfield and show path as buttons */
2689 XtUnmanageChild(pb->textfield);
2690 pathbar_resize(pb->widget, pb, NULL);
2694 static void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) {
2696 pathbar_resize(p->widget, p, NULL);
2699 static void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) {
2703 pathbar_resize(p->widget, p, NULL);
2706 static void pathbar_list_select(Widget w, PathBar *p, XmListCallbackStruct *cb) {
2708 XmStringGetLtoR(cb->item, XmSTRING_DEFAULT_CHARSET, &value);
2709 p->updateDir(p->updateDirData, value);
2710 PathBarSetPath(p, value);
2714 static void pathbar_popup(Widget w, PathBar *p, XtPointer d) {
2715 Widget parent = XtParent(w);
2716 Display *dp = XtDisplay(parent);
2717 Window root = XDefaultRootWindow(dp);
2721 XTranslateCoordinates(dp, XtWindow(parent), root, 0, 0, &x, &y, &child);
2723 XtManageChild(p->list);
2724 XtPopupSpringLoaded(p->popup);
2725 XtVaSetValues(p->popup, XmNx, x, XmNy, y + parent->core.height, XmNwidth, parent->core.width, XmNheight, 200, NULL);
2727 XmProcessTraversal(p->list, XmTRAVERSE_CURRENT);
2730 static void popupEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
2731 PathBar *bar = data;
2733 Window w1 = bar->hs ? XtWindow(bar->hs) : 0;
2734 Window w2 = bar->vs ? XtWindow(bar->vs) : 0;
2736 if(event->type == ButtonPress) {
2737 if(event->xbutton.window != 0 && (event->xbutton.window == w1 || event->xbutton.window == w2)) {
2738 bar->popupScrollEvent = 1;
2740 bar->popupScrollEvent = 0;
2742 } else if(event->type == ButtonRelease) {
2743 if(bar->popupScrollEvent) {
2746 bar->popupScrollEvent = 0;
2747 } else if(event->type == KeyReleaseMask) {
2748 int keycode = event->xkey.keycode;
2749 if(keycode == 36 || keycode == 9) {
2750 XtUnmapWidget(bar->popup);
2755 static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
2757 if(event->type == KeyReleaseMask) {
2758 if(event->xkey.keycode == 9) {
2759 XtUnmanageChild(pb->textfield);
2760 pathbar_resize(pb->widget, pb, NULL);
2767 PathBar* CreatePathBar(Widget parent, ArgList args, int n)
2769 PathBar *bar = malloc(sizeof(PathBar));
2771 bar->updateDir = NULL;
2772 bar->updateDirData = NULL;
2773 bar->disableResize = False;
2777 XtSetArg(args[n], XmNmarginWidth, 0); n++;
2778 XtSetArg(args[n], XmNmarginHeight, 0); n++;
2779 bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n);
2783 (XtCallbackProc)pathbar_resize,
2788 (XtCallbackProc)pathbar_input,
2792 XtSetArg(args[n], XmNownerEvents, True), n++;
2793 XtSetArg(args[n], XmNgrabStyle, GrabModeSync), n++;
2794 bar->popup = XmCreateGrabShell(bar->widget, "pbpopup", args, n);
2795 bar->list = XmCreateScrolledList(bar->popup, "pblist", NULL, 0);
2796 XtAddEventHandler(bar->popup, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, FALSE, popupEH, bar);
2797 bar->popupScrollEvent = 0;
2801 XmNdefaultActionCallback,
2802 (XtCallbackProc)pathbar_list_select,
2806 XmNbrowseSelectionCallback,
2807 (XtCallbackProc)pathbar_list_select,
2812 XtVaGetValues(XtParent(bar->list), XmNhorizontalScrollBar, &bar->hs, XmNverticalScrollBar, &bar->vs, NULL);
2815 XtSetArg(a[0], XmNshadowThickness, 0);
2816 XtSetArg(a[1], XmNx, 0);
2817 XtSetArg(a[2], XmNy, 0);
2818 bar->textfield = XmCreateTextField(bar->widget, "pbtext", a, 3);
2822 XmNlosingFocusCallback,
2823 (XtCallbackProc)pathbar_losingfocus,
2825 XtAddCallback(bar->textfield, XmNactivateCallback,
2826 (XtCallbackProc)pathbar_pathinput, bar);
2827 XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar);
2829 XtSetArg(a[0], XmNarrowDirection, XmARROW_DOWN);
2830 bar->down = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
2831 XtManageChild(bar->down);
2832 XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT);
2833 bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
2834 XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT);
2835 bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
2838 XmNactivateCallback,
2839 (XtCallbackProc)pathbar_popup,
2843 XmNactivateCallback,
2844 (XtCallbackProc)pathbar_shift_left,
2848 XmNactivateCallback,
2849 (XtCallbackProc)pathbar_shift_right,
2853 XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL);
2854 XtVaSetValues(bar->widget, XmNbackground, bg, NULL);
2856 //XtManageChild(bar->left);
2857 //XtManageChild(bar->right);
2859 XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL);
2860 XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL);
2862 bar->segmentAlloc = 16;
2863 bar->numSegments = 0;
2864 bar->pathSegments = calloc(16, sizeof(Widget));
2871 static void PathBarChangeDir(Widget w, PathBar *bar, XtPointer unused) {
2872 XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False);
2874 for(int i=0;i<bar->numSegments;i++) {
2875 if(bar->pathSegments[i] == w) {
2877 XmToggleButtonSetState(w, True, False);
2882 int plen = strlen(bar->path);
2884 for(int i=0;i<=plen;i++) {
2885 char c = bar->path[i];
2886 if(c == '/' || c == '\0') {
2887 if(countSeg == bar->selection) {
2888 char *dir = malloc(i+2);
2889 memcpy(dir, bar->path, i+1);
2891 if(bar->updateDir) {
2892 bar->updateDir(bar->updateDirData, dir);
2901 void PathBarSetPath(PathBar *bar, const char *path) {
2905 bar->path = strdup(path);
2907 for(int i=0;i<bar->numSegments;i++) {
2908 XtDestroyWidget(bar->pathSegments[i]);
2910 XtUnmanageChild(bar->textfield);
2911 //XtManageChild(bar->left);
2912 //XtManageChild(bar->right);
2918 bar->numSegments = 0;
2921 if(path[0] == '/') {
2922 str = XmStringCreateLocalized("/");
2923 XtSetArg(args[0], XmNlabelString, str);
2924 XtSetArg(args[1], XmNfillOnSelect, True);
2925 XtSetArg(args[2], XmNindicatorOn, False);
2926 bar->pathSegments[0] = XmCreateToggleButton(
2927 bar->widget, "pbbutton", args, 3);
2929 bar->pathSegments[0],
2930 XmNvalueChangedCallback,
2931 (XtCallbackProc)PathBarChangeDir,
2938 int len = strlen(path);
2942 if((c == '/' || c == '\0') && i > begin) {
2943 char *segStr = malloc(i - begin + 1);
2944 memcpy(segStr, path+begin, i-begin);
2945 segStr[i-begin] = '\0';
2948 str = XmStringCreateLocalized(segStr);
2950 XtSetArg(args[0], XmNlabelString, str);
2951 XtSetArg(args[1], XmNfillOnSelect, True);
2952 XtSetArg(args[2], XmNindicatorOn, False);
2953 Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3);
2956 XmNvalueChangedCallback,
2957 (XtCallbackProc)PathBarChangeDir,
2961 if(bar->numSegments >= bar->segmentAlloc) {
2962 bar->segmentAlloc += 8;
2963 bar->pathSegments = realloc(bar->pathSegments, bar->segmentAlloc * sizeof(Widget));
2966 bar->pathSegments[bar->numSegments++] = button;
2970 bar->selection = bar->numSegments-1;
2971 XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False);
2973 XmTextFieldSetString(bar->textfield, (char*)path);
2974 XmTextFieldSetInsertionPosition(bar->textfield, XmTextFieldGetLastPosition(bar->textfield));
2976 pathbar_resize(bar->widget, bar, NULL);
2979 void PathBarSetDirList(PathBar *bar, const char **dirlist, size_t nelm) {
2980 XmStringTable items = calloc(nelm, sizeof(XmString));
2981 for(int i=0;i<nelm;i++) {
2982 items[i] = XmStringCreateLocalized((char*)dirlist[i]);
2984 XmListDeleteAllItems(bar->list);
2985 XmListAddItems(bar->list, items, nelm, 0);
2986 XmListSelectPos(bar->list, 1, False);
2987 for(int i=0;i<nelm;i++) {
2988 XmStringFree(items[i]);
2993 void PathBarDestroy(PathBar *bar) {
2997 free(bar->pathSegments);
3001 /* ------------------------------ public API ------------------------------ */
3003 Widget XnCreateFileSelectionDialog(
3009 Widget dialog = XmCreateDialogShell(parent, "FileDialog", NULL, 0);
3010 Widget fsb = XnCreateFileSelectionBox(dialog, name, arglist, argcount);
3011 char *title = FSBDialogTitle(fsb);
3012 XtVaSetValues(dialog, XmNtitle, title, NULL);
3016 Widget XnCreateFileSelectionBox(
3022 Widget fsb = XtCreateWidget(name, xnFsbWidgetClass, parent, arglist, argcount);
3026 void XnFileSelectionBoxAddView(
3029 FSBViewCreateProc create,
3030 FSBViewUpdateProc update,
3031 FSBViewSelectProc select,
3032 FSBViewCleanupProc cleanup,
3033 FSBViewDestroyProc destroy,
3037 XnFileSelectionBox f = (XnFileSelectionBox)fsb;
3038 if(f->fsb.numviews >= FSB_MAX_VIEWS) {
3039 fprintf(stderr, "XnFileSelectionBox: too many views\n");
3044 view.update = update;
3045 view.select = select;
3046 view.cleanup = cleanup;
3047 view.destroy = destroy;
3048 view.useDirList = useDirList;
3049 view.userData = userData;
3051 FSBViewWidgets widgets = CreateView(f, create, userData, useDirList);
3052 view.widget = widgets.view;
3053 view.focus = widgets.focus;
3055 AddViewMenuItem(f, name, f->fsb.numviews);
3057 f->fsb.view[f->fsb.numviews++] = view;
3060 void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm) {
3061 XnFileSelectionBox f = (XnFileSelectionBox)fsb;
3062 PathBarSetDirList(f->fsb.pathBar, dirlist, nelm);
3065 Widget XnFileSelectionBoxWorkArea(Widget fsb) {
3066 XnFileSelectionBox f = (XnFileSelectionBox)fsb;
3067 return f->fsb.workarea;
3070 Widget XnFileSelectionBoxGetChild(Widget fsb, enum XnFSBChild child) {
3071 XnFileSelectionBox w = (XnFileSelectionBox)fsb;
3073 case XnFSB_DIR_UP_BUTTON: return w->fsb.dirUp;
3074 case XnFSB_HOME_BUTTON: return w->fsb.home;
3075 case XnFSB_NEW_FOLDER_BUTTON: return w->fsb.newFolder;
3076 case XnFSB_DETAIL_TOGGLE_BUTTON: return w->fsb.detailToggleButton;
3077 case XnFSB_VIEW_OPTION_BUTTON: return w->fsb.viewOption;
3078 case XnFSB_FILTER_DROPDOWN: return w->fsb.filter;
3079 case XnFSB_FILTER_BUTTON: return w->fsb.filterButton;
3080 case XnFSB_SHOW_HIDDEN_TOGGLE_BUTTON: return w->fsb.showHiddenButtonW;
3081 case XnFSB_DIRECTORIES_LABEL: return w->fsb.lsDirLabel;
3082 case XnFSB_FILES_LABEL: return w->fsb.lsFileLabel;
3083 case XnFSB_DIRLIST: return w->fsb.dirlist;
3084 case XnFSB_FILELIST: return w->fsb.filelist;
3085 case XnFSB_GRID: return w->fsb.grid;
3086 case XnFSB_OK_BUTTON: return w->fsb.okBtn;
3087 case XnFSB_CANCEL_BUTTON: return w->fsb.cancelBtn;
3088 case XnFSB_HELP_BUTTON: return w->fsb.helpBtn;
3093 void XnFileSelectionBoxDeleteFilters(Widget fsb) {
3094 XnFileSelectionBox w = (XnFileSelectionBox)fsb;
3095 Widget filterList = XmDropDownGetList(w->fsb.filter);
3096 XmListDeleteAllItems(filterList);
3099 void XnFileSelectionBoxAddFilter(Widget fsb, const char *filter) {
3100 XnFileSelectionBox w = (XnFileSelectionBox)fsb;
3101 Widget filterList = XmDropDownGetList(w->fsb.filter);
3103 XmString str = XmStringCreateSimple((char*)filter);
3104 XmListAddItem(filterList, str, 0);