#include "pathbar.h"
#include <unistd.h>
#include <cx/string.h>
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;
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;
}
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;
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;
}
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;
}
PathBarSetPath(p, newpath);
if(p->updateDir) {
p->updateDir(p->updateDirData, newpath,
-1);
}
XtFree(newpath);
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);
}