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