diff options
Diffstat (limited to 'src/Text.c')
-rw-r--r-- | src/Text.c | 3600 |
1 files changed, 3600 insertions, 0 deletions
diff --git a/src/Text.c b/src/Text.c new file mode 100644 index 0000000..4b47bbd --- /dev/null +++ b/src/Text.c @@ -0,0 +1,3600 @@ +/* $XConsortium: Text.c,v 1.197 95/06/14 15:07:27 kaleb Exp $ */ + +/*********************************************************** + +Copyright (c) 1987, 1988, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#include "Xaw3dP.h" +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#ifdef XAW_INTERNATIONALIZATION +#include "XawI18n.h" +#endif +#include <stdio.h> +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/CharSet.h> +#include <X11/Xmu/Converters.h> +#include <X11/Xmu/StdSel.h> +#include <X11/Xmu/Misc.h> +#include <X11/Xaw3d/XawInit.h> +#include <X11/Xaw3d/Cardinals.h> +#include <X11/Xaw3d/Scrollbar.h> +#include <X11/Xaw3d/TextP.h> +#ifdef XAW_INTERNATIONALIZATION +#include <X11/Xaw3d/MultiSinkP.h> +#include <X11/Xaw3d/XawImP.h> +#endif +#include <X11/Xaw3d/ThreeDP.h> +#include <X11/Xfuncs.h> +#include <ctype.h> /* for isprint() */ + +#ifndef MAX_LEN_CT +#define MAX_LEN_CT 6 /* for sequence: ESC $ ( A \xx \xx */ +#endif + +unsigned long FMT8BIT = 0L; +unsigned long XawFmt8Bit = 0L; +#ifdef XAW_INTERNATIONALIZATION +unsigned long XawFmtWide = 0L; +#endif + +#define SinkClearToBG XawTextSinkClearToBackground + +#define SrcScan XawTextSourceScan +#define SrcRead XawTextSourceRead +#define SrcReplace XawTextSourceReplace +#define SrcSearch XawTextSourceSearch +#define SrcCvtSel XawTextSourceConvertSelection +#define SrcSetSelection XawTextSourceSetSelection + +#define BIGNUM ((Dimension)32023) +#define MULTI_CLICK_TIME 500L + +/* + * Compute a the maximum length of a cut buffer that we can pass at any + * time. The 64 allows for the overhead of the Change Property request. + */ + +#define MAX_CUT_LEN(dpy) (XMaxRequestSize(dpy) - 64) + +#define IsValidLine(ctx, num) ( ((num) == 0) || \ + ((ctx)->text.lt.info[(num)].position != 0) ) + +/* + * Defined in Text.c + */ +static void UnrealizeScrollbars(); +static void VScroll(), VJump(), HScroll(), HJump(), ClearWindow(); +static void DisplayTextWindow(), ModifySelection(), PushCopyQueue(); +static void UpdateTextInLine(), UpdateTextInRectangle(), PopCopyQueue(); +static void FlushUpdate(); +static Boolean LineAndXYForPosition(), TranslateExposeRegion(); +static XawTextPosition FindGoodPosition(), _BuildLineTable(); + +void _XawTextAlterSelection(), _XawTextExecuteUpdate(); +void _XawTextSetScrollBars(), _XawTextPrepareToUpdate(); + +/**************************************************************** + * + * Full class record constant + * + ****************************************************************/ + +static XawTextSelectType defaultSelectTypes[] = { + XawselectPosition, XawselectWord, XawselectLine, XawselectParagraph, + XawselectAll, XawselectNull, +}; + +static XPointer defaultSelectTypesPtr = (XPointer)defaultSelectTypes; +extern char *_XawDefaultTextTranslations1, *_XawDefaultTextTranslations2, + *_XawDefaultTextTranslations3, *_XawDefaultTextTranslations4; +static Dimension defWidth = 100; +static Dimension defHeight = DEFAULT_TEXT_HEIGHT; + +#define offset(field) XtOffsetOf(TextRec, field) +static XtResource resources[] = { + {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), + offset(core.width), XtRDimension, (XtPointer)&defWidth}, + {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), + offset(simple.cursor), XtRString, "xterm"}, + {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), + offset(core.height), XtRDimension, (XtPointer)&defHeight}, + {XtNdisplayPosition, XtCTextPosition, XtRInt, sizeof(XawTextPosition), + offset(text.lt.top), XtRImmediate, (XtPointer)0}, + {XtNinsertPosition, XtCTextPosition, XtRInt, sizeof(XawTextPosition), + offset(text.insertPos), XtRImmediate,(XtPointer)0}, + {XtNleftMargin, XtCMargin, XtRPosition, sizeof (Position), + offset(text.r_margin.left), XtRImmediate, (XtPointer)2}, + {XtNrightMargin, XtCMargin, XtRPosition, sizeof (Position), + offset(text.r_margin.right), XtRImmediate, (XtPointer)4}, + {XtNtopMargin, XtCMargin, XtRPosition, sizeof (Position), + offset(text.r_margin.top), XtRImmediate, (XtPointer)2}, + {XtNbottomMargin, XtCMargin, XtRPosition, sizeof (Position), + offset(text.r_margin.bottom), XtRImmediate, (XtPointer)2}, + {XtNselectTypes, XtCSelectTypes, XtRPointer, + sizeof(XawTextSelectType*), offset(text.sarray), + XtRPointer, (XtPointer)&defaultSelectTypesPtr}, + {XtNtextSource, XtCTextSource, XtRWidget, sizeof (Widget), + offset(text.source), XtRImmediate, NULL}, + {XtNtextSink, XtCTextSink, XtRWidget, sizeof (Widget), + offset(text.sink), XtRImmediate, NULL}, + {XtNdisplayCaret, XtCOutput, XtRBoolean, sizeof(Boolean), + offset(text.display_caret), XtRImmediate, (XtPointer)True}, + {XtNscrollVertical, XtCScroll, XtRScrollMode, sizeof(XawTextScrollMode), + offset(text.scroll_vert), XtRImmediate, (XtPointer) XawtextScrollNever}, + {XtNscrollHorizontal, XtCScroll, XtRScrollMode, sizeof(XawTextScrollMode), + offset(text.scroll_horiz), XtRImmediate, (XtPointer) XawtextScrollNever}, + {XtNwrap, XtCWrap, XtRWrapMode, sizeof(XawTextWrapMode), + offset(text.wrap), XtRImmediate, (XtPointer) XawtextWrapNever}, + {XtNresize, XtCResize, XtRResizeMode, sizeof(XawTextResizeMode), + offset(text.resize), XtRImmediate, (XtPointer) XawtextResizeNever}, + {XtNautoFill, XtCAutoFill, XtRBoolean, sizeof(Boolean), + offset(text.auto_fill), XtRImmediate, (XtPointer) FALSE}, + {XtNunrealizeCallback, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(text.unrealize_callbacks), XtRCallback, (XtPointer) NULL} +}; +#undef offset + +/* ARGSUSED */ +static void +CvtStringToScrollMode(args, num_args, fromVal, toVal) +XrmValuePtr args; /* unused */ +Cardinal *num_args; /* unused */ +XrmValuePtr fromVal; +XrmValuePtr toVal; +{ + static XawTextScrollMode scrollMode; + static XrmQuark QScrollNever, QScrollAlways, QScrollWhenNeeded; + XrmQuark q; + char lowerName[40]; + static Boolean inited = FALSE; + + if ( !inited ) { + QScrollNever = XrmPermStringToQuark(XtEtextScrollNever); + QScrollWhenNeeded = XrmPermStringToQuark(XtEtextScrollWhenNeeded); + QScrollAlways = XrmPermStringToQuark(XtEtextScrollAlways); + inited = TRUE; + } + + if (strlen ((char*) fromVal->addr) < sizeof lowerName) { + XmuCopyISOLatin1Lowered (lowerName, (char *)fromVal->addr); + q = XrmStringToQuark(lowerName); + + if (q == QScrollNever) scrollMode = XawtextScrollNever; + else if (q == QScrollWhenNeeded) scrollMode = XawtextScrollWhenNeeded; + else if (q == QScrollAlways) scrollMode = XawtextScrollAlways; + else { + toVal->size = 0; + toVal->addr = NULL; + return; + } + toVal->size = sizeof scrollMode; + toVal->addr = (XPointer) &scrollMode; + return; + } + toVal->size = 0; + toVal->addr = NULL; +} + +/* ARGSUSED */ +static void +CvtStringToWrapMode(args, num_args, fromVal, toVal) +XrmValuePtr args; /* unused */ +Cardinal *num_args; /* unused */ +XrmValuePtr fromVal; +XrmValuePtr toVal; +{ + static XawTextWrapMode wrapMode; + static XrmQuark QWrapNever, QWrapLine, QWrapWord; + XrmQuark q; + char lowerName[BUFSIZ]; + static Boolean inited = FALSE; + + if ( !inited ) { + QWrapNever = XrmPermStringToQuark(XtEtextWrapNever); + QWrapLine = XrmPermStringToQuark(XtEtextWrapLine); + QWrapWord = XrmPermStringToQuark(XtEtextWrapWord); + inited = TRUE; + } + + if (strlen ((char*) fromVal->addr) < sizeof lowerName) { + XmuCopyISOLatin1Lowered (lowerName, (char *)fromVal->addr); + q = XrmStringToQuark(lowerName); + + if (q == QWrapNever) wrapMode = XawtextWrapNever; + else if (q == QWrapLine) wrapMode = XawtextWrapLine; + else if (q == QWrapWord) wrapMode = XawtextWrapWord; + else { + toVal->size = 0; + toVal->addr = NULL; + return; + } + toVal->size = sizeof wrapMode; + toVal->addr = (XPointer) &wrapMode; + return; + } + toVal->size = 0; + toVal->addr = NULL; +} + +/* ARGSUSED */ +static void +CvtStringToResizeMode(args, num_args, fromVal, toVal) +XrmValuePtr args; /* unused */ +Cardinal *num_args; /* unused */ +XrmValuePtr fromVal; +XrmValuePtr toVal; +{ + static XawTextResizeMode resizeMode; + static XrmQuark QResizeNever, QResizeWidth, QResizeHeight, QResizeBoth; + XrmQuark q; + char lowerName[40]; + static Boolean inited = FALSE; + + if ( !inited ) { + QResizeNever = XrmPermStringToQuark(XtEtextResizeNever); + QResizeWidth = XrmPermStringToQuark(XtEtextResizeWidth); + QResizeHeight = XrmPermStringToQuark(XtEtextResizeHeight); + QResizeBoth = XrmPermStringToQuark(XtEtextResizeBoth); + inited = TRUE; + } + + if (strlen ((char*) fromVal->addr) < sizeof lowerName) { + XmuCopyISOLatin1Lowered (lowerName, (char *)fromVal->addr); + q = XrmStringToQuark(lowerName); + + if (q == QResizeNever) resizeMode = XawtextResizeNever; + else if (q == QResizeWidth) resizeMode = XawtextResizeWidth; + else if (q == QResizeHeight) resizeMode = XawtextResizeHeight; + else if (q == QResizeBoth) resizeMode = XawtextResizeBoth; + else { + toVal->size = 0; + toVal->addr = NULL; + return; + } + toVal->size = sizeof resizeMode; + toVal->addr = (XPointer) &resizeMode; + return; + } + toVal->size = 0; + toVal->addr = NULL; +} + +static void +ClassInitialize() +{ + int len1 = strlen (_XawDefaultTextTranslations1); + int len2 = strlen (_XawDefaultTextTranslations2); + int len3 = strlen (_XawDefaultTextTranslations3); + int len4 = strlen (_XawDefaultTextTranslations4); + char *buf = XtMalloc ((unsigned)(len1 + len2 + len3 + len4 + 1)); + char *cp = buf; + + if (!XawFmt8Bit) + FMT8BIT = XawFmt8Bit = XrmPermStringToQuark("FMT8BIT"); +#ifdef XAW_INTERNATIONALIZATION + if (!XawFmtWide) + XawFmtWide = XrmPermStringToQuark("FMTWIDE"); +#endif + + XawInitializeWidgetSet(); + +/* + * Set the number of actions. + */ + + textClassRec.core_class.num_actions = _XawTextActionsTableCount; + + (void) strcpy( cp, _XawDefaultTextTranslations1); cp += len1; + (void) strcpy( cp, _XawDefaultTextTranslations2); cp += len2; + (void) strcpy( cp, _XawDefaultTextTranslations3); cp += len3; + (void) strcpy( cp, _XawDefaultTextTranslations4); + textWidgetClass->core_class.tm_table = buf; + + XtAddConverter(XtRString, XtRScrollMode, CvtStringToScrollMode, + (XtConvertArgList)NULL, (Cardinal)0 ); + XtAddConverter(XtRString, XtRWrapMode, CvtStringToWrapMode, + (XtConvertArgList)NULL, (Cardinal)0 ); + XtAddConverter(XtRString, XtRResizeMode, CvtStringToResizeMode, + (XtConvertArgList)NULL, (Cardinal)0 ); +} + +/* Function Name: PositionHScrollBar. + * Description: Positions the Horizontal scrollbar. + * Arguments: ctx - the text widget. + * Returns: none + */ + +static void +PositionHScrollBar(ctx) +TextWidget ctx; +{ + Widget vbar = ctx->text.vbar, hbar = ctx->text.hbar; + Position top, left = 0; + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + if (ctx->text.hbar == NULL) return; + + if (vbar != NULL) + left += (Position) (vbar->core.width + vbar->core.border_width); + + XtResizeWidget( hbar, ctx->core.width - left - s, hbar->core.height, + hbar->core.border_width ); + + left = s / 2 - (Position) hbar->core.border_width; + if (left < 0) left = 0; + if (vbar != NULL) + left += (Position) (vbar->core.width + vbar->core.border_width); + + top = ctx->core.height - (hbar->core.height + hbar->core.border_width + s / 2); + + XtMoveWidget( hbar, left, top); +} + +/* Function Name: PositionVScrollBar. + * Description: Positions the Vertical scrollbar. + * Arguments: ctx - the text widget. + * Returns: none. + */ + +static void +PositionVScrollBar(ctx) +TextWidget ctx; +{ + Widget vbar = ctx->text.vbar; + Position pos; + Dimension bw; + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + if (vbar == NULL) return; + bw = vbar->core.border_width; + + XtResizeWidget( vbar, vbar->core.width, ctx->core.height - s, bw); + pos = s / 2 - (Position)bw; + if (pos < 0) pos = 0; + + XtMoveWidget( vbar, pos, pos); +} + +static void +CreateVScrollBar(ctx) +TextWidget ctx; +{ + Widget vbar; + + if (ctx->text.vbar != NULL) return; + + ctx->text.vbar = vbar = + XtCreateWidget("vScrollbar", scrollbarWidgetClass, (Widget)ctx, + (ArgList) NULL, ZERO); + XtAddCallback( vbar, XtNscrollProc, VScroll, (XtPointer)ctx ); + XtAddCallback( vbar, XtNjumpProc, VJump, (XtPointer)ctx ); + if (ctx->text.hbar == NULL) + XtAddCallback((Widget) ctx, XtNunrealizeCallback, UnrealizeScrollbars, + (XtPointer) NULL); + + ctx->text.r_margin.left += vbar->core.width + vbar->core.border_width; + ctx->text.margin.left = ctx->text.r_margin.left; + + PositionVScrollBar(ctx); + PositionHScrollBar(ctx); /* May modify location of Horiz. Bar. */ + + if (XtIsRealized((Widget)ctx)) { + XtRealizeWidget(vbar); + XtMapWidget(vbar); + } +} + +/* Function Name: DestroyVScrollBar + * Description: Removes a vertical ScrollBar. + * Arguments: ctx - the parent text widget. + * Returns: none. + */ + +static void +DestroyVScrollBar(ctx) +TextWidget ctx; +{ + Widget vbar = ctx->text.vbar; + + if (vbar == NULL) return; + + ctx->text.r_margin.left -= vbar->core.width + vbar->core.border_width; + ctx->text.margin.left = ctx->text.r_margin.left; + if (ctx->text.hbar == NULL) + XtRemoveCallback((Widget) ctx, XtNunrealizeCallback, UnrealizeScrollbars, + (XtPointer) NULL); + XtDestroyWidget(vbar); + ctx->text.vbar = NULL; + PositionHScrollBar(ctx); +} + +static void +CreateHScrollBar(ctx) +TextWidget ctx; +{ + Arg args[1]; + Widget hbar; + + if (ctx->text.hbar != NULL) return; + + XtSetArg(args[0], XtNorientation, XtorientHorizontal); + ctx->text.hbar = hbar = + XtCreateWidget("hScrollbar", scrollbarWidgetClass, (Widget)ctx, args, ONE); + XtAddCallback( hbar, XtNscrollProc, HScroll, (XtPointer)ctx ); + XtAddCallback( hbar, XtNjumpProc, HJump, (XtPointer)ctx ); + if (ctx->text.vbar == NULL) + XtAddCallback((Widget) ctx, XtNunrealizeCallback, UnrealizeScrollbars, + (XtPointer) NULL); + +/**/ + ctx->text.r_margin.bottom += hbar->core.height + hbar->core.border_width; + ctx->text.margin.bottom = ctx->text.r_margin.bottom; +/**/ + PositionHScrollBar(ctx); + if (XtIsRealized((Widget)ctx)) { + XtRealizeWidget(hbar); + XtMapWidget(hbar); + } +} + +/* Function Name: DestroyHScrollBar + * Description: Removes a horizontal ScrollBar. + * Arguments: ctx - the parent text widget. + * Returns: none. + */ + +static void +DestroyHScrollBar(ctx) +TextWidget ctx; +{ + Widget hbar = ctx->text.hbar; + + if (hbar == NULL) return; + +/**/ + ctx->text.r_margin.bottom -= hbar->core.height + hbar->core.border_width; + ctx->text.margin.bottom = ctx->text.r_margin.bottom; +/**/ + if (ctx->text.vbar == NULL) + XtRemoveCallback((Widget) ctx, XtNunrealizeCallback, UnrealizeScrollbars, + (XtPointer) NULL); + XtDestroyWidget(hbar); + ctx->text.hbar = NULL; +} + +/* ARGSUSED */ +static void +Initialize(request, new, args, num_args) +Widget request, new; +ArgList args; /* unused */ +Cardinal *num_args; /* unused */ +{ + TextWidget ctx = (TextWidget) new; + char error_buf[BUFSIZ]; + int s; + + ctx->text.threeD = XtVaCreateWidget("threeD", threeDWidgetClass, new, + XtNx, 0, XtNy, 0, + XtNwidth, 10, XtNheight, 10, /* dummy */ + NULL); + + s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + ctx->text.r_margin.left += s; + ctx->text.r_margin.right += s; + ctx->text.r_margin.top += s; + ctx->text.r_margin.bottom += s - 1; + + ctx->text.lt.lines = 0; + ctx->text.lt.info = NULL; + (void) bzero((char *) &(ctx->text.origSel), sizeof(XawTextSelection)); + (void) bzero((char *) &(ctx->text.s), sizeof(XawTextSelection)); + ctx->text.s.type = XawselectPosition; + ctx->text.salt = NULL; + ctx->text.hbar = ctx->text.vbar = (Widget) NULL; + ctx->text.lasttime = 0; /* ||| correct? */ + ctx->text.time = 0; /* ||| correct? */ + ctx->text.showposition = TRUE; + ctx->text.lastPos = (ctx->text.source != NULL) ? GETLASTPOS : 0; + ctx->text.file_insert = NULL; + ctx->text.search = NULL; + ctx->text.updateFrom = (XawTextPosition *) XtMalloc((unsigned) ONE); + ctx->text.updateTo = (XawTextPosition *) XtMalloc((unsigned) ONE); + ctx->text.numranges = ctx->text.maxranges = 0; + ctx->text.gc = DefaultGCOfScreen(XtScreen(ctx)); + ctx->text.hasfocus = FALSE; + ctx->text.margin = ctx->text.r_margin; /* Strucure copy. */ + ctx->text.update_disabled = FALSE; + ctx->text.old_insert = -1; + ctx->text.mult = 1; + ctx->text.single_char = FALSE; + ctx->text.copy_area_offsets = NULL; + ctx->text.salt2 = NULL; + + if (ctx->core.height == DEFAULT_TEXT_HEIGHT) { + ctx->core.height = VMargins(ctx); + if (ctx->text.sink != NULL) + ctx->core.height += XawTextSinkMaxHeight(ctx->text.sink, 1); + } + + if (ctx->text.scroll_vert != XawtextScrollNever) + if ( (ctx->text.resize == XawtextResizeHeight) || + (ctx->text.resize == XawtextResizeBoth) ) { + (void) sprintf(error_buf, "Xaw Text Widget %s:\n %s %s.", ctx->core.name, + "Vertical scrolling not allowed with height resize.\n", + "Vertical scrolling has been DEACTIVATED."); + XtAppWarning(XtWidgetToApplicationContext(new), error_buf); + ctx->text.scroll_vert = XawtextScrollNever; + } + else if (ctx->text.scroll_vert == XawtextScrollAlways) + CreateVScrollBar(ctx); + + if (ctx->text.scroll_horiz != XawtextScrollNever) + if (ctx->text.wrap != XawtextWrapNever) { + (void) sprintf(error_buf, "Xaw Text Widget %s:\n %s %s.", ctx->core.name, + "Horizontal scrolling not allowed with wrapping active.\n", + "Horizontal scrolling has been DEACTIVATED."); + XtAppWarning(XtWidgetToApplicationContext(new), error_buf); + ctx->text.scroll_horiz = XawtextScrollNever; + } + else if ( (ctx->text.resize == XawtextResizeWidth) || + (ctx->text.resize == XawtextResizeBoth) ) { + (void) sprintf(error_buf, "Xaw Text Widget %s:\n %s %s.", ctx->core.name, + "Horizontal scrolling not allowed with width resize.\n", + "Horizontal scrolling has been DEACTIVATED."); + XtAppWarning(XtWidgetToApplicationContext(new), error_buf); + ctx->text.scroll_horiz = XawtextScrollNever; + } + else if (ctx->text.scroll_horiz == XawtextScrollAlways) + CreateHScrollBar(ctx); +} + +static void +Realize( w, valueMask, attributes ) +Widget w; +Mask *valueMask; +XSetWindowAttributes *attributes; +{ + TextWidget ctx = (TextWidget)w; + void _XawTextCheckResize(); + + (*textClassRec.core_class.superclass->core_class.realize) + (w, valueMask, attributes); + + if (ctx->text.hbar != NULL) { /* Put up Hbar -- Must be first. */ + XtRealizeWidget(ctx->text.hbar); + XtMapWidget(ctx->text.hbar); + } + + if (ctx->text.vbar != NULL) { /* Put up Vbar. */ + XtRealizeWidget(ctx->text.vbar); + XtMapWidget(ctx->text.vbar); + } + + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); + _XawTextSetScrollBars(ctx); + _XawTextCheckResize(ctx); +} + +/*ARGSUSED*/ +static void UnrealizeScrollbars(widget, client, call) +Widget widget; /* Text widget */ +XtPointer client; /* unused */ +XtPointer call; /* unused */ +{ + TextWidget ctx = (TextWidget) widget; + + if (ctx->text.hbar) + XtUnrealizeWidget(ctx->text.hbar); + if (ctx->text.vbar) + XtUnrealizeWidget(ctx->text.vbar); +} + +/* Utility routines for support of Text */ + +static void +_CreateCutBuffers(d) +Display *d; +{ + static struct _DisplayRec { + struct _DisplayRec *next; + Display *dpy; + } *dpy_list = NULL; + struct _DisplayRec *dpy_ptr; + + for (dpy_ptr = dpy_list; dpy_ptr != NULL; dpy_ptr = dpy_ptr->next) + if (dpy_ptr->dpy == d) return; + + dpy_ptr = XtNew(struct _DisplayRec); + dpy_ptr->next = dpy_list; + dpy_ptr->dpy = d; + dpy_list = dpy_ptr; + +#define Create(buffer) \ + XChangeProperty(d, RootWindow(d, 0), buffer, XA_STRING, 8, \ + PropModeAppend, NULL, 0 ); + + Create( XA_CUT_BUFFER0 ); + Create( XA_CUT_BUFFER1 ); + Create( XA_CUT_BUFFER2 ); + Create( XA_CUT_BUFFER3 ); + Create( XA_CUT_BUFFER4 ); + Create( XA_CUT_BUFFER5 ); + Create( XA_CUT_BUFFER6 ); + Create( XA_CUT_BUFFER7 ); + +#undef Create +} + +/* + * Procedure to manage insert cursor visibility for editable text. It uses + * the value of ctx->insertPos and an implicit argument. In the event that + * position is immediately preceded by an eol graphic, then the insert cursor + * is displayed at the beginning of the next line. +*/ +static void +InsertCursor (w, state) +Widget w; +XawTextInsertState state; +{ + TextWidget ctx = (TextWidget)w; + Position x, y; + int line; + + if (ctx->text.lt.lines < 1) return; + + if ( LineAndXYForPosition(ctx, ctx->text.insertPos, &line, &x, &y) ) { + if (line < ctx->text.lt.lines) + y += (ctx->text.lt.info[line + 1].y - ctx->text.lt.info[line].y) + 1; + else + y += (ctx->text.lt.info[line].y - ctx->text.lt.info[line - 1].y) + 1; + + if (ctx->text.display_caret) + XawTextSinkInsertCursor(ctx->text.sink, x, y, state); + } + ctx->text.ev_x = x; + ctx->text.ev_y = y; + + /* Keep Input Method up to speed */ + +#ifdef XAW_INTERNATIONALIZATION + if ( ctx->simple.international ) { + Arg list[1]; + + XtSetArg (list[0], XtNinsertPosition, ctx->text.insertPos); + _XawImSetValues (w, list, 1); + } +#endif +} + +/* + * Procedure to register a span of text that is no longer valid on the display + * It is used to avoid a number of small, and potentially overlapping, screen + * updates. +*/ + +void +_XawTextNeedsUpdating(ctx, left, right) +TextWidget ctx; +XawTextPosition left, right; +{ + int i; + if (left < right) { + for (i = 0; i < ctx->text.numranges; i++) { + if (left <= ctx->text.updateTo[i] && right >= ctx->text.updateFrom[i]) { + ctx->text.updateFrom[i] = Min(left, ctx->text.updateFrom[i]); + ctx->text.updateTo[i] = Max(right, ctx->text.updateTo[i]); + return; + } + } + ctx->text.numranges++; + if (ctx->text.numranges > ctx->text.maxranges) { + ctx->text.maxranges = ctx->text.numranges; + i = ctx->text.maxranges * sizeof(XawTextPosition); + ctx->text.updateFrom = (XawTextPosition *) + XtRealloc((char *)ctx->text.updateFrom, (unsigned) i); + ctx->text.updateTo = (XawTextPosition *) + XtRealloc((char *)ctx->text.updateTo, (unsigned) i); + } + ctx->text.updateFrom[ctx->text.numranges - 1] = left; + ctx->text.updateTo[ctx->text.numranges - 1] = right; + } +} + +/* + * Procedure to read a span of text in Ascii form. This is purely a hack and + * we probably need to add a function to sources to provide this functionality. + * [note: this is really a private procedure but is used in multiple modules]. + */ + +char * +_XawTextGetText(ctx, left, right) +TextWidget ctx; +XawTextPosition left, right; +{ + char *result, *tempResult; + XawTextBlock text; + int bytes; + + if (_XawTextFormat(ctx) == XawFmt8Bit) + bytes = sizeof(unsigned char); +#ifdef XAW_INTERNATIONALIZATION + else if (_XawTextFormat(ctx) == XawFmtWide) + bytes = sizeof(wchar_t); +#endif + else /* if there is another fomat, add here */ + bytes = 1; + + /* leave space for ZERO */ + tempResult=result=XtMalloc( (unsigned)(((Cardinal)(right-left))+ONE )* bytes); + while (left < right) { + left = SrcRead(ctx->text.source, left, &text, (int)(right - left)); + if (!text.length) + break; + memmove(tempResult, text.ptr, text.length * bytes); + tempResult += text.length * bytes; + } + + if (bytes == sizeof(wchar_t)) + *((wchar_t*)tempResult) = (wchar_t)0; + else + *tempResult = '\0'; + return(result); +} + +/* Like _XawTextGetText, but enforces ICCCM STRING type encoding. This +routine is currently used to put just the ASCII chars in the selection into a +cut buffer. */ + +char * +_XawTextGetSTRING(ctx, left, right) +TextWidget ctx; +XawTextPosition left, right; +{ + unsigned char *s; + unsigned char c; + long i, j, n; + wchar_t *ws, wc; + + /* allow ESC in accordance with ICCCM */ +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide) { + MultiSinkObject sink = (MultiSinkObject) ctx->text.sink; + ws = (wchar_t *)_XawTextGetText(ctx, left, right); + n = wcslen(ws); + for (j = 0, i = 0; j < n; j++) { + wc = ws[j]; + if (XwcTextEscapement (sink->multi_sink.fontset, &wc, 1) || + (wc == _Xaw_atowc(XawTAB)) || (wc == _Xaw_atowc(XawLF)) || (wc == _Xaw_atowc(XawESC))) + ws[i++] = wc; + } + ws[i] = (wchar_t)0; + return (char *)ws; + } else +#endif + { + s = (unsigned char *)_XawTextGetText(ctx, left, right); + /* only HT and NL control chars are allowed, strip out others */ + n = strlen((char *)s); + i = 0; + for (j = 0; j < n; j++) { + c = s[j]; + if (((c >= 0x20) && c <= 0x7f) || + (c >= 0xa0) || (c == XawTAB) || (c == XawLF) || (c == XawESC)) { + s[i] = c; + i++; + } + } + s[i] = 0; + return (char *)s; + } +#undef ESC + +} + +/* + * This routine maps an x and y position in a window that is displaying text + * into the corresponding position in the source. + * + * NOTE: it is illegal to call this routine unless there is a valid line table! + */ + +/*** figure out what line it is on ***/ + +static XawTextPosition +PositionForXY (ctx, x, y) +TextWidget ctx; +Position x,y; +{ + int fromx, line, width, height; + XawTextPosition position; + + if (ctx->text.lt.lines == 0) return 0; + + for (line = 0; line < ctx->text.lt.lines - 1; line++) { + if (y <= ctx->text.lt.info[line + 1].y) + break; + } + position = ctx->text.lt.info[line].position; + if (position >= ctx->text.lastPos) + return(ctx->text.lastPos); + fromx = (int) ctx->text.margin.left; + XawTextSinkFindPosition( ctx->text.sink, position, fromx, x - fromx, + FALSE, &position, &width, &height); + if (position > ctx->text.lastPos) return(ctx->text.lastPos); + if (position >= ctx->text.lt.info[line + 1].position) + position = SrcScan(ctx->text.source, ctx->text.lt.info[line + 1].position, + XawstPositions, XawsdLeft, 1, TRUE); + return(position); +} + +/* + * This routine maps a source position in to the corresponding line number + * of the text that is displayed in the window. + * + * NOTE: It is illegal to call this routine unless there is a valid line table! + */ + +static int +LineForPosition (ctx, position) +TextWidget ctx; +XawTextPosition position; +{ + int line; + + for (line = 0; line < ctx->text.lt.lines; line++) + if (position < ctx->text.lt.info[line + 1].position) + break; + return(line); +} + +/* + * This routine maps a source position into the corresponding line number + * and the x, y coordinates of the text that is displayed in the window. + * + * NOTE: It is illegal to call this routine unless there is a valid line table! + */ + +static Boolean +LineAndXYForPosition (ctx, pos, line, x, y) +TextWidget ctx; +XawTextPosition pos; +int *line; +Position *x, *y; +{ + XawTextPosition linePos, endPos; + Boolean visible; + int realW, realH; + + *line = 0; + *x = ctx->text.margin.left; + *y = ctx->text.margin.top; + if ((visible = IsPositionVisible(ctx, pos))) { + *line = LineForPosition(ctx, pos); + *y = ctx->text.lt.info[*line].y; + *x = ctx->text.margin.left; + linePos = ctx->text.lt.info[*line].position; + XawTextSinkFindDistance( ctx->text.sink, linePos, + *x, pos, &realW, &endPos, &realH); + *x += realW; + } + return(visible); +} + +/* + * This routine builds a line table. It does this by starting at the + * specified position and measuring text to determine the staring position + * of each line to be displayed. It also determines and saves in the + * linetable all the required metrics for displaying a given line (e.g. + * x offset, y offset, line length, etc.). + */ + +void +#if NeedFunctionPrototypes +_XawTextBuildLineTable ( + TextWidget ctx, + XawTextPosition position, + _XtBoolean force_rebuild) +#else +_XawTextBuildLineTable (ctx, position, force_rebuild) + TextWidget ctx; + XawTextPosition position; + Boolean force_rebuild; +#endif +{ + Dimension height = 0; + int lines = 0; + Cardinal size; + + if ((int)ctx->core.height > VMargins(ctx)) { + height = ctx->core.height - VMargins(ctx); + lines = XawTextSinkMaxLines(ctx->text.sink, height); + } + size = sizeof(XawTextLineTableEntry) * (lines + 1); + + if ( (lines != ctx->text.lt.lines) || (ctx->text.lt.info == NULL) ) { + ctx->text.lt.info = (XawTextLineTableEntry *) XtRealloc((char *) ctx->text. + lt.info, size); + ctx->text.lt.lines = lines; + force_rebuild = TRUE; + } + + if ( force_rebuild || (position != ctx->text.lt.top) ) { + (void) bzero((char *) ctx->text.lt.info, size); + (void) _BuildLineTable(ctx, ctx->text.lt.top = position, zeroPosition, 0); + } +} + +/* + * This assumes that the line table does not change size. + */ + +static XawTextPosition +_BuildLineTable(ctx, position, min_pos, line) +TextWidget ctx; +XawTextPosition position, min_pos; +int line; +{ + XawTextLineTableEntry * lt = ctx->text.lt.info + line; + XawTextPosition endPos; + Position y; + int count, width, realW, realH; + Widget src = ctx->text.source; + + if ( ((ctx->text.resize == XawtextResizeWidth) || + (ctx->text.resize == XawtextResizeBoth) ) || + (ctx->text.wrap == XawtextWrapNever) ) + width = BIGNUM; + else + width = Max(0, ((int)ctx->core.width - (int)HMargins(ctx))); + + y = ( (line == 0) ? ctx->text.margin.top : lt->y ); + + /* CONSTCOND */ + while ( TRUE ) { + lt->y = y; + lt->position = position; + + XawTextSinkFindPosition( ctx->text.sink, position, ctx->text.margin.left, + width, ctx->text.wrap == XawtextWrapWord, + &endPos, &realW, &realH); + lt->textWidth = realW; + y += realH; + + if (ctx->text.wrap == XawtextWrapNever) + endPos = SrcScan(src, position, XawstEOL, XawsdRight, 1, TRUE); + + if ( endPos == ctx->text.lastPos) { /* We have reached the end. */ + if(SrcScan(src, position, XawstEOL, XawsdRight, 1, FALSE) == endPos) + break; + } + + ++lt; + ++line; + if ( (line > ctx->text.lt.lines) || + ((lt->position == (position = endPos)) && (position > min_pos)) ) + return(position); + } + +/* + * If we are at the end of the buffer put two special lines in the table. + * + * a) Both have position > text.lastPos and lt->textWidth = 0. + * b) The first has a real height, and the second has a height that + * is the rest of the screen. + * + * I could fill in the rest of the table with valid heights and a large + * lastPos, but this method keeps the number of fill regions down to a + * minimum. + * + * One valid entry is needed at the end of the table so that the cursor + * does not jump off the bottom of the window. + */ + + for ( count = 0; count < 2 ; count++) + if (line++ < ctx->text.lt.lines) { /* make sure not to run of the end. */ + (++lt)->y = (count == 0) ? y : ctx->core.height + - 2 * ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + lt->textWidth = 0; + lt->position = ctx->text.lastPos + 100; + } + + if (line < ctx->text.lt.lines) /* Clear out rest of table. */ + (void) bzero( (char *) (lt + 1), + (ctx->text.lt.lines - line) * sizeof(XawTextLineTableEntry) ); + + ctx->text.lt.info[ctx->text.lt.lines].position = lt->position; + + return(endPos); +} + +/* Function Name: GetWidestLine + * Description: Returns the width (in pixels) of the widest line that + * is currently visable. + * Arguments: ctx - the text widget. + * Returns: the width of the widest line. + * + * NOTE: This function requires a valid line table. + */ + +static Dimension +GetWidestLine(ctx) +TextWidget ctx; +{ + int i; + Dimension widest; + XawTextLineTablePtr lt = &(ctx->text.lt); + + for (i = 0, widest = 1 ; i < lt->lines ; i++) + if (widest < lt->info[i].textWidth) + widest = lt->info[i].textWidth; + + return(widest); +} + +static void +CheckVBarScrolling(ctx) +TextWidget ctx; +{ + float first, last; + Boolean temp = (ctx->text.vbar == NULL); + + if (ctx->text.scroll_vert == XawtextScrollNever) return; + + if ( (ctx->text.lastPos > 0) && (ctx->text.lt.lines > 0)) { + first = ctx->text.lt.top; + first /= (float) ctx->text.lastPos; + last = ctx->text.lt.info[ctx->text.lt.lines].position; + if ( ctx->text.lt.info[ctx->text.lt.lines].position < ctx->text.lastPos ) + last /= (float) ctx->text.lastPos; + else + last = 1.0; + + if (ctx->text.scroll_vert == XawtextScrollWhenNeeded) { + int line; + XawTextPosition last_pos; + Position y = ctx->core.height - ctx->text.margin.bottom; + + if (ctx->text.hbar != NULL) + y -= (ctx->text.hbar->core.height + + 2 * ctx->text.hbar->core.border_width); + + last_pos = PositionForXY(ctx, (Position) ctx->core.width, y); + line = LineForPosition(ctx, last_pos); + + if ( (y < ctx->text.lt.info[line + 1].y) || ((last - first) < 1.0) ) + CreateVScrollBar(ctx); + else + DestroyVScrollBar(ctx); + } + + if (ctx->text.vbar != NULL) + XawScrollbarSetThumb(ctx->text.vbar, first, last - first); + + if ( (ctx->text.vbar == NULL) != temp) { + _XawTextNeedsUpdating(ctx, zeroPosition, ctx->text.lastPos); + if (ctx->text.vbar == NULL) + _XawTextBuildLineTable (ctx, zeroPosition, FALSE); + } + } + else if (ctx->text.vbar != NULL) + if (ctx->text.scroll_vert == XawtextScrollWhenNeeded) + DestroyVScrollBar(ctx); + else if (ctx->text.scroll_vert == XawtextScrollAlways) + XawScrollbarSetThumb(ctx->text.vbar, 0.0, 1.0); +} + +/* + * This routine is used by Text to notify an associated scrollbar of the + * correct metrics (position and shown fraction) for the text being currently + * displayed in the window. + */ + +void +_XawTextSetScrollBars(ctx) +TextWidget ctx; +{ + float first, last, widest; + Boolean temp = (ctx->text.hbar == NULL); + Boolean vtemp = (ctx->text.vbar == NULL); + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + CheckVBarScrolling(ctx); + + if (ctx->text.scroll_horiz == XawtextScrollNever) return; + + if (ctx->text.vbar != NULL) + widest = (int)(ctx->core.width - ctx->text.vbar->core.width - + 2 * s - ctx->text.vbar->core.border_width); + else + widest = ctx->core.width - 2 * s; + widest /= (last = GetWidestLine(ctx)); + if (ctx->text.scroll_horiz == XawtextScrollWhenNeeded) + if (widest < 1.0) + CreateHScrollBar(ctx); + else + DestroyHScrollBar(ctx); + + if ( (ctx->text.hbar == NULL) != temp ) { + _XawTextBuildLineTable (ctx, ctx->text.lt.top, TRUE); + CheckVBarScrolling(ctx); /* Recheck need for vbar, now that we added + or removed the hbar.*/ + } + + if (ctx->text.hbar != NULL) { + first = ctx->text.r_margin.left - ctx->text.margin.left; + first /= last; + XawScrollbarSetThumb(ctx->text.hbar, first, widest); + } + + if (((ctx->text.hbar == NULL) && (ctx->text.margin.left != + ctx->text.r_margin.left)) || + (ctx->text.vbar == NULL) != vtemp) + { + ctx->text.margin.left = ctx->text.r_margin.left; + _XawTextNeedsUpdating(ctx, zeroPosition, ctx->text.lastPos); + FlushUpdate(ctx); + } +} + +/* + * The routine will scroll the displayed text by lines. If the arg is + * positive, move up; otherwise, move down. [note: this is really a private + * procedure but is used in multiple modules]. + */ + +void +_XawTextVScroll(ctx, n) +TextWidget ctx; +int n; +{ + XawTextPosition top, target; + int y; + Arg list[1]; + XawTextLineTable * lt = &(ctx->text.lt); + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + if (abs(n) > ctx->text.lt.lines) + n = (n > 0) ? ctx->text.lt.lines : -ctx->text.lt.lines; + + if (n == 0) return; + + if (n > 0) { + if ( IsValidLine(ctx, n) ) + top = Min(lt->info[n].position, ctx->text.lastPos); + else + top = ctx->text.lastPos; + + y = IsValidLine(ctx, n) ? lt->info[n].y : ctx->core.height - 2 * s; + _XawTextBuildLineTable(ctx, top, FALSE); + if (top >= ctx->text.lastPos) + DisplayTextWindow( (Widget) ctx); + else { + XCopyArea(XtDisplay(ctx), XtWindow(ctx), XtWindow(ctx), ctx->text.gc, + s, y, (int)ctx->core.width - 2 * s, (int)ctx->core.height - y - s, + s, ctx->text.margin.top); + + PushCopyQueue(ctx, 0, (int) -y); + SinkClearToBG(ctx->text.sink, + (Position) s, + (Position) (ctx->text.margin.top + ctx->core.height - y - s), + (Dimension) ctx->core.width - 2 * s, + (Dimension) ctx->core.height - 2 * s); + + if (n < lt->lines) n++; /* update descenders at bottom */ + _XawTextNeedsUpdating(ctx, lt->info[lt->lines - n].position, + ctx->text.lastPos); + _XawTextSetScrollBars(ctx); + } + } + else { + XawTextPosition updateTo; + unsigned int height, clear_height; + + n = -n; + target = lt->top; + top = SrcScan(ctx->text.source, target, XawstEOL, + XawsdLeft, n+1, FALSE); + + _XawTextBuildLineTable(ctx, top, FALSE); + y = IsValidLine(ctx, n) ? lt->info[n].y : ctx->core.height - 2 * s; + updateTo = IsValidLine(ctx, n) ? lt->info[n].position : ctx->text.lastPos; + if (IsValidLine(ctx, lt->lines - n)) + height = lt->info[lt->lines-n].y - ctx->text.margin.top; + else if (ctx->core.height - HMargins(ctx)) + height = ctx->core.height - HMargins(ctx); + else + height = 0; + if (y > (int) ctx->text.margin.top) + clear_height = y - ctx->text.margin.top; + else + clear_height = 0; + + if ( updateTo == target ) { + XCopyArea(XtDisplay(ctx), XtWindow(ctx), XtWindow(ctx), ctx->text.gc, + s, ctx->text.margin.top, (int) ctx->core.width - 2 * s, + height, s, y); + PushCopyQueue(ctx, 0, (int) y); + SinkClearToBG(ctx->text.sink, (Position) s, ctx->text.margin.top, + (Dimension) ctx->core.width - 2 * s, (Dimension) clear_height); + + _XawTextNeedsUpdating(ctx, lt->info[0].position, updateTo); + _XawTextSetScrollBars(ctx); + } + else if (lt->top != target) + DisplayTextWindow((Widget)ctx); + } + XtSetArg (list[0], XtNinsertPosition, ctx->text.lt.top+ctx->text.lt.lines); +#ifdef XAW_INTERNATIONALIZATION + _XawImSetValues ((Widget) ctx, list, 1); +#endif + + _ShadowSurroundedBox((Widget)ctx, (ThreeDWidget)ctx->text.threeD, + 0, 0, ctx->core.width, ctx->core.height, + ((ThreeDWidget)ctx->text.threeD)->threeD.relief, False); +} + +/*ARGSUSED*/ +static void +HScroll(w, closure, callData) +Widget w; +XtPointer closure; /* TextWidget */ +XtPointer callData; /* #pixels */ +{ + TextWidget ctx = (TextWidget) closure; + Widget tw = (Widget) ctx; + Position old_left, pixels = (Position)(int) callData; + XRectangle rect, t_rect; + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + _XawTextPrepareToUpdate(ctx); + + old_left = ctx->text.margin.left; + ctx->text.margin.left -= pixels; + if (ctx->text.margin.left > ctx->text.r_margin.left) { + ctx->text.margin.left = ctx->text.r_margin.left; + pixels = old_left - ctx->text.margin.left; + } + + if (pixels > 0) { + rect.width = (unsigned short) pixels + ctx->text.margin.right; + rect.x = (short) ctx->core.width - (short) rect.width; + rect.y = (short) ctx->text.margin.top; + rect.height = (unsigned short) ctx->core.height - rect.y - 2 * s; + + XCopyArea(XtDisplay(tw), XtWindow(tw), XtWindow(tw), ctx->text.gc, + pixels + s, (int) rect.y, + (unsigned int) rect.x, (unsigned int) ctx->core.height - 2 * s, + s, (int) rect.y); + + PushCopyQueue(ctx, (int) -pixels, 0); + } + else if (pixels < 0) { + rect.x = s; + + if (ctx->text.vbar != NULL) + rect.x += (short) (ctx->text.vbar->core.width + + ctx->text.vbar->core.border_width); + + rect.width = (Position) - pixels; + rect.y = ctx->text.margin.top; + rect.height = ctx->core.height - rect.y - 2 * s; + + XCopyArea(XtDisplay(tw), XtWindow(tw), XtWindow(tw), ctx->text.gc, + (int) rect.x, (int) rect.y, + (unsigned int) ctx->core.width - rect.width - 2 * s, + (unsigned int) rect.height, + (int) rect.x + rect.width, (int) rect.y); + + PushCopyQueue(ctx, (int) rect.width, 0); + +/* + * Redraw the line overflow marks. + */ + + t_rect.x = ctx->core.width - ctx->text.margin.right - s; + t_rect.width = ctx->text.margin.right; + t_rect.y = rect.y; + t_rect.height = rect.height - 2 * s; + + SinkClearToBG(ctx->text.sink, (Position) t_rect.x, (Position) t_rect.y, + (Dimension) t_rect.width, (Dimension) t_rect.height); + + UpdateTextInRectangle(ctx, &t_rect); + } + +/* + * Put in the text that just became visible. + */ + + if ( pixels != 0 ) { + SinkClearToBG(ctx->text.sink, (Position) rect.x, (Position) rect.y, + (Dimension) rect.width, (Dimension) rect.height); + + UpdateTextInRectangle(ctx, &rect); + } + _XawTextExecuteUpdate(ctx); + _XawTextSetScrollBars(ctx); +} + +/*ARGSUSED*/ +static void +HJump(w, closure, callData) +Widget w; +XtPointer closure, callData; /* closure = TextWidget, callData = percent. */ +{ + TextWidget ctx = (TextWidget) closure; + float * percent = (float *) callData; + Position new_left, old_left = ctx->text.margin.left; + + long move; /*difference of Positions can be bigger than Position; lint err */ + + new_left = ctx->text.r_margin.left; + new_left -= (Position) (*percent * GetWidestLine(ctx)); + move = old_left - new_left; + + if (abs(move) < (int)ctx->core.width) { + HScroll(w, (XtPointer) ctx, (XtPointer) move); + return; + } + _XawTextPrepareToUpdate(ctx); + ctx->text.margin.left = new_left; + if (XtIsRealized((Widget) ctx)) DisplayTextWindow((Widget) ctx); + _XawTextExecuteUpdate(ctx); +} + +/* Function Name: UpdateTextInLine + * Description: Updates some text in a given line. + * Arguments: ctx - the text widget. + * line - the line number (in the line table) of this line. + * left, right - left and right pixel offsets of the + * area to update. + * Returns: none. + */ + +static void +UpdateTextInLine(ctx, line, left, right) +TextWidget ctx; +int line; +Position left, right; +{ + XawTextPosition pos1, pos2; + int width, height, local_left, local_width; + XawTextLineTableEntry * lt = ctx->text.lt.info + line; + + if ( ((int)(lt->textWidth + ctx->text.margin.left) < left) || + ( ctx->text.margin.left > right ) ) + return; /* no need to update. */ + + local_width = left - ctx->text.margin.left; + XawTextSinkFindPosition(ctx->text.sink, lt->position, + (int) ctx->text.margin.left, + local_width, FALSE, &pos1, &width, &height); + + if (right >= (Position) lt->textWidth - ctx->text.margin.left) + if ( (IsValidLine(ctx, line + 1)) && + (ctx->text.lt.info[line + 1].position <= ctx->text.lastPos) ) + pos2 = SrcScan( ctx->text.source, (lt + 1)->position, XawstPositions, + XawsdLeft, 1, TRUE); + else + pos2 = GETLASTPOS; + else { + XawTextPosition t_pos; + + local_left = ctx->text.margin.left + width; + local_width = right - local_left; + XawTextSinkFindPosition(ctx->text.sink, pos1, local_left, + local_width, FALSE, &pos2, &width, &height); + + t_pos = SrcScan( ctx->text.source, pos2, + XawstPositions, XawsdRight, 1, TRUE); + if (t_pos < (lt + 1)->position) + pos2 = t_pos; + } + + _XawTextNeedsUpdating(ctx, pos1, pos2); +} + +/* + * The routine will scroll the displayed text by pixels. If the calldata is + * positive, move up; otherwise, move down. + */ + +/*ARGSUSED*/ +static void +VScroll(w, closure, callData) +Widget w; +XtPointer closure; /* TextWidget */ +XtPointer callData; /* #pixels */ +{ + TextWidget ctx = (TextWidget)closure; + int height, nlines, lines = (int) callData; + + height = ctx->core.height - VMargins(ctx); + if (height < 1) + height = 1; + nlines = (int) (lines * (int) ctx->text.lt.lines) / height; +#ifdef XAW_ARROW_SCROLLBARS + if (nlines == 0 && lines != 0) + nlines = lines > 0 ? 1 : -1; +#endif + _XawTextPrepareToUpdate(ctx); + _XawTextVScroll(ctx, nlines); + _XawTextExecuteUpdate(ctx); +} + +/* + * The routine "thumbs" the displayed text. Thumbing means reposition the + * displayed view of the source to a new position determined by a fraction + * of the way from beginning to end. Ideally, this should be determined by + * the number of displayable lines in the source. This routine does it as a + * fraction of the first position and last position and then normalizes to + * the start of the line containing the position. + * + * BUG/deficiency: The normalize to line portion of this routine will + * cause thumbing to always position to the start of the source. + */ + +/*ARGSUSED*/ +static void +VJump(w, closure, callData) +Widget w; +XtPointer closure, callData; /* closuer = TextWidget, callData = percent. */ +{ + float * percent = (float *) callData; + TextWidget ctx = (TextWidget)closure; + XawTextPosition position, old_top, old_bot; + XawTextLineTable * lt = &(ctx->text.lt); + + _XawTextPrepareToUpdate(ctx); + old_top = lt->top; + if ( (lt->lines > 0) && (IsValidLine(ctx, lt->lines - 1)) ) + old_bot = lt->info[lt->lines - 1].position; + else + old_bot = ctx->text.lastPos; + + position = (long) (*percent * (float) ctx->text.lastPos); + position= SrcScan(ctx->text.source, position, XawstEOL, XawsdLeft, 1, FALSE); + if ( (position >= old_top) && (position <= old_bot) ) { + int line = 0; + for (;(line < lt->lines) && (position > lt->info[line].position) ; line++); + _XawTextVScroll(ctx, line); + } + else { + XawTextPosition new_bot; + _XawTextBuildLineTable(ctx, position, FALSE); + new_bot = IsValidLine(ctx, lt->lines-1) ? lt->info[lt->lines-1].position + : ctx->text.lastPos; + + if ((old_top >= lt->top) && (old_top <= new_bot)) { + int line = 0; + for (;(line < lt->lines) && (old_top > lt->info[line].position); line++); + _XawTextBuildLineTable(ctx, old_top, FALSE); + _XawTextVScroll(ctx, -line); + } + else + DisplayTextWindow( (Widget) ctx); + } + _XawTextExecuteUpdate(ctx); +} + +static Boolean +MatchSelection(selection, s) +Atom selection; +XawTextSelection *s; +{ + Atom *match; + int count; + + for (count = 0, match = s->selections; count < s->atom_count; match++, count++) + if (*match == selection) + return True; + return False; +} + +static Boolean +ConvertSelection(w, selection, target, type, value, length, format) +Widget w; +Atom *selection, *target, *type; +XtPointer *value; +unsigned long *length; +int *format; +{ + Display* d = XtDisplay(w); + TextWidget ctx = (TextWidget)w; + Widget src = ctx->text.source; + XawTextEditType edit_mode; + Arg args[1]; + + XawTextSelectionSalt *salt = NULL; + XawTextSelection *s; + + if (*target == XA_TARGETS(d)) { + Atom* targetP, * std_targets; + unsigned long std_length; + + if ( SrcCvtSel(src, selection, target, type, value, length, format) ) + return True; + + XmuConvertStandardSelection(w, ctx->text.time, selection, + target, type, (XPointer*)&std_targets, + &std_length, format); + + *value = XtMalloc((unsigned) sizeof(Atom)*(std_length + 7)); + targetP = *(Atom**)value; + *length = std_length + 6; + *targetP++ = XA_STRING; + *targetP++ = XA_TEXT(d); + *targetP++ = XA_COMPOUND_TEXT(d); + *targetP++ = XA_LENGTH(d); + *targetP++ = XA_LIST_LENGTH(d); + *targetP++ = XA_CHARACTER_POSITION(d); + + XtSetArg(args[0], XtNeditType,&edit_mode); + XtGetValues(src, args, ONE); + + if (edit_mode == XawtextEdit) { + *targetP++ = XA_DELETE(d); + (*length)++; + } + (void) memmove((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); + XtFree((char*)std_targets); + *type = XA_ATOM; + *format = 32; + return True; + } + + if ( SrcCvtSel(src, selection, target, type, value, length, format) ) + return True; + + if (MatchSelection (*selection, &ctx->text.s)) + s = &ctx->text.s; + else + { + for (salt = ctx->text.salt; salt; salt = salt->next) + if (MatchSelection (*selection, &salt->s)) + break; + if (!salt) + return False; + s = &salt->s; + } + if (*target == XA_STRING || + *target == XA_TEXT(d) || + *target == XA_COMPOUND_TEXT(d)) { + if (*target == XA_TEXT(d)) { +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide) + *type = XA_COMPOUND_TEXT(d); + else +#endif + *type = XA_STRING; + } else { + *type = *target; + } + /* + * If salt is True, the salt->contents stores CT string, + * its length is measured in bytes. + * Refer to _XawTextSaltAwaySelection(). + * + * by Li Yuhong, Mar. 20, 1991. + */ + if (!salt) { + *value = _XawTextGetSTRING(ctx, s->left, s->right); +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide) { + XTextProperty textprop; + if (XwcTextListToTextProperty(d, (wchar_t **)value, 1, + XCompoundTextStyle, &textprop) + < Success) { + XtFree(*value); + return False; + } + XtFree(*value); + *value = (XtPointer)textprop.value; + *length = textprop.nitems; + } else +#endif + { + *length = strlen(*value); + } + } else { + *value = XtMalloc((salt->length + 1) * sizeof(unsigned char)); + strcpy (*value, salt->contents); + *length = salt->length; + } +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide && *type == XA_STRING) { + XTextProperty textprop; + wchar_t **wlist; + int count; + textprop.encoding = XA_COMPOUND_TEXT(d); + textprop.value = (unsigned char *)*value; + textprop.nitems = strlen(*value); + textprop.format = 8; + if (XwcTextPropertyToTextList(d, &textprop, (wchar_t ***)&wlist, &count) + < Success) { + XtFree(*value); + return False; + } + XtFree(*value); + if (XwcTextListToTextProperty( d, (wchar_t **)wlist, 1, + XStringStyle, &textprop) < Success) { + XwcFreeStringList( (wchar_t**) wlist ); + return False; + } + *value = (XtPointer) textprop.value; + *length = textprop.nitems; + XwcFreeStringList( (wchar_t**) wlist ); + } +#endif + *format = 8; + return True; + } + + if ( (*target == XA_LIST_LENGTH(d)) || (*target == XA_LENGTH(d)) ) { + long * temp; + + temp = (long *) XtMalloc( (unsigned) sizeof(long) ); + if (*target == XA_LIST_LENGTH(d)) + *temp = 1L; + else /* *target == XA_LENGTH(d) */ + *temp = (long) (s->right - s->left); + + *value = (XPointer) temp; + *type = XA_INTEGER; + *length = 1L; + *format = 32; + return True; + } + + if (*target == XA_CHARACTER_POSITION(d)) { + long * temp; + + temp = (long *) XtMalloc( (unsigned)( 2 * sizeof(long) ) ); + temp[0] = (long) (s->left + 1); + temp[1] = s->right; + *value = (XPointer) temp; + *type = XA_SPAN(d); + *length = 2L; + *format = 32; + return True; + } + + if (*target == XA_DELETE(d)) { + void _XawTextZapSelection(); /* From TextAction.c */ + + if (!salt) + _XawTextZapSelection( ctx, (XEvent *) NULL, TRUE); + *value = NULL; + *type = XA_NULL(d); + *length = 0; + *format = 32; + return True; + } + + if (XmuConvertStandardSelection(w, ctx->text.time, selection, target, type, + (XPointer *)value, length, format)) + return True; + + /* else */ + return False; +} + +/* Function Name: GetCutBuffferNumber + * Description: Returns the number of the cut buffer. + * Arguments: atom - the atom to check. + * Returns: the number of the cut buffer representing this atom or + * NOT_A_CUT_BUFFER. + */ + +#define NOT_A_CUT_BUFFER -1 + +static int +GetCutBufferNumber(atom) +Atom atom; +{ + if (atom == XA_CUT_BUFFER0) return(0); + if (atom == XA_CUT_BUFFER1) return(1); + if (atom == XA_CUT_BUFFER2) return(2); + if (atom == XA_CUT_BUFFER3) return(3); + if (atom == XA_CUT_BUFFER4) return(4); + if (atom == XA_CUT_BUFFER5) return(5); + if (atom == XA_CUT_BUFFER6) return(6); + if (atom == XA_CUT_BUFFER7) return(7); + return(NOT_A_CUT_BUFFER); +} + +static void +LoseSelection(w, selection) +Widget w; +Atom *selection; +{ + TextWidget ctx = (TextWidget) w; + Atom* atomP; + int i; + XawTextSelectionSalt *salt, *prevSalt, *nextSalt; + + _XawTextPrepareToUpdate(ctx); + + atomP = ctx->text.s.selections; + for (i = 0 ; i < ctx->text.s.atom_count; i++, atomP++) + if ( (*selection == *atomP) || + (GetCutBufferNumber(*atomP) != NOT_A_CUT_BUFFER) )/* is a cut buffer */ + *atomP = (Atom)0; + + while (ctx->text.s.atom_count && + ctx->text.s.selections[ctx->text.s.atom_count-1] == 0) + ctx->text.s.atom_count--; + +/* + * Must walk the selection list in opposite order from UnsetSelection. + */ + + atomP = ctx->text.s.selections; + for (i = 0 ; i < ctx->text.s.atom_count; i++, atomP++) + if (*atomP == (Atom)0) { + *atomP = ctx->text.s.selections[--ctx->text.s.atom_count]; + while (ctx->text.s.atom_count && + ctx->text.s.selections[ctx->text.s.atom_count-1] == 0) + ctx->text.s.atom_count--; + } + + if (ctx->text.s.atom_count == 0) + ModifySelection(ctx, ctx->text.insertPos, ctx->text.insertPos); + + if (ctx->text.old_insert >= 0) /* Update in progress. */ + _XawTextExecuteUpdate(ctx); + + prevSalt = 0; + for (salt = ctx->text.salt; salt; salt = nextSalt) + { + atomP = salt->s.selections; + nextSalt = salt->next; + for (i = 0 ; i < salt->s.atom_count; i++, atomP++) + if (*selection == *atomP) + *atomP = (Atom)0; + + while (salt->s.atom_count && + salt->s.selections[salt->s.atom_count-1] == 0) + { + salt->s.atom_count--; + } + + /* + * Must walk the selection list in opposite order from UnsetSelection. + */ + + atomP = salt->s.selections; + for (i = 0 ; i < salt->s.atom_count; i++, atomP++) + if (*atomP == (Atom)0) + { + *atomP = salt->s.selections[--salt->s.atom_count]; + while (salt->s.atom_count && + salt->s.selections[salt->s.atom_count-1] == 0) + salt->s.atom_count--; + } + if (salt->s.atom_count == 0) + { + XtFree ((char *) salt->s.selections); + XtFree (salt->contents); + if (prevSalt) + prevSalt->next = nextSalt; + else + ctx->text.salt = nextSalt; + XtFree ((char *) salt); + } + else + prevSalt = salt; + } +} + +void +_XawTextSaltAwaySelection (ctx, selections, num_atoms) +TextWidget ctx; +Atom* selections; +int num_atoms; +{ + XawTextSelectionSalt *salt; + int i, j; + + for (i = 0; i < num_atoms; i++) + LoseSelection ((Widget) ctx, selections + i); + if (num_atoms == 0) + return; + salt = (XawTextSelectionSalt *) + XtMalloc( (unsigned) sizeof(XawTextSelectionSalt) ); + if (!salt) + return; + salt->s.selections = (Atom *) + XtMalloc( (unsigned) ( num_atoms * sizeof (Atom) ) ); + if (!salt->s.selections) + { + XtFree ((char *) salt); + return; + } + salt->s.left = ctx->text.s.left; + salt->s.right = ctx->text.s.right; + salt->s.type = ctx->text.s.type; + salt->contents = _XawTextGetSTRING(ctx, ctx->text.s.left, ctx->text.s.right); +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide) { + XTextProperty textprop; + if (XwcTextListToTextProperty(XtDisplay((Widget)ctx), + (wchar_t**)(&(salt->contents)), 1, XCompoundTextStyle, + &textprop) < Success) { + XtFree(salt->contents); + salt->length = 0; + return; + } + XtFree(salt->contents); + salt->contents = (char *)textprop.value; + salt->length = textprop.nitems; + } else +#endif + salt->length = strlen (salt->contents); + salt->next = ctx->text.salt; + ctx->text.salt = salt; + j = 0; + for (i = 0; i < num_atoms; i++) + { + if (GetCutBufferNumber (selections[i]) == NOT_A_CUT_BUFFER) + { + salt->s.selections[j++] = selections[i]; + XtOwnSelection ((Widget) ctx, selections[i], ctx->text.time, + ConvertSelection, LoseSelection, (XtSelectionDoneProc)NULL); + } + } + salt->s.atom_count = j; +} + +static void +_SetSelection(ctx, left, right, selections, count) +TextWidget ctx; +XawTextPosition left, right; +Atom *selections; +Cardinal count; +{ + XawTextPosition pos; + + if (left < ctx->text.s.left) { + pos = Min(right, ctx->text.s.left); + _XawTextNeedsUpdating(ctx, left, pos); + } + if (left > ctx->text.s.left) { + pos = Min(left, ctx->text.s.right); + _XawTextNeedsUpdating(ctx, ctx->text.s.left, pos); + } + if (right < ctx->text.s.right) { + pos = Max(right, ctx->text.s.left); + _XawTextNeedsUpdating(ctx, pos, ctx->text.s.right); + } + if (right > ctx->text.s.right) { + pos = Max(left, ctx->text.s.right); + _XawTextNeedsUpdating(ctx, pos, right); + } + + ctx->text.s.left = left; + ctx->text.s.right = right; + + SrcSetSelection(ctx->text.source, left, right, + (count == 0) ? None : selections[0]); + + if (left < right) { + Widget w = (Widget) ctx; + int buffer; + + while (count) { + Atom selection = selections[--count]; + + if ((buffer = GetCutBufferNumber(selection)) != NOT_A_CUT_BUFFER) { + + unsigned char *ptr, *tptr; + unsigned int amount, max_len = MAX_CUT_LEN(XtDisplay(w)); + unsigned long len; + + tptr= ptr= (unsigned char *) _XawTextGetSTRING(ctx, ctx->text.s.left, + ctx->text.s.right); +#ifdef XAW_INTERNATIONALIZATION + if (_XawTextFormat(ctx) == XawFmtWide) { + /* + * Only XA_STRING(Latin 1) is allowed in CUT_BUFFER, + * so we get it from wchar string, then free the wchar string. + */ + XTextProperty textprop; + if (XwcTextListToTextProperty(XtDisplay(w), (wchar_t**)&ptr, 1, + XStringStyle, &textprop) < Success) { + XtFree((char *)ptr); + return; + } + XtFree((char *)ptr); + tptr = ptr = textprop.value; + } +#endif + if (buffer == 0) { + _CreateCutBuffers(XtDisplay(w)); + XRotateBuffers(XtDisplay(w), 1); + } + amount = Min ( (len = strlen((char *)ptr)), max_len); + XChangeProperty(XtDisplay(w), RootWindow(XtDisplay(w), 0), selection, + XA_STRING, 8, PropModeReplace, ptr, amount); + + while (len > max_len) { + len -= max_len; + tptr += max_len; + amount = Min (len, max_len); + XChangeProperty(XtDisplay(w), RootWindow(XtDisplay(w), 0), + selection, XA_STRING, 8, PropModeAppend, + tptr, amount); + } + XtFree ((char *)ptr); + } + else /* This is a real selection */ + XtOwnSelection(w, selection, ctx->text.time, ConvertSelection, + LoseSelection, (XtSelectionDoneProc)NULL); + } + } + else + XawTextUnsetSelection((Widget)ctx); +} + +/* + * This internal routine deletes the text from pos1 to pos2 in a source and + * then inserts, at pos1, the text that was passed. As a side effect it + * "invalidates" that portion of the displayed text (if any). + * + * NOTE: It is illegal to call this routine unless there is a valid line table! + */ + +int +_XawTextReplace (ctx, pos1, pos2, text) +TextWidget ctx; +XawTextPosition pos1, pos2; +XawTextBlock *text; +{ + int i, line1, delta, error; + XawTextPosition updateFrom, updateTo; + Widget src = ctx->text.source; + XawTextEditType edit_mode; + Arg args[1]; + Boolean tmp = ctx->text.update_disabled; + + ctx->text.update_disabled = True; /* No redisplay during replacement. */ + +/* + * The insertPos may not always be set to the right spot in XawtextAppend + */ + + XtSetArg(args[0], XtNeditType, &edit_mode); + XtGetValues(src, args, ONE); + + if ((pos1 == ctx->text.insertPos) && (edit_mode == XawtextAppend)) { + ctx->text.insertPos = ctx->text.lastPos; + pos2 = SrcScan(src, ctx->text.insertPos, XawstPositions, XawsdRight, + (int)(ctx->text.insertPos - pos1), (Boolean)TRUE); + pos1 = ctx->text.insertPos; + if ( (pos1 == pos2) && (text->length == 0) ) { + ctx->text.update_disabled = FALSE; /* rearm redisplay. */ + return( XawEditError ); + } + } + + updateFrom = SrcScan(src, pos1, XawstWhiteSpace, XawsdLeft, 1, FALSE); + updateFrom = Max(updateFrom, ctx->text.lt.top); + + line1 = LineForPosition(ctx, updateFrom); + if ( (error = SrcReplace(src, pos1, pos2, text)) != 0) { + ctx->text.update_disabled = tmp; /* restore redisplay */ + return(error); + } + + XawTextUnsetSelection((Widget)ctx); + + ctx->text.lastPos = GETLASTPOS; + if (ctx->text.lt.top >= ctx->text.lastPos) { + _XawTextBuildLineTable(ctx, ctx->text.lastPos, FALSE); + ClearWindow( (Widget) ctx); + ctx->text.update_disabled = tmp; /* restore redisplay */ + return(0); /* Things are fine. */ + } + + ctx->text.single_char = (text->length <= 1 && pos2 - pos1 <= 1); + + delta = text->length - (pos2 - pos1); + + if (delta < ctx->text.lastPos) { + for (pos2 += delta, i = 0; i < ctx->text.numranges; i++) { + if (ctx->text.updateFrom[i] > pos1) + ctx->text.updateFrom[i] += delta; + if (ctx->text.updateTo[i] >= pos1) + ctx->text.updateTo[i] += delta; + } + } + + /* + * fixup all current line table entries to reflect edit. + * %%% it is not legal to do arithmetic on positions. + * using Scan would be more proper. + */ + if (delta != 0) { + XawTextLineTableEntry *lineP; + i = LineForPosition(ctx, pos1) + 1; + for (lineP = ctx->text.lt.info + i; i <= ctx->text.lt.lines; i++, lineP++) + lineP->position += delta; + } + + /* + * Now process the line table and fixup in case edits caused + * changes in line breaks. If we are breaking on word boundaries, + * this code checks for moving words to and from lines. + */ + + if (IsPositionVisible(ctx, updateFrom)) { + updateTo = _BuildLineTable(ctx, + ctx->text.lt.info[line1].position, pos1, line1); + _XawTextNeedsUpdating(ctx, updateFrom, updateTo); + } + + ctx->text.update_disabled = tmp; /* restore redisplay */ + return(0); /* Things are fine. */ +} + +/* + * This routine will display text between two arbitrary source positions. + * In the event that this span contains highlighted text for the selection, + * only that portion will be displayed highlighted. + * + * NOTE: it is illegal to call this routine unless there + * is a valid line table! + */ + +static void +DisplayText(w, pos1, pos2) +Widget w; +XawTextPosition pos1, pos2; +{ + TextWidget ctx = (TextWidget)w; + Position x, y; + int height, line, i, lastPos = ctx->text.lastPos; + XawTextPosition startPos, endPos; + Boolean clear_eol, done_painting; + Dimension s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + pos1 = (pos1 < ctx->text.lt.top) ? ctx->text.lt.top : pos1; + pos2 = FindGoodPosition(ctx, pos2); + if ( (pos1 >= pos2) || !LineAndXYForPosition(ctx, pos1, &line, &x, &y) ) + return; /* line not visible, or pos1 >= pos2. */ + + for ( startPos = pos1, i = line; IsValidLine(ctx, i) && + (i < ctx->text.lt.lines) ; i++) { + + + if ( (endPos = ctx->text.lt.info[i + 1].position) > pos2 ) { + clear_eol = ((endPos = pos2) >= lastPos); + done_painting = (!clear_eol || ctx->text.single_char); + } + else { + clear_eol = TRUE; + done_painting = FALSE; + } + + height = ctx->text.lt.info[i + 1].y - ctx->text.lt.info[i].y - s + 1; + + if ( (endPos > startPos) ) { + + /* note to self: _ShadowSurroundedBox() hacks are in here */ + + if ( (x == (Position) ctx->text.margin.left) && (x > 0) ) + { + SinkClearToBG (ctx->text.sink, + (Position) s, y, + (Dimension) ctx->text.margin.left, (Dimension)height); + _ShadowSurroundedBox((Widget)ctx, (ThreeDWidget)ctx->text.threeD, + 0, 0, ctx->core.width, ctx->core.height, + ((ThreeDWidget)ctx->text.threeD)->threeD.relief, + False); + } + + if ( (startPos >= ctx->text.s.right) || (endPos <= ctx->text.s.left) ) + XawTextSinkDisplayText(ctx->text.sink, x, y, startPos, endPos, FALSE); + else if ((startPos >= ctx->text.s.left) && (endPos <= ctx->text.s.right)) + XawTextSinkDisplayText(ctx->text.sink, x, y, startPos, endPos, TRUE); + else { + DisplayText(w, startPos, ctx->text.s.left); + DisplayText(w, Max(startPos, ctx->text.s.left), + Min(endPos, ctx->text.s.right)); + DisplayText(w, ctx->text.s.right, endPos); + } + } + startPos = endPos; + if (clear_eol) { + Position myx = ctx->text.lt.info[i].textWidth + ctx->text.margin.left; + + SinkClearToBG(ctx->text.sink, + (Position) myx, + (Position) y, w->core.width - myx/* - 2 * s*/, + (Dimension) height); + _ShadowSurroundedBox((Widget)ctx, (ThreeDWidget)ctx->text.threeD, + 0, 0, ctx->core.width, ctx->core.height, + ((ThreeDWidget)ctx->text.threeD)->threeD.relief, + False); + + /* + * We only get here if single character is true, and we need + * to clear to the end of the screen. We know that since there + * was only one character deleted that this is the same + * as clearing an extra line, so we do this, and are done. + * + * This a performance hack, and a pretty gross one, but it works. + * + * Chris Peterson 11/13/89. + */ + + if (done_painting) { + y += height; + SinkClearToBG(ctx->text.sink, + (Position) ctx->text.margin.left, (Position) y, + w->core.width - ctx->text.margin.left/* - 2 * s*/, + (Dimension) Min(height, ctx->core.height - 2 * s - y)); + _ShadowSurroundedBox((Widget)ctx, (ThreeDWidget)ctx->text.threeD, + 0, 0, ctx->core.width, ctx->core.height, + ((ThreeDWidget)ctx->text.threeD)->threeD.relief, + False); + + break; /* set single_char to FALSE and return. */ + } + } + + x = (Position) ctx->text.margin.left; + y = ctx->text.lt.info[i + 1].y; + if ( done_painting + || (y >= (int)(ctx->core.height - ctx->text.margin.bottom)) ) + break; + } + ctx->text.single_char = FALSE; +} + +/* + * This routine implements multi-click selection in a hardwired manner. + * It supports multi-click entity cycling (char, word, line, file) and mouse + * motion adjustment of the selected entitie (i.e. select a word then, with + * button still down, adjust wich word you really meant by moving the mouse). + * [NOTE: This routine is to be replaced by a set of procedures that + * will allows clients to implements a wide class of draw through and + * multi-click selection user interfaces.] + */ + +static void +DoSelection (ctx, pos, time, motion) +TextWidget ctx; +XawTextPosition pos; +Time time; +Boolean motion; +{ + XawTextPosition newLeft, newRight; + XawTextSelectType newType, *sarray; + Widget src = ctx->text.source; + + if (motion) + newType = ctx->text.s.type; + else { + if ( (abs((long) time - (long) ctx->text.lasttime) < MULTI_CLICK_TIME) && + ((pos >= ctx->text.s.left) && (pos <= ctx->text.s.right))) { + sarray = ctx->text.sarray; + for (;*sarray != XawselectNull && *sarray != ctx->text.s.type; sarray++); + + if (*sarray == XawselectNull) + newType = *(ctx->text.sarray); + else { + newType = *(sarray + 1); + if (newType == XawselectNull) + newType = *(ctx->text.sarray); + } + } + else /* single-click event */ + newType = *(ctx->text.sarray); + + ctx->text.lasttime = time; + } + switch (newType) { + case XawselectPosition: + newLeft = newRight = pos; + break; + case XawselectChar: + newLeft = pos; + newRight = SrcScan(src, pos, XawstPositions, XawsdRight, 1, FALSE); + break; + case XawselectWord: + case XawselectParagraph: + { + XawTextScanType stype; + + if (newType == XawselectWord) + stype = XawstWhiteSpace; + else + stype = XawstParagraph; + + /* + * Somewhat complicated, but basically I treat the space between + * two objects as another object. The object that I am currently + * in then becomes the end of the selection. + * + * Chris Peterson - 4/19/90. + */ + + newRight = SrcScan(ctx->text.source, pos, stype, XawsdRight, 1, FALSE); + newRight =SrcScan(ctx->text.source, newRight,stype,XawsdLeft,1, FALSE); + + if (pos != newRight) + newLeft = SrcScan(ctx->text.source, pos, stype, XawsdLeft, 1, FALSE); + else + newLeft = pos; + + newLeft =SrcScan(ctx->text.source, newLeft, stype, XawsdRight,1,FALSE); + + if (newLeft > newRight) { + XawTextPosition temp = newLeft; + newLeft = newRight; + newRight = temp; + } + } + break; + case XawselectLine: + newLeft = SrcScan(src, pos, XawstEOL, XawsdLeft, 1, FALSE); + newRight = SrcScan(src, pos, XawstEOL, XawsdRight, 1, FALSE); + break; + case XawselectAll: + newLeft = SrcScan(src, pos, XawstAll, XawsdLeft, 1, FALSE); + newRight = SrcScan(src, pos, XawstAll, XawsdRight, 1, FALSE); + break; + default: + XtAppWarning(XtWidgetToApplicationContext((Widget) ctx), + "Text Widget: empty selection array."); + return; + } + + if ( (newLeft != ctx->text.s.left) || (newRight != ctx->text.s.right) + || (newType != ctx->text.s.type)) { + ModifySelection(ctx, newLeft, newRight); + if (pos - ctx->text.s.left < ctx->text.s.right - pos) + ctx->text.insertPos = newLeft; + else + ctx->text.insertPos = newRight; + ctx->text.s.type = newType; + } + if (!motion) { /* setup so we can freely mix select extend calls*/ + ctx->text.origSel.type = ctx->text.s.type; + ctx->text.origSel.left = ctx->text.s.left; + ctx->text.origSel.right = ctx->text.s.right; + + if (pos >= ctx->text.s.left + ((ctx->text.s.right - ctx->text.s.left) / 2)) + ctx->text.extendDir = XawsdRight; + else + ctx->text.extendDir = XawsdLeft; + } +} + +/* + * This routine implements extension of the currently selected text in + * the "current" mode (i.e. char word, line, etc.). It worries about + * extending from either end of the selection and handles the case when you + * cross through the "center" of the current selection (e.g. switch which + * end you are extending!). + */ + +static void +ExtendSelection (ctx, pos, motion) +TextWidget ctx; +XawTextPosition pos; +Boolean motion; +{ + XawTextScanDirection dir; + + if (!motion) { /* setup for extending selection */ + if (ctx->text.s.left == ctx->text.s.right) /* no current selection. */ + ctx->text.s.left = ctx->text.s.right = ctx->text.insertPos; + else { + ctx->text.origSel.left = ctx->text.s.left; + ctx->text.origSel.right = ctx->text.s.right; + } + + ctx->text.origSel.type = ctx->text.s.type; + + if (pos >= ctx->text.s.left + ((ctx->text.s.right - ctx->text.s.left) / 2)) + ctx->text.extendDir = XawsdRight; + else + ctx->text.extendDir = XawsdLeft; + } + else /* check for change in extend direction */ + if ((ctx->text.extendDir == XawsdRight && pos <= ctx->text.origSel.left) || + (ctx->text.extendDir == XawsdLeft && pos >= ctx->text.origSel.right)) { + ctx->text.extendDir = (ctx->text.extendDir == XawsdRight) ? + XawsdLeft : XawsdRight; + ModifySelection(ctx, ctx->text.origSel.left, ctx->text.origSel.right); + } + + dir = ctx->text.extendDir; + switch (ctx->text.s.type) { + case XawselectWord: + case XawselectParagraph: + { + XawTextPosition left_pos, right_pos; + XawTextScanType stype; + + if (ctx->text.s.type == XawselectWord) + stype = XawstWhiteSpace; + else + stype = XawstParagraph; + + /* + * Somewhat complicated, but basically I treat the space between + * two objects as another object. The object that I am currently + * in then becomes the end of the selection. + * + * Chris Peterson - 4/19/90. + */ + + right_pos = SrcScan(ctx->text.source, pos, stype, XawsdRight, 1, FALSE); + right_pos =SrcScan(ctx->text.source, right_pos,stype,XawsdLeft,1, FALSE); + + if (pos != right_pos) + left_pos = SrcScan(ctx->text.source, pos, stype, XawsdLeft, 1, FALSE); + else + left_pos = pos; + + left_pos =SrcScan(ctx->text.source, left_pos, stype, XawsdRight,1,FALSE); + + if (dir == XawsdLeft) + pos = Min(left_pos, right_pos); + else /* dir == XawsdRight */ + pos = Max(left_pos, right_pos); + } + break; + case XawselectLine: + pos = SrcScan(ctx->text.source, pos, XawstEOL, dir, 1, dir == XawsdRight); + break; + case XawselectAll: + pos = ctx->text.insertPos; + case XawselectPosition: /* fall through. */ + default: + break; + } + + if (dir == XawsdRight) + ModifySelection(ctx, ctx->text.s.left, pos); + else + ModifySelection(ctx, pos, ctx->text.s.right); + + ctx->text.insertPos = pos; +} + +/* + * Clear the window to background color. + */ + +static void +ClearWindow (w) +Widget w; +{ + TextWidget ctx = (TextWidget) w; + int s = ((ThreeDWidget)ctx->text.threeD)->threeD.shadow_width; + + if (XtIsRealized(w)) + { + SinkClearToBG(ctx->text.sink, + (Position) s, (Position) s, + w->core.width - 2 * s, w->core.height - 2 * s); + /* note to self: _ShadowSurroundedBox() hack might be needed here */ + } +} + +/* Function Name: _XawTextClearAndCenterDisplay + * Description: Redraws the display with the cursor in insert point + * centered vertically. + * Arguments: ctx - the text widget. + * Returns: none. + */ + +void +_XawTextClearAndCenterDisplay(ctx) +TextWidget ctx; +{ + int insert_line = LineForPosition(ctx, ctx->text.insertPos); + int scroll_by = insert_line - ctx->text.lt.lines/2; + + _XawTextVScroll(ctx, scroll_by); + DisplayTextWindow( (Widget) ctx); +} + +/* + * Internal redisplay entire window. + * Legal to call only if widget is realized. + */ + +static void +DisplayTextWindow (w) +Widget w; +{ + TextWidget ctx = (TextWidget) w; + ClearWindow(w); + _XawTextBuildLineTable(ctx, ctx->text.lt.top, FALSE); + _XawTextNeedsUpdating(ctx, zeroPosition, ctx->text.lastPos); + _XawTextSetScrollBars(ctx); +} + +/* + * This routine checks to see if the window should be resized (grown or + * shrunk) when text to be painted overflows to the right or + * the bottom of the window. It is used by the keyboard input routine. + */ + +void +_XawTextCheckResize(ctx) +TextWidget ctx; +{ + Widget w = (Widget) ctx; + int line = 0, old_height; + XtWidgetGeometry rbox, return_geom; + + if ( (ctx->text.resize == XawtextResizeWidth) || + (ctx->text.resize == XawtextResizeBoth) ) { + XawTextLineTableEntry *lt; + rbox.width = 0; + for (lt = ctx->text.lt.info; + IsValidLine(ctx, line) && (line < ctx->text.lt.lines); + line++, lt++) { + if ((int)(lt->textWidth + ctx->text.margin.left) > (int)rbox.width) + rbox.width = lt->textWidth + ctx->text.margin.left; + } + + rbox.width += ctx->text.margin.right; + if (rbox.width > ctx->core.width) { /* Only get wider. */ + rbox.request_mode = CWWidth; + if (XtMakeGeometryRequest(w, &rbox, &return_geom) == XtGeometryAlmost) + (void) XtMakeGeometryRequest(w, &return_geom, (XtWidgetGeometry*) NULL); + } + } + + if ( !((ctx->text.resize == XawtextResizeHeight) || + (ctx->text.resize == XawtextResizeBoth)) ) + return; + + if (IsPositionVisible(ctx, ctx->text.lastPos)) + line = LineForPosition(ctx, ctx->text.lastPos); + else + line = ctx->text.lt.lines; + + if ( (line + 1) == ctx->text.lt.lines ) return; + + old_height = ctx->core.height; + rbox.request_mode = CWHeight; + rbox.height = XawTextSinkMaxHeight(ctx->text.sink, line + 1) + VMargins(ctx); + + if ((int)rbox.height < old_height) return; /* It will only get taller. */ + + if (XtMakeGeometryRequest(w, &rbox, &return_geom) == XtGeometryAlmost) + if (XtMakeGeometryRequest(w, &return_geom, (XtWidgetGeometry*)NULL) != XtGeometryYes) + return; + + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); +} + +/* + * Converts (params, num_params) to a list of atoms & caches the + * list in the TextWidget instance. + */ + +Atom* +_XawTextSelectionList(ctx, list, nelems) +TextWidget ctx; +String *list; +Cardinal nelems; +{ + Atom * sel = ctx->text.s.selections; + Display *dpy = XtDisplay((Widget) ctx); + int n; + + if (nelems > ctx->text.s.array_size) { + sel = (Atom *) XtRealloc((char *) sel, sizeof(Atom) * nelems); + ctx->text.s.array_size = nelems; + ctx->text.s.selections = sel; + } + for (n=nelems; --n >= 0; sel++, list++) + *sel = XInternAtom(dpy, *list, False); + + ctx->text.s.atom_count = nelems; + return ctx->text.s.selections; +} + +/* Function Name: SetSelection + * Description: Sets the current selection. + * Arguments: ctx - the text widget. + * defaultSel - the default selection. + * l, r - the left and right ends of the selection. + * list, nelems - the selection list (as strings). + * Returns: none. + * + * NOTE: if (ctx->text.s.left >= ctx->text.s.right) then the selection + * is unset. + */ + +void +_XawTextSetSelection(ctx, l, r, list, nelems) +TextWidget ctx; +XawTextPosition l, r; +String *list; +Cardinal nelems; +{ + if (nelems == 1 && !strcmp (list[0], "none")) + return; + if (nelems == 0) { + String defaultSel = "PRIMARY"; + list = &defaultSel; + nelems = 1; + } + _SetSelection(ctx, l, r, _XawTextSelectionList(ctx, list, nelems), nelems); +} + + +/* Function Name: ModifySelection + * Description: Modifies the current selection. + * Arguments: ctx - the text widget. + * left, right - the left and right ends of the selection. + * Returns: none. + * + * NOTE: if (ctx->text.s.left >= ctx->text.s.right) then the selection + * is unset. + */ + +static void +ModifySelection(ctx, left, right) +TextWidget ctx; +XawTextPosition left, right; +{ + if (left == right) + ctx->text.insertPos = left; + _SetSelection( ctx, left, right, (Atom*) NULL, ZERO ); +} + +/* + * This routine is used to perform various selection functions. The goal is + * to be able to specify all the more popular forms of draw-through and + * multi-click selection user interfaces from the outside. + */ + +void +_XawTextAlterSelection (ctx, mode, action, params, num_params) +TextWidget ctx; +XawTextSelectionMode mode; /* {XawsmTextSelect, XawsmTextExtend} */ +XawTextSelectionAction action; /* {XawactionStart, + XawactionAdjust, XawactionEnd} */ +String *params; +Cardinal *num_params; +{ + XawTextPosition position; + Boolean flag; + +/* + * This flag is used by TextPop.c:DoReplace() to determine if the selection + * is okay to use, or if it has been modified. + */ + + if (ctx->text.search != NULL) + ctx->text.search->selection_changed = TRUE; + + position = PositionForXY (ctx, (int) ctx->text.ev_x, (int) ctx->text.ev_y); + + flag = (action != XawactionStart); + if (mode == XawsmTextSelect) + DoSelection (ctx, position, ctx->text.time, flag); + else /* mode == XawsmTextExtend */ + ExtendSelection (ctx, position, flag); + + if (action == XawactionEnd) + _XawTextSetSelection(ctx, ctx->text.s.left, ctx->text.s.right, + params, *num_params); +} + +/* Function Name: RectanglesOverlap + * Description: Returns TRUE if two rectangles overlap. + * Arguments: rect1, rect2 - the two rectangles to check. + * Returns: TRUE iff these rectangles overlap. + */ + +static Boolean +RectanglesOverlap(rect1, rect2) +XRectangle *rect1, *rect2; +{ + return ( (rect1->x < rect2->x + (short) rect2->width) && + (rect2->x < rect1->x + (short) rect1->width) && + (rect1->y < rect2->y + (short) rect2->height) && + (rect2->y < rect1->y + (short) rect1->height) ); +} + +/* Function Name: UpdateTextInRectangle. + * Description: Updates the text in a rectangle. + * Arguments: ctx - the text widget. + * rect - the rectangle to update. + * Returns: none. + */ + +static void +UpdateTextInRectangle(ctx, rect) +TextWidget ctx; +XRectangle * rect; +{ + XawTextLineTableEntry *info = ctx->text.lt.info; + int line, x = rect->x, y = rect->y; + int right = rect->width + x, bottom = rect->height + y; + + for (line = 0;( (line < ctx->text.lt.lines) && + IsValidLine(ctx, line) && (info->y < bottom)); line++, info++) + if ( (info + 1)->y >= y ) + UpdateTextInLine(ctx, line, x, right); +} + +/* + * This routine processes all "expose region" XEvents. In general, its job + * is to the best job at minimal re-paint of the text, displayed in the + * window, that it can. + */ + +/* ARGSUSED */ +static void +ProcessExposeRegion(w, event, region) +Widget w; +XEvent *event; +Region region; /* Unused. */ +{ + TextWidget ctx = (TextWidget) w; + XRectangle expose, cursor; + Boolean need_to_draw; + + if (event->type == Expose) { + expose.x = event->xexpose.x; + expose.y = event->xexpose.y; + expose.width = event->xexpose.width; + expose.height = event->xexpose.height; + } + else if (event->type == GraphicsExpose) { + expose.x = event->xgraphicsexpose.x; + expose.y = event->xgraphicsexpose.y; + expose.width = event->xgraphicsexpose.width; + expose.height = event->xgraphicsexpose.height; + } + else { /* No Expose */ + PopCopyQueue(ctx); + return; /* no more processing necessary. */ + } + + need_to_draw = TranslateExposeRegion(ctx, &expose); + if ((event->type == GraphicsExpose) && (event->xgraphicsexpose.count == 0)) + PopCopyQueue(ctx); + + if (!need_to_draw) + return; /* don't draw if we don't need to. */ + + _XawTextPrepareToUpdate(ctx); + UpdateTextInRectangle(ctx, &expose); + XawTextSinkGetCursorBounds(ctx->text.sink, &cursor); + if (RectanglesOverlap(&cursor, &expose)) { + SinkClearToBG(ctx->text.sink, (Position) cursor.x, (Position) cursor.y, + (Dimension) cursor.width, (Dimension) cursor.height); + UpdateTextInRectangle(ctx, &cursor); + } + _XawTextExecuteUpdate(ctx); + + _ShadowSurroundedBox((Widget)ctx, (ThreeDWidget)ctx->text.threeD, + 0, 0, ctx->core.width, ctx->core.height, + ((ThreeDWidget)ctx->text.threeD)->threeD.relief, False); +} + +/* + * This routine does all setup required to syncronize batched screen updates + */ + +void +_XawTextPrepareToUpdate(ctx) +TextWidget ctx; +{ + if (ctx->text.old_insert < 0) { + InsertCursor((Widget)ctx, XawisOff); + ctx->text.numranges = 0; + ctx->text.showposition = FALSE; + ctx->text.old_insert = ctx->text.insertPos; + } +} + +/* + * This is a private utility routine used by _XawTextExecuteUpdate. It + * processes all the outstanding update requests and merges update + * ranges where possible. + */ + +static +void FlushUpdate(ctx) +TextWidget ctx; +{ + int i, w; + XawTextPosition updateFrom, updateTo; + if (!XtIsRealized((Widget)ctx)) { + ctx->text.numranges = 0; + return; + } + while (ctx->text.numranges > 0) { + updateFrom = ctx->text.updateFrom[0]; + w = 0; + for (i = 1 ; i < ctx->text.numranges ; i++) { + if (ctx->text.updateFrom[i] < updateFrom) { + updateFrom = ctx->text.updateFrom[i]; + w = i; + } + } + updateTo = ctx->text.updateTo[w]; + ctx->text.numranges--; + ctx->text.updateFrom[w] = ctx->text.updateFrom[ctx->text.numranges]; + ctx->text.updateTo[w] = ctx->text.updateTo[ctx->text.numranges]; + for (i = ctx->text.numranges - 1 ; i >= 0 ; i--) { + while (ctx->text.updateFrom[i] <= updateTo && i < ctx->text.numranges) { + updateTo = ctx->text.updateTo[i]; + ctx->text.numranges--; + ctx->text.updateFrom[i] = ctx->text.updateFrom[ctx->text.numranges]; + ctx->text.updateTo[i] = ctx->text.updateTo[ctx->text.numranges]; + } + } + DisplayText((Widget)ctx, updateFrom, updateTo); + } +} + +/* + * This is a private utility routine used by _XawTextExecuteUpdate. This + * routine worries about edits causing new data or the insertion point becoming + * invisible (off the screen, or under the horiz. scrollbar). Currently + * it always makes it visible by scrolling. It probably needs + * generalization to allow more options. + */ + +void +_XawTextShowPosition(ctx) +TextWidget ctx; +{ + int x, y, lines, number; + Boolean no_scroll; + XawTextPosition max_pos, top, first; + + if ( (!XtIsRealized((Widget)ctx)) || (ctx->text.lt.lines <= 0) ) + return; + +/* + * Find out the bottom the visable window, and make sure that the + * cursor does not go past the end of this space. + * + * This makes sure that the cursor does not go past the end of the + * visable window. + */ + + x = ctx->core.width; + y = ctx->core.height - ctx->text.margin.bottom; + if (ctx->text.hbar != NULL) + y -= ctx->text.hbar->core.height + 2 * ctx->text.hbar->core.border_width; + + max_pos = PositionForXY (ctx, x, y); + lines = LineForPosition(ctx, max_pos) + 1; /* number of visable lines. */ + + if ( (ctx->text.insertPos >= ctx->text.lt.top) && + (ctx->text.insertPos < max_pos)) + return; + + first = ctx->text.lt.top; + no_scroll = FALSE; + + if (ctx->text.insertPos < first) { /* We need to scroll down. */ + top = SrcScan(ctx->text.source, ctx->text.insertPos, + XawstEOL, XawsdLeft, 1, FALSE); + + /* count the number of lines we have to scroll */ + + number = 0; + while (first > top) { + first = SrcScan(ctx->text.source, first, + XawstEOL, XawsdLeft, 1, TRUE); + + if ( - number > lines ) + break; + + number--; + } + + if (first <= top) { /* If we found the proper number + of lines. */ + + /* Back up to just before the last CR. */ + + first = SrcScan(ctx->text.source, first, + XawstPositions, XawsdRight, 1, TRUE); + + /* Check to make sure the cursor is visable. */ + + if (first <= top) + number++; + + lines = number; + } + else + no_scroll = TRUE; + } + else { /* We need to Scroll up */ + top = SrcScan(ctx->text.source, ctx->text.insertPos, + XawstEOL, XawsdLeft, lines, FALSE); + + if (top < max_pos) + lines = LineForPosition(ctx, top); + else + no_scroll = TRUE; + } + + if (no_scroll) { + _XawTextBuildLineTable(ctx, top, FALSE); + DisplayTextWindow((Widget)ctx); + } + else + _XawTextVScroll(ctx, lines); + + _XawTextSetScrollBars(ctx); +} + +/* + * This routine causes all batched screen updates to be performed + */ + +void +_XawTextExecuteUpdate(ctx) +TextWidget ctx; +{ + if ( ctx->text.update_disabled || (ctx->text.old_insert < 0) ) + return; + + if((ctx->text.old_insert != ctx->text.insertPos) || (ctx->text.showposition)) + _XawTextShowPosition(ctx); + FlushUpdate(ctx); + InsertCursor((Widget)ctx, XawisOn); + ctx->text.old_insert = -1; +} + + +static void +TextDestroy(w) +Widget w; +{ + TextWidget ctx = (TextWidget)w; + + DestroyHScrollBar(ctx); + DestroyVScrollBar(ctx); + + XtFree((char *)ctx->text.s.selections); + XtFree((char *)ctx->text.lt.info); + XtFree((char *)ctx->text.search); + XtFree((char *)ctx->text.updateFrom); + XtFree((char *)ctx->text.updateTo); +} + +/* + * by the time we are managed (and get this far) we had better + * have both a source and a sink + */ + +static void +Resize(w) +Widget w; +{ + TextWidget ctx = (TextWidget) w; + + PositionVScrollBar(ctx); + PositionHScrollBar(ctx); + + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); + _XawTextSetScrollBars(ctx); +} + +/* + * This routine allow the application program to Set attributes. + */ + +/*ARGSUSED*/ +static Boolean +SetValues(current, request, new, args, num_args) +Widget current, request, new; +ArgList args; +Cardinal *num_args; +{ + TextWidget oldtw = (TextWidget) current; + TextWidget newtw = (TextWidget) new; + Boolean redisplay = FALSE; + Boolean display_caret = newtw->text.display_caret; + + + newtw->text.display_caret = oldtw->text.display_caret; + _XawTextPrepareToUpdate(newtw); + newtw->text.display_caret = display_caret; + + if (oldtw->text.r_margin.left != newtw->text.r_margin.left) { + newtw->text.margin.left = newtw->text.r_margin.left; + if (newtw->text.vbar != NULL) + newtw->text.margin.left += newtw->text.vbar->core.width + + newtw->text.vbar->core.border_width; + redisplay = TRUE; + } + + if (oldtw->text.scroll_vert != newtw->text.scroll_vert) { + if (newtw->text.scroll_vert == XawtextScrollNever) + DestroyVScrollBar(newtw); + else if (newtw->text.scroll_vert == XawtextScrollAlways) + CreateVScrollBar(newtw); + redisplay = TRUE; + } + + if (oldtw->text.r_margin.bottom != newtw->text.r_margin.bottom) { + newtw->text.margin.bottom = newtw->text.r_margin.bottom; + if (newtw->text.hbar != NULL) + newtw->text.margin.bottom += newtw->text.hbar->core.height + + newtw->text.hbar->core.border_width; + redisplay = TRUE; + } + + if (oldtw->text.scroll_horiz != newtw->text.scroll_horiz) { + if (newtw->text.scroll_horiz == XawtextScrollNever) + DestroyHScrollBar(newtw); + else if (newtw->text.scroll_horiz == XawtextScrollAlways) + CreateHScrollBar(newtw); + redisplay = TRUE; + } + + if ( oldtw->text.source != newtw->text.source ) + XawTextSetSource( (Widget) newtw, newtw->text.source, newtw->text.lt.top); + + newtw->text.redisplay_needed = False; + XtSetValues( (Widget)newtw->text.source, args, *num_args ); + XtSetValues( (Widget)newtw->text.sink, args, *num_args ); + + if ( oldtw->text.wrap != newtw->text.wrap || + oldtw->text.lt.top != newtw->text.lt.top || + oldtw->text.r_margin.right != newtw->text.r_margin.right || + oldtw->text.r_margin.top != newtw->text.r_margin.top || + oldtw->text.sink != newtw->text.sink || + newtw->text.redisplay_needed ) + { + _XawTextBuildLineTable(newtw, newtw->text.lt.top, TRUE); + redisplay = TRUE; + } + + if (oldtw->text.insertPos != newtw->text.insertPos) { + newtw->text.showposition = TRUE; + redisplay = TRUE; + } + + _XawTextExecuteUpdate(newtw); + if (redisplay) + _XawTextSetScrollBars(newtw); + + return redisplay; +} + +/* invoked by the Simple widget's SetValues */ +static Boolean ChangeSensitive(w) + Widget w; /* the new widget */ +{ + Arg args[1]; + TextWidget tw = (TextWidget) w; + + (*(&simpleClassRec)->simple_class.change_sensitive)(w); + + XtSetArg(args[0], XtNancestorSensitive, + (tw->core.ancestor_sensitive && tw->core.sensitive)); + if (tw->text.vbar) + XtSetValues(tw->text.vbar, args, ONE); + if (tw->text.hbar) + XtSetValues(tw->text.hbar, args, ONE); + return False; +} + +/* Function Name: GetValuesHook + * Description: This is a get values hook routine that gets the + * values in the text source and sink. + * Arguments: w - the Text Widget. + * args - the argument list. + * num_args - the number of args. + * Returns: none. + */ + +static void +GetValuesHook(w, args, num_args) +Widget w; +ArgList args; +Cardinal * num_args; +{ + XtGetValues( ((TextWidget) w)->text.source, args, *num_args ); + XtGetValues( ((TextWidget) w)->text.sink, args, *num_args ); +} + +/* Function Name: FindGoodPosition + * Description: Returns a valid position given any postition + * Arguments: pos - any position. + * Returns: a position between (0 and lastPos); + */ + +static XawTextPosition +FindGoodPosition(ctx, pos) +TextWidget ctx; +XawTextPosition pos; +{ + if (pos < 0) return(0); + return ( ((pos > ctx->text.lastPos) ? ctx->text.lastPos : pos) ); +} + +/************************************************************ + * + * Routines for handling the copy area expose queue. + * + ************************************************************/ + +/* Function Name: PushCopyQueue + * Description: Pushes a value onto the copy queue. + * Arguments: ctx - the text widget. + * h, v - amount of offset in the horiz and vert directions. + * Returns: none + */ + +static void +PushCopyQueue(ctx, h, v) +TextWidget ctx; +int h, v; +{ + struct text_move * offsets = XtNew(struct text_move); + + offsets->h = h; + offsets->v = v; + offsets->next = NULL; + + if (ctx->text.copy_area_offsets == NULL) + ctx->text.copy_area_offsets = offsets; + else { + struct text_move * end = ctx->text.copy_area_offsets; + for ( ; end->next != NULL; end = end->next) {} + end->next = offsets; + } +} + +/* Function Name: PopCopyQueue + * Description: Pops the top value off of the copy queue. + * Arguments: ctx - the text widget. + * Returns: none. + */ + +static void +PopCopyQueue(ctx) +TextWidget ctx; +{ + struct text_move * offsets = ctx->text.copy_area_offsets; + + if (offsets == NULL) + (void) printf( "Xaw Text widget %s: empty copy queue\n", + XtName( (Widget) ctx ) ); + else { + ctx->text.copy_area_offsets = offsets->next; + XtFree((char *) offsets); /* free what you allocate. */ + } +} + +/* Function Name: TranslateExposeRegion + * Description: Translates the expose that came into + * the cordinates that now exist in the Text widget. + * Arguments: ctx - the text widget. + * expose - a Rectangle, who's region currently + * contains the expose event location. + * this region will be returned containing + * the new rectangle. + * Returns: True if there is drawing that needs to be done. + */ + +static Boolean +TranslateExposeRegion(ctx, expose) +TextWidget ctx; +XRectangle * expose; +{ + struct text_move * offsets = ctx->text.copy_area_offsets; + int value; + int x, y, width, height; + + /* + * Skip over the first one, this has already been taken into account. + */ + + if (!offsets || !(offsets = offsets->next)) + return(TRUE); + + x = expose->x; + y = expose->y; + width = expose->width; + height = expose->height; + + while (offsets) { + x += offsets->h; + y += offsets->v; + offsets = offsets->next; + } + + /* + * remove that area of the region that is now outside the window. + */ + + if (y < 0) { + height += y; + y = 0; + } + + value = y + height - ctx->core.height; + if (value > 0) + height -= value; + + if (height <= 0) + return(FALSE); /* no need to draw outside the window. */ + + /* + * and now in the horiz direction... + */ + + if (x < 0) { + width += x; + x = 0; + } + + value = x + width - ctx->core.width; + if (value > 0) + width -= value; + + if (width <= 0) + return(FALSE); /* no need to draw outside the window. */ + + expose->x = x; + expose->y = y; + expose->width = width; + expose->height = height; + return(TRUE); +} + +/* Li wrote this so the IM can find a given text position's screen position. */ + +void +#if NeedFunctionPrototypes +_XawTextPosToXY( + Widget w, + XawTextPosition pos, + Position* x, + Position* y ) +#else +_XawTextPosToXY( w, pos, x, y ) + Widget w; + XawTextPosition pos; + Position *x, *y; +#endif +{ + int line; + LineAndXYForPosition( (TextWidget)w, pos, &line, x, y ); +} + +/******************************************************************* +The following routines provide procedural interfaces to Text window state +setting and getting. They need to be redone so than the args code can use +them. I suggest we create a complete set that takes the context as an +argument and then have the public version lookup the context and call the +internal one. The major value of this set is that they have actual application +clients and therefore the functionality provided is required for any future +version of Text. +********************************************************************/ + +void +#if NeedFunctionPrototypes +XawTextDisplay (Widget w) +#else +XawTextDisplay (w) +Widget w; +#endif +{ + if (!XtIsRealized(w)) return; + + _XawTextPrepareToUpdate( (TextWidget) w); + DisplayTextWindow(w); + _XawTextExecuteUpdate( (TextWidget) w); +} + +void +#if NeedFunctionPrototypes +XawTextSetSelectionArray(Widget w, XawTextSelectType *sarray) +#else +XawTextSetSelectionArray(w, sarray) +Widget w; +XawTextSelectType *sarray; +#endif +{ + ((TextWidget)w)->text.sarray = sarray; +} + +void +#if NeedFunctionPrototypes +XawTextGetSelectionPos(Widget w, XawTextPosition *left, XawTextPosition *right) +#else +XawTextGetSelectionPos(w, left, right) +Widget w; +XawTextPosition* left,* right; +#endif +{ + *left = ((TextWidget) w)->text.s.left; + *right = ((TextWidget) w)->text.s.right; +} + + +void +#if NeedFunctionPrototypes +XawTextSetSource(Widget w, Widget source, XawTextPosition startPos) +#else +XawTextSetSource(w, source, startPos) +Widget w, source; +XawTextPosition startPos; +#endif +{ + TextWidget ctx = (TextWidget) w; + + ctx->text.source = source; + ctx->text.lt.top = startPos; + ctx->text.s.left = ctx->text.s.right = 0; + ctx->text.insertPos = startPos; + ctx->text.lastPos = GETLASTPOS; + + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); + XawTextDisplay(w); +} + +/* + * This public routine deletes the text from startPos to endPos in a source and + * then inserts, at startPos, the text that was passed. As a side effect it + * "invalidates" that portion of the displayed text (if any), so that things + * will be repainted properly. + */ + +int +#if NeedFunctionPrototypes +XawTextReplace(Widget w, XawTextPosition startPos, XawTextPosition endPos, + XawTextBlock *text) +#else +XawTextReplace(w, startPos, endPos, text) +Widget w; +XawTextPosition startPos, endPos; +XawTextBlock *text; +#endif +{ + TextWidget ctx = (TextWidget) w; + int result; + + _XawTextPrepareToUpdate(ctx); + endPos = FindGoodPosition(ctx, endPos); + startPos = FindGoodPosition(ctx, startPos); + if ((result = _XawTextReplace(ctx, startPos, endPos, text)) == XawEditDone) { + int delta = text->length - (endPos - startPos); + if (ctx->text.insertPos >= (endPos + delta)) { + XawTextScanDirection sd = (delta < 0) ? XawsdLeft : XawsdRight; + ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.insertPos, + XawstPositions, sd, abs(delta), TRUE); + } + } + + _XawTextCheckResize(ctx); + _XawTextExecuteUpdate(ctx); + _XawTextSetScrollBars(ctx); + + return result; +} + +XawTextPosition +#if NeedFunctionPrototypes +XawTextTopPosition(Widget w) +#else +XawTextTopPosition(w) +Widget w; +#endif +{ + return( ((TextWidget) w)->text.lt.top ); +} + +void +#if NeedFunctionPrototypes +XawTextSetInsertionPoint(Widget w, XawTextPosition position) +#else +XawTextSetInsertionPoint(w, position) +Widget w; +XawTextPosition position; +#endif +{ + TextWidget ctx = (TextWidget) w; + + _XawTextPrepareToUpdate(ctx); + ctx->text.insertPos = FindGoodPosition(ctx, position); + ctx->text.showposition = TRUE; + + _XawTextExecuteUpdate(ctx); +} + +XawTextPosition +#if NeedFunctionPrototypes +XawTextGetInsertionPoint(Widget w) +#else +XawTextGetInsertionPoint(w) +Widget w; +#endif +{ + return( ((TextWidget) w)->text.insertPos); +} + +/* + * NOTE: Must walk the selection list in opposite order from LoseSelection. + */ + +void +#if NeedFunctionPrototypes +XawTextUnsetSelection(Widget w) +#else +XawTextUnsetSelection(w) +Widget w; +#endif +{ + TextWidget ctx = (TextWidget)w; + + while (ctx->text.s.atom_count != 0) { + Atom sel = ctx->text.s.selections[ctx->text.s.atom_count - 1]; + if ( sel != (Atom) 0 ) { +/* + * As selections are lost the atom_count will decrement. + */ + if (GetCutBufferNumber(sel) == NOT_A_CUT_BUFFER) + XtDisownSelection(w, sel, ctx->text.time); + LoseSelection(w, &sel); /* In case this is a cut buffer, or + XtDisownSelection failed to call us. */ + } + } +} + +void +#if NeedFunctionPrototypes +XawTextSetSelection (Widget w, XawTextPosition left, XawTextPosition right) +#else +XawTextSetSelection (w, left, right) +Widget w; +XawTextPosition left, right; +#endif +{ + TextWidget ctx = (TextWidget) w; + + _XawTextPrepareToUpdate(ctx); + _XawTextSetSelection(ctx, FindGoodPosition(ctx, left), + FindGoodPosition(ctx, right), (String*)NULL, ZERO); + _XawTextExecuteUpdate(ctx); +} + +void +#if NeedFunctionPrototypes +XawTextInvalidate(Widget w, XawTextPosition from, XawTextPosition to) +#else +XawTextInvalidate(w, from, to) +Widget w; +XawTextPosition from,to; +#endif +{ + TextWidget ctx = (TextWidget) w; + + from = FindGoodPosition(ctx, from); + to = FindGoodPosition(ctx, to); + ctx->text.lastPos = GETLASTPOS; + _XawTextPrepareToUpdate(ctx); + _XawTextNeedsUpdating(ctx, from, to); + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); + _XawTextExecuteUpdate(ctx); +} + +/*ARGSUSED*/ +void +#if NeedFunctionPrototypes +XawTextDisableRedisplay(Widget w) +#else +XawTextDisableRedisplay(w) +Widget w; +#endif +{ + ((TextWidget) w)->text.update_disabled = True; + _XawTextPrepareToUpdate( (TextWidget) w); +} + +void +#if NeedFunctionPrototypes +XawTextEnableRedisplay(Widget w) +#else +XawTextEnableRedisplay(w) +Widget w; +#endif +{ + TextWidget ctx = (TextWidget)w; + XawTextPosition lastPos; + + if (!ctx->text.update_disabled) return; + + ctx->text.update_disabled = False; + lastPos = ctx->text.lastPos = GETLASTPOS; + ctx->text.lt.top = FindGoodPosition(ctx, ctx->text.lt.top); + ctx->text.insertPos = FindGoodPosition(ctx, ctx->text.insertPos); + if ( (ctx->text.s.left > lastPos) || (ctx->text.s.right > lastPos) ) + ctx->text.s.left = ctx->text.s.right = 0; + + _XawTextBuildLineTable(ctx, ctx->text.lt.top, TRUE); + if (XtIsRealized(w)) + DisplayTextWindow(w); + _XawTextExecuteUpdate(ctx); +} + +Widget +#if NeedFunctionPrototypes +XawTextGetSource(Widget w) +#else +XawTextGetSource(w) +Widget w; +#endif +{ + return ((TextWidget)w)->text.source; +} + +void +#if NeedFunctionPrototypes +XawTextDisplayCaret (Widget w, +#if NeedWidePrototypes + /* Boolean */ int display_caret) +#else + Boolean display_caret) +#endif +#else +XawTextDisplayCaret (w, display_caret) +Widget w; +Boolean display_caret; +#endif +{ + TextWidget ctx = (TextWidget) w; + + if (ctx->text.display_caret == display_caret) return; + + if (XtIsRealized(w)) { + _XawTextPrepareToUpdate(ctx); + ctx->text.display_caret = display_caret; + _XawTextExecuteUpdate(ctx); + } + else + ctx->text.display_caret = display_caret; +} + +/* Function Name: XawTextSearch(w, dir, text). + * Description: searches for the given text block. + * Arguments: w - The text widget. + * dir - The direction to search. + * text - The text block containing info about the string + * to search for. + * Returns: The position of the text found, or XawTextSearchError on + * an error. + */ + +XawTextPosition +#if NeedFunctionPrototypes +XawTextSearch(Widget w, +#if NeedWidePrototypes + /* XawTextScanDirection */ int dir, +#else + XawTextScanDirection dir, +#endif + XawTextBlock *text) +#else +XawTextSearch(w, dir, text) +Widget w; +XawTextScanDirection dir; +XawTextBlock * text; +#endif +{ + TextWidget ctx = (TextWidget) w; + + return(SrcSearch(ctx->text.source, ctx->text.insertPos, dir, text)); +} + +TextClassRec textClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &simpleClassRec, + /* class_name */ "Text", + /* widget_size */ sizeof(TextRec), + /* class_initialize */ ClassInitialize, + /* class_part_init */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ Realize, + /* actions */ _XawTextActionsTable, + /* num_actions */ 0, /* Set in ClassInitialize. */ + /* resources */ resources, + /* num_ resource */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure*/ XtExposeGraphicsExpose | XtExposeNoExpose, + /* compress_enterleave*/ TRUE, + /* visible_interest */ FALSE, + /* destroy */ TextDestroy, + /* resize */ Resize, + /* expose */ ProcessExposeRegion, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost*/ XtInheritSetValuesAlmost, + /* get_values_hook */ GetValuesHook, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, /* set in ClassInitialize */ + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator*/ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* Simple fields */ + /* change_sensitive */ ChangeSensitive + }, + { /* text fields */ + /* empty */ 0 + } +}; + +WidgetClass textWidgetClass = (WidgetClass)&textClassRec; |