1 /*************************************<+>*************************************
2 *****************************************************************************
3 **
4 ** File: sub.c
5 **
6 ** Project: X Widgets
7 **
8 ** Description: Code for TextEdit widget
9 **
10 *****************************************************************************
11 **
12 ** Copyright (c) 1988 by Hewlett-Packard Company
13 ** Copyright (c) 1987, 1988 by Digital Equipment Corporation, Maynard,
14 ** Massachusetts, and the Massachusetts Institute of Technology,
15 ** Cambridge, Massachusetts
16 **
17 ** Permission to use, copy, modify, and distribute this software
18 ** and its documentation for any purpose and without fee is hereby
19 ** granted, provided that the above copyright notice appear in all
20 ** copies and that both that copyright notice and this permission
21 ** notice appear in supporting documentation, and that the names of
22 ** Hewlett-Packard, Digital or M.I.T. not be used in advertising or
23 ** publicity pertaining to distribution of the software without
24 ** written prior permission.
25 **
26 ** DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 ** ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
28 ** DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
29 ** ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
30 ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
31 ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32 ** SOFTWARE.
33 **
34 *****************************************************************************
35 *************************************<+>*************************************/
36 /*****************************************************************************
37 *
38 * Procedures declared in this file
39 *
40 ******************************************************************************
41 */
42 static void InsertCursor () ;
43 static void _XtTextNeedsUpdating() ;
44 static XwTextPosition PositionForXY () ;
45 static int LineForPosition () ;
46 static int LineAndXYForPosition () ;
47 static void BuildLineTable () ;
48 static XwTextLineRange UpdateLineTable () ;
49 static void ForceBuildLineTable() ;
50 static void _XtTextScroll() ;
51 static XwEditResult ReplaceText () ;
52 static void DisplayText() ;
53 static void ClearWindow () ;
54 static void ClearText () ;
55 static void DisplayAllText () ;
56 static void CheckResizeOrOverflow() ;
57 static void ProcessExposeRegion() ;
58 static void _XtTextPrepareToUpdate() ;
59 static void FlushUpdate() ;
60 static void _XtTextShowPosition() ;
61 static void _XtTextExecuteUpdate() ;
62
63 /*
64 * Procedure to manage insert cursor visibility for editable text. It uses
65 * the value of ctx->insertPos and an implicit argument. In the event that
66 * position is immediately preceded by an eol graphic, then the insert cursor
67 * is displayed at the beginning of the next line.
68 */
69 /*--------------------------------------------------------------------------+*/
InsertCursor(ctx,state)70 static void InsertCursor (ctx, state)
71 /*--------------------------------------------------------------------------+*/
72 XwTextEditWidget ctx;
73 XwInsertState state;
74 {
75 Position x, y;
76 int dy, line, visible;
77 XwTextBlock text;
78
79 if (ctx->text.lt.lines < 1) return;
80 visible = LineAndXYForPosition(ctx, ctx->text.insertPos, &line, &x, &y);
81 if (line < ctx->text.lt.lines)
82 dy = (ctx->text.lt.info[line + 1].y - ctx->text.lt.info[line].y) + 1;
83 else
84 dy = (ctx->text.lt.info[line].y - ctx->text.lt.info[line - 1].y) + 1;
85
86 /** If the insert position is just after eol then put it on next line **/
87 if (x > ctx->text.leftmargin &&
88 ctx->text.insertPos > 0 &&
89 ctx->text.insertPos >= GETLASTPOS(ctx)) {
90 /* reading the source is bogus and this code should use scan */
91 (*(ctx->text.source->read)) (ctx->text.source,
92 ctx->text.insertPos - 1, &text, 1);
93 if (text.ptr[0] == '\n') {
94 x = ctx->text.leftmargin;
95 y += dy;
96 }
97 }
98 y += dy;
99 if (visible)
100 (*(ctx->text.sink->insertCursor))(ctx, x, y, state);
101 }
102
103
104 /*
105 * Procedure to register a span of text that is no longer valid on the display
106 * It is used to avoid a number of small, and potentially overlapping, screen
107 * updates. [note: this is really a private procedure but is used in
108 * multiple modules].
109 */
110 /*--------------------------------------------------------------------------+*/
_XtTextNeedsUpdating(ctx,left,right)111 static void _XtTextNeedsUpdating(ctx, left, right)
112 /*--------------------------------------------------------------------------+*/
113 XwTextEditWidget ctx;
114 XwTextPosition left, right;
115 {
116 int i;
117 if (left < right) {
118 for (i = 0; i < ctx->text.numranges; i++) {
119 if (left <= ctx->text.updateTo[i]
120 && right >= ctx->text.updateFrom[i])
121 { ctx->text.updateFrom[i] = min(left, ctx->text.updateFrom[i]);
122 ctx->text.updateTo[i] = max(right, ctx->text.updateTo[i]);
123 return;
124 }
125 }
126 ctx->text.numranges++;
127 if (ctx->text.numranges > ctx->text.maxranges) {
128 ctx->text.maxranges = ctx->text.numranges;
129 i = ctx->text.maxranges * sizeof(XwTextPosition);
130 ctx->text.updateFrom = (XwTextPosition *)
131 XtRealloc((char *)ctx->text.updateFrom, (unsigned) i
132 * sizeof(XwTextPosition));
133 ctx->text.updateTo = (XwTextPosition *)
134 XtRealloc((char *)ctx->text.updateTo, (unsigned) i
135 * sizeof(XwTextPosition));
136 }
137 ctx->text.updateFrom[ctx->text.numranges - 1] = left;
138 ctx->text.updateTo[ctx->text.numranges - 1] = right;
139 }
140 }
141
142
143 /*
144 * This routine maps an x and y position in a window that is displaying text
145 * into the corresponding position in the source.
146 */
147 /*--------------------------------------------------------------------------+*/
PositionForXY(ctx,x,y)148 static XwTextPosition PositionForXY (ctx, x, y)
149 /*--------------------------------------------------------------------------+*/
150 XwTextEditWidget ctx;
151 Position x,y;
152 {
153 /* it is illegal to call this routine unless there is a valid line table! */
154 int width, fromx, line;
155 XwTextPosition position, resultstart, resultend;
156 XwTextPosition lastpos = GETLASTPOS(ctx);
157
158 /*** figure out what line it is on ***/
159 for (line = 0; line < ctx->text.lt.lines - 1; line++) {
160 if (y <= ctx->text.lt.info[line + 1].y)
161 break;
162 }
163 position = ctx->text.lt.info[line].position;
164 if (position >= lastpos)
165 return lastpos;
166 fromx = ctx->text.lt.info[line].x; /* starting x in line */
167 width = x - fromx; /* num of pix from starting of line */
168 (*(ctx->text.sink->resolve)) (ctx, position, fromx, width,
169 &resultstart, &resultend);
170 if (resultstart >= ctx->text.lt.info[line + 1].position)
171 resultstart = (*(ctx->text.source->scan))(ctx->text.source,
172 ctx->text.lt.info[line + 1].position, XwstPositions, XwsdLeft,
173 1, TRUE);
174 return resultstart;
175 }
176
177 /*
178 * This routine maps a source position in to the corresponding line number
179 * of the text that is displayed in the window.
180 */
181 /*--------------------------------------------------------------------------+*/
LineForPosition(ctx,position)182 static int LineForPosition (ctx, position)
183 /*--------------------------------------------------------------------------+*/
184 XwTextEditWidget ctx;
185 XwTextPosition position;
186 /* it is illegal to call this routine unless there is a valid line table!*/
187 {
188 int line;
189
190 if (position <= ctx->text.lt.info[0].position)
191 return 0;
192 for (line = 0; line < ctx->text.lt.lines; line++)
193 if (position < ctx->text.lt.info[line + 1].position)
194 break;
195 return line;
196 }
197
198 /*
199 * This routine maps a source position into the corresponding line number
200 * and the x, y coordinates of the text that is displayed in the window.
201 */
202 /*--------------------------------------------------------------------------+*/
LineAndXYForPosition(ctx,pos,line,x,y)203 static int LineAndXYForPosition (ctx, pos, line, x, y)
204 /*--------------------------------------------------------------------------+*/
205 XwTextEditWidget ctx;
206 XwTextPosition pos;
207 int *line;
208 Position *x, *y;
209 /* it is illegal to call this routine unless there is a valid line table!*/
210 {
211 XwTextPosition linePos, endPos;
212 int visible, realW, realH;
213
214 *line = 0;
215 *x = ctx->text.leftmargin;
216 *y = ctx->text.topmargin;
217 visible = IsPositionVisible(ctx, pos);
218 if (visible) {
219 *line = LineForPosition(ctx, pos);
220 *y = ctx->text.lt.info[*line].y;
221 *x = ctx->text.lt.info[*line].x;
222 linePos = ctx->text.lt.info[*line].position;
223 (*(ctx->text.sink->findDistance))(ctx, linePos,
224 *x, pos, &realW, &endPos, &realH);
225 *x = *x + realW;
226 }
227 return visible;
228 }
229
230 /*
231 * This routine builds a line table. It does this by starting at the
232 * specified position and measuring text to determine the staring position
233 * of each line to be displayed. It also determines and saves in the
234 * linetable all the required metrics for displaying a given line (e.g.
235 * x offset, y offset, line length, etc.).
236 */
237 /*--------------------------------------------------------------------------+*/
BuildLineTable(self,position)238 static void BuildLineTable (self, position)
239 /*--------------------------------------------------------------------------+*/
240 XwTextEditWidget self;
241 XwTextPosition position;
242 {
243 XwTextPosition line, lines;
244 Boolean rebuild;
245 XwLineTableEntryPtr lp ;
246
247 rebuild = (Boolean) (position != self->text.lt.top);
248 lines= applyDisplay(maxLines)(self) ;
249
250 /****************
251 *
252 * RBM
253 *
254 * Don't allow a 0-line widget - let clipping occur
255 *
256 * NOTE: THE MAXINT CLIPPING IS AN UGLY HACK THAT NEEDS TO BE FIXED
257 * WITH A CAST!!
258 ****************/
259 if ((lines < 1) || (lines > 32767)) lines = 1;
260
261 if (self->text.lt.info != NULL && lines != self->text.lt.lines) {
262 XtFree((char *) self->text.lt.info);
263 self->text.lt.info = NULL;
264 }
265 if (self->text.lt.info == NULL)
266 { self->text.lt.info = (XwLineTableEntry *)
267 XtCalloc(lines + 1, (unsigned)sizeof(XwLineTableEntry));
268 self->text.lt.lines = lines ;
269 for (line = 0, lp = &(self->text.lt.info[0]) ;
270 line < lines; line++, lp++)
271 { lp->position = lp->drawPos = 0 ;
272 lp->x = lp->y = 0 ;
273 }
274 rebuild = TRUE;
275 }
276
277 self->text.lt.top = position ;
278 if (rebuild) UpdateLineTable ( self
279 , position
280 , 0
281 , self->core.width
282 - self->text.leftmargin
283 - self->text.rightmargin
284 , 0
285 , FALSE
286 ) ;
287 }
288
289 /*--------------------------------------------------------------------------+*/
UpdateLineTable(self,pos0,posF,width,line0,updateMode)290 static XwTextLineRange UpdateLineTable
291 (self, pos0, posF, width, line0, updateMode)
292 /*--------------------------------------------------------------------------+*/
293 XwTextEditWidget self ;
294 XwTextPosition pos0, posF ;
295 Dimension width ;
296 XwTextPosition line0 ;
297 int updateMode ;
298 {
299 XwLineTableEntryPtr currLine ;
300 XwLineTablePtr lt ;
301 XwTextPosition line, nLines ;
302 Dimension x0, y ;
303 int reqW, reqH ;
304 TextFit (*textFitFn)();
305 int wrapEnabled, breakOnWhiteSpace ;
306 XwTextPosition fitPos, drawPos, nextPos ;
307
308 textFitFn = self->text.sink->textFitFn ;
309 lt = &(self->text.lt) ;
310 nLines = lt->lines ;
311 x0 = self->text.leftmargin ;
312 y = updateMode ? lt->info[line0].y : self->text.topmargin ;
313 reqH = 0 ;
314 wrapEnabled = (int) self->text.wrap_mode ;
315 breakOnWhiteSpace =
316 wrapEnabled && (self->text.wrap_break == XwWrapWhiteSpace) ;
317
318 for (line = line0; line <= nLines; line++)
319 { currLine = &(lt->info[line]) ;
320 currLine->x = x0;
321 currLine->y = y;
322 currLine->position = pos0;
323 if (pos0 <= GETLASTPOS(self))
324 { currLine->fit = (*textFitFn) ( self
325 , pos0
326 , x0
327 , width
328 , wrapEnabled
329 , breakOnWhiteSpace
330 , &fitPos
331 , &drawPos
332 , &nextPos
333 , &reqW
334 , &reqH
335 ) ;
336 currLine->drawPos = drawPos ;
337
338 currLine->endX = x0 + reqW ;
339 pos0 = nextPos;
340 /* In update mode we must go through the last line which had a
341 character replaced in it before terminating on a mere position
342 match. Starting position (of replacement) would be sufficient
343 only if we know the font is fixed width. (Good place to
344 optimize someday, huh?)
345 */
346 if (updateMode && (nextPos > posF)
347 && (nextPos == lt->info[line+1].position))
348 { break ;
349 }
350 }
351 else
352 { currLine->endX = x0;
353 currLine->fit = tfEndText ;
354 }
355 y += reqH;
356 } ;
357
358 return ( (line < nLines) ? line : nLines - 1 ) ;
359 }
360
361
362 /*
363 * This routine is used to re-display the entire window, independent of
364 * its current state.
365 */
366 /*--------------------------------------------------------------------------+*/
ForceBuildLineTable(ctx)367 static void ForceBuildLineTable(ctx)
368 /*--------------------------------------------------------------------------+*/
369 XwTextEditWidget ctx;
370 {
371 XwTextPosition position;
372
373 position = ctx->text.lt.top;
374 ctx->text.lt.top++; /* ugly, but it works */
375 BuildLineTable(ctx, position);
376 }
377
378 /*
379 * The routine will scroll the displayed text by lines. If the arg is
380 * positive, move up; otherwise, move down. [note: this is really a private
381 * procedure but is used in multiple modules].
382 */
383 /*--------------------------------------------------------------------------+*/
_XtTextScroll(ctx,n)384 static void _XtTextScroll(ctx, n)
385 /*--------------------------------------------------------------------------+*/
386 XwTextEditWidget ctx;
387 int n;
388 {
389 register XwTextEditPart *text = (XwTextEditPart *) &(ctx->text);
390 register XwLineTablePtr lt = &(text->lt);
391 XwTextPosition top, target, lastpos = GETLASTPOS(ctx);
392 Dimension textwidth =
393 ctx->core.width - (text->leftmargin + text->rightmargin);
394 Dimension ypos;
395 if (n >= 0) {
396 top = min(lt->info[n].position, lastpos);
397 BuildLineTable(ctx, top);
398 if (top >= lastpos)
399 DisplayAllText(ctx);
400 else {
401 XCopyArea(XtDisplay(ctx), XtWindow(ctx), XtWindow(ctx),
402 text->gc,
403 text->leftmargin, /* 0, */
404 lt->info[n].y,
405 textwidth, /* 9999, */
406 ctx->core.height - lt->info[n].y - text->bottommargin,
407 text->leftmargin, /* 0, */
408 lt->info[0].y);
409 ypos = lt->info[0].y + ctx->core.height - lt->info[n].y;
410 (*(text->sink->clearToBackground))
411 (ctx,
412 text->leftmargin, /* 0, */
413 ypos, /* lt->info[0].y + ctx->core.height - lt->info[n].y, */
414 textwidth, /* 9999, */
415 ctx->core.height - (ypos + text->bottommargin) /* 9999 */
416 );
417 if (n < lt->lines) n++;
418 _XtTextNeedsUpdating(ctx,
419 lt->info[lt->lines - n].position, lastpos);
420 }
421 } else {
422 Dimension tempHeight;
423 n = -n;
424 target = lt->top;
425 top = (*(text->source->scan))(text->source, target, XwstEOL,
426 XwsdLeft, n+1, FALSE);
427 tempHeight = lt->info[lt->lines-n].y - text->topmargin;
428 BuildLineTable(ctx, top);
429 if (lt->info[n].position == target) {
430 XCopyArea(XtDisplay(ctx), XtWindow(ctx), XtWindow(ctx),
431 text->gc,
432 text->leftmargin, /* 0, */
433 lt->info[0].y,
434 textwidth, /* 9999, */
435 tempHeight,
436 text->leftmargin, /* 0, */
437 lt->info[n].y);
438 _XtTextNeedsUpdating(ctx,
439 lt->info[0].position, lt->info[n].position);
440 } else if (lt->top != target) DisplayAllText(ctx);
441 }
442 }
443
444
445 /*
446 * This internal routine deletes the text from pos1 to pos2 in a source and
447 * then inserts, at pos1, the text that was passed. As a side effect it
448 * "invalidates" that portion of the displayed text (if any).
449 */
450 /*--------------------------------------------------------------------------+*/
ReplaceText(ctx,pos1,pos2,text,verify)451 static XwEditResult ReplaceText (ctx, pos1, pos2, text, verify)
452 /*--------------------------------------------------------------------------+*/
453 XwTextEditWidget ctx;
454 XwTextPosition pos1, pos2;
455 XwTextBlock *text;
456 Boolean verify;
457
458 /* it is illegal to call this routine unless there is a valid line table!*/
459 {
460 int i, line1, line2, visible, delta;
461 XwEditResult error;
462 Position x, y;
463 XwTextPosition startPos, endPos, updateFrom, lastpos;
464 XwTextVerifyCD cbdata;
465 XwTextBlock newtxtblk;
466
467 newtxtblk.ptr = (unsigned char*) XtMalloc(text->length);
468 newtxtblk.firstPos = text->firstPos;
469 newtxtblk.length = text->length;
470 strncpy(newtxtblk.ptr, text->ptr, text->length);
471 cbdata.operation = modVerify;
472 cbdata.doit = TRUE;
473 cbdata.currInsert = ctx->text.insertPos;
474 cbdata.newInsert = ctx->text.insertPos;
475 cbdata.startPos = pos1;
476 cbdata.endPos = pos2;
477 cbdata.text = &newtxtblk;
478
479 if (verify)
480 { XtCallCallbacks((Widget)ctx, XtNmodifyVerification, &cbdata);
481
482 if (!cbdata.doit) {
483 text->length = 0; /* Necessary inorder to return to initial state */
484 return XweditReject;
485 }
486
487 /* Extract any new data changed by the verification callback */
488 /* newtxtblk is used in the actual replace call later */
489 pos1 = cbdata.startPos;
490 pos2 = cbdata.endPos;
491 ctx->text.insertPos = cbdata.newInsert;
492 }
493
494 /* the insertPos may not always be set to the right spot in XwtextAppend */
495 if ((pos1 == ctx->text.insertPos) &&
496 ((*(ctx->text.source->editType))(ctx->text.source) == XwtextAppend)) {
497 ctx->text.insertPos = GETLASTPOS(ctx);
498 pos2 = pos2 - pos1 + ctx->text.insertPos;
499 pos1 = ctx->text.insertPos;
500 }
501 updateFrom = (*(ctx->text.source->scan))
502 (ctx->text.source, pos1, XwstWhiteSpace, XwsdLeft, 1, TRUE);
503 updateFrom = (*(ctx->text.source->scan))
504 (ctx->text.source, updateFrom, XwstPositions, XwsdLeft, 1, TRUE);
505 startPos = max(updateFrom, ctx->text.lt.top);
506 visible = LineAndXYForPosition(ctx, startPos, &line1, &x, &y);
507 error = (*(ctx->text.source->replace))
508 (ctx->text.source, pos1, pos2, &newtxtblk, &delta);
509 XtFree(newtxtblk.ptr);
510 if (error) return error;
511 lastpos = GETLASTPOS(ctx);
512 if (ctx->text.lt.top >= lastpos) {
513 BuildLineTable(ctx, lastpos);
514 /* ClearWindow(ctx); */
515 ClearText(ctx);
516 return error;
517 }
518 if (delta < lastpos) {
519 for (i = 0; i < ctx->text.numranges; i++) {
520 if (ctx->text.updateFrom[i] > pos1)
521 ctx->text.updateFrom[i] += delta;
522 if (ctx->text.updateTo[i] >= pos1)
523 ctx->text.updateTo[i] += delta;
524 }
525 }
526
527 line2 = LineForPosition(ctx, pos1);
528 /*
529 * fixup all current line table entries to reflect edit.
530 * BUG: it is illegal to do arithmetic on positions. This code should
531 * either use scan or the source needs to provide a function for doing
532 * position arithmetic.
533 */
534 for (i = line2 + 1; i <= ctx->text.lt.lines; i++)
535 ctx->text.lt.info[i].position += delta;
536
537 endPos = pos1;
538 /*
539 * Now process the line table and fixup in case edits caused
540 * changes in line breaks. If we are breaking on word boundaries,
541 * this code checks for moving words to and from lines.
542 */
543 if (visible) {
544 XwTextLineRange lastChangedLine ;
545 if (line1) line1-- ; /* force check for word moving to prev line */
546 lastChangedLine =
547 UpdateLineTable (ctx
548 , ctx->text.lt.info[line1].position
549 , pos2 + delta
550 , ctx->core.width - ctx->text.leftmargin
551 - ctx->text.rightmargin
552 , line1
553 , TRUE
554 ) ;
555 endPos = ctx->text.lt.info[lastChangedLine+1].position ;
556 }
557 lastpos = GETLASTPOS(ctx);
558 if (delta >= lastpos)
559 endPos = lastpos;
560 if (delta >= lastpos || pos2 >= ctx->text.lt.top)
561 _XtTextNeedsUpdating(ctx, updateFrom, endPos);
562 return error;
563 }
564
565
566 /*
567 * This routine will display text between two arbitrary source positions.
568 * In the event that this span contains highlighted text for the selection,
569 * only that portion will be displayed highlighted.
570 */
571 /*--------------------------------------------------------------------------+*/
DisplayText(ctx,pos1,pos2)572 static void DisplayText(ctx, pos1, pos2)
573 /*--------------------------------------------------------------------------+*/
574 XwTextEditWidget ctx;
575 XwTextPosition pos1, pos2;
576 /* it is illegal to call this routine unless there is a valid line table!*/
577 {
578 Position x, y, xlimit, tempx;
579 Dimension height;
580 int line, i, visible;
581 XwTextPosition startPos, endPos, lastpos = GETLASTPOS(ctx);
582
583 if (pos1 < ctx->text.lt.top)
584 pos1 = ctx->text.lt.top;
585 if (pos2 > lastpos)
586 pos2 = lastpos;
587 if (pos1 >= pos2) return;
588 visible = LineAndXYForPosition(ctx, pos1, &line, &x, &y);
589 if (!visible)
590 return;
591 startPos = pos1;
592 xlimit = ctx->core.width - ctx->text.rightmargin + 1 ;
593 height = ctx->text.lt.info[1].y - ctx->text.lt.info[0].y;
594 for (i = line; i < ctx->text.lt.lines; i++) {
595 endPos = ctx->text.lt.info[i].drawPos + 1;
596 if (endPos > pos2)
597 endPos = pos2;
598 if (endPos > startPos) {
599 /* We know this should not be necessary!!!!
600 if (x == ctx->text.leftmargin)
601 (*(ctx->text.sink->clearToBackground))(ctx,
602 0, y, ctx->text.leftmargin, height);
603 */
604 if (startPos >= ctx->text.s.right || endPos <= ctx->text.s.left) {
605 (*(ctx->text.sink->display))(ctx, x, y,
606 startPos, endPos, FALSE);
607 } else if (startPos >= ctx->text.s.left
608 && endPos <= ctx->text.s.right)
609 { (*(ctx->text.sink->display))(ctx, x, y,
610 startPos, endPos, TRUE);
611 } else {
612 DisplayText(ctx, startPos, ctx->text.s.left);
613 DisplayText(ctx, max(startPos, ctx->text.s.left),
614 min(endPos, ctx->text.s.right));
615 DisplayText(ctx, ctx->text.s.right, endPos);
616 }
617 }
618 startPos = ctx->text.lt.info[i + 1].position;
619 height = ctx->text.lt.info[i + 1].y - ctx->text.lt.info[i].y;
620 tempx = ctx->text.lt.info[i].endX;
621 (*(ctx->text.sink->clearToBackground))(ctx,
622 tempx, y, xlimit - tempx, height);
623 x = ctx->text.leftmargin;
624 y = ctx->text.lt.info[i + 1].y;
625 if ((endPos == pos2) && (endPos != lastpos))
626 break;
627 }
628 }
629
630 /*
631 * Clear the window to background color.
632 */
633 /*--------------------------------------------------------------------------+*/
ClearWindow(ctx)634 static void ClearWindow (ctx)
635 /*--------------------------------------------------------------------------+*/
636 XwTextEditWidget ctx;
637 {
638 (*(ctx->text.sink->clearToBackground))(ctx, 0, 0, ctx->core.width,
639 ctx->core.height);
640 }
641
642 /*
643 * Clear the portion of the window that the text in drawn in, or that is
644 * don't clear the margins
645 */
646 /*--------------------------------------------------------------------------+*/
ClearText(ctx)647 static void ClearText (ctx)
648 /*--------------------------------------------------------------------------+*/
649 XwTextEditWidget ctx;
650 {
651 register XwTextEditPart *text = (XwTextEditPart *) &(ctx->text);
652 (*(text->sink->clearToBackground))
653 (ctx, 0, 0, ctx->core.width, ctx->core.height);
654 }
655
656 /*
657 * Internal redisplay entire window.
658 */
659 /*--------------------------------------------------------------------------+*/
DisplayAllText(w)660 static void DisplayAllText (w)
661 /*--------------------------------------------------------------------------+*/
662 Widget w;
663 {
664 XwTextEditWidget ctx = (XwTextEditWidget) w;
665
666 if (!XtIsRealized((Widget)ctx)) return;
667
668 ClearText(ctx);
669 /* ClearWindow(ctx); */
670 /* BuildLineTable(ctx, ctx->text.lt.top); */
671 _XtTextNeedsUpdating(ctx, zeroPosition, GETLASTPOS(ctx));
672
673 }
674
675 /*
676 * This routine checks to see if the window should be resized (grown or
677 * shrunk) or scrolled then text to be painted overflows to the right or
678 * the bottom of the window. It is used by the keyboard input routine.
679 */
680 /*--------------------------------------------------------------------------+*/
CheckResizeOrOverflow(ctx)681 static void CheckResizeOrOverflow(ctx)
682 /*--------------------------------------------------------------------------+*/
683 XwTextEditWidget ctx;
684 {
685 XwTextLineRange i, nLines ;
686 Dimension width;
687 XtWidgetGeometry rbox;
688 XtGeometryResult reply;
689 XwLineTableEntryPtr lp ;
690 XwLineTablePtr lt ;
691 XwTextEditPart *tp ;
692
693 tp = &(ctx->text) ;
694 lt = &(tp->lt) ;
695 nLines = lt->lines ;
696
697 if (tp->grow_state & XwGrowHorizontal)
698 { UpdateLineTable ( ctx, lt->top, 0, INFINITE_WIDTH, 0, FALSE) ;
699 width = 0 ;
700 for (i=0, lp = &(lt->info[0]) ; i < nLines ; i++, lp++)
701 { if (width < lp->endX) width = lp->endX ;
702 } ;
703 width += ctx->text.rightmargin;
704 if (width > ctx->core.width)
705 { rbox.request_mode = CWWidth;
706 rbox.width = width;
707 reply = XtMakeGeometryRequest((Widget)ctx, &rbox, &rbox);
708 if (reply == XtGeometryAlmost)
709 reply = XtMakeGeometryRequest((Widget)ctx, &rbox, NULL);
710 /* NOTE: following test is expected to be a fall-through from
711 previous. Should not be an else if. */
712 if (reply == XtGeometryYes)
713 ctx->core.width = rbox.width;
714 else /* if request not satisfied, disallow future attempts */
715 { tp->grow_state &= ~XwGrowHorizontal ;
716 UpdateLineTable ( ctx, lt->top, 0, ctx->core.width
717 - ctx->text.leftmargin - ctx->text.rightmargin,
718 0, FALSE);
719 }
720 }
721 } ;
722
723 if ((tp->grow_state & XwGrowVertical)
724 && ( ! (lt->info[nLines].fit & tfEndText)
725 || (lt->info[nLines].drawPos > lt->info[nLines].position))
726 )
727 { rbox.request_mode = CWHeight;
728 rbox.height = (*(ctx->text.sink->maxHeight))
729 (ctx, nLines + 1) + tp->topmargin + tp->bottommargin ;
730 reply = XtMakeGeometryRequest((Widget)ctx, &rbox, &rbox);
731 if (reply == XtGeometryAlmost)
732 reply = XtMakeGeometryRequest((Widget)ctx, &rbox, NULL);
733 if (reply == XtGeometryYes)
734 ctx->core.height = rbox.height;
735 else /* if request not satisfied, disallow future attempts */
736 { tp->grow_state &= ~XwGrowVertical ;
737 }
738 } ;
739 }
740
741 /*
742 * This routine processes all "expose region" XEvents. In general, its job
743 * is to the best job at minimal re-paint of the text, displayed in the
744 * window, that it can.
745 */
746 /*--------------------------------------------------------------------------+*/
ProcessExposeRegion(w,event)747 static void ProcessExposeRegion(w, event)
748 /*--------------------------------------------------------------------------+*/
749 Widget w;
750 XEvent *event;
751 {
752 XwTextEditWidget ctx = (XwTextEditWidget) w;
753 XwTextPosition pos1, pos2, resultend;
754 int line;
755 int x = event->xexpose.x;
756 int y = event->xexpose.y;
757 int width = event->xexpose.width;
758 int height = event->xexpose.height;
759 XwLineTableEntryPtr info;
760
761 _XtTextPrepareToUpdate(ctx);
762 if (x < ctx->text.leftmargin) /* stomp on caret tracks */
763 (*(ctx->text.sink->clearToBackground))(ctx, x, y, width, height);
764 /* figure out starting line that was exposed */
765 line = LineForPosition(ctx, PositionForXY(ctx, x, y));
766 while (line < ctx->text.lt.lines && ctx->text.lt.info[line + 1].y < y)
767 line++;
768 while (line < ctx->text.lt.lines) {
769 info = &(ctx->text.lt.info[line]);
770 if (info->y >= y + height)
771 break;
772 (*(ctx->text.sink->resolve))(ctx,
773 info->position, info->x,
774 x - info->x, &pos1, &resultend);
775 (*(ctx->text.sink->resolve))(ctx,
776 info->position, info->x,
777 x + width - info->x, &pos2,
778 &resultend);
779 pos2 = (*(ctx->text.source->scan))(ctx->text.source, pos2,
780 XwstPositions, XwsdRight, 1, TRUE);
781 _XtTextNeedsUpdating(ctx, pos1, pos2);
782 line++;
783 }
784 _XtTextExecuteUpdate(ctx);
785
786 }
787
788 /*
789 * This routine does all setup required to syncronize batched screen updates
790 */
791 /*--------------------------------------------------------------------------+*/
_XtTextPrepareToUpdate(ctx)792 static void _XtTextPrepareToUpdate(ctx)
793 /*--------------------------------------------------------------------------+*/
794 XwTextEditWidget ctx;
795 {
796
797 if ((ctx->text.oldinsert < 0) && ctx->text.update_flag) {
798 InsertCursor(ctx, XwisOff);
799 ctx->text.numranges = 0;
800 ctx->text.showposition = FALSE;
801 ctx->text.oldinsert = ctx->text.insertPos;
802 }
803 }
804
805
806 /*
807 * This is a private utility routine used by _XtTextExecuteUpdate. It
808 * processes all the outstanding update requests and merges update
809 * ranges where possible.
810 */
811 /*--------------------------------------------------------------------------+*/
FlushUpdate(ctx)812 static void FlushUpdate(ctx)
813 /*--------------------------------------------------------------------------+*/
814 XwTextEditWidget ctx;
815 {
816 int i, w;
817 XwTextPosition updateFrom, updateTo;
818 while (ctx->text.numranges > 0) {
819 updateFrom = ctx->text.updateFrom[0];
820 w = 0;
821 for (i=1 ; i<ctx->text.numranges ; i++) {
822 if (ctx->text.updateFrom[i] < updateFrom) {
823 updateFrom = ctx->text.updateFrom[i];
824 w = i;
825 }
826 }
827 updateTo = ctx->text.updateTo[w];
828 ctx->text.numranges--;
829 ctx->text.updateFrom[w] = ctx->text.updateFrom[ctx->text.numranges];
830 ctx->text.updateTo[w] = ctx->text.updateTo[ctx->text.numranges];
831 for (i=ctx->text.numranges-1 ; i>=0 ; i--) {
832 while (ctx->text.updateFrom[i] <= updateTo
833 && i < ctx->text.numranges)
834 {
835 updateTo = ctx->text.updateTo[i];
836 ctx->text.numranges--;
837 ctx->text.updateFrom[i] =
838 ctx->text.updateFrom[ctx->text.numranges];
839 ctx->text.updateTo[i] =
840 ctx->text.updateTo[ctx->text.numranges];
841 }
842 }
843 DisplayText(ctx, updateFrom, updateTo);
844 }
845 }
846
847
848 /*
849 * This is a private utility routine used by _XtTextExecuteUpdate. This routine
850 * worries about edits causing new data or the insertion point becoming
851 * invisible (off the screen). Currently it always makes it visible by
852 * scrolling. It probably needs generalization to allow more options.
853 */
854 /*--------------------------------------------------------------------------+*/
_XtTextShowPosition(ctx)855 static void _XtTextShowPosition(ctx)
856 /*--------------------------------------------------------------------------+*/
857 XwTextEditWidget ctx;
858 {
859 XwTextPosition top, first, second, insertPos ;
860 XwTextPosition lastpos = GETLASTPOS(ctx);
861 XwTextEditPart *text = &(ctx->text) ;
862 XwLineTablePtr lt = &(text->lt) ;
863 short hScroll ;
864
865 /* NOTE: Following code relies on current assumption that
866 horizontal scrolling will be enabled only when there is
867 only one display line.
868 */
869
870 insertPos = text->insertPos ;
871 if ( insertPos < lt->top
872 || insertPos >= lt->info[lt->lines].position
873 || (hScroll = ((text->scroll_state & XwAutoScrollHorizontal)
874 && (insertPos > lt->info[0].drawPos)
875 && ( lt->info[0].drawPos + 1 < lastpos))
876 ? 1 : 0 )
877
878 )
879 {
880 if ( lt->lines > 0
881 && (insertPos < lt->top
882 || lt->info[lt->lines].position <= lastpos
883 || hScroll)
884 ) {
885 first = lt->top;
886 second = lt->info[1].position;
887 /*
888 if (insertPos < first)
889 top = (*(text->source->scan))(
890 text->source, insertPos, XwstEOL,
891 XwsdLeft, 1, FALSE);
892 else
893 top = (*(text->source->scan))(
894 text->source, insertPos, XwstEOL,
895 XwsdLeft, lt->lines, FALSE);
896 BuildLineTable(ctx, top);
897 while (insertPos >= lt->info[lt->lines].position) {
898 if (lt->info[lt->lines].position > lastpos)
899 break;
900 BuildLineTable(ctx, lt->info[1].position);
901 }
902 */
903 if (text->scroll_state & XwAutoScrollHorizontal) {
904 if ((insertPos > lt->info[0].drawPos) &&
905 (lt->info[0].drawPos + 1 < lastpos)) {
906 /* smooth scroll: scroll by one character at a time */
907
908 XwTextPosition delta = 0 ;
909 top = insertPos - (lt->info[0].drawPos
910 - lt->info[0].position) - 1;
911 while (insertPos > lt->info[0].drawPos+1) {
912 BuildLineTable (ctx, top += delta) ;
913 delta = (insertPos - top) >> 2 ;
914 }
915 first = -1 ; /* prevent scroll down by one line */
916 }
917
918 else if (insertPos < first) {
919 /* Do the same as above, for traveling to the left */
920
921 XwTextPosition delta = 0 ;
922 if (insertPos < 0) insertPos = 0;
923 top = insertPos;
924 if (top < 0) top = 0;
925 BuildLineTable (ctx, top);
926 while (insertPos < lt->top) {
927 top -= delta;
928 if (top < 0) top = 0;
929 BuildLineTable (ctx, top);
930 delta = (insertPos - top) >> 2;
931 }
932 first = -1 ; /* prevent scroll down by one line */
933 }
934 }
935
936 if (lt->top == second && lt->lines > 1) {
937 BuildLineTable(ctx, first);
938 _XtTextScroll(ctx, 1);
939 } else if (lt->info[1].position == first && lt->lines > 1) {
940 BuildLineTable(ctx, first);
941 _XtTextScroll(ctx, -1);
942 } else {
943 text->numranges = 0;
944 if (lt->top != first)
945 DisplayAllText(ctx);
946 }
947 }
948 }
949 }
950
951
952
953 /*
954 * This routine causes all batched screen updates to be performed
955 */
956 /*--------------------------------------------------------------------------+*/
_XtTextExecuteUpdate(ctx)957 static void _XtTextExecuteUpdate(ctx)
958 /*--------------------------------------------------------------------------+*/
959 XwTextEditWidget ctx;
960 {
961 if ((ctx->text.oldinsert >= 0) && ctx->text.update_flag) {
962 if (ctx->text.oldinsert != ctx->text.insertPos
963 || ctx->text.showposition)
964 _XtTextShowPosition(ctx);
965 FlushUpdate(ctx);
966 InsertCursor(ctx, XwisOn);
967 ctx->text.oldinsert = -1;
968 }
969 }
970