Modified the UGetCursor() routine to return a valid response if the
[xcircuit.git] / Xw / sub.c
blobe515acf33e94e0063f5ed785c50c0a6b4bf9ce44
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 /*****************************************************************************
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 /*--------------------------------------------------------------------------+*/
72 XwTextEditWidget ctx;
73 XwInsertState state;
75 Position x, y;
76 int dy, line, visible;
77 XwTextBlock text;
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;
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;
98 y += dy;
99 if (visible)
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
108 * multiple modules].
110 /*--------------------------------------------------------------------------+*/
111 static void _XtTextNeedsUpdating(ctx, left, right)
112 /*--------------------------------------------------------------------------+*/
113 XwTextEditWidget ctx;
114 XwTextPosition left, right;
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;
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;
151 Position x,y;
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)
161 break;
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;
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!*/
188 int line;
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;
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;
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!*/
211 XwTextPosition linePos, endPos;
212 int visible, realW, realH;
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;
227 return visible;
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;
244 Boolean rebuild;
245 XwLineTableEntryPtr lp ;
247 rebuild = (Boolean) (position != self->text.lt.top);
248 lines= applyDisplay(maxLines)(self) ;
250 /****************
252 * RBM
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
257 * WITH A CAST!!
258 ****************/
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 ;
272 lp->x = lp->y = 0 ;
274 rebuild = TRUE;
277 self->text.lt.top = position ;
278 if (rebuild) UpdateLineTable ( self
279 , position
281 , self->core.width
282 - self->text.leftmargin
283 - self->text.rightmargin
285 , FALSE
289 /*--------------------------------------------------------------------------+*/
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 ;
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 ;
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) ;
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
336 currLine->drawPos = drawPos ;
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?)
346 if (updateMode && (nextPos > posF)
347 && (nextPos == lt->info[line+1].position))
348 { break ;
351 else
352 { currLine->endX = x0;
353 currLine->fit = tfEndText ;
355 y += reqH;
358 return ( (line < nLines) ? line : nLines - 1 ) ;
363 * This routine is used to re-display the entire window, independent of
364 * its current state.
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;
387 int n;
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 */
417 if (n < lt->lines) n++;
418 _XtTextNeedsUpdating(ctx,
419 lt->info[lt->lines - n].position, lastpos);
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);
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;
455 XwTextBlock *text;
456 Boolean verify;
458 /* it is illegal to call this routine unless there is a valid line table!*/
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;
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;
479 if (verify)
480 { XtCallCallbacks((Widget)ctx, XtNmodifyVerification, &cbdata);
482 if (!cbdata.doit) {
483 text->length = 0; /* Necessary inorder to return to initial state */
484 return XweditReject;
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); */
515 ClearText(ctx);
516 return error;
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;
537 endPos = pos1;
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.
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
555 endPos = ctx->text.lt.info[lastChangedLine+1].position ;
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;
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;
579 Dimension height;
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;
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);
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);
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;
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,
639 ctx->core.height);
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 /*--------------------------------------------------------------------------+*/
662 Widget w;
664 XwTextEditWidget ctx = (XwTextEditWidget) w;
666 if (!XtIsRealized((Widget)ctx)) return;
668 ClearText(ctx);
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 ;
686 Dimension width;
687 XtWidgetGeometry rbox;
688 XtGeometryResult reply;
689 XwLineTableEntryPtr lp ;
690 XwLineTablePtr lt ;
691 XwTextEditPart *tp ;
693 tp = &(ctx->text) ;
694 lt = &(tp->lt) ;
695 nLines = lt->lines ;
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 ;
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);
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 /*--------------------------------------------------------------------------+*/
749 Widget w;
750 XEvent *event;
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;
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++;
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;
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;
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) ;
863 short hScroll ;
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))
876 ? 1 : 0 )
880 if ( lt->lines > 0
881 && (insertPos < lt->top
882 || lt->info[lt->lines].position <= lastpos
883 || hScroll)
885 first = lt->top;
886 second = lt->info[1].position;
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);
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;
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;
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);
942 } else {
943 text->numranges = 0;
944 if (lt->top != first)
945 DisplayAllText(ctx);
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);
965 FlushUpdate(ctx);
966 InsertCursor(ctx, XwisOn);
967 ctx->text.oldinsert = -1;