1 /*************************************<+>*************************************
2 *****************************************************************************
8 ** Description: Code for TextEdit widget
10 *****************************************************************************
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
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.
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
34 *****************************************************************************
35 *************************************<+>*************************************/
36 /*****************************************************************************
38 * Procedures declared in this file
40 ******************************************************************************
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() ;
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.
69 /*--------------------------------------------------------------------------+*/
70 static void InsertCursor (ctx
, state
)
71 /*--------------------------------------------------------------------------+*/
76 int dy
, line
, visible
;
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;
84 dy
= (ctx
->text
.lt
.info
[line
].y
- ctx
->text
.lt
.info
[line
- 1].y
) + 1;
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
;
100 (*(ctx
->text
.sink
->insertCursor
))(ctx
, x
, y
, state
);
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
110 /*--------------------------------------------------------------------------+*/
111 static void _XtTextNeedsUpdating(ctx
, left
, right
)
112 /*--------------------------------------------------------------------------+*/
113 XwTextEditWidget ctx
;
114 XwTextPosition 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
]);
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
));
137 ctx
->text
.updateFrom
[ctx
->text
.numranges
- 1] = left
;
138 ctx
->text
.updateTo
[ctx
->text
.numranges
- 1] = right
;
144 * This routine maps an x and y position in a window that is displaying text
145 * into the corresponding position in the source.
147 /*--------------------------------------------------------------------------+*/
148 static XwTextPosition
PositionForXY (ctx
, x
, y
)
149 /*--------------------------------------------------------------------------+*/
150 XwTextEditWidget ctx
;
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
);
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
)
163 position
= ctx
->text
.lt
.info
[line
].position
;
164 if (position
>= 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
,
178 * This routine maps a source position in to the corresponding line number
179 * of the text that is displayed in the window.
181 /*--------------------------------------------------------------------------+*/
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!*/
190 if (position
<= ctx
->text
.lt
.info
[0].position
)
192 for (line
= 0; line
< ctx
->text
.lt
.lines
; line
++)
193 if (position
< ctx
->text
.lt
.info
[line
+ 1].position
)
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.
202 /*--------------------------------------------------------------------------+*/
203 static int LineAndXYForPosition (ctx
, pos
, line
, x
, y
)
204 /*--------------------------------------------------------------------------+*/
205 XwTextEditWidget ctx
;
209 /* it is illegal to call this routine unless there is a valid line table!*/
211 XwTextPosition linePos
, endPos
;
212 int visible
, realW
, realH
;
215 *x
= ctx
->text
.leftmargin
;
216 *y
= ctx
->text
.topmargin
;
217 visible
= IsPositionVisible(ctx
, pos
);
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
);
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.).
237 /*--------------------------------------------------------------------------+*/
238 static void BuildLineTable (self
, position
)
239 /*--------------------------------------------------------------------------+*/
240 XwTextEditWidget self
;
241 XwTextPosition position
;
243 XwTextPosition line
, lines
;
245 XwLineTableEntryPtr lp
;
247 rebuild
= (Boolean
) (position
!= self
->text
.lt
.top
);
248 lines
= applyDisplay(maxLines
)(self
) ;
254 * Don't allow a 0-line widget - let clipping occur
256 * NOTE: THE MAXINT CLIPPING IS AN UGLY HACK THAT NEEDS TO BE FIXED
259 if ((lines
< 1) || (lines
> 32767)) lines
= 1;
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
;
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 ;
277 self
->text
.lt
.top
= position
;
278 if (rebuild
) UpdateLineTable ( self
282 - self
->text
.leftmargin
283 - self
->text
.rightmargin
289 /*--------------------------------------------------------------------------+*/
290 static XwTextLineRange UpdateLineTable
291 (self
, pos0
, posF
, width
, line0
, updateMode
)
292 /*--------------------------------------------------------------------------+*/
293 XwTextEditWidget self
;
294 XwTextPosition pos0
, posF
;
296 XwTextPosition line0
;
299 XwLineTableEntryPtr currLine
;
301 XwTextPosition line
, nLines
;
304 TextFit (*textFitFn
)();
305 int wrapEnabled
, breakOnWhiteSpace
;
306 XwTextPosition fitPos
, drawPos
, nextPos
;
308 textFitFn
= self
->text
.sink
->textFitFn
;
309 lt
= &(self
->text
.lt
) ;
311 x0
= self
->text
.leftmargin
;
312 y
= updateMode
? lt
->info
[line0
].y
: self
->text
.topmargin
;
314 wrapEnabled
= (int) self
->text
.wrap_mode
;
316 wrapEnabled
&& (self
->text
.wrap_break
== XwWrapWhiteSpace
) ;
318 for (line
= line0
; line
<= nLines
; line
++)
319 { currLine
= &(lt
->info
[line
]) ;
322 currLine
->position
= pos0
;
323 if (pos0
<= GETLASTPOS(self
))
324 { currLine
->fit
= (*textFitFn
) ( self
336 currLine
->drawPos
= drawPos
;
338 currLine
->endX
= x0
+ reqW
;
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?)
346 if (updateMode
&& (nextPos
> posF
)
347 && (nextPos
== lt
->info
[line
+1].position
))
352 { currLine
->endX
= x0
;
353 currLine
->fit
= tfEndText
;
358 return ( (line
< nLines
) ? line
: nLines
- 1 ) ;
363 * This routine is used to re-display the entire window, independent of
366 /*--------------------------------------------------------------------------+*/
367 static void ForceBuildLineTable(ctx
)
368 /*--------------------------------------------------------------------------+*/
369 XwTextEditWidget ctx
;
371 XwTextPosition position
;
373 position
= ctx
->text
.lt
.top
;
374 ctx
->text
.lt
.top
++; /* ugly, but it works */
375 BuildLineTable(ctx
, position
);
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].
383 /*--------------------------------------------------------------------------+*/
384 static void _XtTextScroll(ctx
, n
)
385 /*--------------------------------------------------------------------------+*/
386 XwTextEditWidget ctx
;
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
);
396 top
= min(lt
->info
[n
].position
, lastpos
);
397 BuildLineTable(ctx
, top
);
401 XCopyArea(XtDisplay(ctx
), XtWindow(ctx
), XtWindow(ctx
),
403 text
->leftmargin
, /* 0, */
405 textwidth
, /* 9999, */
406 ctx
->core
.height
- lt
->info
[n
].y
- text
->bottommargin
,
407 text
->leftmargin
, /* 0, */
409 ypos
= lt
->info
[0].y
+ ctx
->core
.height
- lt
->info
[n
].y
;
410 (*(text
->sink
->clearToBackground
))
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 */
417 if (n
< lt
->lines
) n
++;
418 _XtTextNeedsUpdating(ctx
,
419 lt
->info
[lt
->lines
- n
].position
, lastpos
);
422 Dimension tempHeight
;
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
),
432 text
->leftmargin
, /* 0, */
434 textwidth
, /* 9999, */
436 text
->leftmargin
, /* 0, */
438 _XtTextNeedsUpdating(ctx
,
439 lt
->info
[0].position
, lt
->info
[n
].position
);
440 } else if (lt
->top
!= target
) DisplayAllText(ctx
);
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).
450 /*--------------------------------------------------------------------------+*/
451 static XwEditResult
ReplaceText (ctx
, pos1
, pos2
, text
, verify
)
452 /*--------------------------------------------------------------------------+*/
453 XwTextEditWidget ctx
;
454 XwTextPosition pos1
, pos2
;
458 /* it is illegal to call this routine unless there is a valid line table!*/
460 int i
, line1
, line2
, visible
, delta
;
463 XwTextPosition startPos
, endPos
, updateFrom
, lastpos
;
464 XwTextVerifyCD cbdata
;
465 XwTextBlock newtxtblk
;
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
;
473 cbdata
.currInsert
= ctx
->text
.insertPos
;
474 cbdata
.newInsert
= ctx
->text
.insertPos
;
475 cbdata
.startPos
= pos1
;
476 cbdata
.endPos
= pos2
;
477 cbdata
.text
= &newtxtblk
;
480 { XtCallCallbacks((Widget
)ctx
, XtNmodifyVerification
, &cbdata
);
483 text
->length
= 0; /* Necessary inorder to return to initial state */
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
;
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
;
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); */
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
;
527 line2
= LineForPosition(ctx
, pos1
);
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.
534 for (i
= line2
+ 1; i
<= ctx
->text
.lt
.lines
; i
++)
535 ctx
->text
.lt
.info
[i
].position
+= delta
;
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.
544 XwTextLineRange lastChangedLine
;
545 if (line1
) line1
-- ; /* force check for word moving to prev line */
548 , ctx
->text
.lt
.info
[line1
].position
550 , ctx
->core
.width
- ctx
->text
.leftmargin
551 - ctx
->text
.rightmargin
555 endPos
= ctx
->text
.lt
.info
[lastChangedLine
+1].position
;
557 lastpos
= GETLASTPOS(ctx
);
558 if (delta
>= lastpos
)
560 if (delta
>= lastpos
|| pos2
>= ctx
->text
.lt
.top
)
561 _XtTextNeedsUpdating(ctx
, updateFrom
, endPos
);
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.
571 /*--------------------------------------------------------------------------+*/
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!*/
578 Position x
, y
, xlimit
, tempx
;
580 int line
, i
, visible
;
581 XwTextPosition startPos
, endPos
, lastpos
= GETLASTPOS(ctx
);
583 if (pos1
< ctx
->text
.lt
.top
)
584 pos1
= ctx
->text
.lt
.top
;
587 if (pos1
>= pos2
) return;
588 visible
= LineAndXYForPosition(ctx
, pos1
, &line
, &x
, &y
);
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;
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);
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
);
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
);
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
))
631 * Clear the window to background color.
633 /*--------------------------------------------------------------------------+*/
634 static void ClearWindow (ctx
)
635 /*--------------------------------------------------------------------------+*/
636 XwTextEditWidget ctx
;
638 (*(ctx
->text
.sink
->clearToBackground
))(ctx
, 0, 0, ctx
->core
.width
,
643 * Clear the portion of the window that the text in drawn in, or that is
644 * don't clear the margins
646 /*--------------------------------------------------------------------------+*/
647 static void ClearText (ctx
)
648 /*--------------------------------------------------------------------------+*/
649 XwTextEditWidget ctx
;
651 register XwTextEditPart
*text
= (XwTextEditPart
*) &(ctx
->text
);
652 (*(text
->sink
->clearToBackground
))
653 (ctx
, 0, 0, ctx
->core
.width
, ctx
->core
.height
);
657 * Internal redisplay entire window.
659 /*--------------------------------------------------------------------------+*/
660 static void DisplayAllText (w
)
661 /*--------------------------------------------------------------------------+*/
664 XwTextEditWidget ctx
= (XwTextEditWidget
) w
;
666 if (!XtIsRealized((Widget
)ctx
)) return;
669 /* ClearWindow(ctx); */
670 /* BuildLineTable(ctx, ctx->text.lt.top); */
671 _XtTextNeedsUpdating(ctx
, zeroPosition
, GETLASTPOS(ctx
));
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.
680 /*--------------------------------------------------------------------------+*/
681 static void CheckResizeOrOverflow(ctx
)
682 /*--------------------------------------------------------------------------+*/
683 XwTextEditWidget ctx
;
685 XwTextLineRange i
, nLines
;
687 XtWidgetGeometry rbox
;
688 XtGeometryResult reply
;
689 XwLineTableEntryPtr lp
;
697 if (tp
->grow_state
& XwGrowHorizontal
)
698 { UpdateLineTable ( ctx
, lt
->top
, 0, INFINITE_WIDTH
, 0, FALSE
) ;
700 for (i
=0, lp
= &(lt
->info
[0]) ; i
< nLines
; i
++, lp
++)
701 { if (width
< lp
->endX
) width
= lp
->endX
;
703 width
+= ctx
->text
.rightmargin
;
704 if (width
> ctx
->core
.width
)
705 { rbox
.request_mode
= CWWidth
;
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
,
723 if ((tp
->grow_state
& XwGrowVertical
)
724 && ( ! (lt
->info
[nLines
].fit
& tfEndText
)
725 || (lt
->info
[nLines
].drawPos
> lt
->info
[nLines
].position
))
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
;
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.
746 /*--------------------------------------------------------------------------+*/
747 static void ProcessExposeRegion(w
, event
)
748 /*--------------------------------------------------------------------------+*/
752 XwTextEditWidget ctx
= (XwTextEditWidget
) w
;
753 XwTextPosition pos1
, pos2
, resultend
;
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
;
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
)
768 while (line
< ctx
->text
.lt
.lines
) {
769 info
= &(ctx
->text
.lt
.info
[line
]);
770 if (info
->y
>= y
+ height
)
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
,
779 pos2
= (*(ctx
->text
.source
->scan
))(ctx
->text
.source
, pos2
,
780 XwstPositions
, XwsdRight
, 1, TRUE
);
781 _XtTextNeedsUpdating(ctx
, pos1
, pos2
);
784 _XtTextExecuteUpdate(ctx
);
789 * This routine does all setup required to syncronize batched screen updates
791 /*--------------------------------------------------------------------------+*/
792 static void _XtTextPrepareToUpdate(ctx
)
793 /*--------------------------------------------------------------------------+*/
794 XwTextEditWidget ctx
;
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
;
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.
811 /*--------------------------------------------------------------------------+*/
812 static void FlushUpdate(ctx
)
813 /*--------------------------------------------------------------------------+*/
814 XwTextEditWidget ctx
;
817 XwTextPosition updateFrom
, updateTo
;
818 while (ctx
->text
.numranges
> 0) {
819 updateFrom
= ctx
->text
.updateFrom
[0];
821 for (i
=1 ; i
<ctx
->text
.numranges
; i
++) {
822 if (ctx
->text
.updateFrom
[i
] < updateFrom
) {
823 updateFrom
= ctx
->text
.updateFrom
[i
];
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
)
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
];
843 DisplayText(ctx
, updateFrom
, updateTo
);
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.
854 /*--------------------------------------------------------------------------+*/
855 static void _XtTextShowPosition(ctx
)
856 /*--------------------------------------------------------------------------+*/
857 XwTextEditWidget ctx
;
859 XwTextPosition top
, first
, second
, insertPos
;
860 XwTextPosition lastpos
= GETLASTPOS(ctx
);
861 XwTextEditPart
*text
= &(ctx
->text
) ;
862 XwLineTablePtr lt
= &(text
->lt
) ;
865 /* NOTE: Following code relies on current assumption that
866 horizontal scrolling will be enabled only when there is
867 only one display line.
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
))
881 && (insertPos
< lt
->top
882 || lt
->info
[lt
->lines
].position
<= lastpos
886 second
= lt
->info
[1].position
;
888 if (insertPos < first)
889 top = (*(text->source->scan))(
890 text->source, insertPos, XwstEOL,
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)
900 BuildLineTable(ctx, lt->info[1].position);
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 */
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 ;
915 first
= -1 ; /* prevent scroll down by one line */
918 else if (insertPos
< first
) {
919 /* Do the same as above, for traveling to the left */
921 XwTextPosition delta
= 0 ;
922 if (insertPos
< 0) insertPos
= 0;
924 if (top
< 0) top
= 0;
925 BuildLineTable (ctx
, top
);
926 while (insertPos
< lt
->top
) {
928 if (top
< 0) top
= 0;
929 BuildLineTable (ctx
, top
);
930 delta
= (insertPos
- top
) >> 2;
932 first
= -1 ; /* prevent scroll down by one line */
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);
944 if (lt
->top
!= first
)
954 * This routine causes all batched screen updates to be performed
956 /*--------------------------------------------------------------------------+*/
957 static void _XtTextExecuteUpdate(ctx
)
958 /*--------------------------------------------------------------------------+*/
959 XwTextEditWidget ctx
;
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
);
966 InsertCursor(ctx
, XwisOn
);
967 ctx
->text
.oldinsert
= -1;