1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include "textfieldP.h"
24 #include "textfield.h"
25 #include <Xm/DrawP.h>
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <string.h>
30
31 #include "../source/textBuf.h"
32 #include "../source/textSel.h"
33 #include "../source/textDisp.h"
34 #include "../source/text.h"
35
36 #define TF_DEFAULT_FONT_NAME "Monospace:size=10"
37
38 #define TF_VPADDING 3
39 #define TF_HPADDING 2
40
41 #define TF_BUF_BLOCK 128
42
43 #define TF_TAB_STR " "
44
45 static NFont *defaultFont;
46
47 static void textfield_class_init(
void);
48
49 static void mouse1DownAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
50 static void mouse1UpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
51 static void adjustselectionAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
52
53 static void insertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
54 static void actionAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
55 static void deletePrevCharAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
56 static void deleteNextCharAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
57 static void deletePrevWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
58 static void deleteNextWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
59
60 static void moveLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
61 static void moveRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
62 static void moveLeftWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
63 static void moveRightWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
64
65 static void focusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
66 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
67
68 static void enterAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
69 static void leaveAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
70
71 static void cutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
72 static void copyAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
73 static void pasteAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
74 static void endLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
75 static void beginLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
76 static void selectAllAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
77 static void insertPrimaryAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
78
79 static void tfCalcCursorPos(TextFieldWidget tf);
80
81 static int tfPosToIndex(TextFieldWidget tf,
int pos);
82
83 static int tfXToPos(TextFieldWidget tf,
int x);
84 static int tfIndexToX(TextFieldWidget tf,
int pos);
85 static void tfSelection(TextFieldWidget tf,
int *start,
int *end,
int *startX,
int *endX);
86 static void tfSelectionIndex(TextFieldWidget tf,
int *start,
int *end);
87
88 static void tfSetSelection(TextFieldWidget tf,
int from,
int to);
89 static void tfClearSelection(TextFieldWidget tf);
90
91 static void tfInsertPrimary(TextFieldWidget tf, XEvent *event);
92
93 static void TFInsert(TextFieldWidget tf,
const char *chars,
size_t nchars);
94 static int TFLeftPos(TextFieldWidget tf);
95 static int TFRightPos(TextFieldWidget tf);
96 static void TFDelete(TextFieldWidget tf,
int from,
int to);
97
98 static void wordbounds(TextFieldWidget tf,
int index,
int *out_wleft,
int *out_wright);
99
100 static Dimension tfCalcHeight(TextFieldWidget tf);
101
102 static Atom aTargets;
103 static Atom aUtf8String;
104
105 static Boolean convertSelection(
106 Widget w,
107 Atom *seltype,
108 Atom *target,
109 Atom *type,
110 XtPointer *value,
111 unsigned long *length,
112 int *format);
113
114 static void loseSelection(Widget w, Atom *type);
115
116 static XtResource resources[] = {
117 {XmNtextRenderTable, XmCTextRenderTable, XmRString,
sizeof(XmString),XtOffset(TextFieldWidget, textfield.renderTable), XmRString,
NULL},
118 {textNXftFont, textCXftFont, textTXftFont,
sizeof(NFont *), XtOffset(TextFieldWidget, textfield.font), textTXftFont, &defaultFont},
119 {XmNvalueChangedCallback, XmCCallback, XmRCallback,
sizeof(XtCallbackList), XtOffset(TextFieldWidget, textfield.valueChangedCB), XmRCallback,
NULL},
120 {XmNfocusCallback, XmCCallback, XmRCallback,
sizeof(XtCallbackList), XtOffset(TextFieldWidget, textfield.focusCB), XmRCallback,
NULL},
121 {XmNlosingFocusCallback, XmCCallback, XmRCallback,
sizeof(XtCallbackList), XtOffset(TextFieldWidget, textfield.losingFocusCB), XmRCallback,
NULL},
122 {XmNactivateCallback, XmCCallback, XmRCallback,
sizeof(XtCallbackList), XtOffset(TextFieldWidget, textfield.activateCB), XmRCallback,
NULL},
123 {XmNblinkRate, XmCBlinkRate , XmRInt,
sizeof(
int), XtOffset(TextFieldWidget, textfield.blinkrate), XmRImmediate, (XtPointer)
500}
124 };
125
126 static XtActionsRec actionslist[] = {
127 {
"mouse1down",mouse1DownAP},
128 {
"mouse1up",mouse1UpAP},
129 {
"adjustselection",adjustselectionAP},
130 {
"insert",insertAP},
131 {
"action",actionAP},
132 {
"moveleft",moveLeftAP},
133 {
"moveright",moveRightAP},
134 {
"moveleftword",moveLeftWordAP},
135 {
"moverightword",moveRightWordAP},
136 {
"deleteprev",deletePrevCharAP},
137 {
"deletenext",deleteNextCharAP},
138 {
"deleteprevword",deletePrevWordAP},
139 {
"deletenextword",deleteNextWordAP},
140 {
"focusIn",focusInAP},
141 {
"focusOut",focusOutAP},
142 {
"enter",enterAP},
143 {
"leave",leaveAP},
144 {
"cut-clipboard",cutAP},
145 {
"copy-clipboard",copyAP},
146 {
"paste-clipboard",pasteAP},
147 {
"endLine",endLineAP},
148 {
"beginLine",beginLineAP},
149 {
"selectAll",selectAllAP},
150 {
"insertPrimary",insertPrimaryAP},
151 {
"NULL",
NULL}
152 };
153
154
155 static char defaultTranslations[] =
"\
156 <FocusIn>: focusIn()\n\
157 <FocusOut>: focusOut()\n\
158 <EnterWindow>: leave()\n\
159 <LeaveWindow>: enter()\n\
160 s ~m ~a <Key>Tab: PrimitivePrevTabGroup()\n\
161 ~m ~a <Key>Tab: PrimitiveNextTabGroup()\n\
162 Shift<Btn1Down>: mouse1down(\"select\")\n\
163 <Btn1Down>: mouse1down()\n\
164 <Btn1Up>: mouse1up()\n\
165 <Btn2Up>: insertPrimary()\n\
166 Ctrl<KeyPress>v: paste-clipboard()\n\
167 Button1<MotionNotify>: adjustselection()\n\
168 :<Key>KP_7: insert()\n\
169 :<Key>KP_8: insert()\n\
170 :<Key>KP_9: insert()\n\
171 :<Key>KP_4: insert()\n\
172 :<Key>KP_6: insert()\n\
173 :<Key>KP_1: insert()\n\
174 :<Key>KP_2: insert()\n\
175 :<Key>KP_3: insert()\n\
176 :<Key>KP_0: insert()\n\
177 :<Key>KP_Separator: insert()\n\
178 <KeyPress>Return: PrimitiveParentActivate() action()\n\
179 <Key>osfActivate: PrimitiveParentActivate() action()\n\
180 <Key>osfCancel: PrimitiveParentCancel()\n\
181 Ctrl<KeyPress>osfBackSpace: deleteprevword()\n\
182 Ctrl<KeyPress>osfDelete: deletenextword()\n\
183 <KeyPress>osfBackSpace: deleteprev()\n\
184 <KeyPress>osfDelete: deletenext()\n\
185 Shift<Key>osfBeginLine: beginLine(l)\n\
186 <Key>osfBeginLine: beginLine()\n\
187 Shift<Key>osfEndLine: endLine(r)\n\
188 <Key>osfEndLine: endLine()\n\
189 Ctrl Shift<KeyPress>osfLeft: moveleftword(l)\n\
190 Ctrl Shift<KeyPress>osfRight: moverightword(r)\n\
191 Ctrl<KeyPress>osfLeft: moveleftword()\n\
192 Ctrl<KeyPress>osfRight: moverightword()\n\
193 Ctrl<Key>a: selectAll()\n\
194 Shift<KeyPress>osfLeft: moveleft(l)\n\
195 Shift<KeyPress>osfRight: moveright(r)\n\
196 <KeyPress>osfLeft: moveleft()\n\
197 <KeyPress>osfRight: moveright()\n\
198 <KeyPress>: insert()";
199
200
201
202 TextFieldClassRec tfWidgetClassRec = {
203
204 {
205 (WidgetClass)&xmPrimitiveClassRec,
206 "XmTextField",
207 sizeof(TextFieldRec),
208 textfield_class_init,
209 NULL,
210 FALSE,
211 textfield_init,
212 NULL,
213 textfield_realize,
214 actionslist,
215 XtNumber(actionslist),
216 resources,
217 XtNumber(resources),
218 NULLQUARK,
219 True,
220 True,
221 True,
222 False,
223 textfield_destroy,
224 textfield_resize,
225 textfield_expose,
226 textfield_set_values,
227 NULL,
228 XtInheritSetValuesAlmost,
229 NULL,
230 textfield_acceptfocus,
231 XtVersion,
232 NULL,
233 defaultTranslations,
234 XtInheritQueryGeometry,
235 NULL,
236 NULL,
237 },
238
239 {
240 (XtWidgetProc)_XtInherit,
241 (XtWidgetProc)_XtInherit,
242 NULL,
243 NULL,
244 NULL,
245 0,
246 NULL
247 },
248
249 {
250 0
251 }
252 };
253
254 WidgetClass textfieldWidgetClass = (WidgetClass)&tfWidgetClassRec;
255
256
257 static Boolean XftFontConvert(
258 Display* dpy,
259 XrmValue* args,
260 Cardinal* num_args,
261 XrmValue* from,
262 XrmValue* to,
263 XtPointer* converter_data)
264 {
265 NFont *font = FontFromName(dpy, from->addr);
266 if(font) {
267 memcpy(to->addr, &font,
sizeof(NFont*));
268 return True;
269 }
else {
270 to->addr =
0;
271 to->size =
0;
272 return True;
273 }
274 }
275
276 static void textfield_class_init(
void) {
277 XtSetTypeConverter(XmRString, textTXftFont, XftFontConvert,
NULL,
0, XtCacheNone,
NULL);
278 }
279
280 Widget XNECreateTextField(Widget parent,
char *name, ArgList arglist, Cardinal argcount) {
281 return XtCreateWidget(name, textfieldWidgetClass, parent, arglist, argcount);
282 }
283
284
285 void textfield_init(Widget request, Widget neww, ArgList args, Cardinal *num_args) {
286 TextFieldWidget tf = (TextFieldWidget)neww;
287 tf->textfield.alloc =
TF_BUF_BLOCK;
288 tf->textfield.length =
0;
289 tf->textfield.pos =
0;
290 tf->textfield.buffer = XtMalloc(
TF_BUF_BLOCK);
291
292 tf->textfield.posX =
0;
293 tf->textfield.posCalc =
0;
294
295 tf->textfield.scrollX =
0;
296
297 tf->textfield.hasSelection =
0;
298 tf->textfield.selStart =
0;
299 tf->textfield.selEnd =
0;
300
301 tf->textfield.btn1ClickPrev =
0;
302 tf->textfield.btn1ClickPrev2 =
0;
303
304 tf->textfield.blinkProcId =
0;
305 tf->textfield.cursorOn =
0;
306
307 if(aTargets ==
0) {
308 aTargets = XInternAtom(XtDisplay(request),
"TARGETS",
0);
309 }
310 if(aUtf8String ==
0) {
311 aUtf8String = XInternAtom(XtDisplay(request),
"UTF8_STRING",
0);
312 }
313
314 if(tf->textfield.font) {
315 tf->core.height = tfCalcHeight(tf);
316 }
317 }
318
319 static void tfInitXft(TextFieldWidget w) {
320 XWindowAttributes attributes;
321 XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attributes);
322
323 Screen *screen = w->core.screen;
324 Visual *visual = screen->root_visual;
325 for(
int i=
0;i<screen->ndepths;i++) {
326 Depth d = screen->depths[i];
327 if(d.depth == w->core.depth) {
328 visual = d.visuals;
329 break;
330 }
331 }
332
333 Display *dp = XtDisplay(w);
334 w->textfield.d = XftDrawCreate(
335 dp,
336 XtWindow(w),
337 visual,
338 w->core.colormap);
339
340 }
341
342 static void get_default_font(TextFieldWidget tf, Display *dp) {
343 int ret;
344 char *resName;
345 XrmValue value;
346 char *resourceType =
NULL;
347 char *rtFontName =
NULL;
348 char *rtFontSize =
NULL;
349
350
351
352
353
354
355
356
357
358 char resNameBuf[
256];
359 resName =
"defaultRT.fontName";
360 if(tf->textfield.renderTable) {
361 ret = snprintf(resNameBuf,
256,
"%s.fontName", tf->textfield.renderTable);
362 if(ret <
256) {
363 resName = resNameBuf;
364 }
365 }
366 if(XrmGetResource(XtDatabase(dp), resName,
NULL, &resourceType, &value)) {
367 if(!strcmp(resourceType,
"String")) {
368 rtFontName = value.addr;
369 }
370 }
371 resName =
"defaultRT.fontSize";
372 if(tf->textfield.renderTable) {
373 ret = snprintf(resNameBuf,
256,
"%s.fontSize", tf->textfield.renderTable);
374 if(ret <
256) {
375 resName = resNameBuf;
376 }
377 }
378 if(XrmGetResource(XtDatabase(dp),
"fixedRT.fontSize",
NULL, &resourceType, &value)) {
379 if(!strcmp(resourceType,
"String")) {
380 rtFontSize = value.addr;
381 }
382 }
383
384 char buf[
256];
385 char *fontname =
TF_DEFAULT_FONT_NAME;
386 if(rtFontName && rtFontSize) {
387 ret = snprintf(buf,
256,
"%s:size=%s", rtFontName, rtFontSize);
388 if(ret <
256) {
389 fontname = buf;
390 }
391 }
392
393 defaultFont = FontFromName(dp, fontname);
394 if(!defaultFont) {
395 defaultFont = FontFromName(dp,
TF_DEFAULT_FONT_NAME);
396 }
397 }
398
399 void textfield_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) {
400 Display *dpy = XtDisplay(widget);
401 TextFieldWidget text = (TextFieldWidget)widget;
402
403 if(!defaultFont) {
404 get_default_font(text, dpy);
405 }
406 if(!text->textfield.font) {
407 text->textfield.font = defaultFont;
408 }
409
410 textfield_recalc_size((TextFieldWidget)widget);
411 (coreClassRec.core_class.realize)(widget, mask, attributes);
412
413 text->textfield.xim = XmImGetXIM(widget);
414 if(text->textfield.xim) {
415 Window win = XtWindow(widget);
416 XIMStyle style = XIMPreeditNothing | XIMStatusNothing;
417 text->textfield.xic = XCreateIC(
418 text->textfield.xim,
419 XNInputStyle,
420 style,
421 XNClientWindow,
422 win,
423 XNFocusWindow,
424 win,
425 NULL);
426 }
427
428 tfInitXft(text);
429
430 text->textfield.foregroundColor = PixelToColor(widget, text->primitive.foreground);
431 text->textfield.backgroundColor = PixelToColor(widget, text->core.background_pixel);
432
433 XGCValues gcvals;
434 gcvals.foreground = text->primitive.foreground;
435 gcvals.background = text->core.background_pixel;
436 text->textfield.gc = XCreateGC(dpy, XtWindow(widget), (GCForeground|GCBackground), &gcvals);
437
438 gcvals.foreground = text->core.background_pixel;
439 gcvals.background = text->primitive.foreground;
440 text->textfield.gcInv = XCreateGC(dpy, XtWindow(widget), (GCForeground|GCBackground), &gcvals);
441
442 gcvals.foreground = XtParent(text)->core.background_pixel;
443 gcvals.background = XtParent(text)->core.background_pixel;
444 text->textfield.highlightBackground = XCreateGC(dpy, XtWindow(widget), (GCForeground|GCBackground), &gcvals);
445
446 text->textfield.textarea_xoff = text->primitive.shadow_thickness + text->primitive.highlight_thickness +
TF_HPADDING;
447 text->textfield.textarea_yoff = text->primitive.shadow_thickness + text->primitive.highlight_thickness +
TF_VPADDING;
448
449 text->textfield.posX = text->textfield.textarea_xoff;
450 }
451
452 void textfield_destroy(Widget widget) {
453 TextFieldWidget tf = (TextFieldWidget)widget;
454 XtFree(tf->textfield.buffer);
455 if(tf->textfield.font != defaultFont) {
456 FontUnref(tf->textfield.font);
457 }
458 if(tf->textfield.gc) {
459 XFreeGC(XtDisplay(widget), tf->textfield.gc);
460 }
461 if(tf->textfield.gcInv) {
462 XFreeGC(XtDisplay(widget), tf->textfield.gcInv);
463 }
464 if(tf->textfield.highlightBackground) {
465 XFreeGC(XtDisplay(widget), tf->textfield.highlightBackground);
466 }
467 if(tf->textfield.blinkProcId !=
0) {
468 XtRemoveTimeOut(tf->textfield.blinkProcId);
469 }
470 }
471
472 void textfield_resize(Widget widget) {
473
474 }
475
476
477 static int tfDrawString(TextFieldWidget tf, XftFont *font, XftColor *color,
int x,
const char *text,
size_t len) {
478 NFontList *fl = tf->textfield.font->fonts;
479
480 int yoff = tf->textfield.textarea_yoff;
481 int area = tf->core.height -
2*yoff;
482 int pad = area - (fl->font->ascent + fl->font->descent);
483 int hpad = pad/
2;
484
485
486 XftDrawStringUtf8(
487 tf->textfield.d,
488 color,
489 font,
490 x - tf->textfield.scrollX,
491 tf->core.height - tf->textfield.textarea_yoff -fl->font->descent - hpad,
492 (FcChar8*)text,
493 len);
494
495 XGlyphInfo extents;
496 XftTextExtentsUtf8(XtDisplay(tf), font, (FcChar8*)text, len, &extents);
497 return extents.xOff;
498 }
499
500 static void tfDrawCursor(TextFieldWidget tf) {
501 Display *dp = XtDisplay(tf);
502 Window w = XtWindow(tf);
503 int x = tf->textfield.posX - tf->textfield.scrollX;
504 int top = tf->textfield.textarea_yoff;
505 int bottom = tf->core.height-tf->textfield.textarea_yoff;
506 if(tf->textfield.hasFocus) {
507 XDrawLine(
508 dp,
509 w,
510 tf->textfield.cursorOn ? tf->textfield.gc : tf->textfield.gcInv,
511 x,
512 top,
513 x,
514 bottom);
515 }
else {
516 int diff = bottom-top;
517 int max = (diff/
2)+
1;
518 XPoint *points = calloc(max,
sizeof(XPoint));
519 int y = top;
520 int i;
521 for(i=
0;i<max && y <= bottom;i++) {
522 points[i].x = x;
523 points[i].y = y;
524 y +=
2;
525 }
526 XDrawPoints(dp, w, tf->textfield.gc, points, i, CoordModeOrigin);
527 free(points);
528 }
529
530 }
531
532 static void tfRedrawText(TextFieldWidget tf) {
533 tfCalcCursorPos(tf);
534
535 int border = tf->primitive.shadow_thickness + tf->primitive.highlight_thickness;
536
537 XClearArea(XtDisplay(tf), XtWindow(tf), border, border, tf->core.width -
2*border, tf->core.height -
2*border, False);
538
539 int selStart, selEnd, selStartX, selEndX;
540
541
542 if(tf->textfield.hasSelection) {
543 tfSelection(tf, &selStart, &selEnd, &selStartX, &selEndX);
544
545 XftDrawRect(
546 tf->textfield.d,
547 &tf->textfield.foregroundColor,
548 selStartX - tf->textfield.scrollX,
549 tf->textfield.textarea_yoff,
550 selEndX - selStartX,
551 tf->core.height -
2*tf->textfield.textarea_yoff);
552 }
553
554
555 XRectangle rect;
556 rect.x =
0;
557 rect.y =
0;
558 rect.width = tf->core.width -
2 * tf->textfield.textarea_xoff;
559 rect.height = tf->core.height;
560 XftDrawSetClipRectangles(tf->textfield.d, tf->textfield.textarea_xoff,
0, &rect,
1);
561
562 XftFont *font = tf->textfield.font->fonts->font;
563 XftColor *color = &tf->textfield.foregroundColor;
564
565 const char *buf = tf->textfield.buffer;
566 size_t length = tf->textfield.length;
567 size_t start =
0;
568
569 int xoff = tf->textfield.textarea_xoff;
570
571 int charlen =
1;
572 int pos =
0;
573 for(
int i=
0;i<length;i+=charlen) {
574 FcChar32 c;
575 charlen = Utf8ToUcs4(buf + i, &c, length - i);
576
577 XftFont *cFont = FindFont(tf->textfield.font, c);
578 XftColor *cColor = &tf->textfield.foregroundColor;
579 if(tf->textfield.hasSelection) {
580 if(i >= selStart && i < selEnd) {
581 cColor = &tf->textfield.backgroundColor;
582 }
583 }
584
585 if(c ==
'\t' || cFont != font || color != cColor) {
586
587 size_t drawLen = i - start;
588 if(drawLen >
0) {
589 xoff += tfDrawString(tf, font, color, xoff, buf + start, drawLen);
590 start = i;
591 }
592 font = cFont;
593 color = cColor;
594 if(c ==
'\t') {
595 xoff += tfDrawString(tf, font, color, xoff,
TF_TAB_STR,
sizeof(
TF_TAB_STR)-
1);
596 start++;
597 }
598 }
599
600 pos++;
601 }
602 int drawLen = length - start;
603 if(drawLen >
0) {
604 tfDrawString(tf, font, color, xoff, buf + start, drawLen);
605 }
606
607 tfDrawCursor(tf);
608 }
609
610 static void tfDrawHighlight(TextFieldWidget tf) {
611 XmeDrawHighlight(
612 XtDisplay(tf),
613 XtWindow(tf),
614 tf->textfield.hasFocus ? tf->primitive.highlight_GC : tf->textfield.highlightBackground,
615 0,
616 0,
617 tf->core.width,
618 tf->core.height,
619 tf->primitive.highlight_thickness);
620 }
621
622 void textfield_expose(Widget widget, XEvent* event, Region region) {
623 TextFieldWidget tf = (TextFieldWidget)widget;
624
625 tfDrawHighlight(tf);
626
627
628 XmeDrawShadows(
629 XtDisplay(tf),
630 XtWindow(tf),
631 tf->primitive.bottom_shadow_GC,
632 tf->primitive.top_shadow_GC,
633 tf->primitive.highlight_thickness,
634 tf->primitive.highlight_thickness,
635 tf->core.width - (
2 * tf->primitive.highlight_thickness),
636 tf->core.height - (
2 * tf->primitive.highlight_thickness),
637 tf->primitive.shadow_thickness,
638 XmSHADOW_OUT);
639
640
641 tfRedrawText((TextFieldWidget)widget);
642 }
643
644 Boolean textfield_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
645 Boolean redraw = False;
646
647 TextFieldWidget cur = (TextFieldWidget)old;
648 TextFieldWidget new = (TextFieldWidget)neww;
649
650 if(!new->textfield.font) {
651 if(!defaultFont) {
652 get_default_font(new, XtDisplay(neww));
653 }
654 new->textfield.font = defaultFont;
655 }
656
657 if(cur->textfield.font != new->textfield.font) {
658 textfield_recalc_size(new);
659 redraw = True;
660 }
661
662
663 return redraw;
664 }
665
666 Boolean textfield_acceptfocus(Widget widget, Time *time) {
667 return 0;
668 }
669
670 static Dimension tfCalcHeight(TextFieldWidget tf) {
671 NFont *font = tf->textfield.font;
672 Dimension height = font->fonts->font->ascent + font->fonts->font->descent;
673 height +=
2*(tf->primitive.highlight_thickness + tf->primitive.shadow_thickness) +
2*
TF_VPADDING;
674 return height;
675 }
676
677 void textfield_recalc_size(TextFieldWidget w) {
678 int height = tfCalcHeight(w);
679 int width = w->core.width;
680
681 XtMakeResizeRequest((Widget)w, width, height,
NULL,
NULL);
682 }
683
684
685
686
687 static void ownSelection(TextFieldWidget tf) {
688 if(tf->textfield.selStart != tf->textfield.selEnd && !tf->textfield.hasSelection) {
689 XtOwnSelection((Widget)tf,
XA_PRIMARY, XtLastTimestampProcessed(XtDisplay((Widget)tf)), convertSelection, loseSelection,
NULL);
690 tf->textfield.hasSelection =
1;
691 }
692 }
693
694 static void adjustSelection(TextFieldWidget tf,
int x) {
695 int pos = tfXToPos(tf, x);
696 int index = tfPosToIndex(tf, pos);
697
698 tf->textfield.selEnd = index;
699 tf->textfield.pos = index;
700 tf->textfield.selEndX = tfIndexToX(tf, index);
701
702 ownSelection(tf);
703 }
704
705 static void mouse1DownAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
706 TextFieldWidget tf = (TextFieldWidget)w;
707
708 int select =
0;
709 if(*nArgs >=
1) {
710 if(!strcmp(args[
0],
"select")) {
711 select =
1;
712 }
713 }
714
715 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
716
717 int pos = tfXToPos(tf, event->xbutton.x);
718 int index = tfPosToIndex(tf, pos);
719 int prevPos = tf->textfield.pos;
720 tf->textfield.pos = index;
721
722 int selStart, selEnd;
723
724 Time t = event->xbutton.time;
725 int multiclicktime = XtGetMultiClickTime(XtDisplay(w));
726 if(select) {
727 if(tf->textfield.hasSelection) {
728 if(index < tf->textfield.selStart) {
729 selStart = index;
730 selEnd = tf->textfield.selEnd;
731 }
else {
732 selStart = tf->textfield.selStart;
733 selEnd = index;
734 }
735 }
else {
736 if(prevPos > index) {
737 selStart = index;
738 selEnd = prevPos;
739 }
else {
740 selStart = prevPos;
741 selEnd = index;
742 }
743 }
744
745 tf->textfield.dontAdjustSel =
1;
746 }
else if(t - tf->textfield.btn1ClickPrev2 <
2*multiclicktime) {
747
748 t =
0;
749
750 selStart =
0;
751 selEnd = tf->textfield.length;
752 tf->textfield.pos = selEnd;
753 tf->textfield.dontAdjustSel =
1;
754 }
else if(t - tf->textfield.btn1ClickPrev < multiclicktime) {
755
756
757 int wleft, wright;
758 wordbounds(tf, index, &wleft, &wright);
759
760 selStart = wleft;
761 selEnd = wright;
762 tf->textfield.pos = wright;
763 tf->textfield.dontAdjustSel =
1;
764 }
else {
765 selStart = index;
766 selEnd = index;
767
768 tf->textfield.dontAdjustSel =
0;
769 }
770
771 tf->textfield.btn1ClickPrev2 = tf->textfield.btn1ClickPrev;
772 tf->textfield.btn1ClickPrev = t;
773
774 tfSetSelection(tf, selStart, selEnd);
775
776 tfRedrawText(tf);
777
778
779 }
780
781 static void mouse1UpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
782 TextFieldWidget tf = (TextFieldWidget)w;
783 if(tf->textfield.dontAdjustSel)
return;
784
785 adjustSelection(tf, event->xbutton.x);
786
787 if(tf->textfield.selStart == tf->textfield.selEnd) {
788 tfClearSelection(tf);
789 }
790
791 tfRedrawText(tf);
792 }
793
794 static void adjustselectionAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
795 TextFieldWidget tf = (TextFieldWidget)w;
796
797 adjustSelection(tf, event->xbutton.x);
798
799 tfRedrawText(tf);
800 }
801
802 static void insertText(TextFieldWidget tf,
char *chars,
int nchars, XEvent *event) {
803 if(nchars ==
0)
return;
804
805 if(tf->textfield.hasSelection) {
806 int selStart, selEnd;
807 tfSelectionIndex(tf, &selStart, &selEnd);
808 TFDelete(tf, selStart, selEnd);
809 tfClearSelection(tf);
810 tf->textfield.pos = selStart;
811 }
812 TFInsert(tf, chars, nchars);
813
814
815 XmAnyCallbackStruct cb;
816 cb.reason = XmCR_VALUE_CHANGED;
817 cb.event = event;
818 XtCallCallbacks((Widget)tf, XmNvalueChangedCallback, &cb);
819
820
821 tfRedrawText(tf);
822 }
823
824 static void insertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
825 TextFieldWidget tf = (TextFieldWidget)w;
826
827 char chars[
128];
828 KeySym keysym;
829 int nchars;
830 int status;
831 static XComposeStatus compose = {
NULL,
0};
832
833 if(tf->textfield.xic) {
834 #ifdef X_HAVE_UTF8_STRING
835 nchars = Xutf8LookupString(tf->textfield.xic, &event->xkey, chars,
127, &keysym, &status);
836 #else
837 nchars = XmbLookupString(tf->textfield.xic, &event->xkey, chars,
127, &keysym, &status);
838 #endif
839 }
else {
840 nchars = XLookupString(&event->xkey, chars,
127, &keysym, &compose);
841 }
842
843 insertText(tf, chars, nchars, event);
844 }
845
846 static void actionAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
847 TextFieldWidget tf = (TextFieldWidget)w;
848 XmAnyCallbackStruct cb;
849 cb.reason = XmCR_ACTIVATE;
850 cb.event = event;
851 XtCallCallbacks((Widget)tf, XmNactivateCallback, &cb);
852 }
853
854 static void deleteText(TextFieldWidget tf,
int from,
int to, XEvent *event) {
855 TFDelete(tf, from, to);
856
857 XmAnyCallbackStruct cb;
858 cb.reason = XmCR_VALUE_CHANGED;
859 cb.event = event;
860 XtCallCallbacks((Widget)tf, XmNvalueChangedCallback, &cb);
861
862 tf->textfield.pos = from;
863 tfRedrawText(tf);
864 }
865
866 static void deletePrevCharAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
867 TextFieldWidget tf = (TextFieldWidget)w;
868
869 int from;
870 int to;
871 if(tf->textfield.hasSelection) {
872 tfSelectionIndex(tf, &from, &to);
873 tfClearSelection(tf);
874 }
else {
875 from = TFLeftPos(tf);
876 to = tf->textfield.pos;
877 }
878
879 deleteText(tf, from, to, event);
880 }
881
882 static void deleteNextCharAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
883 TextFieldWidget tf = (TextFieldWidget)w;
884
885 int from;
886 int to;
887 if(tf->textfield.hasSelection) {
888 tfSelectionIndex(tf, &from, &to);
889 tfClearSelection(tf);
890 }
else {
891 from = tf->textfield.pos;
892 to = TFRightPos(tf);
893 }
894
895 deleteText(tf, from, to, event);
896 }
897
898 static void wordbounds(TextFieldWidget tf,
int index,
int *out_wleft,
int *out_wright) {
899
900 int spc = index < tf->textfield.length ? isspace(tf->textfield.buffer[index]) :
1;
901
902 int wleft, wright;
903
904
905 if(out_wleft) {
906 for(wleft=index;wleft>=
0;wleft--) {
907 if(isspace(tf->textfield.buffer[wleft]) != spc) {
908 wleft++;
909 break;
910 }
911 }
912 if(wleft <
0) wleft =
0;
913 *out_wleft = wleft;
914 }
915
916
917 if(out_wright) {
918 for(wright=index;wright<tf->textfield.length;wright++) {
919 if(isspace(tf->textfield.buffer[wright]) != spc) {
920 break;
921 }
922 }
923 *out_wright = wright;
924 }
925 }
926
927 static void deletePrevWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
928 TextFieldWidget tf = (TextFieldWidget)w;
929 if(tf->textfield.pos ==
0 || tf->textfield.length ==
0)
return;
930
931 int wleft;
932 int index = tf->textfield.pos >
0 ? tf->textfield.pos -
1 :
0;
933 for(
int n=
0;n<
2;n++) {
934 wordbounds(tf, index, &wleft,
NULL);
935
936 if(wleft == tf->textfield.length || !isspace(tf->textfield.buffer[wleft])) {
937 break;
938 }
939 index = wleft >
0 ? wleft -
1 :
0;
940 }
941
942 TFDelete(tf, wleft, tf->textfield.pos);
943 tf->textfield.pos = wleft;
944 tfRedrawText(tf);
945 }
946
947 static void deleteNextWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
948 TextFieldWidget tf = (TextFieldWidget)w;
949 if(tf->textfield.pos == tf->textfield.length || tf->textfield.length ==
0)
return;
950
951 int wright;
952 int index = tf->textfield.pos;
953 wordbounds(tf, index,
NULL, &wright);
954
955 TFDelete(tf, index, wright);
956 tfRedrawText(tf);
957 }
958
959 static void moveSelect(TextFieldWidget tf, String *args, Cardinal *nArgs,
int old_pos,
int new_pos) {
960 if(*nArgs ==
1) {
961 String d = args[
0];
962
963 if(tf->textfield.hasSelection) {
964 if(new_pos == (d[
0] ==
'l' ? tf->textfield.selStart : tf->textfield.selEnd)) {
965 tf->textfield.hasSelection =
0;
966 }
else if(d[
0] ==
'r' && new_pos < tf->textfield.selEnd) {
967 tfSetSelection(tf, new_pos, tf->textfield.selEnd);
968 }
else if(new_pos > tf->textfield.selStart) {
969 tfSetSelection(tf, tf->textfield.selStart, new_pos);
970 }
else {
971 tfSetSelection(tf, new_pos, tf->textfield.selEnd);
972 }
973 }
else {
974 tfSetSelection(tf, old_pos, new_pos);
975 }
976 }
else {
977 tf->textfield.hasSelection =
0;
978 }
979 }
980
981 static void moveLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
982 TextFieldWidget tf = (TextFieldWidget)w;
983 int old_pos = tf->textfield.pos;
984 tf->textfield.pos = TFLeftPos(tf);
985 moveSelect(tf, args, nArgs, old_pos, tf->textfield.pos);
986 tfRedrawText(tf);
987 }
988
989 static void moveRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
990 TextFieldWidget tf = (TextFieldWidget)w;
991 int old_pos = tf->textfield.pos;
992 tf->textfield.pos = TFRightPos(tf);
993 moveSelect(tf, args, nArgs, old_pos, tf->textfield.pos);
994 tfRedrawText(tf);
995 }
996
997 static void moveLeftWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
998 TextFieldWidget tf = (TextFieldWidget)w;
999
1000 int pos = tf->textfield.pos;
1001 int old_pos = pos;
1002 int word =
0;
1003 while(pos >
0) {
1004 pos = TFLeftPos(tf);
1005
1006 if(isspace(tf->textfield.buffer[pos])) {
1007 if(word) {
1008 break;
1009 }
1010 }
else {
1011 word =
1;
1012 }
1013 tf->textfield.pos = pos;
1014 }
1015
1016 int new_pos = tf->textfield.pos;
1017
1018 moveSelect(tf, args, nArgs, old_pos, new_pos);
1019
1020 tfRedrawText(tf);
1021 }
1022
1023 static void moveRightWordAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1024 TextFieldWidget tf = (TextFieldWidget)w;
1025 int old_pos = tf->textfield.pos;
1026
1027 int space =
0;
1028 while(tf->textfield.pos < tf->textfield.length) {
1029 tf->textfield.pos = TFRightPos(tf);
1030
1031 if(!isspace(tf->textfield.buffer[tf->textfield.pos])) {
1032 if(space) {
1033 break;
1034 }
1035 }
else {
1036 space =
1;
1037 }
1038 }
1039 int new_pos = tf->textfield.pos;
1040
1041 moveSelect(tf, args, nArgs, old_pos, new_pos);
1042
1043 tfRedrawText(tf);
1044 }
1045
1046 static void blinkCB(XtPointer data, XtIntervalId *id) {
1047 TextFieldWidget tf = data;
1048 tf->textfield.cursorOn = !tf->textfield.cursorOn;
1049
1050 tfDrawCursor(tf);
1051
1052 tf->textfield.blinkProcId = XtAppAddTimeOut(
1053 XtWidgetToApplicationContext((Widget)tf),
1054 tf->textfield.blinkrate,
1055 blinkCB,
1056 tf);
1057 }
1058
1059 static void focusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1060 TextFieldWidget tf = (TextFieldWidget)w;
1061 if(!event->xfocus.send_event)
return;
1062
1063 tf->textfield.hasFocus =
1;
1064
1065 if(tf->textfield.xic) {
1066 XSetICFocus(tf->textfield.xic);
1067 }
1068
1069
1070 XmAnyCallbackStruct cb;
1071 cb.reason = XmCR_FOCUS;
1072 cb.event = event;
1073 XtCallCallbackList (w, tf->textfield.focusCB, (XtPointer) &cb);
1074
1075 if(tf->textfield.blinkProcId ==
0) {
1076 tf->textfield.blinkProcId = XtAppAddTimeOut(
1077 XtWidgetToApplicationContext(w),
1078 tf->textfield.blinkrate,
1079 blinkCB,
1080 tf);
1081 }
1082
1083 Region r =
NULL;
1084 textfield_expose(w, event, r);
1085 }
1086
1087 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1088 TextFieldWidget tf = (TextFieldWidget)w;
1089
1090 if(tf->textfield.xic) {
1091 XUnsetICFocus(tf->textfield.xic);
1092 }
1093
1094 if(tf->textfield.blinkProcId !=
0) {
1095 XtRemoveTimeOut(tf->textfield.blinkProcId);
1096 tf->textfield.blinkProcId =
0;
1097 }
1098 tf->textfield.cursorOn =
1;
1099
1100 tf->textfield.hasFocus =
0;
1101
1102 XmAnyCallbackStruct cb;
1103 cb.reason = XmCR_LOSING_FOCUS;
1104 cb.event = event;
1105 XtCallCallbackList (w, tf->textfield.losingFocusCB, (XtPointer) &cb);
1106
1107 Region r =
NULL;
1108 textfield_expose(w, event, r);
1109 }
1110
1111 static void enterAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1112
1113 }
1114
1115 static void leaveAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1116
1117 }
1118
1119 static void cutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1120 TextFieldWidget tf = (TextFieldWidget)w;
1121 if(!tf->textfield.hasSelection)
return;
1122
1123 int from, to;
1124 tfSelectionIndex(tf, &from, &to);
1125
1126 size_t len = to - from;
1127
1128 CopyStringToClipboard(w, event->xkey.time, tf->textfield.buffer + from, len);
1129 TFDelete(tf, from, to);
1130 tf->textfield.pos = from;
1131
1132 tfRedrawText(tf);
1133 }
1134
1135 static void copyAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1136 TextFieldWidget tf = (TextFieldWidget)w;
1137 if(!tf->textfield.hasSelection)
return;
1138
1139 int from, to;
1140 tfSelectionIndex(tf, &from, &to);
1141
1142 size_t len = to - from;
1143
1144 CopyStringToClipboard(w, event->xkey.time, tf->textfield.buffer + from, len);
1145 }
1146
1147 static void pasteAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1148 TextFieldWidget tf = (TextFieldWidget)w;
1149
1150 char *clipboard = GetClipboard(w);
1151 if(!clipboard)
return;
1152
1153 int len = strlen(clipboard);
1154
1155 insertText(tf, clipboard, len, event);
1156 XtFree(clipboard);
1157 }
1158
1159 static void endLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1160 TextFieldWidget tf = (TextFieldWidget)w;
1161 int old_pos = tf->textfield.pos;
1162 tf->textfield.pos = tf->textfield.length;
1163 moveSelect(tf, args, nArgs, old_pos, tf->textfield.pos);
1164 tfRedrawText(tf);
1165 }
1166
1167 static void beginLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1168 TextFieldWidget tf = (TextFieldWidget)w;
1169 int old_pos = tf->textfield.pos;
1170 tf->textfield.pos =
0;
1171 moveSelect(tf, args, nArgs,
0, old_pos);
1172 tfRedrawText(tf);
1173 }
1174
1175 static void selectAllAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1176 TextFieldWidget tf = (TextFieldWidget)w;
1177 tfSetSelection(tf,
0, tf->textfield.length);
1178 tfRedrawText(tf);
1179 }
1180
1181 static void insertPrimaryAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
1182 TextFieldWidget tf = (TextFieldWidget)w;
1183 tfInsertPrimary(tf, event);
1184 }
1185
1186 static int tfIndexToX(TextFieldWidget tf,
int pos) {
1187 XftFont *font = tf->textfield.font->fonts->font;
1188 const char *buf = tf->textfield.buffer;
1189 size_t length = tf->textfield.length;
1190 size_t start =
0;
1191
1192 XGlyphInfo extents;
1193
1194 int xoff = tf->textfield.textarea_xoff;
1195
1196 int i;
1197 int charlen =
1;
1198 for(i=
0;i<pos;i+=charlen) {
1199 FcChar32 c;
1200 charlen = Utf8ToUcs4(buf + i, &c, length - i);
1201
1202 XftFont *cFont = FindFont(tf->textfield.font, c);
1203 if(c ==
'\t' || cFont != font) {
1204
1205 size_t drawLen = i - start;
1206 if(drawLen >
0) {
1207 XftTextExtentsUtf8(
1208 XtDisplay(tf),
1209 font,
1210 (FcChar8*)buf + start,
1211 drawLen,
1212 &extents
1213 );
1214 xoff += extents.xOff;
1215 start = i;
1216 }
1217 font = cFont;
1218 if(c ==
'\t') {
1219 start++;
1220 XftTextExtentsUtf8(
1221 XtDisplay(tf),
1222 font,
1223 (FcChar8*)
TF_TAB_STR,
1224 sizeof(
TF_TAB_STR)-
1,
1225 &extents
1226 );
1227 xoff += extents.xOff;
1228 }
1229 }
1230 }
1231 int drawLen = i - start;
1232 if(drawLen >
0) {
1233 XftTextExtentsUtf8(
1234 XtDisplay(tf),
1235 font,
1236 (FcChar8*)buf + start,
1237 drawLen,
1238 &extents
1239 );
1240 xoff += extents.xOff;
1241 }
1242
1243 return xoff;
1244 }
1245
1246 static void tfSelection(TextFieldWidget tf,
int *start,
int *end,
int *startX,
int *endX) {
1247 int selStart, selEnd, selStartX, selEndX;
1248 if(tf->textfield.selStart > tf->textfield.selEnd) {
1249 selStart = tf->textfield.selEnd;
1250 selEnd = tf->textfield.selStart;
1251 selStartX = tf->textfield.selEndX;
1252 selEndX = tf->textfield.selStartX;
1253 }
else {
1254 selEnd = tf->textfield.selEnd;
1255 selStart = tf->textfield.selStart;
1256 selEndX = tf->textfield.selEndX;
1257 selStartX = tf->textfield.selStartX;
1258 }
1259
1260 if(start) *start = selStart;
1261 if(end) *end = selEnd;
1262 if(startX) *startX = selStartX;
1263 if(endX) *endX = selEndX;
1264 }
1265
1266 static void tfSelectionIndex(TextFieldWidget tf,
int *start,
int *end) {
1267 tfSelection(tf, start, end,
NULL,
NULL);
1268 }
1269
1270 static void tfSetSelection(TextFieldWidget tf,
int from,
int to) {
1271 if(from > to) {
1272 int f = from;
1273 from = to;
1274 to = f;
1275 }
1276
1277 tf->textfield.selStart = from;
1278 tf->textfield.selEnd = to > tf->textfield.length ? tf->textfield.length : to;
1279 tf->textfield.selStartX = tfIndexToX(tf, tf->textfield.selStart);
1280 tf->textfield.selEndX = tfIndexToX(tf, tf->textfield.selEnd);
1281
1282 ownSelection(tf);
1283 }
1284
1285 static void tfClearSelection(TextFieldWidget tf) {
1286 tf->textfield.hasSelection =
0;
1287 tf->textfield.selStart =
0;
1288 tf->textfield.selEnd =
0;
1289 }
1290
1291 static void tfCalcCursorPos(TextFieldWidget tf) {
1292 if(tf->textfield.pos == tf->textfield.posCalc)
return;
1293
1294 int xoff = tfIndexToX(tf, tf->textfield.pos);
1295
1296 tf->textfield.posX = xoff;
1297 tf->textfield.posCalc = tf->textfield.pos;
1298
1299 int posX = tf->textfield.posX;
1300 int margin = tf->textfield.textarea_xoff;
1301 if(posX > tf->core.width + tf->textfield.scrollX - margin) {
1302 tf->textfield.scrollX = -tf->core.width + posX + margin;
1303 }
else if(posX < tf->textfield.scrollX + margin) {
1304 tf->textfield.scrollX = posX - tf->textfield.textarea_xoff;
1305 }
1306 }
1307
1308 static int tfPosToIndex(TextFieldWidget tf,
int pos) {
1309 const char *buf = tf->textfield.buffer;
1310 size_t length = tf->textfield.length;
1311
1312 int p =
0;
1313 int charlen =
1;
1314 int i;
1315 for(i=
0;i<length;i+=charlen) {
1316 if(p == pos)
break;
1317
1318 FcChar32 c;
1319 charlen = Utf8ToUcs4(buf + i, &c, length - i);
1320
1321 p++;
1322 }
1323 return i;
1324 }
1325
1326 static int tfXToPos(TextFieldWidget tf,
int x) {
1327 const char *buf = tf->textfield.buffer;
1328 size_t length = tf->textfield.length;
1329
1330 x += tf->textfield.scrollX;
1331
1332 int pos =
0;
1333
1334 XGlyphInfo extents;
1335
1336 int xoff = tf->textfield.textarea_xoff;
1337
1338 int charlen =
1;
1339 for(
int i=
0;i<length;i+=charlen) {
1340 FcChar32 c;
1341 charlen = Utf8ToUcs4(buf + i, &c, length - i);
1342
1343 const char *str;
1344 size_t slen;
1345 if(c ==
'\t') {
1346 str =
TF_TAB_STR;
1347 slen =
sizeof(
TF_TAB_STR)-
1;
1348 }
else {
1349 str = buf+i;
1350 slen = charlen;
1351 }
1352
1353 XftFont *font = FindFont(tf->textfield.font, c);
1354 XftTextExtentsUtf8(
1355 XtDisplay(tf),
1356 font,
1357 (FcChar8*)str,
1358 slen,
1359 &extents
1360 );
1361 xoff += extents.xOff;
1362 if(xoff > x + (extents.xOff /
2)) {
1363 break;
1364 }
1365 pos++;
1366 }
1367
1368 return pos;
1369 }
1370
1371
1372
1373
1374 static void TFInsert(TextFieldWidget tf,
const char *chars,
size_t nchars) {
1375
1376 if(tf->textfield.length + nchars >= tf->textfield.alloc) {
1377 tf->textfield.alloc +=
TF_BUF_BLOCK;
1378 tf->textfield.buffer = XtRealloc(tf->textfield.buffer, tf->textfield.alloc);
1379 }
1380
1381 if(tf->textfield.pos == tf->textfield.length) {
1382
1383 memcpy(tf->textfield.buffer + tf->textfield.length, chars, nchars);
1384 }
else {
1385
1386 char *insertpos = tf->textfield.buffer + tf->textfield.pos;
1387 memmove(insertpos + nchars, insertpos, tf->textfield.length - tf->textfield.pos);
1388 memmove(insertpos, chars, nchars);
1389 }
1390
1391 tf->textfield.length += nchars;
1392 tf->textfield.pos += nchars;
1393
1394 tfClearSelection(tf);
1395 }
1396
1397 static int TFLeftPos(TextFieldWidget tf) {
1398 int left =
0;
1399 int cur =
0;
1400 while(cur < tf->textfield.pos) {
1401 left = cur;
1402 cur += Utf8CharLen((
unsigned char*)tf->textfield.buffer + cur);
1403 }
1404 return left;
1405 }
1406
1407 static int TFRightPos(TextFieldWidget tf) {
1408 int pos = tf->textfield.pos;
1409 int right = pos + Utf8CharLen((
unsigned char*)tf->textfield.buffer + pos);
1410 return right > tf->textfield.length ? tf->textfield.length : right;
1411 }
1412
1413 static void TFDelete(TextFieldWidget tf,
int from,
int to) {
1414 if(from >= to)
return;
1415
1416 if(to >= tf->textfield.length) {
1417 tf->textfield.length = from;
1418 return;
1419 }
1420
1421 int len = tf->textfield.length - to;
1422 memmove(tf->textfield.buffer + from, tf->textfield.buffer + to, len);
1423
1424 tf->textfield.length -= to - from;
1425
1426 tfClearSelection(tf);
1427 }
1428
1429
1430 struct PSelection {
1431 TextFieldWidget tf;
1432 XEvent *event;
1433 char *xastring;
1434 char *utf8string;
1435 int target;
1436 };
1437
1438 static void getPrimary(
1439 Widget w,
1440 XtPointer clientData,
1441 Atom *selType,
1442 Atom *type,
1443 XtPointer value,
1444 unsigned long *length,
1445 int *format)
1446 {
1447 struct PSelection *sel = clientData;
1448 sel->target++;
1449
1450 if(value && *format ==
8 && *length >
0 && (*type ==
XA_STRING || *type == aUtf8String)) {
1451 char *str = XtMalloc((*length) +
1);
1452 memcpy(str, value, *length);
1453 str[*length] =
0;
1454
1455 if(*type == aUtf8String) {
1456 sel->utf8string = str;
1457 }
else {
1458 sel->xastring = str;
1459 }
1460 }
1461
1462 if(sel->target ==
2) {
1463 char *insert = sel->utf8string ? sel->utf8string : sel->xastring;
1464 if(insert) {
1465 insertText(sel->tf, insert, strlen(insert), sel->event);
1466 }
1467
1468 if(sel->utf8string) {
1469 XtFree(sel->utf8string);
1470 }
1471 if(sel->xastring) {
1472 XtFree(sel->xastring);
1473 }
1474 XtFree((
void*)sel);
1475 }
1476 }
1477
1478 static void tfInsertPrimary(TextFieldWidget tf, XEvent *event) {
1479 struct PSelection *sel = (
void*)XtMalloc(
sizeof(
struct PSelection));
1480 sel->tf = tf;
1481 sel->event = event;
1482 sel->target =
0;
1483 sel->xastring =
NULL;
1484 sel->utf8string =
NULL;
1485
1486 Atom targets[
2] = {aUtf8String,
XA_STRING};
1487 Time time = XtLastTimestampProcessed(XtDisplay((Widget)tf));
1488
1489 void *data[
2] = { sel, sel };
1490
1491 #ifdef __APPLE__
1492 XtGetSelectionValue((Widget)tf,
XA_PRIMARY, targets[
0], getPrimary, sel, time);
1493 XtGetSelectionValue((Widget)tf,
XA_PRIMARY, targets[
1], getPrimary, sel, time);
1494 #else
1495 XtGetSelectionValues((Widget)tf,
XA_PRIMARY, targets,
2, getPrimary, data, time);
1496 #endif
1497 }
1498
1499
1500
1501
1502 static Boolean convertSelection(
1503 Widget w,
1504 Atom *seltype,
1505 Atom *target,
1506 Atom *type,
1507 XtPointer *value,
1508 unsigned long *length,
1509 int *format)
1510 {
1511 TextFieldWidget tf = (TextFieldWidget)w;
1512
1513 if(*target == aTargets) {
1514 Atom *retTargets = calloc(
3,
sizeof(Atom));
1515 retTargets[
0] =
XA_STRING;
1516 retTargets[
1] = aUtf8String;
1517 retTargets[
2] = aTargets;
1518 *type =
XA_ATOM;
1519 *value = retTargets;
1520 *length =
3;
1521 *format =
32;
1522 return True;
1523 }
1524
1525 if(*target ==
XA_STRING || *target == aUtf8String) {
1526 char *selectedText =
NULL;
1527 size_t len =
0;
1528
1529 if(tf->textfield.hasSelection) {
1530 int from, to;
1531 tfSelectionIndex(tf, &from, &to);
1532 len = to - from;
1533 selectedText = XtMalloc(len +
1);
1534 memcpy(selectedText, tf->textfield.buffer + from, len);
1535 selectedText[len] =
0;
1536 }
else {
1537 selectedText = XtMalloc(
4);
1538 selectedText[
0] =
0;
1539 }
1540
1541 *type = *target == aUtf8String ? aUtf8String :
XA_STRING;
1542 *value = selectedText;
1543 *length = len;
1544 *format =
8;
1545 return True;
1546 }
1547
1548 return False;
1549 }
1550
1551 static void loseSelection(Widget w, Atom *type) {
1552 TextFieldWidget tf = (TextFieldWidget)w;
1553 tfClearSelection(tf);
1554 tfRedrawText(tf);
1555 }
1556
1557
1558
1559
1560 void XNETextFieldSetString(Widget widget,
char *value) {
1561 if(!value) {
1562 value =
"";
1563 }
1564
1565 size_t len = strlen(value);
1566
1567 TextFieldWidget tf = (TextFieldWidget)widget;
1568 if(len > tf->textfield.alloc) {
1569 size_t alloc = len +
TF_BUF_BLOCK - (len %
TF_BUF_BLOCK);
1570 tf->textfield.buffer = XtRealloc(tf->textfield.buffer, alloc);
1571 tf->textfield.alloc = alloc;
1572 }
1573
1574 memcpy(tf->textfield.buffer, value, len);
1575
1576 tf->textfield.length = len;
1577 tf->textfield.pos =
0;
1578
1579 tfClearSelection(tf);
1580 if(XtIsRealized(widget)) {
1581 tfRedrawText(tf);
1582 }
1583 }
1584
1585 char* XNETextFieldGetString(Widget widget) {
1586 TextFieldWidget tf = (TextFieldWidget)widget;
1587
1588 char *r = XtMalloc(tf->textfield.length +
1);
1589 memcpy(r, tf->textfield.buffer, tf->textfield.length);
1590 r[tf->textfield.length] =
'\0';
1591 return r;
1592 }
1593
1594 XmTextPosition XNETextFieldGetLastPosition(Widget widget) {
1595 TextFieldWidget tf = (TextFieldWidget)widget;
1596 return tf->textfield.length;
1597 }
1598
1599 void XNETextFieldSetInsertionPosition(Widget widget, XmTextPosition i) {
1600 TextFieldWidget tf = (TextFieldWidget)widget;
1601 tf->textfield.pos = i <= tf->textfield.length ? i : tf->textfield.length;
1602 if(XtIsRealized(widget)) {
1603 tfRedrawText(tf);
1604 }
1605 }
1606
1607 void XNETextFieldSetSelection(Widget w, XmTextPosition first, XmTextPosition last, Time sel_time) {
1608 TextFieldWidget tf = (TextFieldWidget)w;
1609 tfSetSelection(tf, first, last);
1610 if(XtIsRealized(w)) {
1611 tfRedrawText(tf);
1612 }
1613 }
1614