--- a/ui/motif/text.c Sat Nov 22 18:40:24 2025 +0100 +++ b/ui/motif/text.c Sun Nov 23 08:35:40 2025 +0100 @@ -32,6 +32,7 @@ #include "text.h" #include "container.h" +#include "pathbar.h" #include <cx/string.h> @@ -508,459 +509,6 @@ } - - - -/* -------------------- 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;i<p->numSegments;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;i<p->numSegments;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;i<bar->numSegments;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;i<nelm;i++) { - free(elms[i].name); - free(elms[i].path); - } - free(elms); -} - -void PathBarSetPath(PathBar *bar, const char *path) -{ - if(bar->path) { - free(bar->path); - } - bar->path = strdup(path); - - for(int i=0;i<bar->numSegments;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;i<nelm;i++) { - UiPathElm elm = path_elm[i]; - - cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len)); - str = XmStringCreateLocalized(elm.name); - free(name.ptr); - - 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); - - 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) {