1 /*
2 * wmslib/src/but/tbin.c, part of wmslib (Library functions)
3 * Copyright (C) 1994-1996 William Shubert.
4 * See "configure.h.in" for more copyright information.
5 */
6
7 /*
8 * This is absolutely huge and disgusting. It is no longer a cute little
9 * button. It has grown into a monstrosity. Some day I will break it off,
10 * make it into its own module, that reads configuration files, etc.
11 * Then it will be cool! Then it will be its own work processor, and I
12 * will just have to wrap a few extra buttons around it to have my very
13 * own beautiful wysiwyg word processor/editor! Yay!
14 */
15
16 #include <configure.h>
17
18 #ifdef X11_DISP
19
20 #ifdef STDC_HEADERS
21 #include <stdlib.h>
22 #include <unistd.h>
23 #endif /* STDC_HEADERS */
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/cursorfont.h>
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31 #include <sys/time.h>
32 #include <wms.h>
33 #include <but/but.h>
34 #include <but/tbin.h>
35 #include <but/timer.h>
36
37
38 /**********************************************************************
39 * Constants
40 **********************************************************************/
41 #define MAX_PASTE (64*1024)
42
43
44 /**********************************************************************
45 * Data types
46 **********************************************************************/
47 typedef enum {
48 insert_none, insert_ok, insert_cr, insert_bad
49 } Insert;
50
51
52 typedef enum {
53 break_loCur, break_eoLoCur, break_eol
54 } BreakType;
55
56
57 typedef struct Loc_struct {
58 int tin, index;
59 } Loc;
60
61
62 typedef struct Tin_struct {
63 int start, len;
64 int width; /* In pixels. */
65 } Tin;
66
67
68 typedef enum {
69 tbin_loCur, tbin_hiCur, tbin_mouse, tbin_press
70 } TbinLoc;
71 #define tbin_numLocs (tbin_press + 1)
72 #define tbinLoc_iter(i) for (i = 0; i < tbin_numLocs; ++i)
73
74
75 typedef struct Tbin_struct {
76 ButTimer *cTimer;
77
78 int mouseX;
79 Loc locs[tbin_numLocs];
80 bool mouseInBut;
81 int clicks, prevClickNum;
82
83 bool cursorVisible;
84 bool readOnly;
85 int lMargin, rMargin;
86
87 int maxLines; /* If you exceed this, chop off the top. */
88 int numTins, maxTins, loTinBreak, hiTinBreak;
89 Tin *tins;
90
91 int bufLen, loBreak, hiBreak;
92 char *buf;
93
94 void (*offWinCallback)(But *but, int activeLine, int passiveLine,
95 int mouseY);
96
97 MAGIC_STRUCT
98 } Tbin;
99
100
101 static But *cut_butnum = NULL;
102 static But *paste_butnum = NULL;
103
104 /**********************************************************************
105 * Forward declarations
106 **********************************************************************/
107 static int locateMouse(XFontStruct *fs, const char *text, int textLen, int x,
108 bool *rightSide);
109 static void ti_cursor(But *but);
110 static void ti_redrawCursor(But *but);
111 static ButOut mmove(But *but, int x, int y);
112 static ButOut mleave(But *but);
113 static ButOut mpress(But *but, int butnum, int x, int y);
114 static ButOut mrelease(But *but, int butnum, int x, int y);
115 static ButOut kpress(But *but, const char *keystr, KeySym sym);
116 static void draw(But *but, int x,int y, int w,int h);
117 static ButOut destroy(But *but);
118 static Insert insert(ButEnv *env, const char *src, Tbin *but,
119 int butW, bool *changePrev);
120 static void cut(ButEnv *env, But *but);
121 static void paste(ButEnv *env, But *but);
122 static void enableCTimer(But *but);
123 static void disableCTimer(But *but);
124
125 static bool sreq(ButEnv *env, XSelectionRequestEvent *xsre);
126 static int sclear(ButEnv *env);
127 static int snotify(ButEnv *env, XSelectionEvent *xsre);
128 static void flags(But *but, uint flags);
129
130 static void wordsel_adjust(Tbin *tbin);
131
132 static void tin_draw(But *but, Tbin *tb, int tinNum,
133 int x, int y, int w, int h);
134 static int calcTinNum(But *but, Tbin *tbin, int y);
135 static void setMouseX(But *but, Tbin *tbin, int tin, int loc);
136 static void checkOffWin(But *but, int mouseY, bool force);
137 #define tinNum_next(tn, tb) (((tn)+1 == (tb)->loTinBreak) ? \
138 (tb)->hiTinBreak:(tn)+1)
139 #define tinNum_prev(tn, tb) (((tn) == (tb)->hiTinBreak) ? \
140 (tb)->loTinBreak-1:(tn)-1)
141 #define tinNum_line(tn, tb) ((tn) >= (tb)->loTinBreak ? \
142 (tn) + (tb)->loTinBreak - (tb)->hiTinBreak : \
143 (tn))
144 #define tinNum_line2TinNum(l, tb) \
145 ((l) >= (tb)->loTinBreak ? \
146 (l) + (tb)->hiTinBreak - (tb)->loTinBreak : \
147 (l))
148 #define tinNum_valid(tn, tb) (((tn) >= 0) && ((tn) < (tb)->maxTins) && \
149 (((tn) < (tb)->loTinBreak) || \
150 ((tn) >= (tb)->hiTinBreak)))
151 #define loc_valid(l, tb) (tinNum_valid((l).tin, (tb)) && \
152 ((l).index >= 0) && \
153 ((l).index <= (tb)->tins[(l).tin].len))
154 #define loc_eq(a, b) (((a).index == (b).index) && ((a).tin == (b).tin))
155 static void adjustBreak(Tbin *tb, BreakType bType, int breakTin);
156 static void addMoreBufferSpace(Tbin *tb);
157 static void addMoreTins(Tbin *tb);
158 static void breakLine(Tbin *tb, int tinNum, ButEnv *env, int butW);
159 static int calcTinWidth(Tin *tin, const char *buf, ButEnv *env);
160 static bool tryJoinLines(Tbin *tb, int tinNum, ButEnv *env, int butW);
161 static bool resize(But *but, int oldX, int oldY, int oldW, int oldH);
162 static void killTopLines(Tbin *tb, int lines);
163 static void setLoc(Tbin *tb, TbinLoc locNum, int position);
164
165
166 /**********************************************************************
167 * Global variables
168 **********************************************************************/
169
170
171 static ButAction action = {
172 mmove, mleave, mpress, mrelease,
173 kpress, NULL, draw, destroy, flags, NULL, resize};
174
175
butTbin_create(ButWin * win,int layer,int flags,const char * text)176 But *butTbin_create(ButWin *win, int layer, int flags,
177 const char *text) {
178 But *but;
179 Tbin *tb;
180 TbinLoc tbinLoc;
181
182 tb = wms_malloc(sizeof(Tbin));
183 MAGIC_SET(tb);
184 but = but_create(win, tb, &action);
185 but->layer = layer;
186 but->flags = flags;
187
188 if (text == NULL)
189 text = "";
190 tb->cTimer = NULL;
191 tb->mouseX = -1;
192 tbinLoc_iter(tbinLoc) {
193 tb->locs[tbinLoc].tin = 0;
194 tb->locs[tbinLoc].index = 0;
195 }
196 tb->mouseInBut = FALSE;
197 tb->clicks = tb->prevClickNum = 0;
198
199 tb->cursorVisible = FALSE;
200 tb->readOnly = FALSE;
201 tb->lMargin = tb->rMargin = butEnv_stdBw(win->env);
202
203 tb->maxLines = -1;
204 tb->numTins = 1;
205 tb->maxTins = 2;
206 tb->tins = wms_malloc(tb->maxTins * sizeof(Tin));
207
208 tb->bufLen = 4 * 1024;
209 tb->loBreak = 0;
210 tb->hiBreak = tb->bufLen;
211 tb->buf = wms_malloc(tb->bufLen);
212
213 butTbin_set(but, text);
214
215 tb->offWinCallback = NULL;
216 return(but);
217 }
218
219
butTbin_get(But * but)220 const char *butTbin_get(But *but) {
221 Tbin *tb = but->iPacket;
222
223 assert(but->action == &action);
224 assert(MAGIC(tb));
225 adjustBreak(tb, break_eol, tinNum_prev(tb->maxTins, tb));
226 if (tb->loBreak == tb->hiBreak)
227 addMoreBufferSpace(tb);
228 tb->buf[tb->loBreak] = '\0';
229 return(tb->buf);
230 }
231
232
butTbin_numLines(But * but)233 int butTbin_numLines(But *but) {
234 Tbin *tbin = but->iPacket;
235
236 assert(MAGIC(tbin));
237 return(tbin->numTins);
238 }
239
240
butTbin_setOffWinCallback(But * but,void (* func)(But * but,int activeLine,int passiveLine,int mouseY))241 void butTbin_setOffWinCallback(But *but,
242 void (*func)(But *but, int activeLine,
243 int passiveLine,
244 int mouseY)) {
245 Tbin *tbin;
246
247 assert(MAGIC(but));
248 tbin = but->iPacket;
249 assert(MAGIC(tbin));
250 tbin->offWinCallback = func;
251 }
252
253
destroy(But * but)254 static ButOut destroy(But *but) {
255 Tbin *tb = but->iPacket;
256 ButEnv *env = but->win->env;
257
258 assert(but->action == &action);
259 assert(MAGIC(tb));
260 if (cut_butnum == but) {
261 if (env->sClear == sclear)
262 env->sClear = NULL;
263 if (env->sReq == sreq)
264 env->sReq = NULL;
265 cut_butnum = NULL;
266 }
267 if (paste_butnum == but) {
268 if (env->sNotify == snotify)
269 env->sNotify = NULL;
270 paste_butnum = NULL;
271 }
272 wms_free(tb->tins);
273 wms_free(tb->buf);
274 MAGIC_UNSET(tb);
275 wms_free(tb);
276 return(0);
277 }
278
279
draw(But * but,int x,int y,int w,int h)280 static void draw(But *but, int x,int y, int w,int h) {
281 Tbin *tb = but->iPacket;
282 ButEnv *env = but->win->env;
283 int fontH, i, startLine;
284 int tinY;
285
286 assert(but->action == &action);
287 assert(MAGIC(tb));
288 fontH = butEnv_fontH(env, 0);
289 XSetFont(env->dpy, env->gc, env->fonts[0]->fid);
290 startLine = (y - fontH + 1 - but->y) / fontH;
291 if (startLine < 0)
292 startLine = 0;
293 tinY = but->y + startLine * fontH;
294 for (i = tinNum_line2TinNum(startLine, tb);
295 (i < tb->maxTins) && (tinY < y + h);
296 i = tinNum_next(i, tb)) {
297 tin_draw(but, tb, i, but->x, tinY, but->w, fontH);
298 tinY += fontH;
299 }
300 }
301
302
tin_draw(But * but,Tbin * tb,int tinNum,int x,int y,int w,int h)303 static void tin_draw(But *but, Tbin *tb, int tinNum,
304 int x, int y, int w, int h) {
305 ButWin *win = but->win;
306 ButEnv *env = win->env;
307 Tin *ti = &tb->tins[tinNum];
308 XFontStruct *fs = env->fonts[0];
309 int loCur, hiCur;
310 int blockL, blockW;
311
312 butEnv_setXFg(env, BUT_FG);
313 if (tb->locs[tbin_loCur].tin < tinNum) {
314 loCur = -1;
315 } else if (tb->locs[tbin_loCur].tin == tinNum) {
316 loCur = tb->locs[tbin_loCur].index;
317 } else {
318 loCur = ti->len + 1;
319 }
320 if (tb->locs[tbin_hiCur].tin < tinNum) {
321 hiCur = -1;
322 } else if (tb->locs[tbin_hiCur].tin == tinNum) {
323 hiCur = tb->locs[tbin_hiCur].index;
324 } else {
325 hiCur = ti->len + 1;
326 }
327 if (loCur != hiCur) {
328 blockW = 0;
329 assert(hiCur >= 0);
330 if (loCur == -1) {
331 if (hiCur == 0)
332 blockL = x + tb->lMargin;
333 else {
334 blockL = x;
335 blockW += tb->lMargin;
336 }
337 } else {
338 assert((loCur >= 0) && (loCur <= ti->len));
339 blockL = x + tb->lMargin + XTextWidth(fs, tb->buf + ti->start, loCur);
340 }
341 if (hiCur <= ti->len) {
342 blockW += XTextWidth(fs, tb->buf + ti->start + loCur, hiCur - loCur);
343 } else {
344 assert(hiCur == ti->len + 1);
345 if (loCur == 0)
346 blockL = x;
347 blockW = w - blockL;
348 }
349 butEnv_setXFg(env, BUT_SELBG);
350 XFillRectangle(env->dpy, win->win, env->gc, blockL-win->xOff, y-win->yOff,
351 blockW, h);
352 butEnv_setXFg(env, BUT_FG);
353 }
354 XDrawString(env->dpy, win->win, env->gc,
355 x + tb->lMargin - win->xOff, y + fs->ascent - win->yOff,
356 tb->buf + ti->start, ti->len);
357 if (!(but->flags & BUT_PRESSABLE)) {
358 butEnv_stdFill2(env);
359 }
360 if ((tb->locs[tbin_loCur].tin == tinNum) && (loCur == hiCur) && tb->cursorVisible &&
361 (but->flags & BUT_PRESSABLE))
362 ti_cursor(but);
363 }
364
365
mmove(But * but,int x,int y)366 static ButOut mmove(But *but, int x, int y) {
367 Tbin *tb = but->iPacket;
368 Tin *ti;
369 int tinNum;
370 ButWin *win = but->win;
371 ButEnv *env = win->env;
372 int newMousePress;
373 bool rightSide;
374 int oldLoTin = tb->locs[tbin_loCur].tin, oldLo = tb->locs[tbin_loCur].index;
375 int oldHiTin = tb->locs[tbin_hiCur].tin, oldHi = tb->locs[tbin_hiCur].index;
376
377 if (!(but->flags & BUT_PRESSABLE))
378 return(BUTOUT_CAUGHT);
379 if (!tb->mouseInBut) {
380 tb->mouseInBut = TRUE;
381 butEnv_setCursor(env, but, butCur_text);
382 }
383 tinNum = calcTinNum(but, tb, y);
384 if ((tinNum >= 0) && (tinNum < tb->maxTins))
385 ti = &tb->tins[tinNum];
386 else
387 ti = NULL;
388 switch(tb->clicks) {
389 case(0):
390 break;
391 case(1):
392 case(2):
393 if (ti) {
394 if (y < but->y) {
395 newMousePress = 0;
396 rightSide = FALSE;
397 } else
398 newMousePress = locateMouse(env->fonts[0], tb->buf + ti->start,
399 ti->len,
400 x - (but->x+tb->lMargin), &rightSide);
401 if (rightSide && (newMousePress == ti->len) &&
402 ((tinNum > tb->locs[tbin_press].tin) ||
403 ((tinNum == tb->locs[tbin_press].tin) &&
404 (tb->locs[tbin_press].index < newMousePress)))) {
405 tinNum = tinNum_next(tinNum, tb);
406 newMousePress = 0;
407 if (tinNum >= tb->maxTins) {
408 tinNum = tinNum_prev(tb->maxTins, tb);
409 ti = NULL;
410 } else {
411 ti = &tb->tins[tinNum];
412 }
413 }
414 } else {
415 newMousePress = 0;
416 rightSide = FALSE;
417 }
418 if (ti && (tb->clicks == 2)) {
419 if (rightSide &&
420 ((tinNum > tb->locs[tbin_press].tin) ||
421 ((tinNum == tb->locs[tbin_press].tin) &&
422 (newMousePress >= tb->locs[tbin_press].index))) &&
423 (newMousePress < ti->len) && (newMousePress > 0))
424 ++newMousePress;
425 else if (!rightSide &&
426 ((tinNum < tb->locs[tbin_press].tin) ||
427 ((tinNum == tb->locs[tbin_press].tin) &&
428 (newMousePress <= tb->locs[tbin_press].index))) &&
429 (newMousePress > 0))
430 --newMousePress;
431 }
432 if ((newMousePress != tb->locs[tbin_mouse].index) ||
433 (tinNum != tb->locs[tbin_mouse].tin)) {
434 tb->locs[tbin_mouse].index = newMousePress;
435 tb->locs[tbin_mouse].tin = tinNum;
436 assert(loc_valid(tb->locs[tbin_mouse], tb));
437 if ((tb->locs[tbin_mouse].tin < tb->locs[tbin_press].tin) ||
438 ((tb->locs[tbin_mouse].tin == tb->locs[tbin_press].tin) &&
439 (tb->locs[tbin_mouse].index < tb->locs[tbin_press].index))) {
440 tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
441 tb->locs[tbin_hiCur] = tb->locs[tbin_press];
442 } else {
443 tb->locs[tbin_loCur] = tb->locs[tbin_press];
444 tb->locs[tbin_hiCur] = tb->locs[tbin_mouse];
445 }
446 if (tb->clicks == 2) {
447 wordsel_adjust(tb);
448 }
449 }
450 break;
451 case(3):
452 /* Triple-click. */
453 tb->locs[tbin_mouse].tin = tinNum;
454 tb->locs[tbin_mouse].index = 0;
455 tb->locs[tbin_loCur].index = tb->locs[tbin_hiCur].index = 0;
456 if (tb->locs[tbin_mouse].tin < tb->locs[tbin_press].tin) {
457 tb->locs[tbin_loCur].tin = tb->locs[tbin_mouse].tin;
458 tb->locs[tbin_hiCur].tin = tb->locs[tbin_press].tin;
459 } else {
460 tb->locs[tbin_loCur].tin = tb->locs[tbin_press].tin;
461 tb->locs[tbin_hiCur].tin = tb->locs[tbin_mouse].tin;
462 }
463 if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) < tb->maxTins) {
464 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
465 } else
466 tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
467 assert(loc_valid(tb->locs[tbin_mouse], tb));
468 break;
469 default:
470 assert(tb->clicks == 4);
471 tb->locs[tbin_mouse].tin = tinNum;
472 assert(loc_valid(tb->locs[tbin_mouse], tb));
473 break;
474 }
475 if (tb->clicks) {
476 /*
477 * We cannot call this if no clicks. Why? Because with no clicks, we
478 * _could_ be doing a resize (due to a new line being added).
479 * Resize calls mmove, which would call checkOffWin, which calls
480 * resize, etc...boom!
481 * Even this is a bit hairy.
482 */
483 checkOffWin(but, y, TRUE);
484 }
485 if ((tb->locs[tbin_loCur].tin != oldLoTin) ||
486 (tb->locs[tbin_loCur].index != oldLo) ||
487 (tb->locs[tbin_hiCur].tin != oldHiTin) ||
488 (tb->locs[tbin_hiCur].index != oldHi))
489 but_draw(but);
490 return(BUTOUT_CAUGHT);
491 }
492
493
mleave(But * but)494 static ButOut mleave(But *but) {
495 Tbin *tb = but->iPacket;
496
497 if (tb->mouseInBut) {
498 tb->mouseInBut = FALSE;
499 butEnv_setCursor(but->win->env, but, butCur_idle);
500 }
501 return(BUTOUT_CAUGHT);
502 }
503
504
mpress(But * but,int butnum,int x,int y)505 static ButOut mpress(But *but, int butnum, int x, int y) {
506 Tbin *tb = but->iPacket;
507 Tin *ti;
508 ButEnv *env = but->win->env;
509 static Time lastPressTime = -1;
510 static int lastPressNum = -2;
511 int rightSide;
512
513 if (tb->cTimer) {
514 butTimer_reset(tb->cTimer);
515 tb->cursorVisible = TRUE;
516 }
517 tb->locs[tbin_mouse].tin = calcTinNum(but, tb, y);
518 if ((tb->locs[tbin_mouse].tin >= 0) && (tb->locs[tbin_mouse].tin < tb->maxTins)) {
519 ti = &tb->tins[tb->locs[tbin_mouse].tin];
520 if (y < but->y) {
521 tb->locs[tbin_mouse].index = 0;
522 rightSide = FALSE;
523 } else
524 tb->locs[tbin_mouse].index = locateMouse(env->fonts[0],
525 tb->buf + ti->start,
526 ti->len,
527 x - (but->x+tb->lMargin),
528 &rightSide);
529 } else {
530 ti = NULL;
531 tb->locs[tbin_mouse].index = 0;
532 }
533 switch (butnum) {
534 case 1:
535 /* Button 1 selects, drags, etc. */
536 assert(loc_valid(tb->locs[tbin_mouse], tb));
537 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur] = tb->locs[tbin_press] =
538 tb->locs[tbin_mouse];
539 if (!tb->readOnly && !(but->flags & BUT_KEYED))
540 but_newFlags(but, but->flags | BUT_KEYED);
541 but_newFlags(but, but->flags | BUT_LOCKED);
542 if ((lastPressTime + BUT_DCLICK > env->eventTime) &&
543 (lastPressNum + 1 == env->eventNum)) {
544 tb->clicks = tb->prevClickNum;
545 if (tb->clicks == 1) {
546 /* It's a double click! */
547 ++tb->clicks;
548 if (rightSide)
549 ++tb->locs[tbin_hiCur].index;
550 else
551 --tb->locs[tbin_loCur].index;
552 wordsel_adjust(tb);
553 } else if (tb->clicks == 2) {
554 /* It's a triple click! */
555 ++tb->clicks;
556 tb->locs[tbin_loCur].index = 0;
557 tb->locs[tbin_hiCur].index = 0;
558 if (tinNum_next(tb->locs[tbin_mouse].tin, tb) < tb->maxTins)
559 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
560 } else if (tb->clicks == 3) {
561 /* Quadruple click! Mark everything! */
562 ++tb->clicks;
563 tb->locs[tbin_loCur].tin = 0;
564 tb->locs[tbin_hiCur].tin = tinNum_prev(tb->maxTins, tb);
565 tb->locs[tbin_loCur].index = 0;
566 tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
567 } else {
568 /* Pentuple click! Restart! */
569 tb->clicks = 1;
570 }
571 } else {
572 tb->clicks = 1;
573 }
574 lastPressTime = env->eventTime;
575 lastPressNum = env->eventNum;
576 but_draw(but);
577 break;
578 case 2:
579 /* Paste! */
580 if (tb->readOnly)
581 return(BUTOUT_ERR);
582 else
583 paste(env, but);
584 break;
585 default:
586 /*
587 * Weird. I don't know what to do with mice with more than 3 buttons
588 * so I guess I might as well beep. I like beeping.
589 */
590 return(BUTOUT_ERR);
591 break;
592 }
593 checkOffWin(but, y, TRUE);
594 return(BUTOUT_CAUGHT);
595 }
596
597
mrelease(But * but,int butnum,int x,int y)598 static ButOut mrelease(But *but, int butnum, int x, int y) {
599 Tbin *tb = but->iPacket;
600
601 if (tb->cTimer) {
602 butTimer_reset(tb->cTimer);
603 tb->cursorVisible = TRUE;
604 }
605 if ((butnum == 1) && tb->clicks) {
606 tb->prevClickNum = tb->clicks;
607 tb->clicks = 0;
608 if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
609 (tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index)) {
610 cut(but->win->env, but);
611 }
612 but_newFlags(but, but->flags & ~BUT_LOCKED);
613 }
614 checkOffWin(but, y, TRUE);
615 return(BUTOUT_CAUGHT);
616 }
617
618
kpress(But * but,const char * newtext,KeySym keysym)619 static ButOut kpress(But *but, const char *newtext, KeySym keysym) {
620 Tbin *tb = but->iPacket;
621 ButEnv *env = but->win->env;
622 bool need_draw = FALSE;
623 ButOut result = 0;
624 bool clearMouseX = TRUE;
625 int drawLo = tb->locs[tbin_loCur].tin, drawHi = tb->locs[tbin_hiCur].tin;
626 Insert insertResult = insert_none;
627 bool changePrev = FALSE;
628
629 assert(but->flags & BUT_KEYED);
630 butTimer_reset(tb->cTimer);
631 tb->cursorVisible = TRUE;
632 if (((keysym >= XK_KP_Space) && (keysym <= XK_KP_9)) ||
633 ((keysym >= XK_space) && (keysym <= XK_ydiaeresis))) {
634 need_draw = TRUE;
635 switch(newtext[0]) {
636 case '\001': /* Ctrl-A: Beginning of line. */
637 tb->locs[tbin_loCur].index = 0;
638 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
639 break;
640 case '\020': /* Ctrl-P: Up a line. */
641 clearMouseX = FALSE;
642 if (tb->locs[tbin_loCur].tin == 0) {
643 if (tb->locs[tbin_loCur].index || tb->locs[tbin_hiCur].index ||
644 tb->locs[tbin_hiCur].tin) {
645 tb->locs[tbin_loCur].index = 0;
646 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
647 } else {
648 need_draw = FALSE;
649 result |= BUTOUT_ERR;
650 }
651 } else {
652 if (tb->mouseX == -1)
653 setMouseX(but, tb, tb->locs[tbin_loCur].tin,
654 tb->locs[tbin_loCur].index);
655 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
656 tb->locs[tbin_loCur].index =
657 locateMouse(env->fonts[0],
658 tb->buf + tb->tins[tb->locs[tbin_loCur].tin].start,
659 tb->tins[tb->locs[tbin_loCur].tin].len,
660 tb->mouseX, NULL);
661 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
662 }
663 break;
664 case '\016': /* Ctrl-N: Down a line. */
665 clearMouseX = FALSE;
666 if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) == tb->maxTins) {
667 if (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) {
668 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
669 } else {
670 need_draw = FALSE;
671 result |= BUTOUT_ERR;
672 }
673 } else {
674 if (tb->mouseX == -1)
675 setMouseX(but, tb, tb->locs[tbin_hiCur].tin,
676 tb->locs[tbin_hiCur].index);
677 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
678 tb->locs[tbin_hiCur].index =
679 locateMouse(env->fonts[0],
680 tb->buf + tb->tins[tb->locs[tbin_hiCur].tin].start,
681 tb->tins[tb->locs[tbin_hiCur].tin].len,
682 tb->mouseX, NULL);
683 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
684 }
685 break;
686 case '\002': /* Ctrl-B: Back a character. */
687 if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
688 (tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index)) {
689 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
690 need_draw = TRUE;
691 } else if (tb->locs[tbin_loCur].index) {
692 tb->locs[tbin_hiCur].index = --tb->locs[tbin_loCur].index;
693 } else if (tb->locs[tbin_loCur].tin) {
694 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
695 tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
696 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
697 } else {
698 need_draw = FALSE;
699 result |= BUTOUT_ERR;
700 }
701 break;
702 case '\004': /* Ctrl-D: Delete right. */
703 if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
704 (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin)) {
705 insertResult = insert(env, "", tb, but->w, &changePrev);
706 need_draw = TRUE;
707 } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins) {
708 if (tb->locs[tbin_loCur].index <
709 tb->tins[tb->locs[tbin_loCur].tin].len)
710 ++tb->locs[tbin_hiCur].index;
711 else if (tb->buf[tb->loBreak - 1] == '\n') {
712 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
713 tb->locs[tbin_hiCur].index = 0;
714 } else {
715 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
716 tb->locs[tbin_hiCur].index = 1;
717 }
718 insertResult = insert(env, "", tb, but->w, &changePrev);
719 need_draw = TRUE;
720 } else
721 result |= BUTOUT_ERR;
722 break;
723 case '\005': /* Ctrl-E: End of line. */
724 tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
725 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
726 break;
727 case '\006': /* Ctrl-F: Forward a character. */
728 need_draw = TRUE;
729 if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
730 (tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index)) {
731 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
732 } else if (tb->locs[tbin_loCur].index <
733 tb->tins[tb->locs[tbin_loCur].tin].len) {
734 tb->locs[tbin_hiCur].index = ++tb->locs[tbin_loCur].index;
735 } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins) {
736 tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
737 tb->locs[tbin_loCur].index = 0;
738 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
739 } else {
740 result |= BUTOUT_ERR;
741 need_draw = FALSE;
742 }
743 break;
744 case '\013': /* Ctrl-K: Kill to end of line. */
745 need_draw = TRUE;
746 if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
747 (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))
748 insertResult = insert(env, "", tb, but->w, &changePrev);
749 else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins) {
750 if (tb->locs[tbin_loCur].index ==
751 tb->tins[tb->locs[tbin_loCur].tin].len) {
752 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
753 tb->locs[tbin_hiCur].index = 0;
754 } else {
755 tb->locs[tbin_hiCur].tin = tb->locs[tbin_loCur].tin;
756 tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
757 }
758 insertResult = insert(env, "", tb, but->w, &changePrev);
759 } else {
760 result |= BUTOUT_ERR;
761 need_draw = FALSE;
762 }
763 break;
764 case '\025': /* Ctrl-U: Kill from beginning of line. */
765 if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
766 (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))
767 insertResult = insert(env, "", tb, but->w, &changePrev);
768 else if (tb->locs[tbin_loCur].index > 0) {
769 tb->locs[tbin_hiCur].index = 0;
770 tb->locs[tbin_hiCur].tin = tb->locs[tbin_loCur].tin;
771 insertResult = insert(env, "", tb, but->w, &changePrev);
772 } else {
773 result |= BUTOUT_ERR;
774 need_draw = FALSE;
775 }
776 break;
777 default:
778 insertResult = insert(env, newtext, tb, but->w, &changePrev);
779 break;
780 }
781 } else if ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)) {
782 } else if ((keysym >= XK_F1) && (keysym <= XK_F35)) {
783 insertResult = insert(env, newtext, tb, but->w, &changePrev);
784 } else if ((keysym == XK_BackSpace) || (keysym == XK_Delete)) {
785 if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
786 (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin)) {
787 insertResult = insert(env, "", tb, but->w, &changePrev);
788 need_draw = TRUE;
789 } else if (tb->locs[tbin_loCur].index > 0) {
790 --tb->locs[tbin_loCur].index;
791 assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
792 drawHi = tinNum_line(drawHi, tb);
793 insertResult = insert(env, "", tb, but->w, &changePrev);
794 drawHi = tinNum_line2TinNum(drawHi, tb);
795 assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
796 need_draw = TRUE;
797 } else if (tb->locs[tbin_loCur].tin > 0) {
798 if (tb->buf[tb->tins[tb->locs[tbin_loCur].tin].start - 1] == '\n') {
799 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
800 tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
801 } else {
802 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
803 tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len - 1;
804 }
805 insertResult = insert(env, "", tb, but->w, &changePrev);
806 need_draw = TRUE;
807 } else {
808 result |= BUTOUT_ERR;
809 }
810 #if 0 /* People don't like my delete. :-( */
811 } else if (keysym == XK_Delete) {
812 if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
813 (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin)) {
814 insertResult = insert(env, "", tb, but->w, &changePrev);
815 need_draw = TRUE;
816 } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins) {
817 if (tb->locs[tbin_loCur].index < tb->tins[tb->locs[tbin_loCur].tin].len)
818 ++tb->locs[tbin_hiCur].index;
819 else if (tb->buf[tb->loBreak - 1] == '\n') {
820 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
821 tb->locs[tbin_hiCur].index = 0;
822 } else {
823 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
824 tb->locs[tbin_hiCur].index = 1;
825 }
826 insertResult = insert(env, "", tb, but->w, &changePrev);
827 need_draw = TRUE;
828 } else
829 result |= BUTOUT_ERR;
830 #endif
831 } else if (keysym == XK_Left) {
832 need_draw = TRUE;
833 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur])) {
834 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
835 } else if (tb->locs[tbin_loCur].index) {
836 tb->locs[tbin_hiCur].index = --tb->locs[tbin_loCur].index;
837 } else if (tb->locs[tbin_loCur].tin) {
838 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
839 tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
840 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
841 } else
842 result |= BUTOUT_ERR;
843 } else if (keysym == XK_Right) {
844 need_draw = TRUE;
845 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur])) {
846 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
847 } else if (tb->locs[tbin_loCur].index <
848 tb->tins[tb->locs[tbin_loCur].tin].len) {
849 tb->locs[tbin_hiCur].index = ++tb->locs[tbin_loCur].index;
850 } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins) {
851 tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
852 tb->locs[tbin_loCur].index = 0;
853 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
854 } else
855 result |= BUTOUT_ERR;
856 } else if (keysym == XK_Up) {
857 clearMouseX = FALSE;
858 if (tb->locs[tbin_loCur].tin == 0) {
859 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur])) {
860 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
861 need_draw = TRUE;
862 } else
863 result |= BUTOUT_ERR;
864 } else {
865 if (tb->mouseX == -1)
866 setMouseX(but, tb, tb->locs[tbin_loCur].tin,
867 tb->locs[tbin_loCur].index);
868 tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
869 tb->locs[tbin_loCur].index = locateMouse(env->fonts[0],
870 tb->buf +
871 tb->tins[tb->locs[tbin_loCur].
872 tin].start,
873 tb->tins[tb->locs[tbin_loCur].
874 tin].len,
875 tb->mouseX, NULL);
876 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
877 need_draw = TRUE;
878 }
879 } else if (keysym == XK_Down) {
880 clearMouseX = FALSE;
881 assert(loc_valid(tb->locs[tbin_loCur], tb));
882 assert(loc_valid(tb->locs[tbin_hiCur], tb));
883 if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) == tb->maxTins) {
884 if (loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))
885 result |= BUTOUT_ERR;
886 else {
887 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
888 }
889 } else {
890 if (tb->mouseX == -1)
891 setMouseX(but, tb, tb->locs[tbin_hiCur].tin,
892 tb->locs[tbin_hiCur].index);
893 tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
894 tb->locs[tbin_hiCur].index =
895 locateMouse(env->fonts[0],
896 tb->buf + tb->tins[tb->locs[tbin_hiCur].tin].start,
897 tb->tins[tb->locs[tbin_hiCur].tin].len,
898 tb->mouseX, NULL);
899 tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
900 }
901 need_draw = TRUE;
902 } else if ((keysym == XK_Return) || (keysym == XK_Linefeed) ||
903 (keysym == XK_KP_Enter)) {
904 insertResult = insert(env, "\n", tb, but->w, &changePrev);
905 } else {
906 result |= BUTOUT_ERR;
907 }
908 /* Void all mouse movement until it is pressed again. */
909 if (tb->clicks) {
910 tb->clicks = 0;
911 if (but->flags & BUT_LOCKED)
912 but_newFlags(but, but->flags & ~BUT_LOCKED);
913 }
914 if (need_draw || (insertResult != insert_none)) {
915 result |= BUTOUT_CAUGHT;
916 if (tb->locs[tbin_loCur].tin < drawLo)
917 drawLo = tb->locs[tbin_loCur].tin;
918 if (tb->locs[tbin_hiCur].tin > drawHi) {
919 drawHi = tb->locs[tbin_hiCur].tin;
920 assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
921 }
922 if (changePrev && drawLo) {
923 drawLo = tinNum_prev(drawLo, tb);
924 }
925 assert(drawHi >= drawLo);
926 drawLo = tinNum_line(drawLo, tb) * butEnv_fontH(env, 0);
927 if (insertResult == insert_cr) {
928 drawHi = but->h;
929 } else {
930 assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
931 drawHi = (tinNum_line(drawHi, tb) + 1) * butEnv_fontH(env, 0);
932 }
933 if (drawLo < but->h) {
934 assert(drawHi >= drawLo);
935 if (drawHi > but->h) {
936 drawHi = but->h;
937 assert(drawHi > drawLo);
938 }
939 butWin_redraw(but->win, but->x,but->y+drawLo, but->w,drawHi - drawLo);
940 }
941 }
942 if (clearMouseX)
943 tb->mouseX = -1;
944 if (insertResult == insert_bad)
945 result |= BUTOUT_ERR;
946 checkOffWin(but, 0, FALSE);
947 return(result);
948 }
949
950
insert(ButEnv * env,const char * src,Tbin * tb,int butW,bool * drawLo)951 static Insert insert(ButEnv *env, const char *src, Tbin *tb, int butW,
952 bool *drawLo) {
953 int i, len, loTinWidthCheck;
954 Insert result = insert_ok;
955 bool lineBroken, dummy;
956
957 if (drawLo == NULL)
958 drawLo = &dummy;
959 butW -= tb->lMargin + tb->rMargin;
960 assert(tb->loTinBreak <= tb->hiTinBreak);
961 assert(loc_valid(tb->locs[tbin_loCur], tb));
962 assert(loc_valid(tb->locs[tbin_hiCur], tb));
963 assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
964 adjustBreak(tb, break_loCur, 0);
965 assert(tb->tins[tb->locs[tbin_loCur].tin].start +
966 tb->locs[tbin_loCur].index == tb->loBreak);
967 assert(loc_valid(tb->locs[tbin_loCur], tb));
968 assert(loc_valid(tb->locs[tbin_hiCur], tb));
969 assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
970 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur])) {
971 /*
972 * Cut out any deleted text.
973 */
974 assert(loc_valid(tb->locs[tbin_mouse], tb));
975 assert(loc_valid(tb->locs[tbin_press], tb));
976 if (tb->locs[tbin_hiCur].tin != tb->locs[tbin_loCur].tin) {
977 result = insert_cr;
978 i = tb->locs[tbin_hiCur].tin + 1 - tb->hiTinBreak;
979 tb->numTins -= i,
980 tb->hiTinBreak += i;
981 }
982 tb->tins[tb->locs[tbin_loCur].tin].len = tb->locs[tbin_loCur].index +
983 tb->tins[tb->locs[tbin_hiCur].tin].len - tb->locs[tbin_hiCur].index;
984 if (tb->locs[tbin_loCur].tin == tb->locs[tbin_hiCur].tin) {
985 tb->hiBreak += tb->locs[tbin_hiCur].index - tb->locs[tbin_loCur].index;
986 } else
987 tb->hiBreak = tb->tins[tb->locs[tbin_hiCur].tin].start +
988 tb->locs[tbin_hiCur].index;
989 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
990 if (!loc_valid(tb->locs[tbin_mouse], tb))
991 tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
992 if (!loc_valid(tb->locs[tbin_press], tb))
993 tb->locs[tbin_press] = tb->locs[tbin_loCur];
994 }
995 assert(tb->tins[tb->locs[tbin_loCur].tin].start +
996 tb->locs[tbin_loCur].index == tb->loBreak);
997 assert(tb->loTinBreak <= tb->hiTinBreak);
998 assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
999 if (src == NULL) {
1000 adjustBreak(tb, break_eoLoCur, 0);
1001 return(insert_bad);
1002 }
1003 len = strlen(src);
1004 /*
1005 * We must use <= in this next condition because we may add an extra \n to
1006 * the buffer at the end.
1007 */
1008 while (tb->hiBreak - tb->loBreak <= len) {
1009 addMoreBufferSpace(tb);
1010 }
1011 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1012 loTinWidthCheck = tinNum_line(tb->locs[tbin_loCur].tin, tb);
1013 for (i = 0; i < len; ++i) {
1014 assert(tb->loTinBreak <= tb->hiTinBreak);
1015 assert(tb->tins[tb->locs[tbin_loCur].tin].start +
1016 tb->locs[tbin_loCur].index == tb->loBreak);
1017 tb->buf[tb->loBreak++] = src[i];
1018 if (src[i] == '\n') {
1019 /*
1020 * If the only line left is the mandatory blank line at the end, then
1021 * don't bother inserting the final '\n'.
1022 */
1023 if ((i + 1 == len) &&
1024 (tinNum_next(tb->locs[tbin_loCur].tin, tb) ==
1025 tinNum_prev(tb->maxTins, tb)) &&
1026 (tb->locs[tbin_loCur].index ==
1027 tb->tins[tb->locs[tbin_loCur].tin].len)) {
1028 --tb->loBreak;
1029 tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
1030 tb->locs[tbin_loCur].index = 0;
1031 break;
1032 }
1033 result = insert_cr;
1034 if (tb->numTins == tb->maxTins)
1035 addMoreTins(tb);
1036 tb->tins[tb->locs[tbin_loCur].tin + 1].start = tb->loBreak;
1037 tb->tins[tb->locs[tbin_loCur].tin + 1].len =
1038 tb->tins[tb->locs[tbin_loCur].tin].len - tb->locs[tbin_loCur].index;
1039 tb->tins[tb->locs[tbin_loCur].tin].len = tb->locs[tbin_loCur].index;
1040 ++tb->numTins;
1041 ++tb->loTinBreak;
1042 ++tb->locs[tbin_loCur].tin;
1043 tb->locs[tbin_loCur].index = 0;
1044 } else {
1045 ++tb->locs[tbin_loCur].index;
1046 ++tb->tins[tb->locs[tbin_loCur].tin].len;
1047 }
1048 }
1049 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1050 assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
1051 if ((tb->hiBreak == tb->bufLen) && (tb->tins[tb->loTinBreak - 1].len > 0) &&
1052 !tb->readOnly) {
1053 /*
1054 * Add an extra '\n' to make sure that there's always a blank line at
1055 * the end of the buffer.
1056 */
1057 tb->buf[tb->loBreak++] = '\n';
1058 result = insert_cr;
1059 if (tb->numTins == tb->maxTins)
1060 addMoreTins(tb);
1061 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1062 tb->tins[tb->locs[tbin_loCur].tin + 1].start = tb->loBreak;
1063 tb->tins[tb->locs[tbin_loCur].tin + 1].len = 0;
1064 tb->tins[tb->locs[tbin_loCur].tin + 1].width = 0;
1065 ++tb->numTins;
1066 ++tb->loTinBreak;
1067 }
1068 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1069 adjustBreak(tb, break_eoLoCur, 0);
1070 assert(loc_valid(tb->locs[tbin_loCur], tb));
1071 assert(loc_valid(tb->locs[tbin_hiCur], tb));
1072 assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
1073 assert(tb->hiBreak <= tb->bufLen);
1074 assert(tb->loBreak <= tb->bufLen);
1075 assert(tb->hiBreak >= 0);
1076 assert(tb->loBreak >= 0);
1077 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1078 /*
1079 * Now that the text is inserted, it is time to adjust the EOLs if we have
1080 * to.
1081 */
1082 tryJoinLines(tb, tinNum_line2TinNum(loTinWidthCheck, tb), env, butW);
1083 lineBroken = TRUE;
1084 for (i = tinNum_line2TinNum(loTinWidthCheck, tb);
1085 (i < tb->maxTins) && ((i <= tb->locs[tbin_loCur].tin) || lineBroken);
1086 i = tinNum_next(i, tb)) {
1087 if (calcTinWidth(&tb->tins[i], tb->buf, env) > butW) {
1088 lineBroken = TRUE;
1089 breakLine(tb, i, env, butW);
1090 result = insert_cr;
1091 }
1092 }
1093 if (tryJoinLines(tb, tinNum_next(tb->locs[tbin_loCur].tin, tb), env, butW))
1094 result = insert_cr;
1095 if ((tb->maxLines > 0) && (tb->numTins > tb->maxLines)) {
1096 killTopLines(tb, tb->numTins - tb->maxLines);
1097 }
1098 return(result);
1099 }
1100
1101
1102 /*
1103 * This routine attempts to join the line that you specify with the
1104 * previous line.
1105 * You cannot call this unless the current break is in the middle of neither
1106 * of these lines. It is OK to call this if the current break is in between
1107 * these lines.
1108 * The breakpoint may be changed while in this function.
1109 */
tryJoinLines(Tbin * tb,int tinNum,ButEnv * env,int butW)1110 static bool tryJoinLines(Tbin *tb, int tinNum, ButEnv *env, int butW) {
1111 int prevTinNum, w, curChar;
1112 Tin *a, *b;
1113 XFontStruct *fs = env->fonts[0];
1114 TbinLoc loc;
1115
1116 if ((tinNum == 0) || (tinNum == tb->maxTins))
1117 return(FALSE);
1118 prevTinNum = tinNum_prev(tinNum, tb);
1119 a = &tb->tins[prevTinNum];
1120 b = &tb->tins[tinNum];
1121 if (tinNum == tb->hiTinBreak) {
1122 if (b->start != tb->hiBreak)
1123 return(FALSE);
1124 } else {
1125 if (b->start != a->start + a->len)
1126 return(FALSE);
1127 }
1128 w = a->width;
1129 curChar = a->len - 1;
1130 while ((curChar >= 0) && (tb->buf[a->start + curChar] == ' ')) {
1131 --curChar;
1132 if (fs->per_char == NULL)
1133 w += fs->min_bounds.width;
1134 else
1135 w += fs->per_char[(uchar)tb->buf[a->start + curChar] -
1136 fs->min_char_or_byte2].width;
1137 }
1138 curChar = 0;
1139 while ((curChar < b->len) && (w <= butW)) {
1140 if (fs->per_char == NULL)
1141 w += fs->min_bounds.width;
1142 else
1143 w += fs->per_char[(uchar)tb->buf[b->start + curChar] -
1144 fs->min_char_or_byte2].width;
1145 ++curChar;
1146 }
1147 if (w > butW) {
1148 if (curChar) {
1149 --curChar;
1150 while (curChar && (tb->buf[b->start + curChar] != ' '))
1151 --curChar;
1152 while ((curChar < b->len) &&
1153 (tb->buf[b->start + curChar] == ' '))
1154 ++curChar;
1155 }
1156 }
1157 if (!curChar)
1158 return(FALSE);
1159 if (curChar == b->len) {
1160 /* We can suck the whole damn line up. */
1161 adjustBreak(tb, break_eol, tinNum);
1162 tinNum = tb->loTinBreak - 1;
1163 tbinLoc_iter(loc) {
1164 if (tb->locs[loc].tin == tinNum) {
1165 tb->locs[loc].index += tb->tins[tinNum - 1].len;
1166 --tb->locs[loc].tin;
1167 }
1168 }
1169 tb->tins[tinNum - 1].len += tb->tins[tinNum].len;
1170 calcTinWidth(&tb->tins[tinNum - 1], tb->buf, env);
1171 --tb->numTins;
1172 --tb->loTinBreak;
1173 assert((tb->loTinBreak == 0) ||
1174 (tb->tins[tb->loTinBreak - 1].start < tb->loBreak));
1175 } else {
1176 tbinLoc_iter(loc) {
1177 if (tb->locs[loc].tin == tinNum) {
1178 tb->locs[loc].index -= curChar;
1179 if (tb->locs[loc].index <= 0) {
1180 tb->locs[loc].tin = prevTinNum;
1181 tb->locs[loc].index += a->len + curChar;
1182 }
1183 }
1184 }
1185 a->len += curChar;
1186 b->len -= curChar;
1187 b->start += curChar;
1188 if (tb->hiTinBreak == tinNum) {
1189 adjustBreak(tb, break_eol, prevTinNum);
1190 calcTinWidth(&tb->tins[tb->loTinBreak - 1], tb->buf, env);
1191 calcTinWidth(&tb->tins[tb->hiTinBreak], tb->buf, env);
1192 }
1193 assert((tb->loTinBreak == 0) ||
1194 (tb->tins[tb->loTinBreak - 1].start < tb->loBreak));
1195 }
1196 return(TRUE);
1197 }
1198
1199
breakLine(Tbin * tb,int tinNum,ButEnv * env,int butW)1200 static void breakLine(Tbin *tb, int tinNum, ButEnv *env, int butW) {
1201 XFontStruct *fs = env->fonts[0];
1202 int breakPoint, w;
1203 TbinLoc loc;
1204
1205 assert(tinNum < tb->maxTins);
1206 assert((tinNum < tb->loTinBreak) || (tinNum >= tb->hiTinBreak));
1207 if (butW <= 0) {
1208 return;
1209 }
1210 assert(loc_valid(tb->locs[tbin_loCur], tb));
1211 adjustBreak(tb, break_eol, tinNum);
1212 /*
1213 * I really should make my mark into a loc, but I'll cheese out instead.
1214 */
1215 tinNum = tb->loTinBreak - 1;
1216 assert(loc_valid(tb->locs[tbin_loCur], tb));
1217 breakPoint = tb->tins[tinNum].len;
1218 w = tb->tins[tinNum].width;
1219 assert(w > butW);
1220 while (tb->buf[tb->tins[tinNum].start + breakPoint - 1] == ' ') {
1221 --breakPoint;
1222 assert(breakPoint > 0);
1223 }
1224 while (breakPoint > 0) {
1225 if ((tb->buf[tb->tins[tinNum].start + breakPoint] == ' ') &&
1226 (tb->buf[tb->tins[tinNum].start + breakPoint - 1] != ' ') &&
1227 (w <= butW))
1228 break;
1229 --breakPoint;
1230 if (fs->per_char == NULL)
1231 w -= fs->min_bounds.width;
1232 else
1233 w -= fs->per_char[(uchar)tb->buf[tb->tins[tinNum].start + breakPoint] -
1234 fs->min_char_or_byte2].width;
1235 }
1236 if (breakPoint == 0) {
1237 adjustBreak(tb, break_eoLoCur, 0);
1238 return;
1239 }
1240 while (tb->buf[tb->tins[tinNum].start + breakPoint] == ' ')
1241 ++breakPoint;
1242 if (tb->numTins == tb->maxTins)
1243 addMoreTins(tb);
1244 assert(tb->loTinBreak == tinNum + 1);
1245 if ((tb->hiTinBreak < tb->maxTins) &&
1246 (tb->tins[tb->hiTinBreak].start == tb->hiBreak)) {
1247 /*
1248 * There is no '\n' here, so we can just shuffle the too-long characters
1249 * down to the next line.
1250 */
1251 tb->tins[tb->hiTinBreak].len += tb->tins[tinNum].len - breakPoint;
1252 tb->tins[tb->hiTinBreak].start = tb->tins[tinNum].start + breakPoint;
1253 tb->tins[tb->hiTinBreak].width +=
1254 XTextWidth(env->fonts[0], tb->buf + tb->tins[tb->hiTinBreak].start,
1255 tb->tins[tinNum].len - breakPoint);
1256 tb->tins[tb->loTinBreak] = tb->tins[tb->hiTinBreak];
1257 ++tb->loTinBreak;
1258 ++tb->hiTinBreak;
1259 } else {
1260 tb->tins[tinNum + 1].start = tb->tins[tinNum].start + breakPoint;
1261 tb->tins[tinNum + 1].len = tb->tins[tinNum].len - breakPoint;
1262 ++tb->loTinBreak;
1263 ++tb->numTins;
1264 }
1265 tb->tins[tinNum].len = breakPoint;
1266 tb->tins[tinNum].width = w;
1267 tbinLoc_iter(loc) {
1268 if ((tb->locs[loc].tin == tinNum) &&
1269 (tb->locs[loc].index > tb->tins[tinNum].len)) {
1270 tb->locs[loc].index -= tb->tins[tinNum].len;
1271 tb->locs[loc].tin = tinNum_next(tb->locs[loc].tin, tb);
1272 }
1273 }
1274 ++tinNum;
1275 calcTinWidth(&tb->tins[tinNum], tb->buf, env);
1276 assert(loc_valid(tb->locs[tbin_loCur], tb));
1277 adjustBreak(tb, break_eoLoCur, 0);
1278 assert(loc_valid(tb->locs[tbin_loCur], tb));
1279 }
1280
1281
ti_cursor(But * but)1282 static void ti_cursor(But *but) {
1283 Tbin *tb = but->iPacket;
1284 Tin *ti = &tb->tins[tb->locs[tbin_loCur].tin];
1285 ButWin *win = but->win;
1286 ButEnv *env = win->env;
1287 XFontStruct *fs = env->fonts[0];
1288 int x, y;
1289 int rw, rh;
1290
1291 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]) || !tb->cursorVisible)
1292 return;
1293 x = but->x + XTextWidth(fs, tb->buf + ti->start,
1294 tb->locs[tbin_loCur].index) + tb->lMargin;
1295 y = but->y + tinNum_line(tb->locs[tbin_loCur].tin, tb) * (fs->ascent + fs->descent);
1296 if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
1297 rw = 1;
1298 x -= rw/2;
1299 if (x < but->x) {
1300 rw -= but->x - x;
1301 x = but->x;
1302 } else if (x+rw > but->x + but->w) {
1303 rw = but->x + but->w - x;
1304 }
1305 rh = fs->ascent + fs->descent;
1306 if (y + rh > but->y + but->h)
1307 rh = but->y + but->h - y;
1308 if ((rw <= 0) || (rh <= 0))
1309 return;
1310 butEnv_setXFg(env, BUT_FG);
1311 XFillRectangle(env->dpy, win->win, env->gc, x-win->xOff, y-win->yOff,
1312 rw,rh);
1313 }
1314
1315
ti_redrawCursor(But * but)1316 static void ti_redrawCursor(But *but) {
1317 Tbin *tb = but->iPacket;
1318 Tin *ti = &tb->tins[tb->locs[tbin_loCur].tin];
1319 ButWin *win = but->win;
1320 ButEnv *env = win->env;
1321 XFontStruct *fs = env->fonts[0];
1322 int fontH = fs->ascent + fs->descent;
1323 int x, y;
1324 int rw;
1325
1326 if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur])) {
1327 return;
1328 }
1329 x = but->x + XTextWidth(fs, tb->buf + ti->start,
1330 tb->locs[tbin_loCur].index) + tb->lMargin;
1331 y = but->y + tinNum_line(tb->locs[tbin_loCur].tin, tb) * fontH;
1332 if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
1333 rw = 1;
1334 x -= rw/2;
1335 if (x < but->x) {
1336 rw -= (but->x - x);
1337 x = but->x;
1338 } else if (x+rw > but->x + but->w) {
1339 rw = but->x + but->w - x;
1340 }
1341 if (rw <= 0)
1342 return;
1343 butWin_redraw(win, x,y, rw,fs->ascent + fs->descent);
1344 }
1345
1346
cut(ButEnv * env,But * but)1347 static void cut(ButEnv *env, But *but) {
1348 Tbin *tbin = but->iPacket;
1349 int storeLen;
1350
1351 adjustBreak(tbin, break_loCur, 0);
1352 if (tbin->locs[tbin_loCur].tin == tbin->locs[tbin_hiCur].tin)
1353 storeLen = tbin->locs[tbin_hiCur].index - tbin->locs[tbin_loCur].index;
1354 else
1355 storeLen = tbin->locs[tbin_hiCur].index + tbin->tins[tbin->locs[tbin_hiCur].tin].start -
1356 tbin->hiBreak;
1357 XStoreBytes(env->dpy, tbin->buf + tbin->hiBreak, storeLen);
1358 adjustBreak(tbin, break_eoLoCur, 0);
1359 if ((env->sReq == NULL) ||
1360 ((env->sReq == sreq) && (cut_butnum == but)))
1361 XSetSelectionOwner(env->dpy, XA_PRIMARY, but->win->physWin, 0);
1362 else
1363 env->sClear(env);
1364 if (XGetSelectionOwner(env->dpy, XA_PRIMARY) == but->win->physWin) {
1365 env->sReq = sreq;
1366 env->sClear = sclear;
1367 cut_butnum = but;
1368 }
1369 }
1370
1371
sreq(ButEnv * env,XSelectionRequestEvent * xsre)1372 static bool sreq(ButEnv *env, XSelectionRequestEvent *xsre) {
1373 Tbin *tbin;
1374 int storeLen;
1375
1376 if (xsre->target != XA_STRING)
1377 return(FALSE);
1378 tbin = cut_butnum->iPacket;
1379 adjustBreak(tbin, break_loCur, 0);
1380 if (tbin->locs[tbin_loCur].tin == tbin->locs[tbin_hiCur].tin)
1381 storeLen = tbin->locs[tbin_hiCur].index - tbin->locs[tbin_loCur].index;
1382 else
1383 storeLen = tbin->locs[tbin_hiCur].index + tbin->tins[tbin->locs[tbin_hiCur].tin].start -
1384 tbin->hiBreak;
1385 XChangeProperty(env->dpy, xsre->requestor, xsre->property, xsre->target,
1386 8, PropModeReplace, tbin->buf + tbin->hiBreak, storeLen);
1387 adjustBreak(tbin, break_eoLoCur, 0);
1388 return(TRUE);
1389 }
1390
1391
sclear(ButEnv * env)1392 static int sclear(ButEnv *env) {
1393 Tbin *tbin = cut_butnum->iPacket;
1394
1395 tbin->locs[tbin_hiCur] = tbin->locs[tbin_loCur];
1396 but_draw(cut_butnum);
1397 env->sReq = NULL;
1398 env->sClear = NULL;
1399 return(0);
1400 }
1401
1402
paste(ButEnv * env,But * but)1403 static void paste(ButEnv *env, But *but) {
1404 ButWin *topWin;
1405
1406 paste_butnum = but;
1407 env->sNotify = snotify;
1408 topWin = but->win;
1409 while (topWin->parent != NULL)
1410 topWin = topWin->parent;
1411 XConvertSelection(env->dpy, XA_PRIMARY, XA_STRING, env->prop,
1412 topWin->physWin, 0);
1413 }
1414
1415
snotify(ButEnv * env,XSelectionEvent * xsnot)1416 static int snotify(ButEnv *env, XSelectionEvent *xsnot) {
1417 unsigned char *propbuf;
1418 unsigned long proplen, propleft;
1419 int blen;
1420 int pformat;
1421 Atom ptype;
1422 Tbin *tbin;
1423 char *pb2;
1424
1425 tbin = paste_butnum->iPacket;
1426 if (xsnot->property != None) {
1427 XGetWindowProperty(env->dpy, xsnot->requestor,
1428 xsnot->property, 0, MAX_PASTE, True, AnyPropertyType,
1429 &ptype, &pformat, &proplen, &propleft,
1430 &propbuf);
1431 tbin->locs[tbin_loCur] = tbin->locs[tbin_hiCur] = tbin->locs[tbin_mouse];
1432 if (insert(env, (char *)propbuf, tbin, paste_butnum->w, NULL) !=
1433 insert_bad)
1434 but_draw(paste_butnum);
1435 else
1436 XBell(env->dpy, 0);
1437 XFree(propbuf);
1438 } else {
1439 tbin->locs[tbin_loCur] = tbin->locs[tbin_hiCur] = tbin->locs[tbin_mouse];
1440 propbuf = (unsigned char *)XFetchBytes(env->dpy, &blen);
1441 if (blen > 0) {
1442 pb2 = (char *)wms_malloc(blen + 1);
1443 pb2[blen] = '\0';
1444 memcpy(pb2, propbuf, blen);
1445 XFree(propbuf);
1446 if (insert(env, pb2, tbin, paste_butnum->w, NULL))
1447 but_draw(paste_butnum);
1448 else
1449 XBell(env->dpy, 0);
1450 wms_free(pb2);
1451 } else
1452 XBell(env->dpy, 0);
1453 }
1454 env->sNotify = NULL;
1455 return(0);
1456 }
1457
1458
1459 /*
1460 * This adjusts your loc and your cutEnd for when you're in word select mode.
1461 */
wordsel_adjust(Tbin * tbin)1462 static void wordsel_adjust(Tbin *tbin) {
1463 char *atp, *btp;
1464 int a, b, at, bt;
1465
1466 a = tbin->locs[tbin_loCur].index;
1467 at = tbin->locs[tbin_loCur].tin;
1468 b = tbin->locs[tbin_hiCur].index;
1469 bt = tbin->locs[tbin_hiCur].tin;
1470 if ((at >= 0) && (at < tbin->numTins))
1471 atp = tbin->buf + tbin->tins[at].start;
1472 else
1473 atp = NULL;
1474 if ((bt >= 0) && (bt < tbin->numTins))
1475 btp = tbin->buf + tbin->tins[bt].start;
1476 else
1477 btp = NULL;
1478 if (atp) {
1479 for (;;) {
1480 if (a <= 0) {
1481 a = 0;
1482 break;
1483 }
1484 if (((isalnum(atp[a]) || (atp[a] == '_')) &&
1485 (isalnum(atp[a-1]) || (atp[a-1] == '_'))) ||
1486 (atp[a] == atp[a-1]) ||
1487 (isdigit(atp[a]) && (atp[a-1] == '.') &&
1488 (a >= 2) && isdigit(atp[a-2])) ||
1489 (isdigit(atp[a+1]) && (atp[a] == '.') && isdigit(atp[a-1])))
1490 --a;
1491 else
1492 break;
1493 }
1494 }
1495 if (btp && (b > 0)) {
1496 for (;;) {
1497 if (b >= tbin->tins[bt].len) {
1498 b = tbin->tins[bt].len;
1499 break;
1500 }
1501 if (((isalnum(btp[b-1]) || (btp[b-1] == '_')) &&
1502 (isalnum(btp[b]) || (btp[b] == '_'))) || (btp[b-1] == btp[b]) ||
1503 (isdigit(btp[b-1]) && (btp[b] == '.') && isdigit(btp[b+1])) ||
1504 ((b >= 2) && isdigit(btp[b-2]) && (btp[b-1] == '.') &&
1505 isdigit(btp[b])))
1506 ++b;
1507 else
1508 break;
1509 }
1510 }
1511 tbin->locs[tbin_loCur].index = a;
1512 tbin->locs[tbin_hiCur].index = b;
1513 }
1514
1515
1516 /* Returns the index of the character just to the right of the cursor. */
locateMouse(XFontStruct * fs,const char * text,int textLen,int x,bool * rightSide)1517 static int locateMouse(XFontStruct *fs, const char *text, int textLen, int x,
1518 bool *rightSide) {
1519 int i;
1520 int prev_x = x;
1521 int spaceWidth;
1522 bool dummy;
1523
1524 if (rightSide == NULL)
1525 rightSide = &dummy;
1526 for (i = 0; (x > 0) && (i < textLen); ++i) {
1527 prev_x = x;
1528 if (fs->per_char == NULL)
1529 /* Monospace font. */
1530 x -= fs->min_bounds.width;
1531 else
1532 x -= fs->per_char[(uchar)text[i] - fs->min_char_or_byte2].width;
1533 }
1534 if (fs->per_char == NULL)
1535 spaceWidth = fs->min_bounds.width;
1536 else
1537 spaceWidth = fs->per_char[' ' - fs->min_char_or_byte2].width;
1538 if (x >= spaceWidth)
1539 *rightSide = TRUE;
1540 else if ((*rightSide = ((i > 0) && (x < 0) && (prev_x < -x))))
1541 --i;
1542 return(i);
1543 }
1544
1545
flags(But * but,uint flags)1546 static void flags(But *but, uint flags) {
1547 Tbin *tb = but->iPacket;
1548
1549 if ((but->flags & BUT_KEYED) != (flags & BUT_KEYED)) {
1550 if (flags & BUT_KEYED) {
1551 enableCTimer(but);
1552 tb->cursorVisible = TRUE;
1553 } else {
1554 tb->cursorVisible = FALSE;
1555 disableCTimer(but);
1556 }
1557 }
1558 assert((but->flags & BUT_PRESSABLE) || !(but->flags & BUT_KEYED));
1559 but->flags = flags;
1560 but_draw(but);
1561 }
1562
1563
blinkCursor(ButTimer * timer)1564 static ButOut blinkCursor(ButTimer *timer) {
1565 But *but = butTimer_packet(timer);
1566 Tbin *tb = but->iPacket;
1567
1568 tb->cursorVisible = !(butTimer_ticks(timer) & 1);
1569 ti_redrawCursor(but);
1570 return(0);
1571 }
1572
1573
enableCTimer(But * but)1574 static void enableCTimer(But *but) {
1575 Tbin *tb = but->iPacket;
1576 struct timeval halfSec;
1577
1578 assert(tb->cTimer == NULL);
1579 halfSec.tv_sec = 0;
1580 halfSec.tv_usec = 500000;
1581 tb->cTimer = butTimer_create(but, but, halfSec, halfSec, TRUE,
1582 blinkCursor);
1583 }
1584
1585
disableCTimer(But * but)1586 static void disableCTimer(But *but) {
1587 Tbin *tb = but->iPacket;
1588
1589 if (tb->cTimer != NULL) {
1590 butTimer_destroy(tb->cTimer);
1591 tb->cTimer = NULL;
1592 }
1593 }
1594
1595
butTbin_set(But * but,const char * newStr)1596 void butTbin_set(But *but, const char *newStr) {
1597 Tbin *tb = but->iPacket;
1598
1599 assert(MAGIC(but));
1600 assert(but->action == &action);
1601 if (but->flags & BUT_KEYED)
1602 but_setFlags(but, BUT_NOKEY);
1603 tb->numTins = 1;
1604 tb->loTinBreak = 1;
1605 tb->hiTinBreak = tb->maxTins;
1606 tb->locs[tbin_loCur].index = 0;
1607 tb->locs[tbin_loCur].tin = 0;
1608 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1609 tb->tins[0].len = 0;
1610 tb->tins[0].start = 0;
1611 tb->loBreak = 0;
1612 tb->hiBreak = tb->bufLen;
1613 insert(but->win->env, newStr, tb, but->w, NULL);
1614 but_draw(but);
1615 }
1616
1617
calcTinNum(But * but,Tbin * tbin,int y)1618 static int calcTinNum(But *but, Tbin *tbin, int y) {
1619 int n;
1620
1621 if (y < but->y)
1622 return(0);
1623 n = (y - but->y) / butEnv_fontH(but->win->env, 0);
1624 if (n >= tbin->loTinBreak)
1625 n += tbin->hiTinBreak - tbin->loTinBreak;
1626 if (n >= tbin->maxTins)
1627 n = tinNum_prev(tbin->maxTins, tbin);
1628 return(n);
1629 }
1630
1631
setMouseX(But * but,Tbin * tbin,int tin,int loc)1632 static void setMouseX(But *but, Tbin *tbin, int tin, int loc) {
1633 assert((tin >= 0) && (tin < tbin->maxTins));
1634 assert((loc >= 0) && (loc <= tbin->tins[tin].len));
1635 tbin->mouseX = XTextWidth(but->win->env->fonts[0],
1636 tbin->buf + tbin->tins[tin].start, loc);
1637 }
1638
1639
checkOffWin(But * but,int mouseY,bool force)1640 static void checkOffWin(But *but, int mouseY, bool force) {
1641 ButWin *win = but->win;
1642 Tbin *tbin = but->iPacket;
1643 int y, h, cy, ch, maxCy, loLineNum, hiLineNum;
1644
1645 assert(MAGIC(but));
1646 assert(MAGIC(tbin));
1647 assert(tbin->maxTins == tbin->numTins +
1648 tbin->hiTinBreak - tbin->loTinBreak);
1649 assert(loc_valid(tbin->locs[tbin_loCur], tbin));
1650 assert(loc_valid(tbin->locs[tbin_hiCur], tbin));
1651 if (!loc_eq(tbin->locs[tbin_loCur], tbin->locs[tbin_hiCur]) &&
1652 !tbin->clicks) {
1653 /*
1654 * The text is selected, but no mouse is moving around. Don't scroll
1655 * around!
1656 * Perhaps we should move around in this case if text is inserted or
1657 * something. Tough call, but probably we shouldn't since inserting
1658 * text will (of necessity) move around the cutEnd and the loc.
1659 */
1660 return;
1661 }
1662 if (tbin->offWinCallback) {
1663 y = win->yOff;
1664 h = win->h;
1665 ch = butEnv_fontH(win->env, 0);
1666 if (tbin->clicks) {
1667 assert(loc_valid(tbin->locs[tbin_mouse], tbin));
1668 cy = loLineNum = tinNum_line(tbin->locs[tbin_mouse].tin, tbin);
1669 hiLineNum = tinNum_line(tbin->locs[tbin_press].tin, tbin);
1670 } else {
1671 cy = loLineNum = hiLineNum = tinNum_line(tbin->locs[tbin_loCur].tin,
1672 tbin);
1673 mouseY = 0;
1674 }
1675 cy = but->y + cy * ch;
1676 maxCy = tbin->numTins * ch;
1677 if (force || (cy < y) || (cy + ch > y + h) || (maxCy + ch > but->h)) {
1678 tbin->offWinCallback(but, loLineNum, hiLineNum, mouseY);
1679 }
1680 }
1681 }
1682
1683
1684 /**
1685 * Adjust the breakpoint of the text field.
1686 *
1687 * Parameters:
1688 * tb - The tbin being adjusted.
1689 * bType = Ony of:
1690 * break_loCur (move break to the lo cursor)
1691 * break_eoLoCur (move break to end of the line containing the lo cursor)
1692 * break_eol (move break to the end of line breakTin).
1693 * breakTin - Used in break_eol. Zero otherwise.
1694 */
adjustBreak(Tbin * tb,BreakType bType,int breakTin)1695 static void adjustBreak(Tbin *tb, BreakType bType, int breakTin) {
1696 int newLoBreak, breakIndex = 0, newHiBreak;
1697 int origHiTinBreak, origLoTinBreak;
1698 int breakDiff;
1699 TbinLoc loc;
1700
1701 assert(tb->hiBreak <= tb->bufLen);
1702 assert(tb->loBreak <= tb->bufLen);
1703 assert(tb->hiBreak >= 0);
1704 assert(tb->loBreak >= 0);
1705 switch(bType) {
1706 case break_loCur:
1707 assert(breakTin == 0);
1708 breakTin = tb->locs[tbin_loCur].tin;
1709 breakIndex = tb->locs[tbin_loCur].index;
1710 break;
1711 case break_eoLoCur:
1712 assert(breakTin == 0);
1713 breakTin = tb->locs[tbin_loCur].tin;
1714 breakIndex = tb->tins[tb->locs[tbin_loCur].tin].len;
1715 break;
1716 case break_eol:
1717 assert(breakTin < tb->maxTins);
1718 assert((breakTin < tb->loTinBreak) || (breakTin >= tb->hiTinBreak));
1719 breakIndex = tb->tins[breakTin].len;
1720 break;
1721 }
1722 newLoBreak = tb->tins[breakTin].start;
1723 assert(newLoBreak >= 0);
1724 if (newLoBreak > tb->loBreak) {
1725 assert(newLoBreak >= tb->hiBreak);
1726 newLoBreak -= tb->hiBreak - tb->loBreak;
1727 }
1728 newLoBreak += breakIndex;
1729 assert(newLoBreak >= 0);
1730 assert(newLoBreak >= 0);
1731 assert(newLoBreak <= tb->loBreak + tb->bufLen - tb->hiBreak);
1732 breakDiff = tb->hiBreak - tb->loBreak;
1733 newHiBreak = newLoBreak + breakDiff;
1734 assert(newHiBreak <= tb->bufLen);
1735 if (newLoBreak < tb->loBreak) {
1736 memmove(tb->buf + newHiBreak, tb->buf + newLoBreak,
1737 tb->loBreak - newLoBreak);
1738 origHiTinBreak = tb->hiTinBreak;
1739 while (tb->loTinBreak > breakTin + 1) {
1740 --tb->loTinBreak;
1741 --tb->hiTinBreak;
1742 assert(tb->tins[tb->loTinBreak].start <= tb->loBreak);
1743 tb->tins[tb->hiTinBreak] = tb->tins[tb->loTinBreak];
1744 tb->tins[tb->hiTinBreak].start += tb->hiBreak - tb->loBreak;
1745 if (breakTin == tb->loTinBreak)
1746 breakTin = tb->hiTinBreak;
1747 assert(tb->tins[tb->loTinBreak].start <= tb->bufLen);
1748 }
1749 assert(newLoBreak >= 0);
1750 tb->loBreak = newLoBreak;
1751 tb->hiBreak = newHiBreak;
1752 tbinLoc_iter(loc) {
1753 if ((tb->locs[loc].tin < origHiTinBreak) &&
1754 (tb->locs[loc].tin >= tb->loTinBreak))
1755 tb->locs[loc].tin += tb->hiTinBreak - tb->loTinBreak;
1756 }
1757 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1758 assert((bType != break_loCur) ||
1759 (tb->tins[tb->locs[tbin_loCur].tin].start +
1760 tb->locs[tbin_loCur].index == tb->loBreak));
1761 } else /* newLoBreak >= tb->loBreak */ {
1762 if (newLoBreak != tb->loBreak)
1763 memmove(tb->buf + tb->loBreak, tb->buf + tb->hiBreak,
1764 newLoBreak - tb->loBreak);
1765 origLoTinBreak = tb->loTinBreak;
1766 while (tb->hiTinBreak <= breakTin) {
1767 assert(tb->tins[tb->hiTinBreak].start >= tb->hiBreak);
1768 tb->tins[tb->loTinBreak] = tb->tins[tb->hiTinBreak];
1769 tb->tins[tb->loTinBreak].start -= tb->hiBreak - tb->loBreak;
1770 if (breakTin == tb->hiTinBreak)
1771 breakTin = tb->loTinBreak;
1772 assert(tb->tins[tb->loTinBreak].start >= 0);
1773 ++tb->loTinBreak;
1774 ++tb->hiTinBreak;
1775 }
1776 assert(newLoBreak >= 0);
1777 tb->loBreak = newLoBreak;
1778 tb->hiBreak = newHiBreak;
1779 tbinLoc_iter(loc) {
1780 if ((tb->locs[loc].tin >= origLoTinBreak) &&
1781 (tb->locs[loc].tin < tb->hiTinBreak))
1782 tb->locs[loc].tin -= tb->hiTinBreak - tb->loTinBreak;
1783 }
1784 assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1785 assert((bType != break_loCur) ||
1786 (tb->tins[tb->locs[tbin_loCur].tin].start +
1787 tb->locs[tbin_loCur].index == tb->loBreak));
1788 }
1789 assert(tb->hiBreak <= tb->bufLen);
1790 assert(tb->loBreak <= tb->bufLen);
1791 assert(tb->hiBreak >= 0);
1792 assert(tb->loBreak >= 0);
1793 }
1794
1795
addMoreBufferSpace(Tbin * tb)1796 static void addMoreBufferSpace(Tbin *tb) {
1797 char *newBuf;
1798 int newLen, i;
1799 int newHiBreak;
1800
1801 newLen = tb->bufLen * 2;
1802 newBuf = wms_malloc(newLen);
1803 newHiBreak = tb->hiBreak + newLen - tb->bufLen;
1804 memcpy(newBuf, tb->buf, tb->loBreak);
1805 memcpy(newBuf + newHiBreak, tb->buf + tb->hiBreak, newLen - newHiBreak);
1806 for (i = tb->hiTinBreak; i < tb->maxTins; ++i) {
1807 tb->tins[i].start += newLen - tb->bufLen;
1808 }
1809 wms_free(tb->buf);
1810 tb->buf = newBuf;
1811 tb->hiBreak = newHiBreak;
1812 tb->bufLen = newLen;
1813 }
1814
1815
addMoreTins(Tbin * tb)1816 static void addMoreTins(Tbin *tb) {
1817 Tin *newTins;
1818 int newMaxTins, i;
1819 int newHiBreak, tinDiff;
1820
1821 newMaxTins = tb->maxTins * 2;
1822 newTins = wms_malloc(newMaxTins * sizeof(Tin));
1823 newHiBreak = tb->hiTinBreak + newMaxTins - tb->maxTins;
1824 for (i = 0; i < tb->loTinBreak; ++i) {
1825 newTins[i] = tb->tins[i];
1826 }
1827 tinDiff = newMaxTins - tb->maxTins;
1828 for (i = tb->hiTinBreak; i < tb->maxTins; ++i) {
1829 newTins[i + tinDiff] = tb->tins[i];
1830 }
1831 if (tb->locs[tbin_mouse].tin >= tb->loTinBreak)
1832 tb->locs[tbin_mouse].tin += tinDiff;
1833 if (tb->locs[tbin_press].tin >= tb->loTinBreak)
1834 tb->locs[tbin_press].tin += tinDiff;
1835 if (tb->locs[tbin_loCur].tin >= tb->loTinBreak)
1836 tb->locs[tbin_loCur].tin += tinDiff;
1837 if (tb->locs[tbin_hiCur].tin >= tb->loTinBreak)
1838 tb->locs[tbin_hiCur].tin += tinDiff;
1839 wms_free(tb->tins);
1840 tb->tins = newTins;
1841 tb->hiTinBreak = newHiBreak;
1842 tb->maxTins = newMaxTins;
1843 }
1844
1845
calcTinWidth(Tin * tin,const char * buf,ButEnv * env)1846 static int calcTinWidth(Tin *tin, const char *buf, ButEnv *env) {
1847 int textLen;
1848
1849 textLen = tin->len;
1850 while ((textLen > 0) && (buf[tin->start + textLen - 1] == ' '))
1851 --textLen;
1852 tin->width = XTextWidth(env->fonts[0], buf + tin->start, textLen);
1853 return(tin->width);
1854 }
1855
1856
resize(But * but,int oldX,int oldY,int oldW,int oldH)1857 static bool resize(But *but, int oldX, int oldY, int oldW, int oldH) {
1858 Tbin *tb;
1859 int newW, i;
1860
1861 if (oldW != but->w) {
1862 tb = but->iPacket;
1863 newW = but->w - (tb->rMargin + tb->lMargin);
1864 for (i = 0; i < tb->maxTins; i = tinNum_next(i, tb)) {
1865 if (tb->tins[i].width > newW)
1866 breakLine(tb, i, but->win->env, newW);
1867 }
1868 return(TRUE);
1869 } else
1870 return((but->x != oldX) || (but->y != oldY) || (but->h != oldH));
1871 }
1872
1873
killTopLines(Tbin * tb,int lines)1874 static void killTopLines(Tbin *tb, int lines) {
1875 int charsDead, i;
1876 TbinLoc loc;
1877
1878 assert(lines < tb->numTins);
1879 adjustBreak(tb, break_eol, tinNum_prev(tb->maxTins, tb));
1880 charsDead = tb->tins[lines].start;
1881 memmove(tb->buf, tb->buf + charsDead, tb->loBreak - charsDead);
1882 tb->loBreak -= charsDead;
1883 tb->numTins -= lines;
1884 tb->loTinBreak -= lines;
1885 for (i = 0; i < tb->loTinBreak; ++i) {
1886 tb->tins[i].start = tb->tins[i + lines].start - charsDead;
1887 tb->tins[i].len = tb->tins[i + lines].len;
1888 tb->tins[i].width = tb->tins[i + lines].width;
1889 }
1890 tbinLoc_iter(loc) {
1891 tb->locs[loc].tin -= lines;
1892 if (tb->locs[loc].tin < 0) {
1893 tb->locs[loc].tin = 0;
1894 tb->locs[loc].index = 0;
1895 }
1896 }
1897 }
1898
1899
butTbin_insert(But * but,const char * appText)1900 void butTbin_insert(But *but, const char *appText) {
1901 bool saveOldCurs;
1902 Tbin *tb;
1903
1904 assert(MAGIC(but));
1905 tb = but->iPacket;
1906 assert(MAGIC(tb));
1907 saveOldCurs = (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]) ||
1908 (tinNum_next(tb->locs[tbin_loCur].tin, tb) !=
1909 tb->maxTins));
1910 if (saveOldCurs) {
1911 tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
1912 tb->locs[tbin_press] = tb->locs[tbin_hiCur];
1913 tb->locs[tbin_loCur].tin = tinNum_prev(tb->maxTins, tb);
1914 tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
1915 tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1916 }
1917 insert(but->win->env, appText, tb,
1918 but->w - (tb->lMargin + tb->rMargin), NULL);
1919 checkOffWin(but, 0, TRUE);
1920 if (saveOldCurs && !loc_eq(tb->locs[tbin_mouse], tb->locs[tbin_press])) {
1921 tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
1922 tb->locs[tbin_hiCur] = tb->locs[tbin_press];
1923 }
1924 but_draw(but);
1925 }
1926
1927
butTbin_len(But * but)1928 int butTbin_len(But *but) {
1929 Tbin *tb;
1930
1931 assert(MAGIC(but));
1932 tb = but->iPacket;
1933 assert(MAGIC(tb));
1934 return(tb->bufLen - (tb->hiBreak - tb->loBreak));
1935 }
1936
1937
butTbin_setMaxLines(But * but,int maxLines)1938 void butTbin_setMaxLines(But *but, int maxLines) {
1939 Tbin *tb = but->iPacket;
1940
1941 assert(MAGIC(tb));
1942 tb->maxLines = maxLines;
1943 }
1944
1945
butTbin_setReadOnly(But * but,bool ro)1946 void butTbin_setReadOnly(But *but, bool ro) {
1947 Tbin *tb = but->iPacket;
1948
1949 assert(MAGIC(tb));
1950 tb->readOnly = ro;
1951 }
1952
1953
butTbin_delete(But * but,int delStart,int delLen)1954 void butTbin_delete(But *but, int delStart, int delLen) {
1955 Tbin *tb = but->iPacket;
1956
1957 assert(but->action == &action);
1958 assert(MAGIC(tb));
1959 tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
1960 tb->locs[tbin_press] = tb->locs[tbin_hiCur];
1961 setLoc(tb, tbin_loCur, delStart);
1962 setLoc(tb, tbin_hiCur, delStart + delLen);
1963 insert(but->win->env, "", tb, but->w, NULL);
1964 tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
1965 tb->locs[tbin_hiCur] = tb->locs[tbin_press];
1966 }
1967
1968
setLoc(Tbin * tb,TbinLoc locNum,int position)1969 static void setLoc(Tbin *tb, TbinLoc locNum, int position) {
1970 Loc *l = &tb->locs[locNum];
1971 int i;
1972
1973 if (position > tb->loBreak)
1974 position += (tb->hiBreak - tb->loBreak);
1975 i = 0;
1976 while (tb->tins[i].start + tb->tins[i].len < position) {
1977 i = tinNum_next(i, tb);
1978 assert(i < tb->maxTins);
1979 }
1980 l->tin = i;
1981 l->index = position - tb->tins[i].start;
1982 }
1983
1984
1985 #endif /* X11_DISP */
1986