Sun, 07 Dec 2025 12:24:12 +0100
implement ui_listview_destroy (Motif)
/* * Copyright 2021 Olaf Wintermann * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ //define FSB_ENABLE_DETAIL #include "Fsb.h" #include "FsbP.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <inttypes.h> #include <errno.h> #include <sys/stat.h> #include <limits.h> #include <dirent.h> #include <fnmatch.h> #include <Xm/XmAll.h> #include <Xm/DropDown.h> #include "pathbar.h" #include "../common/utils.h" #ifdef FSB_ENABLE_DETAIL #include <XmL/Grid.h> #endif #define WIDGET_SPACING 5 #define WINDOW_SPACING 8 #define BUTTON_EXTRA_SPACE 4 #define DATE_FORMAT_SAME_YEAR "%b %d %H:%M" #define DATE_FORMAT_OTHER_YEAR "%b %d %Y" #define KB_SUFFIX "KiB" #define MB_SUFFIX "MiB" #define GB_SUFFIX "GiB" #define TB_SUFFIX "TiB" #define FSB_ERROR_TITLE "Error" #define FSB_ERROR_CHAR "Character '/' is not allowed in file names" #define FSB_ERROR_RENAME "Cannot rename file: %s" #define FSB_ERROR_DELETE "Cannot delete file: %s" #define FSB_ERROR_CREATE_FOLDER "Cannot create folder: %s" #define FSB_ERROR_OPEN_DIR "Cannot open directory: %s" #define FSB_DETAIL_HEADINGS "Name|Size|Last Modified" static void fsb_class_init(void); static void fsb_class_part_init (WidgetClass wc); static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args); static void fsb_resize(Widget widget); static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes); static void fsb_destroy(Widget widget); static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args); static Boolean fsb_acceptfocus(Widget widget, Time *time); static void fsb_insert_child(Widget child); static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb); static int FSBGlobFilter(const char *a, const char *b); static void FSBUpdateTitle(Widget w); static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg); static void FSBRename(XnFileSelectionBox fsb, const char *path); static void FSBDelete(XnFileSelectionBox fsb, const char *path); static void FSBSelectItem(XnFileSelectionBox fsb, const char *item); static char* set_selected_path(XnFileSelectionBox data, XmString item); static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd); static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback); static void FileListUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData); static void FileListSelect(Widget fsb, Widget view, const char *item); static void FileListCleanup(Widget fsb, Widget view, void *userData); static void FileListDestroy(Widget fsb, Widget view, void *userData); static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb); static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb); static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count); #ifdef FSB_ENABLE_DETAIL static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData); static void FileListDetailSelect(Widget fsb, Widget view, const char *item); static void FileListDetailCleanup(Widget fsb, Widget view, void *userData); static void FileListDetailDestroy(Widget fsb, Widget view, void *userData); static void FileListDetailAdjustColWidth(Widget grid); static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth); #endif static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u); static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u); static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value); static void CreateUI(XnFileSelectionBox w); static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc createProc, void *userData, Boolean useDirList); static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex); static void SelectView(XnFileSelectionBox f, int view); static char* FSBDialogTitle(Widget w); static FSBViewWidgets CreateListView(Widget fsb, ArgList args, int n, void *userData); static FSBViewWidgets CreateDetailView(Widget fsb, ArgList args, int n, void *userData); static const char* GetHomeDir(void); static char* ConcatPath(const char *parent, const char *name); static char* FileName(char *path); static char* ParentPath(const char *path); //static int CheckFileName(const char *name); static int filedialog_update_dir(XnFileSelectionBox data, const char *path); static void filedialog_cleanup_filedata(XnFileSelectionBox data); static XtResource resources[] = { {XmNokCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.okCallback), XmRCallback, NULL}, {XmNcancelCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.cancelCallback), XmRCallback, NULL}, {XnNwidgetSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.widgetSpacing), XmRImmediate, (XtPointer)WIDGET_SPACING}, {XnNwindowSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.windowSpacing), XmRImmediate, (XtPointer)WINDOW_SPACING}, {XnNfsbType, XnCfsbType, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.type), XmRImmediate, (XtPointer)FILEDIALOG_OPEN}, {XnNshowHidden, XnCshowHidden, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHidden), XmRImmediate, (XtPointer)False}, {XnNshowHiddenButton, XnCshowHiddenButton, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHiddenButton), XmRImmediate, (XtPointer)True}, {XnNshowViewMenu, XnCshowViewMenu, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showViewMenu), XmRImmediate, (XtPointer)False}, {XnNselectedView, XnCselectedView, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.selectedview), XmRImmediate, (XtPointer)0}, {XnNdirectory, XnCdirectory, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.currentPath), XmRString, NULL}, {XnNselectedPath, XnCselectedPath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.selectedPath), XmRString, NULL}, {XnNhomePath, XnChomePath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.homePath), XmRString, NULL}, {XnNfilter,XnCfilter,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.filterStr), XmRString, "*"}, {XnNfilterFunc,XnCfilterFunc,XmRFunction,sizeof(FSBFilterFunc),XtOffset(XnFileSelectionBox, fsb.filterFunc), XmRFunction, NULL}, {XnNlabelListView,XnClabelListView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelListView), XmRString, "List"}, {XnNlabelDetailView,XnClabelDetailView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDetailView), XmRString, "Detail"}, {XnNlabelOpenFileTitle,XnClabelOpenFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpenFileTitle), XmRString, "Open File"}, {XnNlabelSaveFileTitle,XnClabelSaveFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSaveFileTitle), XmRString, "Save File"}, {XnNlabelDirUp,XnClabelDirUp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirUp), XmRString, "Dir Up"}, {XnNlabelHome,XnClabelHome,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHome), XmRString, "Home"}, {XnNlabelNewFolder,XnClabelNewFolder,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFolder), XmRString, "New Folder"}, {XnNlabelFilterButton,XnClabelFilterButton,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFilterButton), XmRString, "Filter"}, {XnNlabelShowHiddenFiles,XnClabelShowHiddenFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelShowHiddenFiles), XmRString, "Show hiden files"}, {XnNlabelDirectories,XnClabelDirectories,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectories), XmRString, "Directories"}, {XnNlabelFiles,XnClabelFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFiles), XmRString, "Files"}, {XnNlabelRename,XnClabelRename,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelRename), XmRString, "Rename"}, {XnNlabelDelete,XnClabelDelete,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDelete), XmRString, "Delete"}, {XnNlabelOpen,XnClabelOpen,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpen), XmRString, "Open"}, {XnNlabelSave,XnClabelSave,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSave), XmRString, "Save"}, {XnNlabelOk,XnClabelOk,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOk), XmRString, "OK"}, {XnNlabelCancel,XnClabelCancel,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelCancel), XmRString, "Cancel"}, {XnNlabelHelp,XnClabelHelp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHelp), XmRString, "Help"}, {XnNlabelFileName,XnClabelFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFileName), XmRString, "New File Name"}, {XnNlabelDirectoryName,XnClabelDirectoryName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectoryName), XmRString, "Directory name:"}, {XnNlabelNewFileName,XnClabelNewFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFileName), XmRString, "New file name:"}, {XnNlabelDeleteFile,XnClabelDeleteFile,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDeleteFile), XmRString, "Delete file '%s'?"}, {XnNdetailHeadings,XnCdetailHeadings,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.detailHeadings), XmRString,FSB_DETAIL_HEADINGS}, {XnNdateFormatSameYear,XnCdateFormatSameYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatSameYear), XmRString,DATE_FORMAT_SAME_YEAR}, {XnNdateFormatOtherYear,XnNdateFormatOtherYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatOtherYear), XmRString,DATE_FORMAT_OTHER_YEAR}, {XnNsuffixBytes,XnCsuffixBytes,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixBytes), XmRString,"bytes"}, {XnNsuffixKB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixKB), XmRString,KB_SUFFIX}, {XnNsuffixMB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixMB), XmRString,MB_SUFFIX}, {XnNsuffixGB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixGB), XmRString,GB_SUFFIX}, {XnNsuffixTB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixTB), XmRString,TB_SUFFIX}, {XnNerrorTitle,XnCerrorTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorTitle), XmRString,FSB_ERROR_TITLE}, {XnNerrorIllegalChar,XnCerrorIllegalChar,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorIllegalChar), XmRString,FSB_ERROR_CHAR}, {XnNerrorRename,XnCerrorRename,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorRename), XmRString,FSB_ERROR_RENAME}, {XnNerrorCreateFolder,XnCerrorCreateFolder,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorFolder), XmRString,FSB_ERROR_CREATE_FOLDER}, {XnNerrorDelete,XnCerrorDelete,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorDelete), XmRString,FSB_ERROR_DELETE}, {XnNerrorOpenDir,XnCerrorOpenDir,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorOpenDir), XmRString,FSB_ERROR_OPEN_DIR} }; static XtActionsRec actionslist[] = { {"focusIn", FocusInAP}, {"NULL", NULL} }; static char defaultTranslations[] = "<FocusIn>: focusIn()"; static XtResource constraints[] = {}; FSBClassRec fsbWidgetClassRec = { // Core Class { (WidgetClass)&xmFormClassRec, "XnFSB", // class_name sizeof(FSBRec), // widget_size fsb_class_init, // class_initialize fsb_class_part_init, // class_part_initialize FALSE, // class_inited fsb_init, // initialize NULL, // initialize_hook fsb_realize, // realize actionslist, // actions XtNumber(actionslist), // num_actions resources, // resources XtNumber(resources), // num_resources NULLQUARK, // xrm_class True, // compress_motion True, // compress_exposure True, // compress_enterleave False, // visible_interest fsb_destroy, // destroy fsb_resize, // resize XtInheritExpose, // expose fsb_set_values, // set_values NULL, // set_values_hook XtInheritSetValuesAlmost, // set_values_almost NULL, // get_values_hook fsb_acceptfocus, // accept_focus XtVersion, // version NULL, // callback_offsets defaultTranslations, // tm_table XtInheritQueryGeometry, // query_geometry XtInheritDisplayAccelerator, // display_accelerator NULL, // extension }, // Composite Class { XtInheritGeometryManager, // geometry_manager XtInheritChangeManaged, // change_managed fsb_insert_child, // insert_child XtInheritDeleteChild, // delete_child NULL, // extension }, // Constraint Class { constraints, // resources XtNumber(constraints), // num_resources sizeof(XmFormConstraintRec), // constraint_size NULL, // initialize NULL, // destroy NULL, // set_value NULL, // extension }, // XmManager Class { XtInheritTranslations, // translations NULL, // syn_resources 0, // num_syn_resources NULL, // syn_constraint_resources 0, // num_syn_constraint_resources XmInheritParentProcess, // parent_process NULL // extension }, // XmBulletinBoard { FALSE, NULL, XmInheritFocusMovedProc, NULL }, // XmForm Class { NULL }, // FSB Class { 0 } }; WidgetClass xnFsbWidgetClass = (WidgetClass)&fsbWidgetClassRec; static void fsb_class_init(void) { } static void fsb_class_part_init (WidgetClass wc) { FSBClassRec *fsbClass = (FSBClassRec*)wc; XmFormClassRec *formClass = (XmFormClassRec*)xmFormWidgetClass; fsbClass->constraint_class.initialize = formClass->constraint_class.initialize; fsbClass->constraint_class.set_values = formClass->constraint_class.set_values; } #define STRDUP_RES(a) if(a) a = strdup(a) #define XMS_STRDUP_RES(a) if(a) a = XmStringCopy(a) static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args) { XnFileSelectionBox fsb = (XnFileSelectionBox)neww; (xmFormClassRec.core_class.initialize)(request, neww, args, num_args); fsb->fsb.disable_set_values = 0; STRDUP_RES(fsb->fsb.homePath); STRDUP_RES(fsb->fsb.selectedPath); STRDUP_RES(fsb->fsb.currentPath); STRDUP_RES(fsb->fsb.filterStr); STRDUP_RES(fsb->fsb.labelListView); STRDUP_RES(fsb->fsb.labelDetailView); STRDUP_RES(fsb->fsb.labelOpenFileTitle); STRDUP_RES(fsb->fsb.labelSaveFileTitle); XMS_STRDUP_RES(fsb->fsb.labelDirUp); XMS_STRDUP_RES(fsb->fsb.labelHome); XMS_STRDUP_RES(fsb->fsb.labelNewFolder); XMS_STRDUP_RES(fsb->fsb.labelFilterButton); XMS_STRDUP_RES(fsb->fsb.labelShowHiddenFiles); XMS_STRDUP_RES(fsb->fsb.labelDirectories); XMS_STRDUP_RES(fsb->fsb.labelFiles); XMS_STRDUP_RES(fsb->fsb.labelRename); XMS_STRDUP_RES(fsb->fsb.labelDelete); XMS_STRDUP_RES(fsb->fsb.labelOpen); XMS_STRDUP_RES(fsb->fsb.labelSave); XMS_STRDUP_RES(fsb->fsb.labelCancel); XMS_STRDUP_RES(fsb->fsb.labelHelp); XMS_STRDUP_RES(fsb->fsb.labelFileName); XMS_STRDUP_RES(fsb->fsb.labelDirectoryName); XMS_STRDUP_RES(fsb->fsb.labelNewFileName); STRDUP_RES(fsb->fsb.labelDeleteFile); STRDUP_RES(fsb->fsb.detailHeadings); STRDUP_RES(fsb->fsb.dateFormatSameYear); STRDUP_RES(fsb->fsb.dateFormatOtherYear); STRDUP_RES(fsb->fsb.suffixBytes); STRDUP_RES(fsb->fsb.suffixKB); STRDUP_RES(fsb->fsb.suffixMB); STRDUP_RES(fsb->fsb.suffixGB); STRDUP_RES(fsb->fsb.suffixTB); STRDUP_RES(fsb->fsb.errorTitle); STRDUP_RES(fsb->fsb.errorIllegalChar); STRDUP_RES(fsb->fsb.errorRename); STRDUP_RES(fsb->fsb.errorFolder); STRDUP_RES(fsb->fsb.errorDelete); STRDUP_RES(fsb->fsb.errorOpenDir); CreateUI((XnFileSelectionBox)fsb); XtAddCallback(neww, XmNmapCallback, fsb_mapcb, NULL); } #define STR_FREE(a) if(a) free(a) #define XMSTR_FREE(a) if(a) XmStringFree(a) static void fsb_destroy(Widget widget) { XnFileSelectionBox w = (XnFileSelectionBox)widget; // destroy all views for(int i=0;i<w->fsb.numviews;i++) { FSBView v = w->fsb.view[i]; v.destroy(widget, v.widget, v.userData); } STR_FREE(w->fsb.homePath); // free filelists filedialog_cleanup_filedata(w); STR_FREE(w->fsb.currentPath); STR_FREE(w->fsb.selectedPath); STR_FREE(w->fsb.filterStr); PathBarDestroy(w->fsb.pathBar); // free strings STR_FREE(w->fsb.labelListView); STR_FREE(w->fsb.labelDetailView); STR_FREE(w->fsb.labelOpenFileTitle); STR_FREE(w->fsb.labelSaveFileTitle); XMSTR_FREE(w->fsb.labelDirUp); XMSTR_FREE(w->fsb.labelHome); XMSTR_FREE(w->fsb.labelNewFolder); XMSTR_FREE(w->fsb.labelFilterButton); XMSTR_FREE(w->fsb.labelShowHiddenFiles); XMSTR_FREE(w->fsb.labelDirectories); XMSTR_FREE(w->fsb.labelFiles); XMSTR_FREE(w->fsb.labelRename); XMSTR_FREE(w->fsb.labelDelete); XMSTR_FREE(w->fsb.labelOpen); XMSTR_FREE(w->fsb.labelSave); XMSTR_FREE(w->fsb.labelCancel); XMSTR_FREE(w->fsb.labelHelp); XMSTR_FREE(w->fsb.labelFileName); XMSTR_FREE(w->fsb.labelDirectoryName); XMSTR_FREE(w->fsb.labelNewFileName); STR_FREE(w->fsb.labelDeleteFile); STR_FREE(w->fsb.detailHeadings); STR_FREE(w->fsb.dateFormatSameYear); STR_FREE(w->fsb.dateFormatOtherYear); STR_FREE(w->fsb.suffixBytes); STR_FREE(w->fsb.suffixKB); STR_FREE(w->fsb.suffixMB); STR_FREE(w->fsb.suffixGB); STR_FREE(w->fsb.suffixTB); STR_FREE(w->fsb.errorTitle); STR_FREE(w->fsb.errorIllegalChar); STR_FREE(w->fsb.errorRename); STR_FREE(w->fsb.errorFolder); STR_FREE(w->fsb.errorDelete); STR_FREE(w->fsb.errorOpenDir); } static void fsb_resize(Widget widget) { XnFileSelectionBox w = (XnFileSelectionBox)widget; (xmFormClassRec.core_class.resize)(widget); #ifdef FSB_ENABLE_DETAIL if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) { FileListDetailAdjustColWidth(w->fsb.grid); } #endif } static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) { XnFileSelectionBox w = (XnFileSelectionBox)widget; (xmFormClassRec.core_class.realize)(widget, mask, attributes); FSBView view = w->fsb.view[w->fsb.selectedview]; XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT); #ifdef FSB_ENABLE_DETAIL if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) { FileListDetailAdjustColWidth(w->fsb.grid); } #endif } static void FSBUpdateTitle(Widget w) { if(XtParent(w)->core.widget_class == xmDialogShellWidgetClass) { char *title = FSBDialogTitle(w); XtVaSetValues(XtParent(w), XmNtitle, title, NULL); } } static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { Boolean r = False; XnFileSelectionBox o = (XnFileSelectionBox)old; XnFileSelectionBox n = (XnFileSelectionBox)neww; int setOkBtnLabel = 0; int ismanaged = XtIsManaged(neww); Dimension width, height; if(!ismanaged) { width = n->core.width; height = n->core.height; if(n->fsb.pathBar) { n->fsb.pathBar->disableResize = True; } } if(o->fsb.selectedview != n->fsb.selectedview) { int selectedview = n->fsb.selectedview; n->fsb.selectedview = o->fsb.selectedview; SelectView(n, selectedview); } char *updateDir = NULL; int selectItem = 0; if(o->fsb.selectedPath != n->fsb.selectedPath) { STR_FREE(o->fsb.selectedPath); STRDUP_RES(n->fsb.selectedPath); XmTextFieldSetString(n->fsb.name, FileName(n->fsb.selectedPath)); // also update current directory updateDir = ParentPath(n->fsb.selectedPath); selectItem = 1; } if(o->fsb.currentPath != n->fsb.currentPath) { STR_FREE(o->fsb.currentPath); updateDir = strdup(n->fsb.currentPath); n->fsb.currentPath = NULL; } if(o->fsb.filterStr != n->fsb.filterStr) { STR_FREE(o->fsb.filterStr); STRDUP_RES(n->fsb.filterStr); XmTextFieldSetString(XmDropDownGetText(n->fsb.filter), n->fsb.filterStr); if(!updateDir) { filedialog_update_dir(n, NULL); } } if(updateDir) { filedialog_update_dir(n, updateDir); PathBarSetPath(n->fsb.pathBar, updateDir); free(updateDir); } if(o->fsb.type != n->fsb.type) { if(n->fsb.type == FILEDIALOG_OPEN) { XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.separator, NULL); XtUnmanageChild(n->fsb.name); XtUnmanageChild(n->fsb.nameLabel); } else { XtManageChild(n->fsb.name); XtManageChild(n->fsb.nameLabel); XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.nameLabel, NULL); } FSBUpdateTitle(neww); setOkBtnLabel = 1; } // label strings int updateTitle = 0; if(o->fsb.labelListView != n->fsb.labelListView) { STR_FREE(o->fsb.labelListView); STRDUP_RES(n->fsb.labelListView); XmString label = XmStringCreateLocalized(n->fsb.labelListView); XtVaSetValues(n->fsb.viewSelectorList, XmNlabelString, label, NULL); XmStringFree(label); } if(o->fsb.labelDetailView != n->fsb.labelDetailView) { STR_FREE(o->fsb.labelDetailView); STRDUP_RES(n->fsb.labelDetailView); XmString label = XmStringCreateLocalized(n->fsb.labelDetailView); XtVaSetValues(n->fsb.viewSelectorDetail, XmNlabelString, label, NULL); if(n->fsb.detailToggleButton) { XtVaSetValues(n->fsb.detailToggleButton, XmNlabelString, label, NULL); } XmStringFree(label); } if(o->fsb.labelOpenFileTitle != n->fsb.labelOpenFileTitle) { STR_FREE(o->fsb.labelOpenFileTitle); STRDUP_RES(n->fsb.labelOpenFileTitle); updateTitle = 1; } if(o->fsb.labelSaveFileTitle != n->fsb.labelSaveFileTitle) { STR_FREE(o->fsb.labelSaveFileTitle); STRDUP_RES(n->fsb.labelSaveFileTitle); updateTitle = 1; } if(o->fsb.labelDirUp != n->fsb.labelDirUp) { XMSTR_FREE(o->fsb.labelDirUp); XMS_STRDUP_RES(n->fsb.labelDirUp); XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelDirUp, NULL); } if(o->fsb.labelHome != n->fsb.labelHome) { XMSTR_FREE(o->fsb.labelHome); XMS_STRDUP_RES(n->fsb.labelHome); XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelHome, NULL); } if(o->fsb.labelNewFolder != n->fsb.labelNewFolder) { XMSTR_FREE(o->fsb.labelNewFolder); XMS_STRDUP_RES(n->fsb.labelNewFolder); XtVaSetValues(n->fsb.newFolder, XmNlabelString, n->fsb.labelNewFolder, NULL); } if(o->fsb.labelFilterButton != n->fsb.labelFilterButton) { XMSTR_FREE(o->fsb.labelFilterButton); XMS_STRDUP_RES(n->fsb.labelFilterButton); XtVaSetValues(n->fsb.filterButton, XmNlabelString, n->fsb.labelFilterButton, NULL); } if(o->fsb.labelShowHiddenFiles != n->fsb.labelShowHiddenFiles) { XMSTR_FREE(o->fsb.labelShowHiddenFiles); XMS_STRDUP_RES(n->fsb.labelShowHiddenFiles); XtVaSetValues(n->fsb.showHiddenButtonW, XmNlabelString, n->fsb.labelShowHiddenFiles, NULL); } if(o->fsb.labelDirectories != n->fsb.labelDirectories) { XMSTR_FREE(o->fsb.labelDirectories); XMS_STRDUP_RES(n->fsb.labelDirectories); XtVaSetValues(n->fsb.lsDirLabel, XmNlabelString, n->fsb.labelDirectories, NULL); } if(o->fsb.labelFiles != n->fsb.labelFiles) { XMSTR_FREE(o->fsb.labelFiles); XMS_STRDUP_RES(n->fsb.labelFiles); XtVaSetValues(n->fsb.lsFileLabel, XmNlabelString, n->fsb.labelFiles, NULL); } int recreateContextMenu = 0; if(o->fsb.labelRename != n->fsb.labelRename) { XMSTR_FREE(o->fsb.labelRename); XMS_STRDUP_RES(n->fsb.labelRename); recreateContextMenu = 1; } if(o->fsb.labelDelete != n->fsb.labelDelete) { XMSTR_FREE(o->fsb.labelDelete); XMS_STRDUP_RES(n->fsb.labelDelete); recreateContextMenu = 1; } if(o->fsb.labelOpen != n->fsb.labelOpen) { XMSTR_FREE(o->fsb.labelOpen); XMS_STRDUP_RES(n->fsb.labelOpen); setOkBtnLabel = 1; } if(o->fsb.labelSave != n->fsb.labelSave) { XMSTR_FREE(o->fsb.labelSave); XMS_STRDUP_RES(n->fsb.labelSave); setOkBtnLabel = 1; } if(o->fsb.labelCancel != n->fsb.labelCancel) { XMSTR_FREE(o->fsb.labelCancel); XMS_STRDUP_RES(n->fsb.labelCancel); XtVaSetValues(n->fsb.cancelBtn, XmNlabelString, n->fsb.labelCancel, NULL); } if(o->fsb.labelHelp != n->fsb.labelHelp) { XMSTR_FREE(o->fsb.labelHelp); XMS_STRDUP_RES(n->fsb.labelHelp); XtVaSetValues(n->fsb.helpBtn, XmNlabelString, n->fsb.labelHelp, NULL); } if(o->fsb.labelFileName != n->fsb.labelFileName) { XMSTR_FREE(o->fsb.labelFileName); XMS_STRDUP_RES(n->fsb.labelFileName); XtVaSetValues(n->fsb.nameLabel, XmNlabelString, n->fsb.labelFileName, NULL); } if(o->fsb.labelDirectoryName != n->fsb.labelDirectoryName) { XMSTR_FREE(o->fsb.labelDirectoryName); XMS_STRDUP_RES(n->fsb.labelDirectoryName); } if(o->fsb.labelNewFileName != n->fsb.labelNewFileName) { XMSTR_FREE(o->fsb.labelNewFileName); XMS_STRDUP_RES(n->fsb.labelNewFileName); } if(o->fsb.labelDeleteFile != n->fsb.labelDeleteFile) { STR_FREE(o->fsb.labelDeleteFile); STRDUP_RES(n->fsb.labelDeleteFile); } #ifdef FSB_ENABLE_DETAIL if(o->fsb.detailHeadings != n->fsb.detailHeadings) { STR_FREE(o->fsb.detailHeadings); STRDUP_RES(n->fsb.detailHeadings); XtVaSetValues(n->fsb.grid, XmNsimpleHeadings, n->fsb.detailHeadings, NULL); } #endif if(o->fsb.dateFormatSameYear != n->fsb.dateFormatSameYear) { STR_FREE(o->fsb.dateFormatSameYear); STRDUP_RES(n->fsb.dateFormatSameYear); } if(o->fsb.dateFormatOtherYear != n->fsb.dateFormatOtherYear) { STR_FREE(o->fsb.dateFormatOtherYear); STRDUP_RES(n->fsb.dateFormatOtherYear); } if(o->fsb.suffixBytes != n->fsb.suffixBytes) { STR_FREE(o->fsb.suffixBytes); STRDUP_RES(n->fsb.suffixBytes); } if(o->fsb.suffixMB != n->fsb.suffixMB) { STR_FREE(o->fsb.suffixMB); STRDUP_RES(n->fsb.suffixMB); } if(o->fsb.suffixGB != n->fsb.suffixGB) { STR_FREE(o->fsb.suffixGB); STRDUP_RES(n->fsb.suffixGB); } if(o->fsb.suffixTB != n->fsb.suffixTB) { STR_FREE(o->fsb.suffixTB); STRDUP_RES(n->fsb.suffixTB); } if(o->fsb.errorTitle != n->fsb.errorTitle) { STR_FREE(o->fsb.errorTitle); STRDUP_RES(n->fsb.errorTitle); } if(o->fsb.errorIllegalChar != n->fsb.errorIllegalChar) { STR_FREE(o->fsb.errorIllegalChar); STRDUP_RES(n->fsb.errorIllegalChar); } if(o->fsb.errorRename != n->fsb.errorRename) { STR_FREE(o->fsb.errorRename); STRDUP_RES(n->fsb.errorRename); } if(o->fsb.errorFolder != n->fsb.errorFolder) { STR_FREE(o->fsb.errorFolder); STRDUP_RES(n->fsb.errorFolder); } if(o->fsb.errorDelete != n->fsb.errorDelete) { STR_FREE(o->fsb.errorDelete); STRDUP_RES(n->fsb.errorDelete); } if(o->fsb.errorOpenDir != n->fsb.errorOpenDir) { STR_FREE(o->fsb.errorOpenDir); STRDUP_RES(n->fsb.errorOpenDir); } if(updateTitle) { FSBUpdateTitle(neww); } if(recreateContextMenu) { XtDestroyWidget(n->fsb.listContextMenu); XtDestroyWidget(n->fsb.gridContextMenu); n->fsb.listContextMenu = CreateContextMenu(n, n->fsb.filelist, FileContextMenuCB); n->fsb.gridContextMenu = CreateContextMenu(n, n->fsb.grid, FileContextMenuCB); } if(setOkBtnLabel) { XtVaSetValues(n->fsb.okBtn, XmNlabelString, n->fsb.type == FILEDIALOG_OPEN ? n->fsb.labelOpen : n->fsb.labelSave, NULL); } if(!ismanaged && !n->fsb.disable_set_values) { n->fsb.disable_set_values = 1; XtVaSetValues(neww, XmNwidth, width, XmNheight, height, NULL); n->fsb.disable_set_values = 0; if(n->fsb.pathBar) n->fsb.pathBar->disableResize = False; } if(selectItem) { if(ismanaged) { FSBSelectItem(n, FileName(n->fsb.selectedPath)); } } Boolean fr = (xmFormClassRec.core_class.set_values)(old, request, neww, args, num_args); return fr ? fr : r; } static void fsb_insert_child(Widget child) { XnFileSelectionBox p = (XnFileSelectionBox)XtParent(child); (xmFormClassRec.composite_class.insert_child)(child); if(!p->fsb.gui_created) { return; } // custom child widget insert XtVaSetValues(child, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, p->fsb.bottom_widget, XmNbottomOffset, p->fsb.widgetSpacing, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, p->fsb.windowSpacing, XmNrightAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, p->fsb.windowSpacing, NULL); XtVaSetValues(p->fsb.listform, XmNbottomWidget, child, XmNbottomOffset, 0, NULL); p->fsb.workarea = child; } Boolean fsb_acceptfocus(Widget widget, Time *time) { return 0; } static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb) { XnFileSelectionBox w = (XnFileSelectionBox)widget; pathbar_resize(w->fsb.pathBar->widget, w->fsb.pathBar, NULL); if(w->fsb.type == FILEDIALOG_OPEN) { FSBView view = w->fsb.view[w->fsb.selectedview]; XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT); } else { XmProcessTraversal(w->fsb.name, XmTRAVERSE_CURRENT); } if(w->fsb.selectedPath) { FSBSelectItem(w, FileName(w->fsb.selectedPath)); } } static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { } static int apply_filter(XnFileSelectionBox w, const char *pattern, const char *string) { if(!pattern) return 0; FSBFilterFunc func = w->fsb.filterFunc ? w->fsb.filterFunc : FSBGlobFilter; return func(pattern, string); } static int FSBGlobFilter(const char *a, const char *b) { return fnmatch(a, b, 0); } static void errCB(Widget w, XtPointer d, XtPointer cbs) { XtDestroyWidget(w); } static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg) { Arg args[16]; int n = 0; XmString titleStr = XmStringCreateLocalized((char*)title); XmString msg = XmStringCreateLocalized((char*)errmsg); XtSetArg(args[n], XmNdialogTitle, titleStr); n++; XtSetArg(args[n], XmNselectionLabelString, msg); n++; XtSetArg(args[n], XmNokLabelString, w->fsb.labelOk); n++; XtSetArg(args[n], XmNcancelLabelString, w->fsb.labelCancel); n++; Widget dialog = XmCreatePromptDialog ((Widget)w, "NewFolderPrompt", args, n); Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON); XtUnmanageChild(help); Widget cancel = XmSelectionBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON); XtUnmanageChild(cancel); Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT); XtUnmanageChild(text); XtAddCallback(dialog, XmNokCallback, errCB, NULL); XtManageChild(dialog); XmStringFree(titleStr); XmStringFree(msg); } static void rename_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) { XnFileSelectionBox fsb = NULL; XtVaGetValues(w, XmNuserData, &fsb, NULL); char *fileName = NULL; XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &fileName); // make sure the new file name doesn't contain a path separator if(strchr(fileName, '/')) { ErrDialog(fsb, fsb->fsb.errorTitle, fsb->fsb.errorIllegalChar); XtFree(fileName); return; } char *parentPath = ParentPath(path); char *newPath = ConcatPath(parentPath, fileName); if(rename(path, newPath)) { char errmsg[256]; snprintf(errmsg, 256, fsb->fsb.errorRename, strerror(errno)); ErrDialog(fsb, fsb->fsb.errorTitle, errmsg); } else { filedialog_update_dir(fsb, parentPath); } free(parentPath); free(newPath); XtFree(fileName); XtDestroyWidget(XtParent(w)); } static void selectionbox_cancel(Widget w, XtPointer data, XtPointer d) { XtDestroyWidget(XtParent(w)); } static void FSBRename(XnFileSelectionBox fsb, const char *path) { Arg args[16]; int n = 0; Widget w = (Widget)fsb; char *name = FileName((char*)path); XmString filename = XmStringCreateLocalized(name); XtSetArg(args[n], XmNselectionLabelString,fsb->fsb.labelNewFileName); n++; XtSetArg(args[n], XmNtextString, filename); n++; XtSetArg(args[n], XmNuserData, fsb); n++; XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelRename); n++; XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++; XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++; Widget dialog = XmCreatePromptDialog (w, "RenameFilePrompt", args, n); Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON); XtUnmanageChild(help); XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)rename_file_cb, (char*)path); XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL); XmStringFree(filename); XtManageChild(dialog); } static void delete_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) { XnFileSelectionBox fsb = NULL; XtVaGetValues(w, XmNuserData, &fsb, NULL); if(unlink(path)) { char errmsg[256]; snprintf(errmsg, 256, fsb->fsb.errorDelete, strerror(errno)); ErrDialog(fsb, fsb->fsb.errorTitle, errmsg); } else { char *parentPath = ParentPath(path); filedialog_update_dir(fsb, parentPath); free(parentPath); } XtDestroyWidget(XtParent(w)); } static void FSBDelete(XnFileSelectionBox fsb, const char *path) { Arg args[16]; int n = 0; Widget w = (Widget)fsb; char *name = FileName((char*)path); size_t len = strlen(name); size_t msglen = len + strlen(fsb->fsb.labelDeleteFile) + 4; char *msg = malloc(msglen); snprintf(msg, msglen, fsb->fsb.labelDeleteFile, name); XmString prompt = XmStringCreateLocalized(msg); XtSetArg(args[n], XmNselectionLabelString, prompt); n++; XtSetArg(args[n], XmNuserData, fsb); n++; XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelDelete); n++; XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++; XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++; Widget dialog = XmCreatePromptDialog (w, "DeleteFilePrompt", args, n); Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON); XtUnmanageChild(help); Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT); XtUnmanageChild(text); XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)delete_file_cb, (char*)path); XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL); free(msg); XmStringFree(prompt); XtManageChild(dialog); } static void FSBSelectItem(XnFileSelectionBox fsb, const char *item) { FSBView view = fsb->fsb.view[fsb->fsb.selectedview]; if(view.select) { view.select((Widget)fsb, view.widget, item); } } static char* set_selected_path(XnFileSelectionBox data, XmString item) { char *name = NULL; XmStringGetLtoR(item, XmFONTLIST_DEFAULT_TAG, &name); if(!name) { return NULL; } char *path = ConcatPath(data->fsb.currentPath, name); XtFree(name); if(data->fsb.selectedPath) { free(data->fsb.selectedPath); } data->fsb.selectedPath = path; return path; } // item0: rename // item1: delete static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd) { intptr_t i = (intptr_t)index; Widget parent = XtParent(item); XnFileSelectionBox fsb = NULL; XtVaGetValues(parent, XmNuserData, &fsb, NULL); const char *path = fsb->fsb.selectedPath; if(path) { if(i == 0) { FSBRename(fsb, path); } else if(i == 1) { FSBDelete(fsb, path); } } } static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback) { return XmVaCreateSimplePopupMenu( parent, "popup", callback, XmNpopupEnabled, XmPOPUP_AUTOMATIC, XmNuserData, fsb, XmVaPUSHBUTTON, fsb->fsb.labelRename, 'R', NULL, NULL, XmVaPUSHBUTTON, fsb->fsb.labelDelete, 'D', NULL, NULL, NULL); } static void FileListUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) { XnFileSelectionBox data = userData; FileListWidgetAdd(data, data->fsb.filelist, data->fsb.showHidden, filter, files, filecount); } static void FileListSelect(Widget fsb, Widget view, const char *item) { XnFileSelectionBox w = (XnFileSelectionBox)fsb; int numItems = 0; XmStringTable items = NULL; XtVaGetValues(w->fsb.filelist, XmNitemCount, &numItems, XmNitems, &items, NULL); for(int i=0;i<numItems;i++) { char *str = NULL; XmStringGetLtoR(items[i], XmFONTLIST_DEFAULT_TAG, &str); if(!strcmp(str, item)) { XmListSelectPos(w->fsb.filelist, i+1, False); break; } XtFree(str); } } static void FileListCleanup(Widget fsb, Widget view, void *userData) { XnFileSelectionBox data = userData; XmListDeleteAllItems(data->fsb.filelist); } static void FileListDestroy(Widget fsb, Widget view, void *userData) { // unused } static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb) { char *path = set_selected_path(data, cb->item); if(path) { data->fsb.end = True; data->fsb.status = FILEDIALOG_OK; data->fsb.selIsDir = False; FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath); } } static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb) { if(data->fsb.type == FILEDIALOG_SAVE) { char *name = NULL; XmStringGetLtoR(cb->item, XmFONTLIST_DEFAULT_TAG, &name); XmTextFieldSetString(data->fsb.name, name); XtFree(name); } else { char *path = set_selected_path(data, cb->item); if(path) { data->fsb.selIsDir = False; } } } static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count) { if(count > 0) { XmStringTable items = calloc(count, sizeof(XmString)); int i = 0; for(int j=0;j<count;j++) { FileElm *e = &ls[j]; char *name = FileName(e->path); if((!showHidden && name[0] == '.') || apply_filter(fsb, filter, name)) { continue; } items[i] = XmStringCreateLocalized(name); i++; } XmListAddItems(w, items, i, 0); for(i=0;i<count;i++) { XmStringFree(items[i]); } free(items); } } #ifdef FSB_ENABLE_DETAIL static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) { XnFileSelectionBox data = userData; FileListDetailAdd(data, data->fsb.grid, data->fsb.showHidden, filter, files, filecount, maxnamelen); } #endif /* * create file size string with kb/mb/gb/tb suffix */ static char* size_str(XnFileSelectionBox fsb, FileElm *f) { char *str = malloc(16); uint64_t size = f->size; if(f->isDirectory) { str[0] = '\0'; } else if(size < 0x400) { snprintf(str, 16, "%d %s", (int)size, fsb->fsb.suffixBytes); } else if(size < 0x100000) { float s = (float)size/0x400; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x2800 && diff != 0) { // size < 10 KiB snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixKB); } else { snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixKB); } } else if(size < 0x40000000) { float s = (float)size/0x100000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0xa00000 && diff != 0) { // size < 10 MiB snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixMB); } else { snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixMB); } } else if(size < 0x1000000000ULL) { float s = (float)size/0x40000000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x280000000 && diff != 0) { // size < 10 GiB snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixGB); } else { snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixGB); } } else { size /= 1024; float s = (float)size/0x40000000; int diff = (s*100 - (int)s*100); if(diff > 90) { diff = 0; s += 0.10f; } if(size < 0x280000000 && diff != 0) { // size < 10 TiB snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixTB); } else { snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixTB); } } return str; } static char* date_str(XnFileSelectionBox fsb, time_t tm) { struct tm t; struct tm n; time_t now = time(NULL); localtime_r(&tm, &t); localtime_r(&now, &n); char *str = malloc(24); if(t.tm_year == n.tm_year) { strftime(str, 24, fsb->fsb.dateFormatSameYear, &t); } else { strftime(str, 24, fsb->fsb.dateFormatOtherYear, &t); } return str; } #ifdef FSB_ENABLE_DETAIL static void FileListDetailAdjustColWidth(Widget grid) { XmLGridColumn column0 = XmLGridGetColumn(grid, XmCONTENT, 0); XmLGridColumn column1 = XmLGridGetColumn(grid, XmCONTENT, 1); XmLGridColumn column2 = XmLGridGetColumn(grid, XmCONTENT, 2); Dimension col0Width = XmLGridColumnWidthInPixels(column0); Dimension col1Width = XmLGridColumnWidthInPixels(column1); Dimension col2Width = XmLGridColumnWidthInPixels(column2); Dimension totalWidth = col0Width + col1Width + col2Width; Dimension gridWidth = 0; Dimension gridShadow = 0; XtVaGetValues(grid, XmNwidth, &gridWidth, XmNshadowThickness, &gridShadow, NULL); Dimension widthDiff = gridWidth - totalWidth - gridShadow - gridShadow; if(gridWidth > totalWidth) { XtVaSetValues(grid, XmNcolumnRangeStart, 0, XmNcolumnRangeEnd, 0, XmNcolumnWidth, col0Width + widthDiff - XmLGridVSBWidth(grid) - 2, XmNcolumnSizePolicy, XmCONSTANT, NULL); } } static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth) { XmLGridAddRows(grid, XmCONTENT, 1, count); int row = 0; for(int i=0;i<count;i++) { FileElm *e = &ls[i]; char *name = FileName(e->path); if((!showHidden && name[0] == '.') || (!e->isDirectory && apply_filter(fsb, filter, name))) { continue; } // name XmString str = XmStringCreateLocalized(name); XtVaSetValues(grid, XmNcolumn, 0, XmNrow, row, XmNcellString, str, NULL); XmStringFree(str); // size char *szbuf = size_str(fsb, e); str = XmStringCreateLocalized(szbuf); XtVaSetValues(grid, XmNcolumn, 1, XmNrow, row, XmNcellString, str, NULL); free(szbuf); XmStringFree(str); // date char *datebuf = date_str(fsb, e->lastModified); str = XmStringCreateLocalized(datebuf); XtVaSetValues(grid, XmNcolumn, 2, XmNrow, row, XmNcellString, str, NULL); free(datebuf); XmStringFree(str); XtVaSetValues(grid, XmNrow, row, XmNrowUserData, e, NULL); row++; } // remove unused rows if(count > row) { XmLGridDeleteRows(grid, XmCONTENT, row, count-row); } if(maxWidth < 16) { maxWidth = 16; } XtVaSetValues(grid, XmNcolumnRangeStart, 0, XmNcolumnRangeEnd, 0, XmNcolumnWidth, maxWidth, XmNcellAlignment, XmALIGNMENT_LEFT, XmNcolumnSizePolicy, XmVARIABLE, NULL); XtVaSetValues(grid, XmNcolumnRangeStart, 1, XmNcolumnRangeEnd, 1, XmNcolumnWidth, 9, XmNcellAlignment, XmALIGNMENT_LEFT, XmNcolumnSizePolicy, XmVARIABLE, NULL); XtVaSetValues(grid, XmNcolumnRangeStart, 2, XmNcolumnRangeEnd, 2, XmNcolumnWidth, 16, XmNcellAlignment, XmALIGNMENT_RIGHT, XmNcolumnSizePolicy, XmVARIABLE, NULL); FileListDetailAdjustColWidth(grid); } static void FileListDetailSelect(Widget fsb, Widget view, const char *item) { XnFileSelectionBox w = (XnFileSelectionBox)fsb; int numRows = 0; XtVaGetValues(w->fsb.grid, XmNrows, &numRows, NULL); XmLGridColumn col = XmLGridGetColumn(w->fsb.grid, XmCONTENT, 0); for(int i=0;i<numRows;i++) { XmLGridRow row = XmLGridGetRow(w->fsb.grid, XmCONTENT, i); FileElm *elm = NULL; XtVaGetValues(w->fsb.grid, XmNrowPtr, row, XmNcolumnPtr, col, XmNrowUserData, &elm, NULL); if(elm) { if(!strcmp(item, FileName(elm->path))) { XmLGridSelectRow(w->fsb.grid, i, False); XmLGridFocusAndShowRow(w->fsb.grid, i+1); break; } } } } static void FileListDetailCleanup(Widget fsb, Widget view, void *userData) { XnFileSelectionBox data = userData; // cleanup grid Cardinal rows = 0; XtVaGetValues(data->fsb.grid, XmNrows, &rows, NULL); XmLGridDeleteRows(data->fsb.grid, XmCONTENT, 0, rows); } static void FileListDetailDestroy(Widget fsb, Widget view, void *userData) { // unused } #endif static void create_folder(Widget w, XnFileSelectionBox data, XmSelectionBoxCallbackStruct *cbs) { char *fileName = NULL; XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &fileName); char *newFolder = ConcatPath(data->fsb.currentPath ? data->fsb.currentPath : "", fileName); if(mkdir(newFolder, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { char errmsg[256]; snprintf(errmsg, 256, data->fsb.errorFolder, strerror(errno)); ErrDialog(data, data->fsb.errorTitle, errmsg); } else { char *p = strdup(data->fsb.currentPath); filedialog_update_dir(data, p); free(p); } free(newFolder); XtDestroyWidget(XtParent(w)); } static void new_folder_cancel(Widget w, XnFileSelectionBox data, XtPointer d) { XtDestroyWidget(XtParent(w)); } static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u) { Arg args[16]; int n = 0; XtSetArg(args[n], XmNdialogTitle, data->fsb.labelNewFolder); n++; XtSetArg (args[n], XmNselectionLabelString, data->fsb.labelDirectoryName); n++; XtSetArg(args[n], XmNokLabelString, data->fsb.labelOk); n++; XtSetArg(args[n], XmNcancelLabelString, data->fsb.labelCancel); n++; Widget dialog = XmCreatePromptDialog (w, "NewFolderPrompt", args, n); Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON); XtUnmanageChild(help); XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)create_folder, data); XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)new_folder_cancel, data); XtManageChild(dialog); } static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u) { const char *homePath = data->fsb.homePath ? data->fsb.homePath : GetHomeDir(); filedialog_update_dir(data, homePath); PathBarSetPath(data->fsb.pathBar, homePath); } /* * file_cmp_field * 0: compare path * 1: compare size * 2: compare mtime */ static int file_cmp_field = 0; /* * 1 or -1 */ static int file_cmp_order = 1; static int filecmp(const void *f1, const void *f2) { const FileElm *file1 = f1; const FileElm *file2 = f2; if(file1->isDirectory != file2->isDirectory) { return file1->isDirectory < file2->isDirectory; } int cmp_field = file_cmp_field; int cmp_order = file_cmp_order; if(file1->isDirectory) { cmp_field = 0; cmp_order = 1; } int ret = 0; switch(cmp_field) { case 0: { ret = strcmp(FileName(file1->path), FileName(file2->path)); break; } case 1: { if(file1->size < file2->size) { ret = -1; } else if(file1->size == file2->size) { ret = 0; } else { ret = 1; } break; } case 2: { if(file1->lastModified < file2->lastModified) { ret = -1; } else if(file1->lastModified == file2->lastModified) { ret = 0; } else { ret = 1; } break; } } return ret * cmp_order; } static void free_files(FileElm *ls, int count) { for(int i=0;i<count;i++) { if(ls[i].path) { free(ls[i].path); } } free(ls); } static void filedialog_cleanup_filedata(XnFileSelectionBox data) { free_files(data->fsb.dirs, data->fsb.dircount); free_files(data->fsb.files, data->fsb.filecount); data->fsb.dirs = NULL; data->fsb.files = NULL; data->fsb.dircount = 0; data->fsb.filecount = 0; data->fsb.maxnamelen = 0; } #define FILE_ARRAY_SIZE 1024 static void file_array_add(FileElm **files, int *alloc, int *count, FileElm elm) { int c = *count; int a = *alloc; if(c >= a) { a *= 2; FileElm *newarray = realloc(*files, sizeof(FileElm) * a); *files = newarray; *alloc = a; } (*files)[c] = elm; c++; *count = c; } static int filedialog_update_dir(XnFileSelectionBox data, const char *path) { DIR *dir = NULL; if(path) { // try to check first, if we can open the path dir = opendir(path); if(!dir) { char errmsg[256]; snprintf(errmsg, 256, data->fsb.errorOpenDir, strerror(errno)); ErrDialog(data, data->fsb.errorTitle, errmsg); return 1; } } FSBView view = data->fsb.view[data->fsb.selectedview]; view.cleanup((Widget)data, view.widget, view.userData); if(view.useDirList) { XmListDeleteAllItems(data->fsb.dirlist); } /* read dir and insert items */ if(path) { int dircount = 0; int filecount = 0; size_t maxNameLen = 0; FileElm *dirs = calloc(sizeof(FileElm), FILE_ARRAY_SIZE); FileElm *files = calloc(sizeof(FileElm), FILE_ARRAY_SIZE); int dirs_alloc = FILE_ARRAY_SIZE; int files_alloc = FILE_ARRAY_SIZE; filedialog_cleanup_filedata(data); /* dir reading complete - set the path textfield */ XmTextFieldSetString(data->fsb.path, (char*)path); char *oldPath = data->fsb.currentPath; data->fsb.currentPath = strdup(path); if(oldPath) { free(oldPath); } path = data->fsb.currentPath; struct dirent *ent; while((ent = readdir(dir)) != NULL) { if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { continue; } char *entpath = ConcatPath(path, ent->d_name); struct stat s; if(stat(entpath, &s)) { free(entpath); continue; } FileElm new_entry; new_entry.path = entpath; new_entry.isDirectory = S_ISDIR(s.st_mode); new_entry.size = (uint64_t)s.st_size; new_entry.lastModified = s.st_mtime; size_t nameLen = strlen(ent->d_name); if(nameLen > maxNameLen) { maxNameLen = nameLen; } if(new_entry.isDirectory) { file_array_add(&dirs, &dirs_alloc, &dircount, new_entry); } else { file_array_add(&files, &files_alloc, &filecount, new_entry); } } closedir(dir); data->fsb.dirs = dirs; data->fsb.files = files; data->fsb.dircount = dircount; data->fsb.filecount = filecount; data->fsb.maxnamelen = maxNameLen; // sort file arrays qsort(dirs, dircount, sizeof(FileElm), filecmp); qsort(files, filecount, sizeof(FileElm), filecmp); } Widget filterTF = XmDropDownGetText(data->fsb.filter); char *filter = XmTextFieldGetString(filterTF); char *filterStr = filter; if(!filter || strlen(filter) == 0) { filterStr = "*"; } if(view.useDirList) { FileListWidgetAdd(data, data->fsb.dirlist, data->fsb.showHidden, NULL, data->fsb.dirs, data->fsb.dircount); view.update( (Widget)data, view.widget, NULL, 0, data->fsb.files, data->fsb.filecount, filterStr, data->fsb.maxnamelen, view.userData); } else { view.update( (Widget)data, view.widget, data->fsb.dirs, data->fsb.dircount, data->fsb.files, data->fsb.filecount, filterStr, data->fsb.maxnamelen, view.userData); } if(filter) { XtFree(filter); } return 0; } static void dirlist_activate(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb) { char *path = set_selected_path(data, cb->item); if(path) { if(!filedialog_update_dir(data, path)) { PathBarSetPath(data->fsb.pathBar, path); data->fsb.selIsDir = TRUE; } } } static void dirlist_select(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb) { char *path = set_selected_path(data, cb->item); if(path) { data->fsb.selIsDir = TRUE; } } static void filedialog_enable_detailview(Widget w, XnFileSelectionBox data, XmToggleButtonCallbackStruct *tb) { SelectView(data, tb->set); // 0: list, 1: detail } static void filedialog_setshowhidden( Widget w, XnFileSelectionBox data, XmToggleButtonCallbackStruct *tb) { data->fsb.showHidden = tb->set; filedialog_update_dir(data, NULL); } static void filedialog_filter(Widget w, XnFileSelectionBox data, XtPointer c) { filedialog_update_dir(data, NULL); } static void filedialog_update_filter(Widget w, XnFileSelectionBox data, XtPointer c) { filedialog_update_dir(data, NULL); } static void filedialog_goup(Widget w, XnFileSelectionBox data, XtPointer d) { char *newPath = ParentPath(data->fsb.currentPath); filedialog_update_dir(data, newPath); PathBarSetPath(data->fsb.pathBar, newPath); free(newPath); } static void filedialog_ok(Widget w, XnFileSelectionBox data, XtPointer d) { if(data->fsb.type == FILEDIALOG_SAVE) { char *newName = XmTextFieldGetString(data->fsb.name); if(newName) { if(strchr(newName, '/')) { ErrDialog(data, data->fsb.errorTitle, data->fsb.errorIllegalChar); XtFree(newName); return; } if(strlen(newName) > 0) { char *selPath = ConcatPath(data->fsb.currentPath, newName); if(data->fsb.selectedPath) free(data->fsb.selectedPath); data->fsb.selectedPath = selPath; } XtFree(newName); data->fsb.selIsDir = False; } } if(data->fsb.selectedPath) { if(!data->fsb.selIsDir) { data->fsb.status = FILEDIALOG_OK; data->fsb.end = True; FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath); } } } static void filedialog_cancel(Widget w, XnFileSelectionBox data, XtPointer d) { data->fsb.end = 1; data->fsb.status = FILEDIALOG_CANCEL; FileSelectionCallback(data, data->fsb.cancelCallback, XmCR_CANCEL, data->fsb.currentPath); } static void filedialog_help(Widget w, XnFileSelectionBox data, XtPointer d) { FileSelectionCallback(data, data->manager.help_callback, XmCR_HELP, data->fsb.currentPath); } static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value) { XmFileSelectionBoxCallbackStruct cbs; memset(&cbs, 0, sizeof(XmFileSelectionBoxCallbackStruct)); char *dir = fsb->fsb.currentPath; size_t dirlen = dir ? strlen(dir) : 0; if(dir && dirlen > 0) { char *dir2 = NULL; if(dir[dirlen-1] != '/') { // add a trailing / to the dir string dir2 = malloc(dirlen+2); memcpy(dir2, dir, dirlen); dir2[dirlen] = '/'; dir2[dirlen+1] = '\0'; dirlen++; dir = dir2; } cbs.dir = XmStringCreateLocalized(dir); cbs.dir_length = dirlen; if(dir2) { free(dir2); } } else { cbs.dir = XmStringCreateLocalized(""); cbs.dir_length = 0; } cbs.reason = reason; cbs.value = XmStringCreateLocalized((char*)value); cbs.length = strlen(value); XtCallCallbackList((Widget)fsb, cb, (XtPointer)&cbs); XmStringFree(cbs.dir); XmStringFree(cbs.value); } static void CreateUI(XnFileSelectionBox w) { Arg args[32]; int n = 0; XmString str; int widget_spacing = w->fsb.widgetSpacing; int window_spacing = w->fsb.windowSpacing; Widget form = (Widget)w; int type = w->fsb.type; XtVaSetValues((Widget)w, XmNautoUnmanage, False, NULL); /* upper part of the gui */ n = 0; XtSetArg(args[n], XmNlabelString, w->fsb.labelDirUp); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopOffset, window_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNresizable, True); n++; XtSetArg(args[n], XmNarrowDirection, XmARROW_UP); n++; w->fsb.dirUp = XmCreatePushButton(form, "DirUp", args, n); XtManageChild(w->fsb.dirUp); XtAddCallback(w->fsb.dirUp, XmNactivateCallback, (XtCallbackProc)filedialog_goup, w); // View Option Menu n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopOffset, window_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightOffset, window_spacing); n++; XtSetArg(args[n], XmNshadowThickness, 0); n++; Widget viewframe = XmCreateForm(form, "vframe", args, n); XtManageChild(viewframe); w->fsb.viewMenu = XmCreatePulldownMenu(viewframe, "menu", NULL, 0); Widget view; if(w->fsb.showViewMenu) { n = 0; XtSetArg(args[n], XmNsubMenuId, w->fsb.viewMenu); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; XtSetArg(args[n], XmNmarginWidth, 0); n++; view = XmCreateOptionMenu(viewframe, "option_menu", args, n); XtManageChild(view); w->fsb.viewOption = view; w->fsb.detailToggleButton = NULL; } else { n = 0; str = XmStringCreateLocalized(w->fsb.labelDetailView); XtSetArg(args[n], XmNlabelString, str); n++; XtSetArg(args[n], XmNfillOnSelect, True); n++; XtSetArg(args[n], XmNindicatorOn, False); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; if(w->fsb.selectedview == 1) { XtSetArg(args[n], XmNset, 1); n++; } w->fsb.detailToggleButton = XmCreateToggleButton(viewframe, "ToggleDetailView", args, n); XtManageChild(w->fsb.detailToggleButton); view = w->fsb.detailToggleButton; XmStringFree(str); XtAddCallback( w->fsb.detailToggleButton, XmNvalueChangedCallback, (XtCallbackProc)filedialog_enable_detailview, w); w->fsb.viewOption = NULL; } n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightWidget, view); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelNewFolder); n++; w->fsb.newFolder = XmCreatePushButton(viewframe, "NewFolder", args, n); XtManageChild(w->fsb.newFolder); XtAddCallback( w->fsb.newFolder, XmNactivateCallback, (XtCallbackProc)FSBNewFolder, w); n = 0; XtSetArg(args[n], XmNlabelString, w->fsb.labelHome); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, w->fsb.newFolder); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; w->fsb.home = XmCreatePushButton(viewframe, "Home", args, n); XtManageChild(w->fsb.home); XtAddCallback( w->fsb.home, XmNactivateCallback, (XtCallbackProc)FSBHome, w); // match visual appearance of detailToggleButton with the other buttons if(w->fsb.detailToggleButton) { Dimension highlight, shadow; XtVaGetValues(w->fsb.newFolder, XmNshadowThickness, &shadow, XmNhighlightThickness, &highlight, NULL); XtVaSetValues(w->fsb.detailToggleButton, XmNshadowThickness, shadow, XmNhighlightThickness, highlight, NULL); } // pathbar n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopOffset, window_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNleftWidget, w->fsb.dirUp); n++; XtSetArg(args[n], XmNleftOffset, widget_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, viewframe); n++; XtSetArg(args[n], XmNrightOffset, widget_spacing); n++; XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++; Widget pathBarFrame = XmCreateFrame(form, "pathbar_frame", args, n); XtManageChild(pathBarFrame); w->fsb.pathBar = CreatePathBar(pathBarFrame, args, 0); w->fsb.pathBar->getpathelm = ui_default_pathelm_func; w->fsb.pathBar->updateDir = (updatedir_callback)filedialog_update_dir; w->fsb.pathBar->updateDirData = w; XtManageChild(w->fsb.pathBar->widget); w->fsb.path = XmCreateTextField(form, "textfield", args, 0); XtVaSetValues(w->fsb.dirUp, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL); if(!w->fsb.showViewMenu) { XtVaSetValues(viewframe, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL); } n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, pathBarFrame); n++; XtSetArg(args[n], XmNtopOffset, 2*widget_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightOffset, window_spacing); n++; w->fsb.filterForm = XmCreateForm(form, "filterform", args, n); XtManageChild(w->fsb.filterForm); n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelDirectories); n++; w->fsb.lsDirLabel = XmCreateLabel(w->fsb.filterForm, "labelDirs", args, n); XtManageChild(w->fsb.lsDirLabel); n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNleftPosition, 35); n++; XtSetArg(args[n], XmNleftOffset, widget_spacing); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++; w->fsb.lsFileLabel = XmCreateLabel(w->fsb.filterForm, "labelFiles", args, n); XtManageChild(w->fsb.lsFileLabel); if(w->fsb.showHiddenButton) { n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelShowHiddenFiles); n++; XtSetArg(args[n], XmNset, w->fsb.showHidden); n++; w->fsb.showHiddenButtonW = XmCreateToggleButton(w->fsb.filterForm, "showHidden", args, n); XtManageChild(w->fsb.showHiddenButtonW); XtAddCallback(w->fsb.showHiddenButtonW, XmNvalueChangedCallback, (XtCallbackProc)filedialog_setshowhidden, w); } n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelFilterButton); n++; if(w->fsb.showHiddenButton) { XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, w->fsb.showHiddenButtonW); n++; } else { XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; } w->fsb.filterButton = XmCreatePushButton(w->fsb.filterForm, "filedialog_filter", args, n); XtManageChild(w->fsb.filterButton); XtAddCallback(w->fsb.filterButton, XmNactivateCallback, (XtCallbackProc)filedialog_filter, w); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNleftWidget, w->fsb.lsFileLabel); n++; XtSetArg(args[n], XmNleftOffset, widget_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, w->fsb.filterButton); n++; XtSetArg(args[n], XmNrightOffset, widget_spacing); n++; XtSetArg(args[n], XmNshowLabel, False); n++; XtSetArg(args[n], XmNuseTextField, True); n++; XtSetArg(args[n], XmNverify, False); n++; w->fsb.filter = XmCreateDropDown(w->fsb.filterForm, "filedialog_filter_textfield", args, n); XtManageChild(w->fsb.filter); XmTextFieldSetString(XmDropDownGetText(w->fsb.filter), w->fsb.filterStr); XtAddCallback(XmDropDownGetText(w->fsb.filter), XmNactivateCallback, (XtCallbackProc)filedialog_filter, w); XtAddCallback(w->fsb.filter, XmNupdateTextCallback, (XtCallbackProc)filedialog_update_filter, w); Widget filterList = XmDropDownGetList(w->fsb.filter); str = XmStringCreateSimple("*"); XmListAddItem(filterList, str, 0); XmStringFree(str); /* lower part */ n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomOffset, window_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightOffset, window_spacing); n++; XtSetArg(args[n], XmNtopOffset, widget_spacing * 2); n++; Widget buttons = XmCreateForm(form, "buttons", args, n); XtManageChild(buttons); n = 0; str = type == FILEDIALOG_OPEN ? w->fsb.labelOpen : w->fsb.labelSave; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, str); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNrightPosition, 14); n++; w->fsb.okBtn = XmCreatePushButton(buttons, "filedialog_open", args, n); XtManageChild(w->fsb.okBtn); XmStringFree(str); XtAddCallback(w->fsb.okBtn, XmNactivateCallback, (XtCallbackProc)filedialog_ok, w); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelHelp); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNleftPosition, 86); n++; w->fsb.helpBtn = XmCreatePushButton(buttons, "filedialog_help", args, n); XtManageChild(w->fsb.helpBtn); XtAddCallback(w->fsb.helpBtn, XmNactivateCallback, (XtCallbackProc)filedialog_help, w); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNleftPosition, 43); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNrightPosition, 57); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelCancel); n++; w->fsb.cancelBtn = XmCreatePushButton(buttons, "filedialog_cancel", args, n); XtManageChild(w->fsb.cancelBtn); XtAddCallback(w->fsb.cancelBtn, XmNactivateCallback, (XtCallbackProc)filedialog_cancel, w); n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomWidget, buttons); n++; XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, 1); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightOffset, 1); n++; w->fsb.separator = XmCreateSeparator(form, "ofd_separator", args, n); XtManageChild(w->fsb.separator); Widget bottomWidget = w->fsb.separator; n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomWidget, w->fsb.separator); n++; XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightOffset, window_spacing); n++; w->fsb.name = XmCreateTextField(form, "textfield", args, n); XtAddCallback(w->fsb.name, XmNactivateCallback, (XtCallbackProc)filedialog_ok, w); n = 0; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomWidget, w->fsb.name); n++; XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelFileName); n++; w->fsb.nameLabel = XmCreateLabel(form, "label", args, n); if(type == FILEDIALOG_SAVE) { bottomWidget = w->fsb.nameLabel; XtManageChild(w->fsb.name); XtManageChild(w->fsb.nameLabel); } w->fsb.bottom_widget = bottomWidget; // middle // form for dir/file lists n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNtopWidget, w->fsb.filterForm); n++; XtSetArg(args[n], XmNtopOffset, widget_spacing); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomWidget, bottomWidget); n++; XtSetArg(args[n], XmNleftOffset, window_spacing); n++; XtSetArg(args[n], XmNrightOffset, window_spacing); n++; XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++; XtSetArg(args[n], XmNwidth, 580); n++; XtSetArg(args[n], XmNheight, 400); n++; w->fsb.listform = XmCreateForm(form, "fds_listform", args, n); // dir/file lists n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopWidget, w->fsb.lsDirLabel); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg(args[n], XmNrightPosition, 35); n++; w->fsb.dirlist = XmCreateScrolledList(w->fsb.listform, "dirlist", args, n); Dimension width, height; XtMakeResizeRequest(w->fsb.dirlist, 150, 200, &width, &height); XtManageChild(w->fsb.dirlist); XtAddCallback( w->fsb.dirlist, XmNdefaultActionCallback, (XtCallbackProc)dirlist_activate, w); XtAddCallback( w->fsb.dirlist, XmNbrowseSelectionCallback, (XtCallbackProc)dirlist_select, w); // FileList XnFileSelectionBoxAddView( (Widget)w, w->fsb.labelListView, CreateListView, FileListUpdate, FileListSelect, FileListCleanup, FileListDestroy, True, w); // Detail FileList #ifdef FSB_ENABLE_DETAIL XnFileSelectionBoxAddView( (Widget)w, w->fsb.labelDetailView, CreateDetailView, FileListDetailUpdate, FileListDetailSelect, FileListDetailCleanup, FileListDetailDestroy, True, w); #endif /* n = 0; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++; XtSetArg(args[n], XmNleftOffset, widget_spacing); n++; //XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; //XtSetArg(args[n], XmNbottomWidget, w->fsb.filelist); n++; XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++; w->fsb.lsFileLabel = XmCreateLabel(w->fsb.listform, "label", args, n); XtManageChild(w->fsb.lsFileLabel); */ XtManageChild(w->fsb.listform); int selview = w->fsb.selectedview; if(selview < 2) { XtManageChild(w->fsb.view[selview].widget); } else { w->fsb.selectedview = 0; } if(w->fsb.selectedPath) { char *parentPath = ParentPath(w->fsb.selectedPath); filedialog_update_dir(w, parentPath); PathBarSetPath(w->fsb.pathBar, parentPath); free(parentPath); if(w->fsb.type == FILEDIALOG_SAVE) { XmTextFieldSetString(w->fsb.name, FileName(w->fsb.selectedPath)); } } else { char cwd[PATH_MAX]; const char *currentPath = w->fsb.currentPath; if(!currentPath) { if(getcwd(cwd, PATH_MAX)) { currentPath = cwd; } else { currentPath = GetHomeDir(); } } filedialog_update_dir(w, currentPath); PathBarSetPath(w->fsb.pathBar, w->fsb.currentPath); } w->fsb.selectedview = selview; XtVaSetValues((Widget)w, XmNcancelButton, w->fsb.cancelBtn, NULL); w->fsb.gui_created = 1; } static char* FSBDialogTitle(Widget widget) { XnFileSelectionBox w = (XnFileSelectionBox)widget; if(w->fsb.type == FILEDIALOG_OPEN) { return w->fsb.labelOpenFileTitle; } else { return w->fsb.labelSaveFileTitle; } } static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc create, void *userData, Boolean useDirList) { Arg args[64]; int n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; if(useDirList) { XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++; XtSetArg(args[n], XmNleftOffset, w->fsb.widgetSpacing); n++; } else { XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopOffset, w->fsb.widgetSpacing); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; } return create(w->fsb.listform, args, n, userData); } typedef struct FSBViewSelection { XnFileSelectionBox fsb; int index; } FSBViewSelection; static void SelectView(XnFileSelectionBox f, int view) { FSBView current = f->fsb.view[f->fsb.selectedview]; FSBView newview = f->fsb.view[view]; XtUnmanageChild(current.widget); if(newview.useDirList != current.useDirList) { if(current.useDirList) { XtUnmanageChild(f->fsb.listform); } else { XtManageChild(f->fsb.listform); } } current.cleanup((Widget)f, current.widget, current.userData); XtManageChild(newview.widget); f->fsb.selectedview = view; filedialog_update_dir(f, NULL); XmProcessTraversal(newview.focus, XmTRAVERSE_CURRENT); } static void SelectViewCallback(Widget w, FSBViewSelection *data, XtPointer u) { SelectView(data->fsb, data->index); } static void SelectViewItemDestroy(Widget w, FSBViewSelection *data, XtPointer u) { free(data); } static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex) { Arg args[4]; int n = 0; XmString label = XmStringCreateLocalized((char*)name); XtSetArg(args[n], XmNlabelString, label); n++; XtSetArg(args[1], XmNpositionIndex, w->fsb.selectedview == w->fsb.numviews ? 0 : w->fsb.numviews+1); n++; Widget item = XmCreatePushButton(w->fsb.viewMenu, "menuitem", args, n); if(viewIndex == 0) { w->fsb.viewSelectorList = item; } else if(viewIndex == 1) { w->fsb.viewSelectorDetail = item; } XtManageChild(item); XmStringFree(label); FSBViewSelection *data = malloc(sizeof(FSBViewSelection)); data->fsb = w; data->index = viewIndex; XtAddCallback( item, XmNactivateCallback, (XtCallbackProc)SelectViewCallback, data); XtAddCallback( item, XmNdestroyCallback, (XtCallbackProc)SelectViewItemDestroy, data); } static FSBViewWidgets CreateListView(Widget parent, ArgList args, int n, void *userData) { XnFileSelectionBox fsb = (XnFileSelectionBox)userData; XtSetArg(args[n], XmNshadowThickness, 0); n++; Widget frame = XmCreateFrame(parent, "filelistframe", args, n); fsb->fsb.filelist = XmCreateScrolledList(frame, "filelist", NULL, 0); XtManageChild(fsb->fsb.filelist); XtAddCallback( fsb->fsb.filelist, XmNdefaultActionCallback, (XtCallbackProc)FileListActivateCB, userData); XtAddCallback( fsb->fsb.filelist, XmNbrowseSelectionCallback, (XtCallbackProc)FileListSelectCB, userData); fsb->fsb.listContextMenu = CreateContextMenu(fsb, fsb->fsb.filelist, FileContextMenuCB); FSBViewWidgets widgets; widgets.view = frame; widgets.focus = fsb->fsb.filelist; return widgets; } #ifdef FSB_ENABLE_DETAIL static void set_path_from_row(XnFileSelectionBox data, int row) { FileElm *elm = NULL; XmLGridRow rowPtr = XmLGridGetRow(data->fsb.grid, XmCONTENT, row); XtVaGetValues(data->fsb.grid, XmNrowPtr, rowPtr, XmNrowUserData, &elm, NULL); if(!elm) { fprintf(stderr, "error: no row data\n"); return; } char *path = strdup(elm->path); data->fsb.selIsDir = False; if(data->fsb.type == FILEDIALOG_SAVE) { XmTextFieldSetString(data->fsb.name, FileName(path)); } if(data->fsb.selectedPath) { free(data->fsb.selectedPath); } data->fsb.selectedPath = path; } static void grid_select(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) { set_path_from_row(data, cb->row); } static void grid_activate(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) { set_path_from_row(data, cb->row); data->fsb.end = True; data->fsb.status = FILEDIALOG_OK; FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath); } static void grid_key_pressed(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) { char chars[16]; KeySym keysym; int nchars; nchars = XLookupString(&cb->event->xkey, chars, 15, &keysym, NULL); if(nchars == 0) return; // if data->showHidden is 0, data->files contains more items than the grid // this means SelectedRow might not be the correct index for data->files // we have to count files manually and increase 'row', if the file // is actually displayed in the grid int row = 0; int selectedRow = XmLGridGetSelectedRow(w); int match = -1; for(int i=0;i<data->fsb.filecount;i++) { const char *name = FileName(data->fsb.files[i].path); if(!data->fsb.showHidden && name[0] == '.') continue; size_t namelen = strlen(name); size_t cmplen = namelen < nchars ? namelen : nchars; if(!memcmp(name, chars, cmplen)) { if(row <= selectedRow) { if(match == -1) { match = row; } } else { match = row; break; } } row++; } if(match > -1) { XmLGridSelectRow(w, match, True); XmLGridFocusAndShowRow(w, match+1); } else { XBell(XtDisplay(w), 0); } } static void grid_header_clicked(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) { int new_cmp_field = 0; switch(cb->column) { case 0: { new_cmp_field = 0; break; } case 1: { new_cmp_field = 1; break; } case 2: { new_cmp_field = 2; break; } } if(new_cmp_field == file_cmp_field) { file_cmp_order = -file_cmp_order; // revert sort order } else { file_cmp_field = new_cmp_field; // change file cmp order to new field file_cmp_order = 1; } int sort_type = file_cmp_order == 1 ? XmSORT_ASCENDING : XmSORT_DESCENDING; XmLGridSetSort(data->fsb.grid, file_cmp_field, sort_type); qsort(data->fsb.files, data->fsb.filecount, sizeof(FileElm), filecmp); // refresh widget filedialog_update_dir(data, NULL); } static FSBViewWidgets CreateDetailView(Widget parent, ArgList args, int n, void *userData) { XnFileSelectionBox w = userData; XtSetArg(args[n], XmNshadowThickness, 0); n++; Widget gridcontainer = XmCreateFrame(parent, "gridcontainer", args, n); XtManageChild(gridcontainer); n = 0; XtSetArg(args[n], XmNcolumns, 3); n++; XtSetArg(args[n], XmNheadingColumns, 0); n++; XtSetArg(args[n], XmNheadingRows, 1); n++; XtSetArg(args[n], XmNallowColumnResize, 1); n++; XtSetArg(args[n], XmNsimpleHeadings, w->fsb.detailHeadings); n++; XtSetArg(args[n], XmNhorizontalSizePolicy, XmCONSTANT); n++; w->fsb.grid = XmLCreateGrid(gridcontainer, "grid", args, n); XmLGridSetIgnoreModifyVerify(w->fsb.grid, True); XtManageChild(w->fsb.grid); XtVaSetValues( w->fsb.grid, XmNcellDefaults, True, XtVaTypedArg, XmNblankBackground, XmRString, "white", 6, XtVaTypedArg, XmNcellBackground, XmRString, "white", 6, NULL); XtAddCallback(w->fsb.grid, XmNselectCallback, (XtCallbackProc)grid_select, w); XtAddCallback(w->fsb.grid, XmNactivateCallback, (XtCallbackProc)grid_activate, w); XtAddCallback(w->fsb.grid, XmNheaderClickCallback, (XtCallbackProc)grid_header_clicked, w); XtAddCallback(w->fsb.grid, XmNgridKeyPressedCallback, (XtCallbackProc)grid_key_pressed, w); // context menu w->fsb.gridContextMenu = CreateContextMenu(w, w->fsb.grid, FileContextMenuCB); FSBViewWidgets widgets; widgets.view = gridcontainer; widgets.focus = w->fsb.grid; return widgets; } #endif /* ------------------------------ Path Utils ------------------------------ */ const char* GetHomeDir(void) { char *home = getenv("HOME"); if(!home) { home = getenv("USERPROFILE"); if(!home) { home = "/"; } } return home; } static char* ConcatPath(const char *parent, const char *name) { size_t parentlen = strlen(parent); size_t namelen = strlen(name); size_t pathlen = parentlen + namelen + 2; char *path = malloc(pathlen); memcpy(path, parent, parentlen); if(parentlen > 0 && parent[parentlen-1] != '/') { path[parentlen] = '/'; parentlen++; } if(name[0] == '/') { name++; namelen--; } memcpy(path+parentlen, name, namelen); path[parentlen+namelen] = '\0'; return path; } static char* FileName(char *path) { int si = 0; int osi = 0; int i = 0; int p = 0; char c; while((c = path[i]) != 0) { if(c == '/') { osi = si; si = i; p = 1; } i++; } char *name = path + si + p; if(name[0] == 0) { name = path + osi + p; if(name[0] == 0) { return path; } } return name; } static char* ParentPath(const char *path) { char *name = FileName((char*)path); size_t namelen = strlen(name); size_t pathlen = strlen(path); size_t parentlen = pathlen - namelen; if(parentlen == 0) { parentlen++; } char *parent = malloc(parentlen + 1); memcpy(parent, path, parentlen); parent[parentlen] = '\0'; return parent; } // unused at the moment, maybe reactivate if more illegal characters // are defined /* static int CheckFileName(const char *fileName) { size_t len = strlen(fileName); for(int i=0;i<len;i++) { if(fileName[i] == '/') { return 0; } } return 1; } */ /* ------------------------------ public API ------------------------------ */ Widget XnCreateFileSelectionDialog( Widget parent, String name, ArgList arglist, Cardinal argcount) { Widget dialog = XmCreateDialogShell(parent, "FileDialog", NULL, 0); Widget fsb = XnCreateFileSelectionBox(dialog, name, arglist, argcount); char *title = FSBDialogTitle(fsb); XtVaSetValues(dialog, XmNtitle, title, NULL); return fsb; } Widget XnCreateFileSelectionBox( Widget parent, String name, ArgList arglist, Cardinal argcount) { Widget fsb = XtCreateWidget(name, xnFsbWidgetClass, parent, arglist, argcount); return fsb; } void XnFileSelectionBoxAddView( Widget fsb, const char *name, FSBViewCreateProc create, FSBViewUpdateProc update, FSBViewSelectProc select, FSBViewCleanupProc cleanup, FSBViewDestroyProc destroy, Boolean useDirList, void *userData) { XnFileSelectionBox f = (XnFileSelectionBox)fsb; if(f->fsb.numviews >= FSB_MAX_VIEWS) { fprintf(stderr, "XnFileSelectionBox: too many views\n"); return; } FSBView view; view.update = update; view.select = select; view.cleanup = cleanup; view.destroy = destroy; view.useDirList = useDirList; view.userData = userData; FSBViewWidgets widgets = CreateView(f, create, userData, useDirList); view.widget = widgets.view; view.focus = widgets.focus; AddViewMenuItem(f, name, f->fsb.numviews); f->fsb.view[f->fsb.numviews++] = view; } /* void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm) { XnFileSelectionBox f = (XnFileSelectionBox)fsb; PathBarSetDirList(f->fsb.pathBar, dirlist, nelm); } */ Widget XnFileSelectionBoxWorkArea(Widget fsb) { XnFileSelectionBox f = (XnFileSelectionBox)fsb; return f->fsb.workarea; } Widget XnFileSelectionBoxGetChild(Widget fsb, enum XnFSBChild child) { XnFileSelectionBox w = (XnFileSelectionBox)fsb; switch(child) { case XnFSB_DIR_UP_BUTTON: return w->fsb.dirUp; case XnFSB_HOME_BUTTON: return w->fsb.home; case XnFSB_NEW_FOLDER_BUTTON: return w->fsb.newFolder; case XnFSB_DETAIL_TOGGLE_BUTTON: return w->fsb.detailToggleButton; case XnFSB_VIEW_OPTION_BUTTON: return w->fsb.viewOption; case XnFSB_FILTER_DROPDOWN: return w->fsb.filter; case XnFSB_FILTER_BUTTON: return w->fsb.filterButton; case XnFSB_SHOW_HIDDEN_TOGGLE_BUTTON: return w->fsb.showHiddenButtonW; case XnFSB_DIRECTORIES_LABEL: return w->fsb.lsDirLabel; case XnFSB_FILES_LABEL: return w->fsb.lsFileLabel; case XnFSB_DIRLIST: return w->fsb.dirlist; case XnFSB_FILELIST: return w->fsb.filelist; case XnFSB_GRID: return w->fsb.grid; case XnFSB_OK_BUTTON: return w->fsb.okBtn; case XnFSB_CANCEL_BUTTON: return w->fsb.cancelBtn; case XnFSB_HELP_BUTTON: return w->fsb.helpBtn; } return NULL; } void XnFileSelectionBoxDeleteFilters(Widget fsb) { XnFileSelectionBox w = (XnFileSelectionBox)fsb; Widget filterList = XmDropDownGetList(w->fsb.filter); XmListDeleteAllItems(filterList); } void XnFileSelectionBoxAddFilter(Widget fsb, const char *filter) { XnFileSelectionBox w = (XnFileSelectionBox)fsb; Widget filterList = XmDropDownGetList(w->fsb.filter); XmString str = XmStringCreateSimple((char*)filter); XmListAddItem(filterList, str, 0); XmStringFree(str); }