1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "pathbar.h"
30
31 #include <unistd.h>
32 #include <cx/string.h>
33
34
35
36 void pathbar_resize(Widget w, PathBar *p, XtPointer d)
37 {
38 Dimension width, height;
39 XtVaGetValues(w, XmNwidth, &width, XmNheight, &height,
NULL);
40
41 Dimension *segW = (
void*)XtCalloc(p->numSegments,
sizeof(Dimension));
42
43 Dimension maxHeight =
0;
44
45
46 Dimension pathWidth =
0;
47 for(
int i=
0;i<p->numSegments;i++) {
48 Dimension segWidth;
49 Dimension segHeight;
50 XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight,
NULL);
51 segW[i] = segWidth;
52 pathWidth += segWidth;
53 if(segHeight > maxHeight) {
54 maxHeight = segHeight;
55 }
56 }
57 Dimension tfHeight;
58 XtVaGetValues(p->textfield, XmNheight, &tfHeight,
NULL);
59 if(tfHeight > maxHeight) {
60 maxHeight = tfHeight;
61 }
62
63 Boolean arrows = False;
64 if(pathWidth +
10 > width) {
65 arrows = True;
66 pathWidth += p->lw + p->rw;
67 }
68
69
70 int start =
0;
71 if(arrows) {
72 Dimension vis = p->lw+p->rw;
73 for(
int i=p->numSegments;i>
0;i--) {
74 Dimension segWidth = segW[i
-1];
75 if(vis + segWidth +
10 > width) {
76 start = i;
77 arrows = True;
78 break;
79 }
80 vis += segWidth;
81 }
82 }
else {
83 p->shift =
0;
84 }
85
86 int leftShift =
0;
87 if(p->shift <
0) {
88 if(start + p->shift <
0) {
89 leftShift = start;
90 start =
0;
91 p->shift = -leftShift;
92 }
else {
93 leftShift = -p->shift;
94 start += p->shift;
95 }
96 }
97
98 int x =
0;
99 if(arrows) {
100 XtManageChild(p->left);
101 XtManageChild(p->right);
102 x = p->lw;
103 }
else {
104 XtUnmanageChild(p->left);
105 XtUnmanageChild(p->right);
106 }
107
108 for(
int i=
0;i<p->numSegments;i++) {
109 if(i >= start && i < p->numSegments - leftShift && !p->input) {
110 XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy,
0, XmNheight, maxHeight,
NULL);
111 x += segW[i];
112 XtManageChild(p->pathSegments[i]);
113 }
else {
114 XtUnmanageChild(p->pathSegments[i]);
115 }
116 }
117
118 if(arrows) {
119 XtVaSetValues(p->left, XmNx,
0, XmNy,
0, XmNheight, maxHeight,
NULL);
120 XtVaSetValues(p->right, XmNx, x, XmNy,
0, XmNheight, maxHeight,
NULL);
121 }
122
123 free(segW);
124
125 Dimension rw, rh;
126 XtMakeResizeRequest(w, width, maxHeight, &rw, &rh);
127
128 XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh,
NULL);
129 }
130
131 static void pathbarActivateTF(PathBar *p)
132 {
133 XtUnmanageChild(p->left);
134 XtUnmanageChild(p->right);
135 XNETextSetSelection(p->textfield,
0, XNETextGetLastPosition(p->textfield),
0);
136 XtManageChild(p->textfield);
137 p->input =
1;
138
139 XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT);
140
141 pathbar_resize(p->widget, p,
NULL);
142 }
143
144 void PathBarActivateTextfield(PathBar *p)
145 {
146 p->focus =
1;
147 pathbarActivateTF(p);
148 }
149
150 void pathbar_input(Widget w, PathBar *p, XtPointer c)
151 {
152 XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c;
153 XEvent *xevent = cbs->event;
154
155 if (cbs->reason == XmCR_INPUT) {
156 if (xevent->xany.type == ButtonPress) {
157 p->focus =
0;
158 pathbarActivateTF(p);
159 }
160 }
161 }
162
163 void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c)
164 {
165 if(--p->focus <
0) {
166 p->input = False;
167 XtUnmanageChild(p->textfield);
168 }
169 }
170
171 static cxmutstr concat_path_s(cxstring base, cxstring path) {
172 if(!path.ptr) {
173 path =
CX_STR(
"");
174 }
175
176 int add_separator =
0;
177 if(base.length !=
0 && base.ptr[base.length
-1] ==
'/') {
178 if(path.ptr[
0] ==
'/') {
179 base.length--;
180 }
181 }
else {
182 if(path.length ==
0 || path.ptr[
0] !=
'/') {
183 add_separator =
1;
184 }
185 }
186
187 cxmutstr url;
188 if(add_separator) {
189 url = cx_strcat(
3, base,
CX_STR(
"/"), path);
190 }
else {
191 url = cx_strcat(
2, base, path);
192 }
193
194 return url;
195 }
196
197 char* pathbar_concat_path(
const char *path1,
const char *path2) {
198 return concat_path_s(cx_str(path1), cx_str(path2)).ptr;
199 }
200
201 void pathbar_pathinput(Widget w, PathBar *p, XtPointer d)
202 {
203 char *newpath = XNETextGetString(p->textfield);
204 if(newpath) {
205 if(newpath[
0] ==
'~') {
206 char *p = newpath
+1;
207 char *home = getenv(
"HOME");
208 char *cp = pathbar_concat_path(home, p);
209 XtFree(newpath);
210 newpath = cp;
211 }
else if(newpath[
0] !=
'/') {
212 char curdir[
2048];
213 curdir[
0] =
0;
214 getcwd(curdir,
2048);
215 char *cp = pathbar_concat_path(curdir, newpath);
216 XtFree(newpath);
217 newpath = cp;
218 }
219
220
221 PathBarSetPath(p, newpath);
222 if(p->updateDir) {
223 p->updateDir(p->updateDirData, newpath,
-1);
224 }
225 XtFree(newpath);
226
227
228 XtUnmanageChild(p->textfield);
229 pathbar_resize(p->widget, p,
NULL);
230
231 if(p->focus_widget) {
232 XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT);
233 }
234 }
235 }
236
237 void pathbar_shift_left(Widget w, PathBar *p, XtPointer d)
238 {
239 p->shift--;
240 pathbar_resize(p->widget, p,
NULL);
241 }
242
243 void pathbar_shift_right(Widget w, PathBar *p, XtPointer d)
244 {
245 if(p->shift <
0) {
246 p->shift++;
247 }
248 pathbar_resize(p->widget, p,
NULL);
249 }
250
251 static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
252 PathBar *pb = data;
253 if(event->type == KeyReleaseMask) {
254 if(event->xkey.keycode ==
9) {
255 XtUnmanageChild(pb->textfield);
256 pathbar_resize(pb->widget, pb,
NULL);
257 *dispatch = False;
258 }
else if(event->xkey.keycode ==
36) {
259 pathbar_pathinput(pb->textfield, pb,
NULL);
260 *dispatch = False;
261 }
262 }
263 }
264
265 PathBar* CreatePathBar(Widget parent, ArgList args,
int n)
266 {
267 PathBar *bar = (PathBar*)XtMalloc(
sizeof(PathBar));
268 bar->path =
NULL;
269 bar->updateDir =
NULL;
270 bar->updateDirData =
NULL;
271
272 bar->focus_widget =
NULL;
273
274 bar->getpathelm =
NULL;
275 bar->getpathelmdata =
NULL;
276 bar->current_pathelms =
NULL;
277
278 bar->shift =
0;
279
280 XtSetArg(args[n], XmNmarginWidth,
0); n++;
281 XtSetArg(args[n], XmNmarginHeight,
0); n++;
282 bar->widget = XmCreateDrawingArea(parent,
"pathbar", args, n);
283 XtAddCallback(
284 bar->widget,
285 XmNresizeCallback,
286 (XtCallbackProc)pathbar_resize,
287 bar);
288 XtAddCallback(
289 bar->widget,
290 XmNinputCallback,
291 (XtCallbackProc)pathbar_input,
292 bar);
293
294 Arg a[
4];
295 XtSetArg(a[
0], XmNshadowThickness,
0);
296 XtSetArg(a[
1], XmNx,
0);
297 XtSetArg(a[
2], XmNy,
0);
298 bar->textfield = XNECreateText(bar->widget,
"pbtext", a,
3);
299 bar->input =
0;
300 XtAddCallback(
301 bar->textfield,
302 XmNlosingFocusCallback,
303 (XtCallbackProc)pathbar_losingfocus,
304 bar);
305 XtAddCallback(bar->textfield, XmNactivateCallback,
306 (XtCallbackProc)pathbar_pathinput, bar);
307 XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask,
FALSE, pathTextEH, bar);
308
309 XtSetArg(a[
0], XmNarrowDirection, XmARROW_LEFT);
310 bar->left = XmCreateArrowButton(bar->widget,
"pbbutton", a,
1);
311 XtSetArg(a[
0], XmNarrowDirection, XmARROW_RIGHT);
312 bar->right = XmCreateArrowButton(bar->widget,
"pbbutton", a,
1);
313 XtAddCallback(
314 bar->left,
315 XmNactivateCallback,
316 (XtCallbackProc)pathbar_shift_left,
317 bar);
318 XtAddCallback(
319 bar->right,
320 XmNactivateCallback,
321 (XtCallbackProc)pathbar_shift_right,
322 bar);
323
324 Pixel bg;
325 XtVaGetValues(bar->textfield, XmNbackground, &bg,
NULL);
326 XtVaSetValues(bar->widget, XmNbackground, bg,
NULL);
327
328 XtManageChild(bar->left);
329 XtManageChild(bar->right);
330
331 XtVaGetValues(bar->left, XmNwidth, &bar->lw,
NULL);
332 XtVaGetValues(bar->right, XmNwidth, &bar->rw,
NULL);
333
334 bar->segmentAlloc =
16;
335 bar->numSegments =
0;
336 bar->pathSegments = (Widget*)XtCalloc(
16,
sizeof(Widget));
337
338 bar->selection =
0;
339
340 return bar;
341 }
342
343 void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c)
344 {
345 XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False);
346
347 int i;
348 for(i=
0;i<bar->numSegments;i++) {
349 if(bar->pathSegments[i] == w) {
350 bar->selection = i;
351 XmToggleButtonSetState(w, True, False);
352 break;
353 }
354 }
355
356 UiPathElm elm = bar->current_pathelms[i];
357 cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len));
358 if(bar->updateDir) {
359 XNETextSetString(bar->textfield, path.ptr);
360 bar->updateDir(bar->updateDirData, path.ptr, i);
361 }
362 free(path.ptr);
363 }
364
365 static void ui_pathelm_destroy(UiPathElm *elms,
size_t nelm) {
366 for(
int i=
0;i<nelm;i++) {
367 free(elms[i].name);
368 free(elms[i].path);
369 }
370 free(elms);
371 }
372
373 void PathBarSetPath(PathBar *bar,
const char *path)
374 {
375 if(bar->path) {
376 free(bar->path);
377 }
378 bar->path = strdup(path);
379
380 for(
int i=
0;i<bar->numSegments;i++) {
381 XtDestroyWidget(bar->pathSegments[i]);
382 }
383 XtUnmanageChild(bar->textfield);
384 XtManageChild(bar->left);
385 XtManageChild(bar->right);
386 bar->input = False;
387
388 Arg args[
4];
389 XmString str;
390
391 bar->numSegments =
0;
392
393 ui_pathelm_destroy(bar->current_pathelms, bar->numSegments);
394 size_t nelm =
0;
395 UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata);
396 if (!path_elm) {
397 return;
398 }
399 bar->current_pathelms = path_elm;
400 bar->numSegments = nelm;
401 bar->pathSegments = realloc(bar->pathSegments, nelm *
sizeof(Widget*));
402
403 for(
int i=
0;i<nelm;i++) {
404 UiPathElm elm = path_elm[i];
405
406 cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len));
407 str = XmStringCreateLocalized(elm.name);
408 free(name.ptr);
409
410 XtSetArg(args[
0], XmNlabelString, str);
411 XtSetArg(args[
1], XmNfillOnSelect, True);
412 XtSetArg(args[
2], XmNindicatorOn, False);
413 Widget button = XmCreateToggleButton(bar->widget,
"pbbutton", args,
3);
414 XtAddCallback(
415 button,
416 XmNvalueChangedCallback,
417 (XtCallbackProc)PathBarChangeDir,
418 bar);
419 XmStringFree(str);
420
421 bar->pathSegments[i] = button;
422 }
423
424 bar->selection = bar->numSegments
-1;
425 XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False);
426
427 XNETextSetString(bar->textfield, (
char*)path);
428 XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield));
429
430 pathbar_resize(bar->widget, bar,
NULL);
431 }
432
433 void PathBarDestroy(PathBar *pathbar) {
434 if(pathbar->path) {
435 XtFree(pathbar->path);
436 }
437 XtFree((
void*)pathbar->pathSegments);
438 XtFree((
void*)pathbar);
439 }
440