1 /*
2 * wmslib/src/but/textin.c, part of wmslib (Library functions)
3 * Copyright (C) 1994-1997 William Shubert.
4 * See "configure.h.in" for more copyright information.
5 */
6
7 /*
8 * I don't really like this button.
9 * It is slower than it has to be (this may or may not matter).
10 */
11
12 #include <configure.h>
13
14 #ifdef X11_DISP
15
16 #ifdef STDC_HEADERS
17 #include <stdlib.h>
18 #include <unistd.h>
19 #endif /* STDC_HEADERS */
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <X11/cursorfont.h>
25 #include <X11/Xatom.h>
26 #include <X11/keysym.h>
27 #include <sys/time.h>
28 #ifdef HAVE_SYS_SELECT_H
29 #include <sys/select.h>
30 #endif
31 #include <wms.h>
32 #include <but/but.h>
33 #include <but/textin.h>
34 #include <but/box.h>
35 #include <but/timer.h>
36
37
38 /**********************************************************************
39 * Data Types
40 **********************************************************************/
41 typedef struct SpecKey_struct {
42 struct SpecKey_struct *next;
43 KeySym keysym;
44 uint keyModifiers, modMask;
45 ButOut (*callback)(But *but, KeySym keysym, uint keyModifiers,
46 void *context);
47 void *context;
48 MAGIC_STRUCT
49 } SpecKey;
50
51 typedef struct Txtin_struct {
52 ButOut (*callback)(But *but, const char *value);
53 bool hidden;
54 char *str, *dispStr; /* dispStr is only used if hidden is TRUE. */
55 int maxlen, len, loc, cutend;
56 int mousePress, origMousePress, clicks;
57 int xoffset;
58 Pixmap pm;
59 int pm_w, pm_h, slideDir, slideFreq;
60 int but3StartMouse, but3StartXoff;
61 bool mouseInBut, cursorVisible;
62 ButTimer *cTimer, *sTimer;
63 SpecKey *specKeys;
64 MAGIC_STRUCT
65 } Txtin;
66
67
68 /**********************************************************************
69 * Forward Declarations
70 **********************************************************************/
71 static int locateMouse(XFontStruct *fs, char *text, int len, int x,
72 bool *rightSide);
73 static void ti_cursor(But *but);
74 static void ti_redrawCursor(But *but);
75 static ButOut mmove(But *but, int x, int y);
76 static ButOut mleave(But *but);
77 static ButOut mpress(But *but, int butnum, int x, int y);
78 static ButOut mrelease(But *but, int butnum, int x, int y);
79 static ButOut kpress(But *but, const char *keystr, KeySym sym);
80 static void draw(But *but, int x,int y, int w,int h);
81 static ButOut destroy(But *but);
82 static bool insert(ButEnv *env, const char *src, Txtin *but);
83 static void cut(ButEnv *env, char *cutstr, int cutlen, But *but);
84 static void paste(ButEnv *env, But *but);
85 static void enableCTimer(But *but);
86 static void disableCTimer(But *but);
87 static void curInView(But *but);
88 static void textInView(But *but, bool maxInView);
89
90 static bool sreq(ButEnv *env, XSelectionRequestEvent *xsre);
91 static int sclear(ButEnv *env);
92 static int snotify(ButEnv *env, XSelectionEvent *xsre);
93 static void flags(But *but, uint flags);
94
95 static void wordsel_adjust(Txtin *but);
96 static void startSlide(But *but, int dir, bool fast);
97 static ButOut slide(ButTimer *timer);
98
99
100 /**********************************************************************
101 * Globals
102 **********************************************************************/
103 static int cutstr_maxlen = 0, cutstr_len;
104 static char *cutdata = NULL;
105 static But *cut_butnum = NULL;
106 static But *paste_butnum = NULL;
107 static const ButAction action = {
108 mmove, mleave, mpress, mrelease,
109 kpress, NULL, draw, destroy, flags, NULL};
110
111
112 /**********************************************************************
113 * Functions
114 **********************************************************************/
butTextin_create(ButOut (* callback)(But * but,const char * value),void * packet,ButWin * win,int layer,int flags,const char * text,int maxlen)115 But *butTextin_create(ButOut (*callback)(But *but, const char *value),
116 void *packet, ButWin *win, int layer, int flags,
117 const char *text, int maxlen) {
118 But *but;
119 Txtin *ti;
120
121 ti = wms_malloc(sizeof(Txtin));
122 MAGIC_SET(ti);
123 but = but_create(win, ti, &action);
124 but->uPacket = packet;
125 but->layer = layer;
126 but->flags = flags | BUT_OPAQUE;
127
128 if (text == NULL)
129 text = "";
130 ti->callback = callback;
131 ti->hidden = FALSE;
132 ti->maxlen = maxlen;
133 ti->str = (char *)wms_malloc(ti->maxlen + 1);
134 ti->dispStr = ti->str;
135 ti->xoffset = 0;
136 ti->pm_w = ti->pm_h = 0;
137 ti->pm = None;
138 ti->mousePress = -1;
139 ti->mouseInBut = FALSE;
140 strcpy(ti->str, text);
141 ti->len = ti->loc = ti->cutend = strlen(text);
142 ti->cTimer = NULL;
143 if (flags & BUT_KEYED) {
144 ti->cursorVisible = TRUE;
145 } else
146 ti->cursorVisible = FALSE;
147 ti->sTimer = NULL;
148 ti->but3StartXoff = -1;
149 ti->specKeys = NULL;
150 but_init(but);
151 return(but);
152 }
153
154
butTextin_get(But * but)155 const char *butTextin_get(But *but) {
156 Txtin *ti = but->iPacket;
157
158 assert(but->action == &action);
159 assert(MAGIC(ti));
160 return(ti->str);
161 }
162
163
destroy(But * but)164 static ButOut destroy(But *but) {
165 Txtin *ti = but->iPacket;
166 ButEnv *env = but->win->env;
167 SpecKey *spec, *nextSpec;
168
169 assert(but->action == &action);
170 assert(MAGIC(ti));
171 spec = ti->specKeys;
172 while (spec != NULL) {
173 assert(MAGIC(spec));
174 nextSpec = spec->next;
175 MAGIC_UNSET(spec);
176 wms_free(spec);
177 spec = nextSpec;
178 }
179 if (cut_butnum == but) {
180 if (env->sClear == sclear)
181 env->sClear = NULL;
182 if (env->sReq == sreq)
183 env->sReq = NULL;
184 cut_butnum = NULL;
185 }
186 if (paste_butnum == but) {
187 if (env->sNotify == snotify)
188 env->sNotify = NULL;
189 paste_butnum = NULL;
190 }
191 if (ti->pm != None)
192 XFreePixmap(but->win->env->dpy, ti->pm);
193 wms_free(ti->str);
194 if (ti->hidden)
195 wms_free(ti->dispStr);
196 else {
197 assert(ti->dispStr == ti->str);
198 }
199 MAGIC_UNSET(ti);
200 wms_free(ti);
201 return(0);
202 }
203
204
draw(But * but,int x,int y,int w,int h)205 static void draw(But *but, int x,int y, int w,int h) {
206 ButWin *win = but->win;
207 ButEnv *env = win->env;
208 Txtin *ti = but->iPacket;
209 int txtw;
210 int butbw = env->stdButBw;
211 XFontStruct *fs = env->fonts[0];
212
213 assert(but->action == &action);
214 assert(MAGIC(ti));
215 XSetFont(env->dpy, env->gc2, env->fonts[0]->fid);
216 if ((ti->pm_w != but->w - 2*butbw) || (ti->pm_h != but->h - 2*butbw)) {
217 if (ti->pm != None)
218 XFreePixmap(env->dpy, ti->pm);
219 ti->pm = XCreatePixmap(env->dpy, win->win,
220 ti->pm_w = but->w - 2*butbw,
221 ti->pm_h = but->h - 2*butbw,
222 DefaultDepth(env->dpy, DefaultScreen(env->dpy)));
223 }
224 but_drawBox(win, but->x,but->y, but->w,but->h, 1, butbw,
225 BUT_SRIGHT|BUT_SLEFT, BUT_LIT, BUT_SHAD, None, None);
226 txtw = XTextWidth(fs, ti->dispStr, ti->loc);
227 butEnv_setXFg2(env, BUT_HIBG);
228 XFillRectangle(env->dpy, ti->pm, env->gc2,
229 0,0, ti->pm_w,ti->pm_h);
230 if (but->flags & BUT_PRESSABLE)
231 butEnv_setXFg2(env, BUT_FG);
232 else {
233 XSetFillStyle(env->dpy, env->gc2, FillStippled);
234 XSetForeground(env->dpy, env->gc2, env->colors[BUT_FG]);
235 }
236 if (ti->loc == ti->cutend) {
237 XDrawString(env->dpy, ti->pm, env->gc2,
238 x = butbw - ti->xoffset,
239 y = (fs->ascent + ti->pm_h - fs->descent) / 2,
240 ti->dispStr, ti->len);
241 } else {
242 int tw1, tw2;
243 int cl1, cl2;
244
245 if (ti->cutend >= ti->loc) {
246 cl1 = ti->loc;
247 cl2 = ti->cutend;
248 } else {
249 cl1 = ti->cutend;
250 cl2 = ti->loc;
251 }
252 XDrawString(env->dpy, ti->pm, env->gc2,
253 x = butbw - ti->xoffset,
254 y = (fs->ascent + ti->pm_h - fs->descent) / 2,
255 ti->dispStr, cl1);
256 tw1 = XTextWidth(fs, ti->dispStr, cl1);
257 tw2 = XTextWidth(fs, ti->dispStr + cl1, cl2 - cl1);
258 x += tw1 + tw2;
259 XDrawString(env->dpy, ti->pm, env->gc2, x,y,
260 ti->dispStr + cl2, ti->len - cl2);
261 x -= tw2;
262 XSetBackground(env->dpy, env->gc2, env->colors[BUT_SELBG]);
263 if (!env->colorp) {
264 XSetTile(env->dpy, env->gc2, env->colorPmaps[BUT_WHITE]);
265 XSetForeground(env->dpy, env->gc2, env->colors[BUT_WHITE]);
266 }
267 XDrawImageString(env->dpy, ti->pm, env->gc2, x,y,
268 ti->dispStr + cl1, cl2 - cl1);
269
270 }
271 if (!(but->flags & BUT_PRESSABLE)) {
272 butEnv_stdFill2(env);
273 }
274 XCopyArea(env->dpy, ti->pm, win->win, env->gc,
275 0,0, ti->pm_w,ti->pm_h,
276 but->x + butbw - win->xOff, but->y + butbw - win->yOff);
277 if ((ti->loc == ti->cutend) && ti->cursorVisible &&
278 (but->flags & BUT_PRESSABLE))
279 ti_cursor(but);
280 }
281
282
mmove(But * but,int x,int y)283 static ButOut mmove(But *but, int x, int y) {
284 Txtin *ti = but->iPacket;
285 ButWin *win = but->win;
286 ButEnv *env = win->env;
287 int newMousePress;
288 bool rightSide;
289
290 if (!(but->flags & BUT_PRESSABLE))
291 return(BUTOUT_CAUGHT);
292 if (!ti->mouseInBut) {
293 ti->mouseInBut = TRUE;
294 butEnv_setCursor(env, but, butCur_text);
295 }
296 if (ti->mousePress != -1) {
297 if (ti->clicks < 2) {
298 if (x < but->x) {
299 startSlide(but, -1, (x < but->x - but->h + env->stdButBw*2));
300 newMousePress = 0;
301 } else if (x > but->x + but->w) {
302 startSlide(but, 1, (x > but->x + but->w + but->h - env->stdButBw*2));
303 newMousePress = ti->len;
304 } else {
305 if (ti->sTimer != NULL) {
306 butTimer_destroy(ti->sTimer);
307 ti->sTimer = NULL;
308 }
309 if (y < but->y + env->stdButBw*2)
310 newMousePress = 0;
311 else if (y > but->y+but->h - env->stdButBw*2)
312 newMousePress = ti->len;
313 else
314 newMousePress = locateMouse(env->fonts[0], ti->dispStr, ti->len,
315 x - (but->x + 2*env->stdButBw -
316 ti->xoffset), &rightSide);
317 }
318 if (ti->clicks == 1) {
319 if (rightSide && (newMousePress >= ti->origMousePress) &&
320 (newMousePress < ti->len))
321 ++newMousePress;
322 else if (!rightSide && (newMousePress <= ti->origMousePress)
323 && (newMousePress > 0))
324 --newMousePress;
325 }
326 if (newMousePress != ti->mousePress) {
327 ti->mousePress = ti->loc = newMousePress;
328 if (ti->clicks == 1) {
329 ti->cutend = ti->origMousePress;
330 wordsel_adjust(ti);
331 }
332 but_draw(but);
333 }
334 }
335 }
336 if (ti->but3StartXoff != -1) {
337 int oldXoff = ti->xoffset;
338
339 ti->xoffset = ti->but3StartXoff - 10*(x - ti->but3StartMouse);
340 textInView(but, FALSE);
341 if (oldXoff != ti->xoffset)
342 but_draw(but);
343 }
344 return(BUTOUT_CAUGHT);
345 }
346
347
mleave(But * but)348 static ButOut mleave(But *but) {
349 Txtin *ti = but->iPacket;
350
351 if (ti->mouseInBut) {
352 ti->mouseInBut = FALSE;
353 butEnv_setCursor(but->win->env, but, butCur_idle);
354 }
355 return(BUTOUT_CAUGHT);
356 }
357
358
mpress(But * but,int butnum,int x,int y)359 static ButOut mpress(But *but, int butnum, int x, int y) {
360 Txtin *ti = but->iPacket;
361 ButEnv *env = but->win->env;
362 static Time lastPressTime = -1;
363 static int lastPressNum = -2;
364 int rightSide;
365
366 if (ti->cTimer) {
367 butTimer_reset(ti->cTimer);
368 ti->cursorVisible = TRUE;
369 }
370 switch (butnum) {
371 case 1:
372 /* Button 1 selects, drags, etc. */
373 if (ti->but3StartXoff != -1)
374 return(BUTOUT_ERR);
375 if (!(but->flags & BUT_KEYED))
376 but_newFlags(but, but->flags | BUT_KEYED);
377 but_newFlags(but, but->flags | BUT_LOCKED);
378 ti->mousePress = locateMouse(env->fonts[0], ti->dispStr, ti->len,
379 x - (but->x + 2*env->stdButBw -
380 ti->xoffset), &rightSide);
381 ti->loc = ti->cutend = ti->origMousePress = ti->mousePress;
382 if ((lastPressTime + BUT_DCLICK > env->eventTime) &&
383 (lastPressNum + 1 == env->eventNum)) {
384 if (ti->clicks == 0) {
385 /* It's a double click! */
386 ++ti->clicks;
387 if (rightSide)
388 ++ti->loc;
389 else
390 --ti->loc;
391 wordsel_adjust(ti);
392 } else if (ti->clicks == 1) {
393 /* It's a triple click! */
394 ++ti->clicks;
395 ti->loc = 0;
396 ti->cutend = ti->len;
397 } else {
398 /* Quadruple click! Restart! */
399 ti->clicks = 0;
400 }
401 } else
402 ti->clicks = 0;
403 lastPressTime = env->eventTime;
404 lastPressNum = env->eventNum;
405 but_draw(but);
406 break;
407 case 2:
408 /* Paste! */
409 ti->cutend = ti->loc = locateMouse(env->fonts[0], ti->dispStr, ti->len,
410 x - (but->x + 2*env->stdButBw -
411 ti->xoffset), NULL);
412 paste(env, but);
413 break;
414 case 3:
415 /*
416 * Button 3 scrolls as if this input window was a mini-canvas. I'm not
417 * sure how useful this is but I'm doing it anyway just to make my
418 * interface more consistent.
419 */
420 if (ti->mousePress != -1)
421 return(BUTOUT_ERR);
422 but_newFlags(but, but->flags | BUT_LOCKED);
423 butEnv_setCursor(env, but, butCur_lr);
424 ti->but3StartMouse = x;
425 ti->but3StartXoff = ti->xoffset;
426 break;
427 default:
428 /*
429 * Weird. I don't know what to do with mice with more than 3 buttons
430 * so I guess I might as well beep. I like beeping.
431 */
432 return(BUTOUT_ERR);
433 break;
434 }
435 return(BUTOUT_CAUGHT);
436 }
437
438
mrelease(But * but,int butnum,int x,int y)439 static ButOut mrelease(But *but, int butnum, int x, int y) {
440 Txtin *ti = but->iPacket;
441 char *cutstr;
442 int cutlen;
443
444 if (ti->cTimer) {
445 butTimer_reset(ti->cTimer);
446 ti->cursorVisible = TRUE;
447 }
448 if ((butnum == 1) && (ti->mousePress != -1)) {
449 if (ti->sTimer != NULL) {
450 butTimer_destroy(ti->sTimer);
451 ti->sTimer = NULL;
452 }
453 if (ti->loc != ti->cutend) {
454 if (ti->loc < ti->cutend) {
455 cutstr = ti->str + ti->loc;
456 cutlen = ti->cutend - ti->loc;
457 } else {
458 cutstr = ti->str + ti->cutend;
459 cutlen = ti->loc - ti->cutend;
460 }
461 cut(but->win->env, cutstr, cutlen, but);
462 }
463 ti->mousePress = -1;
464 but_newFlags(but, but->flags & ~BUT_LOCKED);
465 } else if ((butnum == 3) && (ti->but3StartXoff != -1)) {
466 butEnv_setCursor(but->win->env, but, butCur_text);
467 ti->but3StartXoff = -1;
468 but_newFlags(but, but->flags & ~BUT_LOCKED);
469 }
470 return(BUTOUT_CAUGHT);
471 }
472
473
kpress(But * but,const char * newtext,KeySym keysym)474 static ButOut kpress(But *but, const char *newtext, KeySym keysym) {
475 Txtin *ti = but->iPacket;
476 ButEnv *env = but->win->env;
477 bool need_draw = FALSE, needCallback = FALSE;
478 int i;
479 ButOut result = 0;
480 SpecKey *spec;
481
482 butTimer_reset(ti->cTimer);
483 ti->cursorVisible = TRUE;
484 for (spec = ti->specKeys; spec != NULL; spec = spec->next) {
485 if ((keysym == spec->keysym) &&
486 ((env->keyModifiers & spec->modMask) == spec->keyModifiers)) {
487 return(spec->callback(but, keysym, env->keyModifiers, spec->context) |
488 BUTOUT_CAUGHT);
489 }
490 }
491 if (((keysym >= XK_KP_Space) && (keysym <= XK_KP_9)) ||
492 ((keysym >= XK_space) && (keysym <= XK_ydiaeresis))) {
493 need_draw = TRUE;
494 switch(newtext[0]) {
495 case '\001': /* Ctrl-A: Beginning of line. */
496 case '\020': /* Ctrl-P: Up a line. */
497 ti->loc = ti->cutend = 0;
498 break;
499 case '\002': /* Ctrl-B: Back a character. */
500 if (ti->loc != ti->cutend) {
501 ti->cutend = ti->loc;
502 } else if (ti->loc) {
503 ti->cutend = --ti->loc;
504 } else {
505 need_draw = FALSE;
506 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
507 }
508 break;
509 case '\004': /* Ctrl-D: Delete right. */
510 if (ti->loc != ti->cutend)
511 need_draw = insert(env, "", ti);
512 else if (ti->loc < ti->len) {
513 for (i = ti->loc; ti->str[i]; ++i)
514 ti->str[i] = ti->str[i+1];
515 --ti->len;
516 need_draw = TRUE;
517 } else
518 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
519 break;
520 case '\005': /* Ctrl-E: End of line. */
521 case '\016': /* Ctrl-N: Down a line. */
522 ti->loc = ti->cutend = ti->len;
523 break;
524 case '\006': /* Ctrl-F: Forward a character. */
525 if (ti->loc != ti->cutend) {
526 ti->loc = ti->cutend;
527 } else if (ti->loc < ti->len) {
528 ti->cutend = ++ti->loc;
529 } else {
530 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
531 need_draw = FALSE;
532 }
533 break;
534 case '\013': /* Ctrl-K: Kill to end of line. */
535 ti->str[ti->len = ti->loc = ti->cutend] = '\0';
536 break;
537 case '\025': /* Ctrl-U: Kill from beginning of line. */
538 for (i = ti->loc; i < ti->len; ++i)
539 ti->str[i - ti->loc] = ti->str[i];
540 ti->str[ti->len -= ti->loc] = '\0';
541 ti->loc = ti->cutend = 0;
542 break;
543 default:
544 if (!(need_draw = insert(env, newtext, ti)))
545 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
546 break;
547 }
548 } else if ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)) {
549 } else if ((keysym >= XK_F1) && (keysym <= XK_F35)) {
550 if (!(need_draw = insert(env, newtext, ti)))
551 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
552 } else if ((keysym == XK_BackSpace) || (keysym == XK_Delete)) {
553 if (ti->loc != ti->cutend)
554 need_draw = insert(env, "", ti);
555 else if (ti->loc > 0) {
556 for (i = ti->cutend = --ti->loc; ti->str[i]; ++i)
557 ti->str[i] = ti->str[i+1];
558 --ti->len;
559 need_draw = TRUE;
560 } else
561 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
562 #if 0 /* People don't like it when delete works the way I like it. :-( */
563 } else if (keysym == XK_Delete) {
564 if (ti->loc != ti->cutend)
565 need_draw = insert(env, "", ti);
566 else if (ti->loc < ti->len) {
567 for (i = ti->loc; ti->str[i]; ++i)
568 ti->str[i] = ti->str[i+1];
569 --ti->len;
570 need_draw = TRUE;
571 } else
572 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
573 #endif
574 } else if (keysym == XK_Left) {
575 if (ti->loc != ti->cutend) {
576 if (ti->loc < ti->cutend)
577 ti->cutend = ti->loc;
578 else
579 ti->loc = ti->cutend;
580 need_draw = TRUE;
581 } else if (ti->loc) {
582 ti->cutend = --ti->loc;
583 need_draw = TRUE;
584 } else
585 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
586 } else if (keysym == XK_Right) {
587 if (ti->loc != ti->cutend) {
588 if (ti->loc > ti->cutend)
589 ti->cutend = ti->loc;
590 else
591 ti->loc = ti->cutend;
592 need_draw = TRUE;
593 } else if (ti->loc < ti->len) {
594 ti->cutend = ++ti->loc;
595 need_draw = TRUE;
596 } else
597 result |= BUTOUT_ERR | BUTOUT_CAUGHT;
598 } else if (keysym == XK_Up) {
599 ti->loc = ti->cutend = 0;
600 need_draw = TRUE;
601 } else if (keysym == XK_Down) {
602 ti->loc = ti->cutend = ti->len;
603 need_draw = TRUE;
604 } else if ((keysym == XK_Return) || (keysym == XK_Linefeed) ||
605 (keysym == XK_KP_Enter)) {
606 result |= BUTOUT_CAUGHT;
607 if (ti->callback == NULL) {
608 but_setFlags(but, BUT_NOKEY);
609 } else {
610 needCallback = TRUE;
611 }
612 }
613 /* Void all mouse movement until it is pressed again. */
614 if (ti->mousePress != -1) {
615 ti->mousePress = -1;
616 if (but->flags & BUT_LOCKED)
617 but_newFlags(but, but->flags & ~BUT_LOCKED);
618 }
619 if (need_draw) {
620 result |= BUTOUT_CAUGHT;
621 curInView(but);
622 but_draw(but);
623 }
624 if (needCallback)
625 result |= ti->callback(but, ti->str);
626 return(result);
627 }
628
629
insert(ButEnv * env,const char * src,Txtin * ti)630 static bool insert(ButEnv *env, const char *src, Txtin *ti) {
631 int i, j, ntLen, cl1, cl2;
632 XFontStruct *fs = env->fonts[0];
633
634 if (src == NULL) {
635 return(FALSE);
636 }
637 if (ti->loc <= ti->cutend) {
638 cl1 = ti->loc;
639 cl2 = ti->cutend;
640 } else {
641 cl1 = ti->cutend;
642 cl2 = ti->loc;
643 }
644 for (i = 0, ntLen = 0; src[i]; ++i) {
645 if ((((uchar)src[i] >= fs->min_char_or_byte2) &&
646 ((uchar)src[i] <= fs->max_char_or_byte2)) ||
647 (fs->all_chars_exist &&
648 fs->per_char[(uchar)src[i] - fs->min_char_or_byte2].width))
649 ++ntLen;
650 }
651 if ((ntLen == 0) && src[0])
652 return(FALSE);
653 if (ntLen + ti->len + cl1 - cl2 > ti->maxlen) {
654 return(FALSE);
655 }
656 if (cl1 != cl2) {
657 for (i = cl1; i < ti->len; ++i)
658 ti->str[i] = ti->str[i + cl2 - cl1];
659 }
660 for (i = ti->len + ntLen; i >= cl1 + ntLen; --i)
661 ti->str[i] = ti->str[i - ntLen];
662 for (i = 0, j = cl1; src[i]; ++i) {
663 if ((((uchar)src[i] >= fs->min_char_or_byte2) &&
664 ((uchar)src[i] <= fs->max_char_or_byte2)) ||
665 (fs->all_chars_exist &&
666 fs->per_char[(uchar)src[i] - fs->min_char_or_byte2].width)) {
667 ti->str[j++] = src[i];
668 }
669 }
670 ti->len += cl1 - cl2 + ntLen;
671 ti->loc = ti->cutend = cl1 + ntLen;
672 return(TRUE);
673 }
674
675
ti_cursor(But * but)676 static void ti_cursor(But *but) {
677 Txtin *ti = but->iPacket;
678 ButWin *win = but->win;
679 ButEnv *env = win->env;
680 XFontStruct *fs = env->fonts[0];
681 int x, y;
682 int rw;
683
684 if (ti->loc != ti->cutend)
685 return;
686 x = but->x - ti->xoffset + env->stdButBw*2 +
687 XTextWidth(fs, ti->dispStr, ti->loc);
688 y = but->y + env->stdButBw*2;
689 if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
690 rw = 1;
691 x -= rw/2;
692 if (x < but->x + env->stdButBw) {
693 rw -= (but->x + env->stdButBw - x);
694 x = but->x + env->stdButBw;
695 } else if (x+rw > but->x + but->w - env->stdButBw) {
696 rw = but->x + but->w - env->stdButBw - x;
697 }
698 if (rw <= 0)
699 return;
700 if (ti->cursorVisible)
701 butEnv_setXFg2(env, BUT_FG);
702 else
703 butEnv_setXFg2(env, BUT_HIBG);
704 XFillRectangle(env->dpy, win->win, env->gc2, x - win->xOff, y - win->yOff,
705 rw, ti->pm_h - env->stdButBw*2);
706 }
707
708
ti_redrawCursor(But * but)709 static void ti_redrawCursor(But *but) {
710 Txtin *ti = but->iPacket;
711 ButWin *win = but->win;
712 ButEnv *env = win->env;
713 XFontStruct *fs = env->fonts[0];
714 int x, y;
715 int rw;
716
717 if (ti->loc != ti->cutend)
718 return;
719 x = but->x - ti->xoffset + env->stdButBw*2 +
720 XTextWidth(fs, ti->dispStr, ti->loc);
721 y = but->y + env->stdButBw*2;
722 if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
723 rw = 1;
724 x -= rw/2;
725 if (x < but->x + env->stdButBw) {
726 rw -= (but->x + env->stdButBw - x);
727 x = but->x + env->stdButBw;
728 } else if (x+rw > but->x + but->w - env->stdButBw) {
729 rw = but->x + but->w - env->stdButBw - x;
730 }
731 if (rw <= 0)
732 return;
733 butWin_redraw(win, x, y,
734 rw, ti->pm_h - env->stdButBw*2);
735 }
736
737
cut(ButEnv * env,char * cutstr,int cutlen,But * but)738 static void cut(ButEnv *env, char *cutstr, int cutlen, But *but) {
739 ButWin *realWin;
740
741 if (cutstr_maxlen < cutlen) {
742 if (cutdata != NULL)
743 wms_free(cutdata);
744 cutdata = (char *)wms_malloc(cutlen);
745 cutstr_maxlen = cutlen;
746 }
747 XStoreBytes(env->dpy, cutstr, cutlen);
748 memcpy(cutdata, cutstr, cutlen);
749 cutstr_len = cutlen;
750 realWin = but->win;
751 while (realWin->parent != NULL)
752 realWin = realWin->parent;
753 if ((env->sReq == NULL) ||
754 ((env->sReq == sreq) && (cut_butnum == but)))
755 XSetSelectionOwner(env->dpy, XA_PRIMARY, realWin->win, 0);
756 else
757 env->sClear(env);
758 if (XGetSelectionOwner(env->dpy, XA_PRIMARY) == realWin->win) {
759 env->sReq = sreq;
760 env->sClear = sclear;
761 cut_butnum = but;
762 }
763 }
764
765
sreq(ButEnv * env,XSelectionRequestEvent * xsre)766 static bool sreq(ButEnv *env, XSelectionRequestEvent *xsre) {
767 if (xsre->target != XA_STRING)
768 return(FALSE);
769 XChangeProperty(env->dpy, xsre->requestor, xsre->property, xsre->target,
770 8, PropModeReplace, cutdata, cutstr_len);
771 return(TRUE);
772 }
773
774
sclear(ButEnv * env)775 static int sclear(ButEnv *env) {
776 Txtin *ti;
777
778 assert(MAGIC(cut_butnum));
779 ti = cut_butnum->iPacket;
780 assert(MAGIC(ti));
781 ti->loc = ti->cutend;
782 but_draw(cut_butnum);
783 env->sReq = NULL;
784 env->sClear = NULL;
785 return(0);
786 }
787
788
paste(ButEnv * env,But * but)789 static void paste(ButEnv *env, But *but) {
790 ButWin *topWin;
791
792 paste_butnum = but;
793 env->sNotify = snotify;
794 topWin = but->win;
795 while (topWin->parent != NULL)
796 topWin = topWin->parent;
797 XConvertSelection(env->dpy, XA_PRIMARY, XA_STRING, env->prop,
798 topWin->win, 0);
799 }
800
801
snotify(ButEnv * env,XSelectionEvent * xsnot)802 static int snotify(ButEnv *env, XSelectionEvent *xsnot) {
803 unsigned char *propbuf;
804 unsigned long proplen, propleft;
805 int blen;
806 int pformat;
807 Atom ptype;
808 Txtin *ti;
809 char *pb2;
810
811 ti = paste_butnum->iPacket;
812 if (xsnot->property != None) {
813 XGetWindowProperty(env->dpy, xsnot->requestor,
814 xsnot->property, 0, ti->maxlen, True, AnyPropertyType,
815 &ptype, &pformat, &proplen, &propleft,
816 &propbuf);
817 pb2 = (char *)wms_malloc(proplen * pformat + 1);
818 pb2[proplen * pformat] = '\0';
819 memcpy(pb2, propbuf, proplen * pformat);
820 XFree(propbuf);
821 if (insert(env, pb2, ti)) {
822 textInView(paste_butnum, FALSE);
823 but_draw(paste_butnum);
824 } else
825 XBell(env->dpy, 0);
826 wms_free(pb2);
827 } else {
828 propbuf = (unsigned char *)XFetchBytes(env->dpy, &blen);
829 if (blen > 0) {
830 pb2 = (char *)wms_malloc(blen + 1);
831 pb2[blen] = '\0';
832 memcpy(pb2, propbuf, blen);
833 XFree(propbuf);
834 if (insert(env, pb2, ti)) {
835 textInView(paste_butnum, FALSE);
836 but_draw(paste_butnum);
837 } else
838 XBell(env->dpy, 0);
839 wms_free(pb2);
840 } else
841 XBell(env->dpy, 0);
842 }
843 env->sNotify = NULL;
844 return(0);
845 }
846
847
848 /*
849 * This adjusts your loc and your cutend for when you're in word select mode.
850 */
wordsel_adjust(Txtin * but)851 static void wordsel_adjust(Txtin *but) {
852 int a, b;
853
854 if (but->loc < but->cutend) {
855 a = but->loc;
856 b = but->cutend;
857 } else {
858 a = but->cutend;
859 b = but->loc;
860 }
861 for (;;) {
862 if (a <= 0) {
863 a = 0;
864 break;
865 }
866 if (((isalnum(but->str[a]) || (but->str[a] == '_')) &&
867 (isalnum(but->str[a-1]) || (but->str[a-1] == '_'))) ||
868 (but->str[a] == but->str[a-1]) ||
869 (isdigit(but->str[a]) && (but->str[a-1] == '.') &&
870 (a >= 2) && isdigit(but->str[a-2])) ||
871 (isdigit(but->str[a+1]) && (but->str[a] == '.') &&
872 isdigit(but->str[a-1])))
873 --a;
874 else
875 break;
876 }
877 if (b <= 0)
878 b = 1;
879 for (;;) {
880 if (b >= but->len) {
881 b = but->len;
882 break;
883 }
884 if (((isalnum(but->str[b-1]) || (but->str[b-1] == '_')) &&
885 (isalnum(but->str[b]) || (but->str[b] == '_'))) ||
886 (but->str[b-1] == but->str[b]) ||
887 (isdigit(but->str[b-1]) && (but->str[b] == '.') &&
888 isdigit(but->str[b+1])) ||
889 ((b >= 2) && isdigit(but->str[b-2]) && (but->str[b-1] == '.') &&
890 isdigit(but->str[b])))
891 ++b;
892 else
893 break;
894 }
895 but->loc = a;
896 but->cutend = b;
897 }
898
899
900 /* Returns the index of the character just to the right of the cursor. */
locateMouse(XFontStruct * fs,char * text,int len,int x,bool * rightSide)901 static int locateMouse(XFontStruct *fs, char *text, int len, int x,
902 bool *rightSide) {
903 int i;
904 int prev_x = x;
905 bool dummy;
906
907 if (rightSide == NULL)
908 rightSide = &dummy;
909 for (i = 0; (x > 0) && (i < len); ++i) {
910 prev_x = x;
911 if (fs->per_char == NULL)
912 /* Monospace font. */
913 x -= fs->min_bounds.width;
914 else
915 x -= fs->per_char[text[i] - fs->min_char_or_byte2].width;
916 }
917 if ((*rightSide = ((i > 0) && (x < 0) && (prev_x < -x))))
918 --i;
919 if (i == len)
920 *rightSide = FALSE;
921 return(i);
922 }
923
924
925 /* Set up xoffset so that the cursor will be in view. */
curInView(But * but)926 static void curInView(But *but) {
927 Txtin *ti = but->iPacket;
928 ButEnv *env = but->win->env;
929 int curIn;
930
931 curIn = XTextWidth(env->fonts[0], ti->dispStr, ti->loc);
932 if (curIn < ti->xoffset)
933 ti->xoffset = curIn;
934 if (curIn - ti->xoffset > ti->pm_w - env->stdButBw * 2)
935 ti->xoffset = curIn - ti->pm_w + env->stdButBw * 2;
936 }
937
938
939 /*
940 * Set up xoffset so that the text will be in view. If maxInView is set,
941 * then make sure that as much text is in the window as possible (that is,
942 * there is no unnecessary empty space to the right of the text).
943 */
textInView(But * but,bool maxInView)944 static void textInView(But *but, bool maxInView) {
945 Txtin *ti = but->iPacket;
946 ButEnv *env = but->win->env;
947 int curIn;
948
949 curIn = XTextWidth(env->fonts[0], ti->dispStr, ti->len);
950 if (maxInView) {
951 if (ti->xoffset > curIn - (but->w - env->stdButBw*4))
952 ti->xoffset = curIn - (but->w - env->stdButBw * 4);
953 }
954 if (ti->xoffset < 0)
955 ti->xoffset = 0;
956 if (ti->xoffset > curIn)
957 ti->xoffset = curIn;
958 }
959
960
flags(But * but,uint flags)961 static void flags(But *but, uint flags) {
962 Txtin *ti = but->iPacket;
963
964 if ((but->flags & BUT_KEYED) != (flags & BUT_KEYED)) {
965 if (flags & BUT_KEYED) {
966 enableCTimer(but);
967 ti->cursorVisible = TRUE;
968 } else {
969 ti->cursorVisible = FALSE;
970 disableCTimer(but);
971 }
972 }
973 but->flags = flags;
974 assert((but->flags & BUT_PRESSABLE) || !(but->flags & BUT_KEYED));
975 but_draw(but);
976 }
977
978
blinkCursor(ButTimer * timer)979 static ButOut blinkCursor(ButTimer *timer) {
980 But *but = butTimer_packet(timer);
981 Txtin *ti = but->iPacket;
982
983 ti->cursorVisible = !(butTimer_ticks(timer) & 1);
984 ti_redrawCursor(but);
985 return(0);
986 }
987
988
slide(ButTimer * timer)989 static ButOut slide(ButTimer *timer) {
990 But *but = butTimer_packet(timer);
991 Txtin *ti = but->iPacket;
992 int newXoff, oldXoff;
993
994 if (ti->slideDir > 0) {
995 oldXoff = ti->xoffset;
996 textInView(but, TRUE);
997 if (oldXoff != ti->xoffset) {
998 ti->xoffset = oldXoff;
999 butTimer_destroy(ti->sTimer);
1000 ti->sTimer = NULL;
1001 return(0);
1002 }
1003 }
1004 newXoff = ti->xoffset + ti->slideDir * butTimer_ticks(timer);
1005 ti->xoffset = newXoff;
1006 textInView(but, ti->slideDir > 0);
1007 if (ti->xoffset != newXoff) {
1008 butTimer_destroy(ti->sTimer);
1009 ti->sTimer = NULL;
1010 }
1011 butTimer_setTicks(timer, 0);
1012 but_draw(but);
1013 return(0);
1014 }
1015
1016
startSlide(But * but,int dir,bool fast)1017 static void startSlide(But *but, int dir, bool fast) {
1018 struct timeval zero;
1019 Txtin *ti = but->iPacket;
1020 int freq;
1021
1022 zero.tv_sec = 0;
1023 zero.tv_usec = 0;
1024 freq = but->h;
1025 if (fast)
1026 freq *= 10;
1027 if (ti->sTimer != NULL) {
1028 if ((ti->slideFreq == freq) && (ti->slideDir == dir))
1029 return;
1030 butTimer_destroy(ti->sTimer);
1031 }
1032 ti->slideDir = dir;
1033 ti->slideFreq = freq;
1034 ti->sTimer = butTimer_fCreate(but, but, zero, freq, FALSE, slide);
1035 }
1036
1037
enableCTimer(But * but)1038 static void enableCTimer(But *but) {
1039 Txtin *ti = but->iPacket;
1040 struct timeval halfSec;
1041
1042 assert(ti->cTimer == NULL);
1043 halfSec.tv_sec = 0;
1044 halfSec.tv_usec = 500000;
1045 ti->cTimer = butTimer_create(but, but, halfSec, halfSec, TRUE,
1046 blinkCursor);
1047 }
1048
1049
disableCTimer(But * but)1050 static void disableCTimer(But *but) {
1051 Txtin *ti = but->iPacket;
1052
1053 if (ti->cTimer != NULL) {
1054 butTimer_destroy(ti->cTimer);
1055 ti->cTimer = NULL;
1056 }
1057 }
1058
1059
butTextin_set(But * but,const char * newStr,bool propagate)1060 void butTextin_set(But *but, const char *newStr, bool propagate) {
1061 Txtin *ti;
1062
1063 assert(MAGIC(but));
1064 assert(but->action == &action);
1065 ti = but->iPacket;
1066 assert(MAGIC(ti));
1067 assert(strlen(newStr) <= ti->maxlen);
1068 strcpy(ti->str, newStr);
1069 ti->len = ti->loc = ti->cutend = strlen(newStr);
1070 textInView(but, TRUE);
1071 but_draw(but);
1072 if (propagate)
1073 ti->callback(but, ti->str);
1074 }
1075
1076
butTextin_setHidden(But * but,bool hidden)1077 void butTextin_setHidden(But *but, bool hidden) {
1078 Txtin *ti;
1079 int i;
1080
1081 assert(MAGIC(but));
1082 assert(but->action == &action);
1083 ti = but->iPacket;
1084 assert(MAGIC(ti));
1085 if (hidden == ti->hidden)
1086 return;
1087 ti->hidden = hidden;
1088 if (hidden) {
1089 ti->dispStr = wms_malloc(ti->maxlen);
1090 for (i = 0; i < ti->maxlen; ++i) {
1091 ti->dispStr[i] = '*';
1092 }
1093 } else {
1094 wms_free(ti->dispStr);
1095 ti->dispStr = ti->str;
1096 }
1097 }
1098
butTextin_setSpecialKey(But * but,KeySym keysym,uint keyModifiers,uint modMask,ButOut callback (But * but,KeySym keysym,uint keyModifiers,void * context),void * context)1099 void butTextin_setSpecialKey(But *but, KeySym keysym,
1100 uint keyModifiers, uint modMask,
1101 ButOut callback(But *but,
1102 KeySym keysym, uint keyModifiers,
1103 void *context),
1104 void *context) {
1105 Txtin *ti;
1106 SpecKey *newSpec;
1107
1108 assert(MAGIC(but));
1109 assert(but->action == &action);
1110 ti = but->iPacket;
1111 assert(MAGIC(ti));
1112 assert((keyModifiers & modMask) == keyModifiers);
1113 newSpec = wms_malloc(sizeof(SpecKey));
1114 MAGIC_SET(newSpec);
1115 newSpec->next = ti->specKeys;
1116 ti->specKeys = newSpec;
1117 newSpec->keysym = keysym;
1118 newSpec->keyModifiers = keyModifiers;
1119 newSpec->modMask = modMask;
1120 newSpec->callback = callback;
1121 newSpec->context = context;
1122 }
1123
1124 #endif /* X11_DISP */
1125