From: Olaf Wintermann Date: Sun, 23 Nov 2025 08:28:57 +0000 (+0100) Subject: implement File -> Open menu item X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=b5757f546d95fb39744a9747270bfeb69fd40f70;p=uwplayer.git implement File -> Open menu item --- diff --git a/ui/common/objs.mk b/ui/common/objs.mk index 4d401b5..176180c 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -42,6 +42,7 @@ COMMON_OBJ += threadpool$(OBJ_EXT) COMMON_OBJ += condvar$(OBJ_EXT) COMMON_OBJ += args$(OBJ_EXT) COMMON_OBJ += wrapper$(OBJ_EXT) +COMMON_OBJ += utils$(OBJ_EXT) TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%) TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c) diff --git a/ui/common/utils.c b/ui/common/utils.c new file mode 100644 index 0000000..c4823f3 --- /dev/null +++ b/ui/common/utils.c @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "utils.h" + +#include + +UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return NULL; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} diff --git a/ui/common/utils.h b/ui/common/utils.h new file mode 100644 index 0000000..688f24a --- /dev/null +++ b/ui/common/utils.h @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UIC_UTILS_H +#define UIC_UTILS_H + +#include "../ui/toolkit.h" +#include "../ui/text.h" + +#ifdef __cplusplus +extern "C" { +#endif + +UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data); + + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_UTILS_H */ + diff --git a/application/Fsb.c b/ui/motif/Fsb.c similarity index 86% rename from application/Fsb.c rename to ui/motif/Fsb.c index 09c8eb6..bcccc7c 100644 --- a/application/Fsb.c +++ b/ui/motif/Fsb.c @@ -41,6 +41,10 @@ #include #include +#include "pathbar.h" + +#include "../common/utils.h" + #ifdef FSB_ENABLE_DETAIL #include #endif @@ -144,7 +148,6 @@ static char* ParentPath(const char *path); static int filedialog_update_dir(XnFileSelectionBox data, const char *path); static void filedialog_cleanup_filedata(XnFileSelectionBox data); -static void pathbar_resize(Widget w, PathBar *p, XtPointer d); static XtResource resources[] = { {XmNokCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.okCallback), XmRCallback, NULL}, @@ -1847,6 +1850,7 @@ static void CreateUI(XnFileSelectionBox w) { 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); @@ -2527,476 +2531,6 @@ static int CheckFileName(const char *fileName) { } */ -/* ------------------------------ PathBar ------------------------------ */ - -static void pathbar_resize(Widget w, PathBar *p, XtPointer d) -{ - if(p->disableResize) return; - - Dimension width, height; - XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); - - Dimension xoff; - XtVaGetValues(p->down, XmNwidth, &xoff, NULL); - - Dimension *segW = calloc(p->numSegments, sizeof(Dimension)); - - Dimension maxHeight = 0; - - /* get width/height from all widgets */ - Dimension pathWidth = 0; - for(int i=0;inumSegments;i++) { - Dimension segWidth; - Dimension segHeight; - XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); - segW[i] = segWidth; - pathWidth += segWidth; - if(segHeight > maxHeight) { - maxHeight = segHeight; - } - } - Dimension tfHeight; - XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); - if(tfHeight > maxHeight) { - maxHeight = tfHeight; - } - - Boolean arrows = False; - if(pathWidth + xoff + 10 > width) { - arrows = True; - //pathWidth += p->lw + p->rw; - } - - /* calc max visible widgets */ - int start = 0; - if(arrows) { - Dimension vis = p->lw+p->rw; - for(int i=p->numSegments;i>0;i--) { - Dimension segWidth = segW[i-1]; - if(vis + segWidth + xoff + 10 > width) { - start = i; - arrows = True; - break; - } - vis += segWidth; - } - } else { - p->shift = 0; - } - - int leftShift = 0; - if(p->shift < 0) { - if(start + p->shift < 0) { - leftShift = start; - start = 0; - p->shift = -leftShift; - } else { - leftShift = -p->shift; /* negative shift */ - start += p->shift; - } - } - - int x = 0; - if(arrows) { - XtManageChild(p->left); - XtManageChild(p->right); - x += p->lw; - } else { - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - } - - for(int i=0;inumSegments;i++) { - if(i >= start && i < p->numSegments - leftShift && !p->input) { - XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - x += segW[i]; - XtManageChild(p->pathSegments[i]); - } else { - XtUnmanageChild(p->pathSegments[i]); - } - } - - if(arrows) { - XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); - XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - } - XtVaSetValues(p->down, XmNx, width-xoff, XmNheight, maxHeight, NULL); - free(segW); - - Dimension rw, rh; - XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); - XtVaSetValues(p->textfield, XmNwidth, rw-xoff, XmNheight, rh, NULL); -} - -static void pathbar_input(Widget w, PathBar *p, XtPointer c) -{ - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - - XtManageChild(p->textfield); - p->input = 1; - - XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); - - pathbar_resize(p->widget, p, NULL); - } - } -} - -static void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) -{ - p->input = False; - XtUnmanageChild(p->textfield); - pathbar_resize(p->widget, p, NULL); -} - -static void pathbar_pathinput(Widget w, PathBar *pb, XtPointer d) -{ - char *newpath = XmTextFieldGetString(pb->textfield); - if(newpath) { - if(newpath[0] == '~') { - char *p = newpath+1; - char *cp = ConcatPath(GetHomeDir(), p); - XtFree(newpath); - newpath = cp; - } else if(newpath[0] != '/') { - char cwd[PATH_MAX]; - if(!getcwd(cwd, sizeof(cwd))) { - cwd[0] = '/'; - cwd[1] = '\0'; - } - char *cp = ConcatPath(cwd, newpath); - XtFree(newpath); - newpath = cp; - } - - /* update path */ - if(pb->updateDir) { - if(!pb->updateDir(pb->updateDirData, newpath)) { - PathBarSetPath(pb, newpath); - } - } else { - PathBarSetPath(pb, newpath); - } - XtFree(newpath); - - /* hide textfield and show path as buttons */ - XtUnmanageChild(pb->textfield); - pathbar_resize(pb->widget, pb, NULL); - } -} - -static void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) { - p->shift--; - pathbar_resize(p->widget, p, NULL); -} - -static void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) { - if(p->shift < 0) { - p->shift++; - } - pathbar_resize(p->widget, p, NULL); -} - -static void pathbar_list_select(Widget w, PathBar *p, XmListCallbackStruct *cb) { - char *value = NULL; - XmStringGetLtoR(cb->item, XmSTRING_DEFAULT_CHARSET, &value); - p->updateDir(p->updateDirData, value); - PathBarSetPath(p, value); - free(value); -} - -static void pathbar_popup(Widget w, PathBar *p, XtPointer d) { - Widget parent = XtParent(w); - Display *dp = XtDisplay(parent); - Window root = XDefaultRootWindow(dp); - - int x, y; - Window child; - XTranslateCoordinates(dp, XtWindow(parent), root, 0, 0, &x, &y, &child); - - XtManageChild(p->list); - XtPopupSpringLoaded(p->popup); - XtVaSetValues(p->popup, XmNx, x, XmNy, y + parent->core.height, XmNwidth, parent->core.width, XmNheight, 200, NULL); - - XmProcessTraversal(p->list, XmTRAVERSE_CURRENT); -} - -static void popupEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - PathBar *bar = data; - - Window w1 = bar->hs ? XtWindow(bar->hs) : 0; - Window w2 = bar->vs ? XtWindow(bar->vs) : 0; - - if(event->type == ButtonPress) { - if(event->xbutton.window != 0 && (event->xbutton.window == w1 || event->xbutton.window == w2)) { - bar->popupScrollEvent = 1; - } else { - bar->popupScrollEvent = 0; - } - } else if(event->type == ButtonRelease) { - if(bar->popupScrollEvent) { - *dispatch = False; - } - bar->popupScrollEvent = 0; - } else if(event->type == KeyReleaseMask) { - int keycode = event->xkey.keycode; - if(keycode == 36 || keycode == 9) { - XtUnmapWidget(bar->popup); - } - } -} - -static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - PathBar *pb = data; - if(event->type == KeyReleaseMask) { - if(event->xkey.keycode == 9) { - XtUnmanageChild(pb->textfield); - pathbar_resize(pb->widget, pb, NULL); - *dispatch = False; - } - } -} - - -PathBar* CreatePathBar(Widget parent, ArgList args, int n) -{ - PathBar *bar = malloc(sizeof(PathBar)); - bar->path = NULL; - bar->updateDir = NULL; - bar->updateDirData = NULL; - bar->disableResize = False; - - bar->shift = 0; - - XtSetArg(args[n], XmNmarginWidth, 0); n++; - XtSetArg(args[n], XmNmarginHeight, 0); n++; - bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); - XtAddCallback( - bar->widget, - XmNresizeCallback, - (XtCallbackProc)pathbar_resize, - bar); - XtAddCallback( - bar->widget, - XmNinputCallback, - (XtCallbackProc)pathbar_input, - bar); - - n = 0; - XtSetArg(args[n], XmNownerEvents, True), n++; - XtSetArg(args[n], XmNgrabStyle, GrabModeSync), n++; - bar->popup = XmCreateGrabShell(bar->widget, "pbpopup", args, n); - bar->list = XmCreateScrolledList(bar->popup, "pblist", NULL, 0); - XtAddEventHandler(bar->popup, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, FALSE, popupEH, bar); - bar->popupScrollEvent = 0; - - XtAddCallback( - bar->list, - XmNdefaultActionCallback, - (XtCallbackProc)pathbar_list_select, - bar); - XtAddCallback( - bar->list, - XmNbrowseSelectionCallback, - (XtCallbackProc)pathbar_list_select, - bar); - - bar->vs = NULL; - bar->hs = NULL; - XtVaGetValues(XtParent(bar->list), XmNhorizontalScrollBar, &bar->hs, XmNverticalScrollBar, &bar->vs, NULL); - - Arg a[4]; - XtSetArg(a[0], XmNshadowThickness, 0); - XtSetArg(a[1], XmNx, 0); - XtSetArg(a[2], XmNy, 0); - bar->textfield = XmCreateTextField(bar->widget, "pbtext", a, 3); - bar->input = 0; - XtAddCallback( - bar->textfield, - XmNlosingFocusCallback, - (XtCallbackProc)pathbar_losingfocus, - bar); - XtAddCallback(bar->textfield, XmNactivateCallback, - (XtCallbackProc)pathbar_pathinput, bar); - XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); - - XtSetArg(a[0], XmNarrowDirection, XmARROW_DOWN); - bar->down = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtManageChild(bar->down); - XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); - bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); - bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtAddCallback( - bar->down, - XmNactivateCallback, - (XtCallbackProc)pathbar_popup, - bar); - XtAddCallback( - bar->left, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_left, - bar); - XtAddCallback( - bar->right, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_right, - bar); - - Pixel bg; - XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); - XtVaSetValues(bar->widget, XmNbackground, bg, NULL); - - //XtManageChild(bar->left); - //XtManageChild(bar->right); - - XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); - XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); - - bar->segmentAlloc = 16; - bar->numSegments = 0; - bar->pathSegments = calloc(16, sizeof(Widget)); - - bar->selection = 0; - - return bar; -} - -static void PathBarChangeDir(Widget w, PathBar *bar, XtPointer unused) { - XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); - - for(int i=0;inumSegments;i++) { - if(bar->pathSegments[i] == w) { - bar->selection = i; - XmToggleButtonSetState(w, True, False); - break; - } - } - - int plen = strlen(bar->path); - int countSeg = 0; - for(int i=0;i<=plen;i++) { - char c = bar->path[i]; - if(c == '/' || c == '\0') { - if(countSeg == bar->selection) { - char *dir = malloc(i+2); - memcpy(dir, bar->path, i+1); - dir[i+1] = '\0'; - if(bar->updateDir) { - bar->updateDir(bar->updateDirData, dir); - } - free(dir); - } - countSeg++; - } - } -} - -void PathBarSetPath(PathBar *bar, const char *path) { - if(bar->path) { - free(bar->path); - } - bar->path = strdup(path); - - for(int i=0;inumSegments;i++) { - XtDestroyWidget(bar->pathSegments[i]); - } - XtUnmanageChild(bar->textfield); - //XtManageChild(bar->left); - //XtManageChild(bar->right); - bar->input = False; - - Arg args[4]; - XmString str; - - bar->numSegments = 0; - - int i=0; - if(path[0] == '/') { - str = XmStringCreateLocalized("/"); - XtSetArg(args[0], XmNlabelString, str); - XtSetArg(args[1], XmNfillOnSelect, True); - XtSetArg(args[2], XmNindicatorOn, False); - bar->pathSegments[0] = XmCreateToggleButton( - bar->widget, "pbbutton", args, 3); - XtAddCallback( - bar->pathSegments[0], - XmNvalueChangedCallback, - (XtCallbackProc)PathBarChangeDir, - bar); - XmStringFree(str); - bar->numSegments++; - i++; - } - - int len = strlen(path); - int begin = i; - for(;i<=len;i++) { - char c = path[i]; - if((c == '/' || c == '\0') && i > begin) { - char *segStr = malloc(i - begin + 1); - memcpy(segStr, path+begin, i-begin); - segStr[i-begin] = '\0'; - begin = i+1; - - str = XmStringCreateLocalized(segStr); - free(segStr); - XtSetArg(args[0], XmNlabelString, str); - XtSetArg(args[1], XmNfillOnSelect, True); - XtSetArg(args[2], XmNindicatorOn, False); - Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3); - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)PathBarChangeDir, - bar); - XmStringFree(str); - - if(bar->numSegments >= bar->segmentAlloc) { - bar->segmentAlloc += 8; - bar->pathSegments = realloc(bar->pathSegments, bar->segmentAlloc * sizeof(Widget)); - } - - bar->pathSegments[bar->numSegments++] = button; - } - } - - bar->selection = bar->numSegments-1; - XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); - - XmTextFieldSetString(bar->textfield, (char*)path); - XmTextFieldSetInsertionPosition(bar->textfield, XmTextFieldGetLastPosition(bar->textfield)); - - pathbar_resize(bar->widget, bar, NULL); -} - -void PathBarSetDirList(PathBar *bar, const char **dirlist, size_t nelm) { - XmStringTable items = calloc(nelm, sizeof(XmString)); - for(int i=0;ilist); - XmListAddItems(bar->list, items, nelm, 0); - XmListSelectPos(bar->list, 1, False); - for(int i=0;ipath) { - free(bar->path); - } - free(bar->pathSegments); - free(bar); -} /* ------------------------------ public API ------------------------------ */ @@ -3057,10 +2591,12 @@ void XnFileSelectionBoxAddView( 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; diff --git a/application/Fsb.h b/ui/motif/Fsb.h similarity index 89% rename from application/Fsb.h rename to ui/motif/Fsb.h index b79891b..4257137 100644 --- a/application/Fsb.h +++ b/ui/motif/Fsb.h @@ -146,7 +146,7 @@ extern WidgetClass xnFsbWidgetClass; /* * int FSBFilterFunc(const char *pattern, const char *string) * - * Checks checks whether the string matches the pattern + * Checks whether the string matches the pattern * * Return * zero if the string matches the pattern @@ -154,40 +154,6 @@ extern WidgetClass xnFsbWidgetClass; */ typedef int(*FSBFilterFunc)(const char*, const char*); -typedef int(*updatedir_callback)(void*,char*); - -typedef struct PathBar { - Widget widget; - Widget textfield; - - Widget down; - Widget left; - Widget right; - Dimension lw; - Dimension rw; - - Widget popup; - Widget list; - - int shift; - - Widget *pathSegments; - size_t numSegments; - size_t segmentAlloc; - - char *path; - int selection; - Boolean input; - - Widget hs; - Widget vs; - int popupScrollEvent; - - updatedir_callback updateDir; - void *updateDirData; - - Boolean disableResize; -} PathBar; typedef struct FileElm FileElm; struct FileElm { @@ -227,11 +193,6 @@ typedef void(*FSBViewSelectProc)(Widget fsb, Widget view, const char *item); typedef void(*FSBViewCleanupProc)(Widget fsb, Widget view, void *userData); typedef void(*FSBViewDestroyProc)(Widget fsb, Widget view, void *userData); -PathBar* CreatePathBar(Widget parent, ArgList args, int n); -void PathBarSetPath(PathBar *bar, const char *path); -void PathBarSetDirList(PathBar *bar, const char **dirlist, size_t nelm); -void PathBarDestroy(PathBar *bar); - Widget XnCreateFileSelectionDialog( Widget parent, String name, @@ -255,7 +216,7 @@ void XnFileSelectionBoxAddView( Boolean useDirList, void *userData); -void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm); +//void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm); Widget XnFileSelectionBoxWorkArea(Widget fsb); diff --git a/application/FsbP.h b/ui/motif/FsbP.h similarity index 99% rename from application/FsbP.h rename to ui/motif/FsbP.h index 71c70b7..6d520ca 100644 --- a/application/FsbP.h +++ b/ui/motif/FsbP.h @@ -30,6 +30,7 @@ #include #include "Fsb.h" +#include "pathbar.h" #ifdef __cplusplus extern "C" { diff --git a/ui/motif/objs.mk b/ui/motif/objs.mk index 2a47e14..8d5d0fd 100644 --- a/ui/motif/objs.mk +++ b/ui/motif/objs.mk @@ -39,6 +39,7 @@ MOTIFOBJ += toolbar.o MOTIFOBJ += button.o MOTIFOBJ += label.o MOTIFOBJ += text.o +MOTIFOBJ += pathbar.o MOTIFOBJ += list.o MOTIFOBJ += graphics.o MOTIFOBJ += range.o @@ -46,6 +47,7 @@ MOTIFOBJ += dnd.o MOTIFOBJ += image.o MOTIFOBJ += Grid.o MOTIFOBJ += entry.o +MOTIFOBJ += Fsb.o TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%) TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c) diff --git a/ui/motif/pathbar.c b/ui/motif/pathbar.c new file mode 100644 index 0000000..4e0cde6 --- /dev/null +++ b/ui/motif/pathbar.c @@ -0,0 +1,439 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pathbar.h" + +#include +#include + + + +void pathbar_resize(Widget w, PathBar *p, XtPointer d) +{ + Dimension width, height; + XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); + + Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); + + Dimension maxHeight = 0; + + /* get width/height from all widgets */ + Dimension pathWidth = 0; + for(int i=0;inumSegments;i++) { + Dimension segWidth; + Dimension segHeight; + XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); + segW[i] = segWidth; + pathWidth += segWidth; + if(segHeight > maxHeight) { + maxHeight = segHeight; + } + } + Dimension tfHeight; + XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); + if(tfHeight > maxHeight) { + maxHeight = tfHeight; + } + + Boolean arrows = False; + if(pathWidth + 10 > width) { + arrows = True; + pathWidth += p->lw + p->rw; + } + + /* calc max visible widgets */ + int start = 0; + if(arrows) { + Dimension vis = p->lw+p->rw; + for(int i=p->numSegments;i>0;i--) { + Dimension segWidth = segW[i-1]; + if(vis + segWidth + 10 > width) { + start = i; + arrows = True; + break; + } + vis += segWidth; + } + } else { + p->shift = 0; + } + + int leftShift = 0; + if(p->shift < 0) { + if(start + p->shift < 0) { + leftShift = start; + start = 0; + p->shift = -leftShift; + } else { + leftShift = -p->shift; /* negative shift */ + start += p->shift; + } + } + + int x = 0; + if(arrows) { + XtManageChild(p->left); + XtManageChild(p->right); + x = p->lw; + } else { + XtUnmanageChild(p->left); + XtUnmanageChild(p->right); + } + + for(int i=0;inumSegments;i++) { + if(i >= start && i < p->numSegments - leftShift && !p->input) { + XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); + x += segW[i]; + XtManageChild(p->pathSegments[i]); + } else { + XtUnmanageChild(p->pathSegments[i]); + } + } + + if(arrows) { + XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); + XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); + } + + free(segW); + + Dimension rw, rh; + XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); + + XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); +} + +static void pathbarActivateTF(PathBar *p) +{ + XtUnmanageChild(p->left); + XtUnmanageChild(p->right); + XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); + XtManageChild(p->textfield); + p->input = 1; + + XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); + + pathbar_resize(p->widget, p, NULL); +} + +void PathBarActivateTextfield(PathBar *p) +{ + p->focus = 1; + pathbarActivateTF(p); +} + +void pathbar_input(Widget w, PathBar *p, XtPointer c) +{ + XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; + XEvent *xevent = cbs->event; + + if (cbs->reason == XmCR_INPUT) { + if (xevent->xany.type == ButtonPress) { + p->focus = 0; + pathbarActivateTF(p); + } + } +} + +void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) +{ + if(--p->focus < 0) { + p->input = False; + XtUnmanageChild(p->textfield); + } +} + +static cxmutstr concat_path_s(cxstring base, cxstring path) { + if(!path.ptr) { + path = CX_STR(""); + } + + int add_separator = 0; + if(base.length != 0 && base.ptr[base.length-1] == '/') { + if(path.ptr[0] == '/') { + base.length--; + } + } else { + if(path.length == 0 || path.ptr[0] != '/') { + add_separator = 1; + } + } + + cxmutstr url; + if(add_separator) { + url = cx_strcat(3, base, CX_STR("/"), path); + } else { + url = cx_strcat(2, base, path); + } + + return url; +} + +char* pathbar_concat_path(const char *path1, const char *path2) { + return concat_path_s(cx_str(path1), cx_str(path2)).ptr; +} + +void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) +{ + char *newpath = XNETextGetString(p->textfield); + if(newpath) { + if(newpath[0] == '~') { + char *p = newpath+1; + char *home = getenv("HOME"); + char *cp = pathbar_concat_path(home, p); + XtFree(newpath); + newpath = cp; + } else if(newpath[0] != '/') { + char curdir[2048]; + curdir[0] = 0; + getcwd(curdir, 2048); + char *cp = pathbar_concat_path(curdir, newpath); + XtFree(newpath); + newpath = cp; + } + + /* update path */ + PathBarSetPath(p, newpath); + if(p->updateDir) { + p->updateDir(p->updateDirData, newpath, -1); + } + XtFree(newpath); + + /* hide textfield and show path as buttons */ + XtUnmanageChild(p->textfield); + pathbar_resize(p->widget, p, NULL); + + if(p->focus_widget) { + XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); + } + } +} + +void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) +{ + p->shift--; + pathbar_resize(p->widget, p, NULL); +} + +void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) +{ + if(p->shift < 0) { + p->shift++; + } + pathbar_resize(p->widget, p, NULL); +} + +static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { + PathBar *pb = data; + if(event->type == KeyReleaseMask) { + if(event->xkey.keycode == 9) { + XtUnmanageChild(pb->textfield); + pathbar_resize(pb->widget, pb, NULL); + *dispatch = False; + } else if(event->xkey.keycode == 36) { + pathbar_pathinput(pb->textfield, pb, NULL); + *dispatch = False; + } + } +} + +PathBar* CreatePathBar(Widget parent, ArgList args, int n) +{ + PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); + bar->path = NULL; + bar->updateDir = NULL; + bar->updateDirData = NULL; + + bar->focus_widget = NULL; + + bar->getpathelm = NULL; + bar->getpathelmdata = NULL; + bar->current_pathelms = NULL; + + bar->shift = 0; + + XtSetArg(args[n], XmNmarginWidth, 0); n++; + XtSetArg(args[n], XmNmarginHeight, 0); n++; + bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); + XtAddCallback( + bar->widget, + XmNresizeCallback, + (XtCallbackProc)pathbar_resize, + bar); + XtAddCallback( + bar->widget, + XmNinputCallback, + (XtCallbackProc)pathbar_input, + bar); + + Arg a[4]; + XtSetArg(a[0], XmNshadowThickness, 0); + XtSetArg(a[1], XmNx, 0); + XtSetArg(a[2], XmNy, 0); + bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); + bar->input = 0; + XtAddCallback( + bar->textfield, + XmNlosingFocusCallback, + (XtCallbackProc)pathbar_losingfocus, + bar); + XtAddCallback(bar->textfield, XmNactivateCallback, + (XtCallbackProc)pathbar_pathinput, bar); + XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); + + XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); + bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); + XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); + bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); + XtAddCallback( + bar->left, + XmNactivateCallback, + (XtCallbackProc)pathbar_shift_left, + bar); + XtAddCallback( + bar->right, + XmNactivateCallback, + (XtCallbackProc)pathbar_shift_right, + bar); + + Pixel bg; + XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); + XtVaSetValues(bar->widget, XmNbackground, bg, NULL); + + XtManageChild(bar->left); + XtManageChild(bar->right); + + XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); + XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); + + bar->segmentAlloc = 16; + bar->numSegments = 0; + bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); + + bar->selection = 0; + + return bar; +} + +void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) +{ + XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); + + int i; + for(i=0;inumSegments;i++) { + if(bar->pathSegments[i] == w) { + bar->selection = i; + XmToggleButtonSetState(w, True, False); + break; + } + } + + UiPathElm elm = bar->current_pathelms[i]; + cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len)); + if(bar->updateDir) { + XNETextSetString(bar->textfield, path.ptr); + bar->updateDir(bar->updateDirData, path.ptr, i); + } + free(path.ptr); +} + +static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { + for(int i=0;ipath) { + free(bar->path); + } + bar->path = strdup(path); + + for(int i=0;inumSegments;i++) { + XtDestroyWidget(bar->pathSegments[i]); + } + XtUnmanageChild(bar->textfield); + XtManageChild(bar->left); + XtManageChild(bar->right); + bar->input = False; + + Arg args[4]; + XmString str; + + bar->numSegments = 0; + + ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); + size_t nelm = 0; + UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); + if (!path_elm) { + return; + } + bar->current_pathelms = path_elm; + bar->numSegments = nelm; + bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); + + for(int i=0;iwidget, "pbbutton", args, 3); + XtAddCallback( + button, + XmNvalueChangedCallback, + (XtCallbackProc)PathBarChangeDir, + bar); + XmStringFree(str); + + bar->pathSegments[i] = button; + } + + bar->selection = bar->numSegments-1; + XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); + + XNETextSetString(bar->textfield, (char*)path); + XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); + + pathbar_resize(bar->widget, bar, NULL); +} + +void PathBarDestroy(PathBar *pathbar) { + if(pathbar->path) { + XtFree(pathbar->path); + } + XtFree((void*)pathbar->pathSegments); + XtFree((void*)pathbar); +} diff --git a/ui/motif/pathbar.h b/ui/motif/pathbar.h new file mode 100644 index 0000000..90ba73b --- /dev/null +++ b/ui/motif/pathbar.h @@ -0,0 +1,95 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PATHBAR_H +#define PATHBAR_H + +#include + +#include "../ui/text.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) +#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) +#define XNETextGetString(widget) XmTextFieldGetString(widget) +#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) +#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) +#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) + +typedef void(*updatedir_callback)(void*,char*,int); + +typedef struct PathBar { + Widget widget; + Widget textfield; + + Widget focus_widget; + + Widget left; + Widget right; + Dimension lw; + Dimension rw; + + int shift; + + UiPathElm *current_pathelms; + Widget *pathSegments; + size_t numSegments; + size_t segmentAlloc; + + char *path; + int selection; + Boolean input; + + int focus; + + Boolean disableResize; + + updatedir_callback updateDir; + void *updateDirData; + + ui_pathelm_func getpathelm; + void *getpathelmdata; +} PathBar; + +PathBar* CreatePathBar(Widget parent, ArgList args, int n); +void PathBarSetPath(PathBar *bar, const char *path); +void PathBarDestroy(PathBar *bar); + +void pathbar_resize(Widget w, PathBar *p, XtPointer d); + +char* pathbar_concat_path(const char *path1, const char *path2); + +#ifdef __cplusplus +} +#endif + +#endif /* PATHBAR_H */ + diff --git a/ui/motif/text.c b/ui/motif/text.c index 00d5702..52f4f16 100644 --- a/ui/motif/text.c +++ b/ui/motif/text.c @@ -32,6 +32,9 @@ #include "text.h" #include "container.h" +#include "pathbar.h" + +#include "../common/utils.h" #include @@ -508,459 +511,6 @@ void ui_textfield_set(UiString *str, const char *value) { } - - - -/* -------------------- path bar -------------------- */ - -#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) -#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) -#define XNETextGetString(widget) XmTextFieldGetString(widget) -#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) -#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) -#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) - -typedef void(*updatedir_callback)(void*,char*,int); - -typedef struct PathBar { - Widget widget; - Widget textfield; - - Widget focus_widget; - - Widget left; - Widget right; - Dimension lw; - Dimension rw; - - int shift; - - UiPathElm *current_pathelms; - Widget *pathSegments; - size_t numSegments; - size_t segmentAlloc; - - char *path; - int selection; - Boolean input; - - int focus; - - updatedir_callback updateDir; - void *updateDirData; - - ui_pathelm_func getpathelm; - void *getpathelmdata; -} PathBar; - -void PathBarSetPath(PathBar *bar, const char *path); - -void pathbar_resize(Widget w, PathBar *p, XtPointer d) -{ - Dimension width, height; - XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); - - Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); - - Dimension maxHeight = 0; - - /* get width/height from all widgets */ - Dimension pathWidth = 0; - for(int i=0;inumSegments;i++) { - Dimension segWidth; - Dimension segHeight; - XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); - segW[i] = segWidth; - pathWidth += segWidth; - if(segHeight > maxHeight) { - maxHeight = segHeight; - } - } - Dimension tfHeight; - XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); - if(tfHeight > maxHeight) { - maxHeight = tfHeight; - } - - Boolean arrows = False; - if(pathWidth + 10 > width) { - arrows = True; - pathWidth += p->lw + p->rw; - } - - /* calc max visible widgets */ - int start = 0; - if(arrows) { - Dimension vis = p->lw+p->rw; - for(int i=p->numSegments;i>0;i--) { - Dimension segWidth = segW[i-1]; - if(vis + segWidth + 10 > width) { - start = i; - arrows = True; - break; - } - vis += segWidth; - } - } else { - p->shift = 0; - } - - int leftShift = 0; - if(p->shift < 0) { - if(start + p->shift < 0) { - leftShift = start; - start = 0; - p->shift = -leftShift; - } else { - leftShift = -p->shift; /* negative shift */ - start += p->shift; - } - } - - int x = 0; - if(arrows) { - XtManageChild(p->left); - XtManageChild(p->right); - x = p->lw; - } else { - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - } - - for(int i=0;inumSegments;i++) { - if(i >= start && i < p->numSegments - leftShift && !p->input) { - XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - x += segW[i]; - XtManageChild(p->pathSegments[i]); - } else { - XtUnmanageChild(p->pathSegments[i]); - } - } - - if(arrows) { - XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); - XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - } - - free(segW); - - Dimension rw, rh; - XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); - - XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); -} - -static void pathbarActivateTF(PathBar *p) -{ - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); - XtManageChild(p->textfield); - p->input = 1; - - XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); - - pathbar_resize(p->widget, p, NULL); -} - -void PathBarActivateTextfield(PathBar *p) -{ - p->focus = 1; - pathbarActivateTF(p); -} - -void pathbar_input(Widget w, PathBar *p, XtPointer c) -{ - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - p->focus = 0; - pathbarActivateTF(p); - } - } -} - -void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) -{ - if(--p->focus < 0) { - p->input = False; - XtUnmanageChild(p->textfield); - } -} - -static cxmutstr concat_path_s(cxstring base, cxstring path) { - if(!path.ptr) { - path = CX_STR(""); - } - - int add_separator = 0; - if(base.length != 0 && base.ptr[base.length-1] == '/') { - if(path.ptr[0] == '/') { - base.length--; - } - } else { - if(path.length == 0 || path.ptr[0] != '/') { - add_separator = 1; - } - } - - cxmutstr url; - if(add_separator) { - url = cx_strcat(3, base, CX_STR("/"), path); - } else { - url = cx_strcat(2, base, path); - } - - return url; -} - -static char* ConcatPath(const char *path1, const char *path2) { - return concat_path_s(cx_str(path1), cx_str(path2)).ptr; -} - -void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) -{ - char *newpath = XNETextGetString(p->textfield); - if(newpath) { - if(newpath[0] == '~') { - char *p = newpath+1; - char *home = getenv("HOME"); - char *cp = ConcatPath(home, p); - XtFree(newpath); - newpath = cp; - } else if(newpath[0] != '/') { - char curdir[2048]; - curdir[0] = 0; - getcwd(curdir, 2048); - char *cp = ConcatPath(curdir, newpath); - XtFree(newpath); - newpath = cp; - } - - /* update path */ - PathBarSetPath(p, newpath); - if(p->updateDir) { - p->updateDir(p->updateDirData, newpath, -1); - } - XtFree(newpath); - - /* hide textfield and show path as buttons */ - XtUnmanageChild(p->textfield); - pathbar_resize(p->widget, p, NULL); - - if(p->focus_widget) { - XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); - } - } -} - -void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) -{ - p->shift--; - pathbar_resize(p->widget, p, NULL); -} - -void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) -{ - if(p->shift < 0) { - p->shift++; - } - pathbar_resize(p->widget, p, NULL); -} - -static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - PathBar *pb = data; - if(event->type == KeyReleaseMask) { - if(event->xkey.keycode == 9) { - XtUnmanageChild(pb->textfield); - pathbar_resize(pb->widget, pb, NULL); - *dispatch = False; - } else if(event->xkey.keycode == 36) { - pathbar_pathinput(pb->textfield, pb, NULL); - *dispatch = False; - } - } -} - -PathBar* CreatePathBar(Widget parent, ArgList args, int n) -{ - PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); - bar->path = NULL; - bar->updateDir = NULL; - bar->updateDirData = NULL; - - bar->focus_widget = NULL; - - bar->getpathelm = NULL; - bar->getpathelmdata = NULL; - bar->current_pathelms = NULL; - - bar->shift = 0; - - XtSetArg(args[n], XmNmarginWidth, 0); n++; - XtSetArg(args[n], XmNmarginHeight, 0); n++; - bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); - XtAddCallback( - bar->widget, - XmNresizeCallback, - (XtCallbackProc)pathbar_resize, - bar); - XtAddCallback( - bar->widget, - XmNinputCallback, - (XtCallbackProc)pathbar_input, - bar); - - Arg a[4]; - XtSetArg(a[0], XmNshadowThickness, 0); - XtSetArg(a[1], XmNx, 0); - XtSetArg(a[2], XmNy, 0); - bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); - bar->input = 0; - XtAddCallback( - bar->textfield, - XmNlosingFocusCallback, - (XtCallbackProc)pathbar_losingfocus, - bar); - XtAddCallback(bar->textfield, XmNactivateCallback, - (XtCallbackProc)pathbar_pathinput, bar); - XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); - - XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); - bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); - bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtAddCallback( - bar->left, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_left, - bar); - XtAddCallback( - bar->right, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_right, - bar); - - Pixel bg; - XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); - XtVaSetValues(bar->widget, XmNbackground, bg, NULL); - - XtManageChild(bar->left); - XtManageChild(bar->right); - - XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); - XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); - - bar->segmentAlloc = 16; - bar->numSegments = 0; - bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); - - bar->selection = 0; - - return bar; -} - -void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) -{ - XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); - - int i; - for(i=0;inumSegments;i++) { - if(bar->pathSegments[i] == w) { - bar->selection = i; - XmToggleButtonSetState(w, True, False); - break; - } - } - - UiPathElm elm = bar->current_pathelms[i]; - cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len)); - if(bar->updateDir) { - XNETextSetString(bar->textfield, path.ptr); - bar->updateDir(bar->updateDirData, path.ptr, i); - } - free(path.ptr); -} - -static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { - for(int i=0;ipath) { - free(bar->path); - } - bar->path = strdup(path); - - for(int i=0;inumSegments;i++) { - XtDestroyWidget(bar->pathSegments[i]); - } - XtUnmanageChild(bar->textfield); - XtManageChild(bar->left); - XtManageChild(bar->right); - bar->input = False; - - Arg args[4]; - XmString str; - - bar->numSegments = 0; - - ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); - size_t nelm = 0; - UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); - if (!path_elm) { - return; - } - bar->current_pathelms = path_elm; - bar->numSegments = nelm; - bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); - - for(int i=0;iwidget, "pbbutton", args, 3); - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)PathBarChangeDir, - bar); - XmStringFree(str); - - bar->pathSegments[i] = button; - } - - bar->selection = bar->numSegments-1; - XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); - - XNETextSetString(bar->textfield, (char*)path); - XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); - - pathbar_resize(bar->widget, bar, NULL); -} - -void PathBarDestroy(PathBar *pathbar) { - if(pathbar->path) { - XtFree(pathbar->path); - } - XtFree((void*)pathbar->pathSegments); - XtFree((void*)pathbar); -} - - /* ---------------------------- Path Text Field ---------------------------- */ static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { @@ -970,47 +520,6 @@ static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { XtFree((void*)pathbar); } -// TODO: move to common -static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { - cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); - - if (nelm == 0) { - *ret_nelm = 0; - return NULL; - } - - UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); - size_t n = nelm; - int j = 0; - for (int i = 0; i < nelm; i++) { - cxstring c = pathelms[i]; - if (c.length == 0) { - if (i == 0) { - c.length = 1; - } - else { - n--; - continue; - } - } - - cxmutstr m = cx_strdup(c); - elms[j].name = m.ptr; - elms[j].name_len = m.length; - - size_t elm_path_len = c.ptr + c.length - full_path; - cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); - elms[j].path = elm_path.ptr; - elms[j].path_len = elm_path.length; - - j++; - } - *ret_nelm = n; - - return elms; -} - static void pathbar_activate(void *data, char *path, int index) { UiEventData *event = data; UiEvent evt; @@ -1036,7 +545,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { PathBar *pathbar = CreatePathBar(parent, xargs, n); if(!args->getpathelm) { - pathbar->getpathelm= default_pathelm_func; + pathbar->getpathelm= ui_default_pathelm_func; } else { pathbar->getpathelm = args->getpathelm; pathbar->getpathelmdata = args->getpathelmdata; diff --git a/ui/motif/toolkit.h b/ui/motif/toolkit.h index 12cc703..f4255d5 100644 --- a/ui/motif/toolkit.h +++ b/ui/motif/toolkit.h @@ -82,8 +82,6 @@ typedef struct UiJob { typedef enum UiOrientation UiOrientation; enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL }; -void ui_exit_mainloop(); - XtAppContext ui_motif_get_app(void); Display* ui_motif_get_display(void); diff --git a/ui/motif/window.c b/ui/motif/window.c index 439230a..e447044 100644 --- a/ui/motif/window.c +++ b/ui/motif/window.c @@ -28,15 +28,20 @@ #include #include +#include +#include +#include #include "toolkit.h" #include "menu.h" #include "toolbar.h" #include "container.h" +#include "pathbar.h" #include "../ui/window.h" #include "../common/context.h" #include "Grid.h" +#include "Fsb.h" #include @@ -142,3 +147,108 @@ UiObject* ui_window(const char *title, void *window_data) { UiObject* ui_simple_window(const char *title, void *window_data) { return create_window(title, window_data, TRUE); } + +static void filedialog_event(UiEventData *event, int result, UiFileList flist) { + UiEvent evt; + evt.obj = event->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.intval = result; + + evt.eventdata = &flist; + evt.eventdatatype = UI_EVENT_DATA_FILE_LIST; + + if(event->callback) { + event->callback(&evt, event->userdata); + } +} + +static void filedialog_select( + Widget widget, + UiEventData *data, + XmFileSelectionBoxCallbackStruct *selection) +{ + UiFileList flist; + + char *value = NULL; + XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value); + flist.files = &value; + flist.nfiles = 1; + + filedialog_event(data, 1, flist); + + XtFree(value); + + XtUnmanageChild(widget); + XtDestroyWidget(widget); +} + +static void filedialog_cancel( + Widget widget, + UiEventData *data, + XmFileSelectionBoxCallbackStruct *selection) +{ + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + filedialog_event(data, 0, flist); + + XtUnmanageChild(widget); + XtDestroyWidget(widget); +} + +void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + Widget dialog = XnCreateFileSelectionDialog(obj->widget, "dialog", NULL, 0); + + UiEventData *data = malloc(sizeof(UiEventData)); + memset(data, 0, sizeof(UiEventData)); + data->obj = obj; + data->callback = file_selected_callback; + data->userdata = cbdata; + + XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, data); + XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, data); + //XtAddCallback(dialog, XmNhelpCallback, (XtCallbackProc)filedialog_help, wd); + + XtManageChild(dialog); +} + +void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) { + Arg args[16]; + int n = 0; + + // Save File Dialog needs this parameter + XtSetArg(args[n], XnNfsbType, FILEDIALOG_SAVE); n++; + char *selectedpath = (char*)name; + if(name) { + if(name[0] != '/') { + char cwd[PATH_MAX]; + if(getcwd(cwd, PATH_MAX)) { + pathbar_concat_path(cwd, name); + } else { + fprintf(stderr, "Error: getcwd failed: %s\n", strerror(errno)); + selectedpath = NULL; + } + } + if(selectedpath) { + XtSetArg(args[n], XnNselectedPath, selectedpath); n++; + } + } + Widget dialog = XnCreateFileSelectionDialog(obj->widget, "dialog", args, n); + + UiEventData *data = malloc(sizeof(UiEventData)); + memset(data, 0, sizeof(UiEventData)); + data->obj = obj; + data->callback = file_selected_callback; + data->userdata = cbdata; + + XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, data); + XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, data); + //XtAddCallback(dialog, XmNhelpCallback, (XtCallbackProc)filedialog_help, wd); + + XtManageChild(dialog); + + if(selectedpath != name) { + free(selectedpath); + } +}