/* $Xorg: TextAction.c,v 1.4 2001/02/09 02:03:46 xorgcvs Exp $ */

/*

Copyright 1989, 1994, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

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
OPEN GROUP 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 Open Group 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 Open Group.

*/
/* $XFree86: xc/lib/Xaw/TextAction.c,v 3.46tsi Exp $ */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xos.h>		/* for select() and struct timeval */
#include <ctype.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>
#include <X11/Xfuncs.h>
#include <X11/Xutil.h>
#include <X11/Xmu/Atoms.h>
#include <X11/Xmu/Misc.h>
#include <X11/Xmu/StdSel.h>
#include <X11/Xmu/SysUtil.h>
#include <X11/Xaw/MultiSinkP.h>
#include <X11/Xaw/MultiSrcP.h>
#include <X11/Xaw/TextP.h>
#include <X11/Xaw/TextSrcP.h>
#include <X11/Xaw/XawImP.h>
#include "Private.h"
#include "XawI18n.h"

#define SrcScan			XawTextSourceScan
#define FindDist		XawTextSinkFindDistance
#define FindPos			XawTextSinkFindPosition
#define MULT(w)			(w->text.mult == 0 ? 4 :		\
				 w->text.mult == 32767 ? -4 : w->text.mult)

#define KILL_RING_APPEND	2
#define KILL_RING_BEGIN		3
#define KILL_RING_YANK		100
#define KILL_RING_YANK_DONE	98

#define XawTextActionMaxHexChars	100

/*
 * Prototypes
 */
static void _DeleteOrKill(TextWidget, XawTextPosition, XawTextPosition, Bool);
static void _SelectionReceived(Widget, XtPointer, Atom*, Atom*, XtPointer,
			       unsigned long*, int*);
static void _LoseSelection(Widget, Atom*, char**, int*);
static void AutoFill(TextWidget);
static Boolean ConvertSelection(Widget, Atom*, Atom*, Atom*, XtPointer*,
				unsigned long*, int*);
static void DeleteOrKill(TextWidget, XEvent*, XawTextScanDirection,
			 XawTextScanType, Bool, Bool);
static void EndAction(TextWidget);
#ifndef OLDXAW
static Bool BlankLine(Widget, XawTextPosition, int*);
static int DoFormatText(TextWidget, XawTextPosition, Bool, int,
			XawTextBlock*, XawTextPosition*, int, Bool);
static int FormatText(TextWidget, XawTextPosition, Bool,
		      XawTextPosition*, int);
static Bool GetBlockBoundaries(TextWidget, XawTextPosition*, XawTextPosition*);
#endif
static int FormRegion(TextWidget, XawTextPosition, XawTextPosition,
		      XawTextPosition*, int);
static void GetSelection(Widget, Time, String*, Cardinal);
static char *IfHexConvertHexElseReturnParam(char*, int*);
static void InsertNewCRs(TextWidget, XawTextPosition, XawTextPosition,
			 XawTextPosition*, int);
static int InsertNewLineAndBackupInternal(TextWidget);
static int LocalInsertNewLine(TextWidget, XEvent*);
static void LoseSelection(Widget, Atom*);
static void ParameterError(Widget, String);
static Bool MatchSelection(Atom, XawTextSelection*);
static void ModifySelection(TextWidget, XEvent*, XawTextSelectionMode,
			    XawTextSelectionAction, String*, Cardinal*);
static void Move(TextWidget, XEvent*, XawTextScanDirection, XawTextScanType,
		 Bool);
static void NotePosition(TextWidget, XEvent*);
static void StartAction(TextWidget, XEvent*);
static XawTextPosition StripOutOldCRs(TextWidget, XawTextPosition,
				      XawTextPosition, XawTextPosition*, int);
#ifndef OLDXAW
static Bool StripSpaces(TextWidget, XawTextPosition, XawTextPosition,
			XawTextPosition*, int, XawTextBlock*);
static Bool Tabify(TextWidget, XawTextPosition, XawTextPosition,
		   XawTextPosition*, int, XawTextBlock*);
static Bool Untabify(TextWidget, XawTextPosition, XawTextPosition,
		     XawTextPosition*, int, XawTextBlock*);
#endif

/*
 * Actions
 */
static void CapitalizeWord(Widget, XEvent*, String*, Cardinal*);
static void DisplayCaret(Widget, XEvent*, String*, Cardinal*);
static void Delete(Widget, XEvent*, String*, Cardinal*);
static void DeleteBackwardChar(Widget, XEvent*, String*, Cardinal*);
static void DeleteBackwardWord(Widget, XEvent*, String*, Cardinal*);
static void DeleteCurrentSelection(Widget, XEvent*, String*, Cardinal*);
static void DeleteForwardChar(Widget, XEvent*, String*, Cardinal*);
static void DeleteForwardWord(Widget, XEvent*, String*, Cardinal*);
static void DowncaseWord(Widget, XEvent*, String*, Cardinal*);
static void ExtendAdjust(Widget, XEvent*, String*, Cardinal*);
static void ExtendEnd(Widget, XEvent*, String*, Cardinal*);
static void ExtendStart(Widget, XEvent*, String*, Cardinal*);
static void FormParagraph(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void Indent(Widget, XEvent*, String*, Cardinal*);
#endif
static void InsertChar(Widget, XEvent*, String*, Cardinal*);
static void InsertNewLine(Widget, XEvent*, String*, Cardinal*);
static void InsertNewLineAndBackup(Widget, XEvent*, String*, Cardinal*);
static void InsertNewLineAndIndent(Widget, XEvent*, String*, Cardinal*);
static void InsertSelection(Widget, XEvent*, String*, Cardinal*);
static void InsertString(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void KeyboardReset(Widget, XEvent*, String*, Cardinal*);
#endif
static void KillBackwardWord(Widget, XEvent*, String*, Cardinal*);
static void KillCurrentSelection(Widget, XEvent*, String*, Cardinal*);
static void KillForwardWord(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void KillRingYank(Widget, XEvent*, String*, Cardinal*);
#endif
static void KillToEndOfLine(Widget, XEvent*, String*, Cardinal*);
static void KillToEndOfParagraph(Widget, XEvent*, String*, Cardinal*);
static void MoveBackwardChar(Widget, XEvent*, String*, Cardinal*);
static void MoveBackwardWord(Widget, XEvent*, String*, Cardinal*);
static void MoveBackwardParagraph(Widget, XEvent*, String*, Cardinal*);
static void MoveBeginningOfFile(Widget, XEvent*, String*, Cardinal*);
static void MoveEndOfFile(Widget, XEvent*, String*, Cardinal*);
static void MoveForwardChar(Widget, XEvent*, String*, Cardinal*);
static void MoveForwardWord(Widget, XEvent*, String*, Cardinal*);
static void MoveForwardParagraph(Widget, XEvent*, String*, Cardinal*);
static void MoveNextLine(Widget, XEvent*, String*, Cardinal*);
static void MoveNextPage(Widget, XEvent*, String*, Cardinal*);
static void MovePage(TextWidget, XEvent*, XawTextScanDirection);
static void MovePreviousLine(Widget, XEvent*, String*, Cardinal*);
static void MovePreviousPage(Widget, XEvent*, String*, Cardinal*);
static void MoveLine(TextWidget, XEvent*, XawTextScanDirection);
static void MoveToLineEnd(Widget, XEvent*, String*, Cardinal*);
static void MoveToLineStart(Widget, XEvent*, String*, Cardinal*);
static void Multiply(Widget, XEvent*, String*, Cardinal*);
static void NoOp(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void Numeric(Widget, XEvent*, String*, Cardinal*);
#endif
static void Reconnect(Widget, XEvent*, String*, Cardinal*);
static void RedrawDisplay(Widget, XEvent*, String*, Cardinal*);
static void Scroll(TextWidget, XEvent*, XawTextScanDirection);
static void ScrollOneLineDown(Widget, XEvent*, String*, Cardinal*);
static void ScrollOneLineUp(Widget, XEvent*, String*, Cardinal*);
static void SelectAdjust(Widget, XEvent*, String*, Cardinal*);
static void SelectAll(Widget, XEvent*, String*, Cardinal*);
static void SelectEnd(Widget, XEvent*, String*, Cardinal*);
static void SelectSave(Widget, XEvent*, String*, Cardinal*);
static void SelectStart(Widget, XEvent*, String*, Cardinal*);
static void SelectWord(Widget, XEvent*, String*, Cardinal*);
static void SetKeyboardFocus(Widget, XEvent*, String*, Cardinal*);
static void TextEnterWindow(Widget, XEvent*, String*, Cardinal*);
static void TextFocusIn(Widget, XEvent*, String*, Cardinal*);
static void TextFocusOut(Widget, XEvent*, String*, Cardinal*);
static void TextLeaveWindow(Widget, XEvent*, String*, Cardinal*);
static void TransposeCharacters(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void ToggleOverwrite(Widget, XEvent*, String*, Cardinal*);
static void Undo(Widget, XEvent*, String*, Cardinal*);
#endif
static void UpcaseWord(Widget, XEvent*, String*, Cardinal*);
static void DestroyFocusCallback(Widget, XtPointer, XtPointer);

/*
 * External
 */
void _XawTextZapSelection(TextWidget, XEvent*, Bool);

/*
 * Defined in TextPop.c
 */
void _XawTextInsertFileAction(Widget, XEvent*, String*, Cardinal*);
void _XawTextInsertFile(Widget, XEvent*, String*, Cardinal*);
void _XawTextSearch(Widget, XEvent*, String*, Cardinal*);
void _XawTextDoSearchAction(Widget, XEvent*, String*, Cardinal*);
void _XawTextDoReplaceAction(Widget, XEvent*, String*, Cardinal*);
void _XawTextSetField(Widget, XEvent*, String*, Cardinal*);
void _XawTextPopdownSearchAction(Widget, XEvent*, String*, Cardinal*);

/*
 * These are defined in Text.c
 */
void _XawTextAlterSelection(TextWidget, XawTextSelectionMode,
			    XawTextSelectionAction, String*, Cardinal*);
void _XawTextClearAndCenterDisplay(TextWidget);
void _XawTextExecuteUpdate(TextWidget);
char *_XawTextGetText(TextWidget, XawTextPosition, XawTextPosition);
void _XawTextPrepareToUpdate(TextWidget);
int _XawTextReplace(TextWidget, XawTextPosition, XawTextPosition,
			   XawTextBlock*);
Atom *_XawTextSelectionList(TextWidget, String*, Cardinal);
void _XawTextSetSelection(TextWidget, XawTextPosition, XawTextPosition,
				 String*, Cardinal);
void _XawTextVScroll(TextWidget, int);
void XawTextScroll(TextWidget, int, int);
void _XawTextSetLineAndColumnNumber(TextWidget, Bool);

#ifndef OLDXAW
/*
 * Defined in TextSrc.c
 */
Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);
Bool _XawTextSrcToggleUndo(TextSrcObject);
void _XawSourceSetUndoErase(TextSrcObject, int);
void _XawSourceSetUndoMerge(TextSrcObject, Bool);
#endif /* OLDXAW */

/*
 * Initialization
 */
#ifndef OLDXAW
#define MAX_KILL_RINGS	1024
XawTextKillRing *xaw_text_kill_ring;
static XawTextKillRing kill_ring_prev, kill_ring_null = { &kill_ring_prev, };
static unsigned num_kill_rings;
#endif

/*
 * Implementation
 */
static void
ParameterError(Widget w, String param)
{
    String params[2];
    Cardinal num_params = 2;
    params[0] = XtName(w);
    params[1] = param;

    XtAppWarningMsg(XtWidgetToApplicationContext(w),
		    "parameterError", "textAction", "XawError",
		    "Widget: %s Parameter: %s",
		    params, &num_params);
    XBell(XtDisplay(w), 50);
}

static void
StartAction(TextWidget ctx, XEvent *event)
{
#ifndef OLDXAW
    Cardinal i;
    TextSrcObject src = (TextSrcObject)ctx->text.source;

    for (i = 0; i < src->textSrc.num_text; i++)
	_XawTextPrepareToUpdate((TextWidget)src->textSrc.text[i]);
    _XawSourceSetUndoMerge(src, False);
#else
    _XawTextPrepareToUpdate(ctx);
#endif

    if (event != NULL) {
	switch (event->type) {
	    case ButtonPress:
	    case ButtonRelease:
		ctx->text.time = event->xbutton.time;
		break;
	    case KeyPress:
	    case KeyRelease:
		ctx->text.time = event->xkey.time;
		break;
	    case MotionNotify:
		ctx->text.time = event->xmotion.time;
		break;
	    case EnterNotify:
	    case LeaveNotify:
		ctx->text.time = event->xcrossing.time;
	}
    }
}

static void
NotePosition(TextWidget ctx, XEvent *event)
{
    switch (event->type) {
	case ButtonPress:
	case ButtonRelease:
	    ctx->text.ev_x = event->xbutton.x;
	    ctx->text.ev_y = event->xbutton.y;
	    break;
	case KeyPress:
	case KeyRelease: {
	    XRectangle cursor;
	    XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
	    ctx->text.ev_x = cursor.x + cursor.width / 2;
	    ctx->text.ev_y = cursor.y + cursor.height / 2;
	}   break;
	case MotionNotify:
	    ctx->text.ev_x = event->xmotion.x;
	    ctx->text.ev_y = event->xmotion.y;
	    break;
	case EnterNotify:
	case LeaveNotify:
	    ctx->text.ev_x = event->xcrossing.x;
	    ctx->text.ev_y = event->xcrossing.y;
    }
}

static void
EndAction(TextWidget ctx)
{
#ifndef OLDXAW
    Cardinal i;
    TextSrcObject src = (TextSrcObject)ctx->text.source;

    for (i = 0; i < src->textSrc.num_text; i++)
	_XawTextExecuteUpdate((TextWidget)src->textSrc.text[i]);

    ctx->text.mult = 1;
    ctx->text.numeric = False;
    if (ctx->text.kill_ring) {
	if (--ctx->text.kill_ring == KILL_RING_YANK_DONE) {
	    if (ctx->text.kill_ring_ptr) {
		--ctx->text.kill_ring_ptr->refcount;
		ctx->text.kill_ring_ptr = NULL;
	    }
	}
    }
#else
    ctx->text.mult = 1;
    _XawTextExecuteUpdate(ctx);
#endif /* OLDXAW */
}

struct _SelectionList {
    String* params;
    Cardinal count;
    Time time;
    int asked;		/* which selection currently has been asked for:
			   0 = UTF8_STRING, 1 = COMPOUND_TEXT, 2 = STRING */
    Atom selection;	/* selection atom (normally XA_PRIMARY) */
};

/*ARGSUSED*/
static void
_SelectionReceived(Widget w, XtPointer client_data, Atom *selection,
		   Atom *type, XtPointer value, unsigned long *length,
		   int *format)
{
    Display *d = XtDisplay(w);
    TextWidget ctx = (TextWidget)w;
    XawTextBlock text;

    if (*type == 0 /*XT_CONVERT_FAIL*/ || *length == 0) {
	struct _SelectionList* list = (struct _SelectionList*)client_data;

	if (list != NULL) {
	    if (list->asked == 0) {
		/* If we just asked for XA_UTF8_STRING and got no response,
		   we'll ask again, this time for XA_COMPOUND_TEXT. */
		list->asked++;
		XtGetSelectionValue(w, list->selection, XA_COMPOUND_TEXT(d),
				    _SelectionReceived,
				    (XtPointer)list, list->time);
	    } else if (list->asked == 1) {
		/* If we just asked for XA_COMPOUND_TEXT and got no response,
		   we'll ask again, this time for XA_STRING. */
		list->asked++;
		XtGetSelectionValue(w, list->selection, XA_STRING,
				    _SelectionReceived,
				    (XtPointer)list, list->time);
	    } else {
		/* We tried all possible text targets in this param.
		   Recurse on the tail of the params list. */
		GetSelection(w, list->time, list->params, list->count);
		XtFree(client_data);
	    }
	}
	return;
    }

    StartAction(ctx, NULL);
    if (XawTextFormat(ctx, XawFmtWide)) {
	XTextProperty textprop;
	wchar_t **wlist;
	int count;

	textprop.encoding = *type;
	textprop.value = (unsigned char *)value;
	textprop.nitems = strlen(value);
	textprop.format = 8;

	if (XwcTextPropertyToTextList(d, &textprop, &wlist, &count)
	    !=	Success
	    || count < 1) {
	    XwcFreeStringList(wlist);

	    /* Notify the user on strerr and in the insertion :) */
	    fprintf(stderr, "Xaw Text Widget: An attempt was made to insert "
		    "an illegal selection.\n");

	    textprop.value = (unsigned char *)" >> ILLEGAL SELECTION << ";
	    textprop.nitems = strlen((char *) textprop.value);
	    if (XwcTextPropertyToTextList(d, &textprop, &wlist, &count)
		!=  Success
		|| count < 1)
		return;
	}

	XFree(value);
	value = (XPointer)wlist[0];

	*length = wcslen(wlist[0]);
	XtFree((XtPointer)wlist);
	text.format = XawFmtWide;
    } else {
	XTextProperty textprop;
	char **list;
	int count;

	textprop.encoding = *type;
	textprop.value = (unsigned char *)value;
	textprop.nitems = strlen(value);
	textprop.format = 8;

	if (XmbTextPropertyToTextList(d, &textprop, &list, &count)
	    !=	Success
	    || count < 1) {
	    XFreeStringList(list);

	    /* Notify the user on strerr and in the insertion :) */
	    fprintf(stderr, "Xaw Text Widget: An attempt was made to insert "
		    "an illegal selection.\n");

	    textprop.value = (unsigned char *)" >> ILLEGAL SELECTION << ";
	    textprop.nitems = strlen((char *) textprop.value);
	    if (XmbTextPropertyToTextList(d, &textprop, &list, &count)
		!=  Success
		|| count < 1)
		return;
	}

	XFree(value);
	value = (XPointer)list[0];

	*length = strlen(list[0]);
	XtFree((XtPointer)list);
	text.format = XawFmt8Bit;
    }
    text.ptr = (char*)value;
    text.firstPos = 0;
    text.length = *length;
    if (_XawTextReplace(ctx, ctx->text.insertPos, ctx->text.insertPos, &text)) {
	XBell(XtDisplay(ctx), 0);
	EndAction(ctx);
	return;
    }

    ctx->text.from_left = -1;
    ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
				  XawstPositions, XawsdRight, text.length, True);

    EndAction(ctx);
    XtFree(client_data);
    XFree(value);	/* the selection value should be freed with XFree */
}

static void
GetSelection(Widget w, Time timev, String *params, Cardinal num_params)
{
    Atom selection;
    int buffer;

    selection = XInternAtom(XtDisplay(w), *params, False);
    switch (selection) {
	case XA_CUT_BUFFER0: buffer = 0; break;
	case XA_CUT_BUFFER1: buffer = 1; break;
	case XA_CUT_BUFFER2: buffer = 2; break;
	case XA_CUT_BUFFER3: buffer = 3; break;
	case XA_CUT_BUFFER4: buffer = 4; break;
	case XA_CUT_BUFFER5: buffer = 5; break;
	case XA_CUT_BUFFER6: buffer = 6; break;
	case XA_CUT_BUFFER7: buffer = 7; break;
	default:	     buffer = -1;
    }
    if (buffer >= 0) {
	int nbytes;
	unsigned long length;
	int fmt8 = 8;
	Atom type = XA_STRING;
	char *line = XFetchBuffer(XtDisplay(w), &nbytes, buffer);

	if ((length = nbytes) != 0L)
	    _SelectionReceived(w, NULL, &selection, &type, line, &length, &fmt8);
	else if (num_params > 1)
	    GetSelection(w, timev, params+1, num_params-1);
    }
    else {
	struct _SelectionList* list;

	if (--num_params) {
	    list = XtNew(struct _SelectionList);
	    list->params = params + 1;
	    list->count = num_params;
	    list->time = timev;
	    list->asked = 0;
	    list->selection = selection;
	}
	else
	    list = NULL;
	XtGetSelectionValue(w, selection, XA_UTF8_STRING(XtDisplay(w)),
			    _SelectionReceived, (XtPointer)list, timev);
    }
}

static void
InsertSelection(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    StartAction((TextWidget)w, event);	/* Get Time. */
    GetSelection(w, ((TextWidget)w)->text.time, params, *num_params);
    EndAction((TextWidget)w);
}

/*
 * Routines for Moving Around
 */
static void
Move(TextWidget ctx, XEvent *event, XawTextScanDirection dir,
     XawTextScanType type, Bool include)
{
    XawTextPosition insertPos;
    short mult = MULT(ctx);

    if (mult < 0) {
	mult = -mult;
	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
    }

    insertPos = SrcScan(ctx->text.source, ctx->text.insertPos,
			type, dir, mult, include);

    StartAction(ctx, event);

    if (ctx->text.s.left != ctx->text.s.right)
	XawTextUnsetSelection((Widget)ctx);

#ifndef OLDXAW
    ctx->text.numeric = False;
#endif
    ctx->text.mult = 1;
    ctx->text.showposition = True;
    ctx->text.from_left = -1;
    ctx->text.insertPos = insertPos;
    EndAction(ctx);
}

/*ARGSUSED*/
static void
MoveForwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdRight, XawstPositions, True);
}

/*ARGSUSED*/
static void
MoveBackwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdLeft, XawstPositions, True);
}

static void
MoveForwardWord(Widget w, XEvent *event, String *p, Cardinal *n)
{
    if (*n && (p[0][0] == 'A' || p[0][0] == 'a'))
	Move((TextWidget)w, event, XawsdRight, XawstAlphaNumeric, False);
    else
	Move((TextWidget)w, event, XawsdRight, XawstWhiteSpace, False);
}

static void
MoveBackwardWord(Widget w, XEvent *event, String *p, Cardinal *n)
{
    if (*n && (p[0][0] == 'A' || p[0][0] == 'a'))
	Move((TextWidget)w, event, XawsdLeft, XawstAlphaNumeric, False);
    else
	Move((TextWidget)w, event, XawsdLeft, XawstWhiteSpace, False);
}

static void
MoveForwardParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition position = ctx->text.insertPos;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MoveBackwardParagraph(w, event, p, n);
	return;
    }

    while (mult--) {
	position = SrcScan(ctx->text.source, position,
			   XawstEOL, XawsdRight, 1, False) - 1;

	while (position == SrcScan(ctx->text.source, position,
				   XawstEOL, XawsdRight, 1, False))
	    if (++position > ctx->text.lastPos) {
		mult = 0;
		break;
	    }

	position = SrcScan(ctx->text.source, position,
			   XawstParagraph, XawsdRight, 1, True);
	if (position != ctx->text.lastPos)
	    position = SrcScan(ctx->text.source, position - 1,
			       XawstEOL, XawsdLeft, 1, False);
	else
	    break;
    }

    if (position != ctx->text.insertPos) {
	XawTextUnsetSelection(w);
	StartAction(ctx, event);
	ctx->text.showposition = True;
	ctx->text.from_left = -1;
	ctx->text.insertPos = position;
	EndAction(ctx);
    }
    else
	ctx->text.mult = 1;
}

/*ARGSUSED*/
static void
MoveBackwardParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition position = ctx->text.insertPos;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MoveForwardParagraph(w, event, p, n);
	return;
    }

    while (mult--) {
	position = SrcScan(ctx->text.source, position,
			   XawstEOL, XawsdLeft, 1, False) + 1;

	while (position == SrcScan(ctx->text.source, position,
				   XawstEOL, XawsdLeft, 1, False))
	    if (--position < 0) {
		mult = 0;
		break;
	    }

	position = SrcScan(ctx->text.source, position,
			   XawstParagraph, XawsdLeft, 1, True);
	if (position > 0 && position < ctx->text.lastPos)
	    ++position;
	else
	    break;
    }

    if (position != ctx->text.insertPos) {
	XawTextUnsetSelection(w);
	StartAction(ctx, event);
	ctx->text.showposition = True;
	ctx->text.from_left = -1;
	ctx->text.insertPos = position;
	EndAction(ctx);
    }
    else
	ctx->text.mult = 1;
}

/*ARGSUSED*/
static void
MoveToLineEnd(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdRight, XawstEOL, False);
}

/*ARGSUSED*/
static void
MoveToLineStart(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdLeft, XawstEOL, False);
}

static void
MoveLine(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
{
    XawTextPosition cnew, next_line, ltemp;
    int itemp, from_left;
    short mult = MULT(ctx);

    StartAction(ctx, event);

    XawTextUnsetSelection((Widget)ctx);

    if (dir == XawsdLeft)
	mult = mult == 0 ? 5 : mult + 1;

    cnew = SrcScan(ctx->text.source, ctx->text.insertPos,
		   XawstEOL, XawsdLeft, 1, False);

    if (ctx->text.from_left < 0)
	FindDist(ctx->text.sink, cnew, ctx->text.left_margin, ctx->text.insertPos,
		 &ctx->text.from_left, &ltemp, &itemp);

    cnew = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, dir,
		   mult, (dir == XawsdRight));

    next_line = SrcScan(ctx->text.source, cnew, XawstEOL, XawsdRight, 1, False);

    FindPos(ctx->text.sink, cnew, ctx->text.left_margin, ctx->text.from_left,
	    False, &ctx->text.insertPos, &from_left, &itemp);

    if (from_left < ctx->text.from_left) {
	XawTextBlock block;

	XawTextSourceRead(ctx->text.source, ctx->text.insertPos, &block, 1);
	if (block.length) {
	    if (XawTextFormat(ctx, XawFmtWide)) {
		if (*(wchar_t *)block.ptr == _Xaw_atowc(XawTAB))
		    ++ctx->text.insertPos;
	    }
	    else if (block.ptr[0] == XawTAB)
		++ctx->text.insertPos;
	}
    }

    if (ctx->text.insertPos > next_line)
	ctx->text.insertPos = next_line;

    EndAction(ctx);
}

static void
MoveNextLine(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MovePreviousLine(w, event, p, n);
	return;
    }

    if (ctx->text.insertPos < ctx->text.lastPos)
	MoveLine(ctx, event, XawsdRight);
    else
	ctx->text.mult = 1;
}

static void
MovePreviousLine(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MoveNextLine(w, event, p, n);
	return;
    }

    if (ctx->text.lt.top != 0 || (ctx->text.lt.lines > 1 &&
	ctx->text.insertPos >= ctx->text.lt.info[1].position))
	MoveLine(ctx, event, XawsdLeft);
    else
	ctx->text.mult = 1;
}

/*ARGSUSED*/
static void
MoveBeginningOfFile(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdLeft, XawstAll, True);
}

/*ARGSUSED*/
static void
MoveEndOfFile(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Move((TextWidget)w, event, XawsdRight, XawstAll, True);
}

static void
Scroll(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
{
    short mult = MULT(ctx);

    if (mult < 0) {
	mult = -mult;
	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
    }

    if (ctx->text.lt.lines > 1
	&& (dir == XawsdRight
	    || ctx->text.lastPos >= ctx->text.lt.info[1].position)) {
	StartAction(ctx, event);

	if (dir == XawsdLeft)
	    _XawTextVScroll(ctx, mult);
	else
	    _XawTextVScroll(ctx, -mult);

	EndAction(ctx);
    }
    else {
	ctx->text.mult = 1;
#ifndef OLDXAW
	ctx->text.numeric = False;
#endif
    }
}

/*ARGSUSED*/
static void
ScrollOneLineUp(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Scroll((TextWidget)w, event, XawsdLeft);
}

/*ARGSUSED*/
static void
ScrollOneLineDown(Widget w, XEvent *event, String *p, Cardinal *n)
{
    Scroll((TextWidget)w, event, XawsdRight);
}

static void
MovePage(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
{
    int scroll_val = 0;
    XawTextPosition old_pos;

    ctx->text.from_left = -1;
    switch (dir) {
	case XawsdLeft:
	    if (ctx->text.lt.top != 0)
		scroll_val = -Max(1, ctx->text.lt.lines - 1);
		break;
	case XawsdRight:
	    if (!IsPositionVisible(ctx, Max(0, ctx->text.lastPos)))
		scroll_val = Max(1, ctx->text.lt.lines - 1);
	    break;
    }

    if (scroll_val)
	XawTextScroll(ctx, scroll_val,
		      ctx->text.left_margin - ctx->text.r_margin.left);

    old_pos = ctx->text.insertPos;
    switch (dir) {
	case XawsdRight:
	    if (IsPositionVisible(ctx, Max(0, ctx->text.lastPos)))
		ctx->text.insertPos = Max(0, ctx->text.lastPos);
	    else
		ctx->text.insertPos = ctx->text.lt.top;
	    if (ctx->text.insertPos < old_pos)
		ctx->text.insertPos = SrcScan(ctx->text.source, old_pos,
					      XawstEOL, XawsdLeft, 1, False);
	    break;
	case XawsdLeft:
	    if (IsPositionVisible(ctx, 0))
		ctx->text.insertPos = 0;
	    else if (ctx->text.lt.lines)
		ctx->text.insertPos =
		    ctx->text.lt.info[ctx->text.lt.lines - 1].position;
	    else
		ctx->text.insertPos = ctx->text.lt.top;
	    if (ctx->text.insertPos > old_pos)
		ctx->text.insertPos = SrcScan(ctx->text.source, old_pos,
					      XawstEOL, XawsdLeft, 1, False);
	    break;
    }
}

static void
MoveNextPage(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MovePreviousPage(w, event, p, n);
	return;
    }

    if (ctx->text.insertPos < ctx->text.lastPos) {
	XawTextUnsetSelection(w);
	StartAction(ctx, event);
	ctx->text.clear_to_eol = True;
	while (mult-- && ctx->text.insertPos < ctx->text.lastPos)
	    MovePage(ctx, event, XawsdRight);
	EndAction(ctx);
    }
    else
	ctx->text.mult = 1;
}

/*ARGSUSED*/
static void
MovePreviousPage(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    short mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = -mult;
	MoveNextPage(w, event, p, n);
	return;
    }

    if (ctx->text.insertPos > 0) {
	XawTextUnsetSelection(w);
	StartAction(ctx, event);
	ctx->text.clear_to_eol = True;
	while (mult-- && ctx->text.insertPos > 0)
	    MovePage(ctx, event, XawsdLeft);
	EndAction(ctx);
    }
    else
	ctx->text.mult = 1;
}

/*
 * Delete Routines
 */
static Bool
MatchSelection(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);
}

#define SrcCvtSel	XawTextSourceConvertSelection

static Boolean
ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *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);

	XtSetArg(args[0], XtNeditType,&edit_mode);
	XtGetValues(src, args, 1);

	XmuConvertStandardSelection(w, ctx->text.time, selection,
				    target, type, (XPointer *)&std_targets,
				    &std_length, format);

	*length = 7 + (edit_mode == XawtextEdit) + std_length;
	*value = XtMalloc((unsigned)sizeof(Atom)*(*length));
	targetP = *(Atom**)value;
	*targetP++ = XA_STRING;
	*targetP++ = XA_TEXT(d);
	*targetP++ = XA_UTF8_STRING(d);
	*targetP++ = XA_COMPOUND_TEXT(d);
	*targetP++ = XA_LENGTH(d);
	*targetP++ = XA_LIST_LENGTH(d);
	*targetP++ = XA_CHARACTER_POSITION(d);
	if (edit_mode == XawtextEdit) {
	    *targetP++ = XA_DELETE(d);
	}
	memcpy((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);

    for (salt = ctx->text.salt2; 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_UTF8_STRING(d)
	|| *target == XA_COMPOUND_TEXT(d)) {
	if (*target == XA_TEXT(d)) {
	    if (XawTextFormat(ctx, XawFmtWide))
		*type = XA_COMPOUND_TEXT(d);
	    else
		*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 = (char *)_XawTextGetSTRING(ctx, s->left, s->right);
	    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
		*length = strlen(*value);
	}
	else {
	    *value = XtMalloc((salt->length + 1) * sizeof(unsigned char));
	    strcpy (*value, salt->contents);
	    *length = salt->length;
	}
	/* Got *value,*length, now in COMPOUND_TEXT format. */
	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, &wlist, &count)
		 < Success
		|| count < 1) {
		XtFree(*value);
		return (False);
	    }
	    XtFree(*value);
	    if (XwcTextListToTextProperty(d, wlist, 1, XStringStyle, &textprop)
		 < Success) {
		XwcFreeStringList((wchar_t**)wlist);
		return (False);
	    }
	    *value = (XtPointer)textprop.value;
	    *length = textprop.nitems;
	    XwcFreeStringList((wchar_t**) wlist);
	} else if (*type == XA_UTF8_STRING(d)) {
	    XTextProperty textprop;
	    char **list;
	    int count;

	    textprop.encoding = XA_COMPOUND_TEXT(d);
	    textprop.value = (unsigned char *)*value;
	    textprop.nitems = strlen(*value);
	    textprop.format = 8;
	    if (Xutf8TextPropertyToTextList(d, &textprop, &list, &count)
		 < Success
		|| count < 1) {
		XtFree(*value);
		return (False);
	    }
	    XtFree(*value);
	    *value = *list;
	    *length = strlen(*list);
	    XFree(list);
	}
	*format = 8;
	return (True);
    }

    if (*target == XA_LIST_LENGTH(d) || *target == XA_LENGTH(d)) {
	long *temp;

	temp = (long *)XtMalloc(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(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)) {
	if (!salt)
	    _XawTextZapSelection(ctx, 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);
  
    return (False);
}

static void
LoseSelection(Widget w, Atom *selection)
{
    _LoseSelection(w, selection, NULL, NULL);
}

static void
_LoseSelection(Widget w, Atom *selection, char **contents, int *length)
{
    TextWidget ctx = (TextWidget)w;
    Atom *atomP;
    int i;
    XawTextSelectionSalt *salt, *prevSalt, *nextSalt;

    prevSalt = 0;
    for (salt = ctx->text.salt2; 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) {
#ifndef OLDXAW
	    if (contents == NULL) {
		XawTextKillRing *kill_ring = XtNew(XawTextKillRing);

		kill_ring->next = xaw_text_kill_ring;
		kill_ring->contents = salt->contents;
		kill_ring->length = salt->length;
		kill_ring->format = XawFmt8Bit;
		xaw_text_kill_ring = kill_ring;
		kill_ring_prev.next = xaw_text_kill_ring;

		if (++num_kill_rings > MAX_KILL_RINGS) {
		    XawTextKillRing *tail = NULL;

		    while (kill_ring->next) {
			tail = kill_ring;
			kill_ring = kill_ring->next;
		    }
		    if (kill_ring->refcount == 0) {
			--num_kill_rings;
			tail->next = NULL;
			XtFree(kill_ring->contents);
			XtFree((char*)kill_ring);
		    }
		}
	    }
	    else {
		*contents = salt->contents;
		*length = salt->length;
	    }
#endif
	    if (prevSalt)
		prevSalt->next = nextSalt;
	    else
		ctx->text.salt2 = nextSalt;

	    XtFree((char *)salt->s.selections);
	    XtFree((char *)salt);
	}
	else
	    prevSalt = salt;
    }
}

static void
_DeleteOrKill(TextWidget ctx, XawTextPosition from, XawTextPosition to,
	      Bool kill)
{
    XawTextBlock text;

#ifndef OLDXAW
    if (ctx->text.kill_ring_ptr) {
	--ctx->text.kill_ring_ptr->refcount;
	ctx->text.kill_ring_ptr = NULL;
    }
#endif
    if (kill && from < to) {
#ifndef OLDXAW
	Bool append = False;
	char *ring = NULL;
	XawTextPosition old_from = from;
#endif
	char *string;
	int size = 0, length;
	XawTextSelectionSalt *salt;
	Atom selection = XInternAtom(XtDisplay(ctx), "SECONDARY", False);

#ifndef OLDXAW
	if (ctx->text.kill_ring == KILL_RING_APPEND) {
	    old_from = ctx->text.salt2->s.left;
	    append = True;
	}
	else
	    ctx->text.kill_ring = KILL_RING_BEGIN;

	if (append)
	    _LoseSelection((Widget)ctx, &selection, &ring, &size);
	else
#endif
	    LoseSelection((Widget)ctx, &selection);

	salt = (XawTextSelectionSalt*)XtMalloc(sizeof(XawTextSelectionSalt));
	salt->s.selections = (Atom *)XtMalloc(sizeof(Atom));
	salt->s.left = from;
	salt->s.right = to;

	string = (char *)_XawTextGetSTRING(ctx, from, to);

	if (XawTextFormat(ctx, XawFmtWide)) {
	    XTextProperty textprop;

	    if (XwcTextListToTextProperty(XtDisplay((Widget)ctx),
					  (wchar_t**)(&string),
					  1, XCompoundTextStyle,
					  &textprop) <  Success) {
		XtFree(string);
		XtFree((char*)salt->s.selections);
		XtFree((char*)salt);
		return;
	    }
	    XtFree(string);
	    string = (char *)textprop.value;
	    length = textprop.nitems;
	}
	else
	    length = strlen(string);

	salt->length = length + size;

#ifndef OLDXAW
	if (!append)
	    salt->contents = string;
	else {
	    salt->contents = XtMalloc(length + size + 1);
	    if (from >= old_from) {
		strncpy(salt->contents, ring, size);
		salt->contents[size] = '\0';
		strncat(salt->contents, string, length);
	    }
	    else {
		strncpy(salt->contents, string, length);
		salt->contents[length] = '\0';
		strncat(salt->contents, ring, size);
	    }
	    salt->contents[length + size] = '\0';
	    XtFree(ring);
	    XtFree(string);
	}

	kill_ring_prev.contents = salt->contents;
	kill_ring_prev.length = salt->length;
	kill_ring_prev.format = XawFmt8Bit;
#else
	salt->contents = string;
#endif

	salt->next = ctx->text.salt2;
	ctx->text.salt2 = salt;

#ifndef OLDXAW
	if (append)
	    ctx->text.kill_ring = KILL_RING_BEGIN;
#endif

	salt->s.selections[0] = selection;

	XtOwnSelection((Widget)ctx, selection, ctx->text.time,
		       ConvertSelection, LoseSelection, NULL);
	salt->s.atom_count = 1;
    }
    text.length = 0;
    text.firstPos = 0;

    text.format = _XawTextFormat(ctx);
    text.ptr = "";

    if (_XawTextReplace(ctx, from, to, &text)) {
	XBell(XtDisplay(ctx), 50);
	return;
    }
    ctx->text.from_left = -1;
    ctx->text.insertPos = from;
    ctx->text.showposition = TRUE;
}

static void
DeleteOrKill(TextWidget ctx, XEvent *event, XawTextScanDirection dir,
	     XawTextScanType type, Bool include, Bool kill)
{
    XawTextPosition from, to;
    short mult = MULT(ctx);

    if (mult < 0) {
	mult = -mult;
	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
    }

    StartAction(ctx, event);
#ifndef OLDXAW
    if (mult == 1)
	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
#endif
    to = SrcScan(ctx->text.source, ctx->text.insertPos,
		 type, dir, mult, include);

    /*
     * If no movement actually happened, then bump the count and try again.
     * This causes the character position at the very beginning and end of
     * a boundary to act correctly
     */
    if (to == ctx->text.insertPos)
	to = SrcScan(ctx->text.source, ctx->text.insertPos,
		     type, dir, mult + 1, include);

    if (dir == XawsdLeft) {
	from = to;
	to = ctx->text.insertPos;
    }
    else 
	from = ctx->text.insertPos;

    _DeleteOrKill(ctx, from, to, kill);
    EndAction(ctx);
}

static void
Delete(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;

    if (ctx->text.s.left != ctx->text.s.right)
	DeleteCurrentSelection(w, event, p, n);
    else
	DeleteBackwardChar(w, event, p, n);
}

static void
DeleteChar(Widget w, XEvent *event, XawTextScanDirection dir)
{
    TextWidget ctx = (TextWidget)w;
    short mul = MULT(ctx);

    if (mul < 0) {
	ctx->text.mult = mul = -mul;
	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
    }
    DeleteOrKill(ctx, event, dir, XawstPositions, True, False);
#ifndef OLDXAW
    if (mul == 1)
	_XawSourceSetUndoErase((TextSrcObject)ctx->text.source,
			       dir == XawsdLeft ? -1 : 1);
#endif
}

/*ARGSUSED*/
static void
DeleteForwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
{
    DeleteChar(w, event, XawsdRight);
}

/*ARGSUSED*/
static void
DeleteBackwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
{
    DeleteChar(w, event, XawsdLeft);
}

static void
DeleteForwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XawTextScanType type;

    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
	type = XawstAlphaNumeric;
    else
	type = XawstWhiteSpace;

    DeleteOrKill((TextWidget)w, event, XawsdRight, type, False, False);
}

static void
DeleteBackwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XawTextScanType type;

    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
	type = XawstAlphaNumeric;
    else
	type = XawstWhiteSpace;

    DeleteOrKill((TextWidget)w, event, XawsdLeft, type, False, False);
}

static void
KillForwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XawTextScanType type;

    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
	type = XawstAlphaNumeric;
    else
	type = XawstWhiteSpace;

    DeleteOrKill((TextWidget)w, event, XawsdRight, type, False, True);
}

static void
KillBackwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XawTextScanType type;

    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
	type = XawstAlphaNumeric;
    else
	type = XawstWhiteSpace;

    DeleteOrKill((TextWidget) w, event, XawsdLeft, type, False, True);
}

/*ARGSUSED*/
static void
KillToEndOfLine(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition end_of_line;
    XawTextScanDirection dir = XawsdRight;
    short mult = MULT(ctx);

    if (mult < 0) {
	dir = XawsdLeft;
	mult = -mult;
    }

    StartAction(ctx, event);
    end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
			  dir, mult, False);
    if (end_of_line == ctx->text.insertPos)
	end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
			      dir, mult, True);

    if (dir == XawsdRight)
	_DeleteOrKill(ctx, ctx->text.insertPos, end_of_line, True);
    else
	_DeleteOrKill(ctx, end_of_line, ctx->text.insertPos, True);
    EndAction(ctx);
}

/*ARGSUSED*/
static void
KillToEndOfParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
{
    DeleteOrKill((TextWidget)w, event, XawsdRight, XawstParagraph, False, True);
}

void
_XawTextZapSelection(TextWidget ctx, XEvent *event, Bool kill)
{
    StartAction(ctx, event);
    _DeleteOrKill(ctx, ctx->text.s.left, ctx->text.s.right, kill);
    EndAction(ctx);
}

/*ARGSUSED*/
static void
KillCurrentSelection(Widget w, XEvent *event, String *p, Cardinal *n)
{
    _XawTextZapSelection((TextWidget) w, event, True);
}

#ifndef OLDXAW
/*ARGSUSED*/
static void
KillRingYank(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition insertPos = ctx->text.insertPos;
    Bool first_yank = False;

    if (ctx->text.s.left != ctx->text.s.right)
	XawTextUnsetSelection((Widget)ctx);

    StartAction(ctx, event);

    if (ctx->text.kill_ring_ptr == NULL) {
	ctx->text.kill_ring_ptr = &kill_ring_prev;
	++ctx->text.kill_ring_ptr->refcount;
	ctx->text.s.left = ctx->text.s.right = insertPos;
	first_yank = True;
    }
    if (ctx->text.kill_ring_ptr) {
	int mul = MULT(ctx);
	XawTextBlock text;

	if (!first_yank) {
	    if (mul < 0)
		mul = 1;
	    --ctx->text.kill_ring_ptr->refcount;
	    while (mul--) {
		if ((ctx->text.kill_ring_ptr = ctx->text.kill_ring_ptr->next) == NULL)
		    ctx->text.kill_ring_ptr = &kill_ring_null;
	    }
	    ++ctx->text.kill_ring_ptr->refcount;
	}
	text.firstPos = 0;
	text.length = ctx->text.kill_ring_ptr->length;
	text.ptr = ctx->text.kill_ring_ptr->contents;
	text.format = ctx->text.kill_ring_ptr->format;

	if (_XawTextReplace(ctx, ctx->text.s.left, insertPos, &text) == XawEditDone) {
	    ctx->text.kill_ring = KILL_RING_YANK;
	    ctx->text.insertPos = ctx->text.s.left + text.length;
	}
    }
    else
	XBell(XtDisplay(w), 0);

    EndAction(ctx);
}
#endif /* OLDXAW */

/*ARGSUSED*/
static void
DeleteCurrentSelection(Widget w, XEvent *event, String *p, Cardinal *n)
{
    _XawTextZapSelection((TextWidget)w, event, False);
}

#ifndef OLDXAW
#define CHECK_SAVE()						\
	if (save && !save->ptr)					\
	    save->ptr = _XawTextGetText(ctx, save->firstPos,	\
		save->firstPos + save->length)
static Bool
StripSpaces(TextWidget ctx, XawTextPosition left, XawTextPosition right,
	    XawTextPosition *pos, int num_pos, XawTextBlock *save)
{
    Bool done, space;
    int i, cpos, count = 0;
    XawTextBlock block, text;
    XawTextPosition ipos, position = left, tmp = left;

    text.firstPos = 0;
    text.format = XawFmt8Bit;
    text.ptr = " ";
    text.length = 1;

    position = XawTextSourceRead(ctx->text.source, position,
				 &block, right - left);
    done = False;
    space = False;
    /* convert tabs and returns to spaces */
    while (!done) {
	if (XawTextFormat(ctx, XawFmt8Bit)) {
	    for (i = 0; i < block.length; i++)
		if (block.ptr[i] == '\t' || block.ptr[i] == '\n') {
		    space = True;
		    break;
		}
	}
	else {
	    wchar_t *wptr = (wchar_t*)block.ptr;
	    for (i = 0; i < block.length; i++)
		if (wptr[i] == _Xaw_atowc('\t') || wptr[i] == _Xaw_atowc('\n')) {
		    space = True;
		    break;
		}
	}
	if (space) {
	    CHECK_SAVE();
	    if (_XawTextReplace(ctx, tmp + i, tmp + i + 1, &text))
		return (False);
	    space = False;
	}
	tmp += i;
	position = XawTextSourceRead(ctx->text.source, tmp,
				     &block, right - tmp);
	if (block.length == 0 || tmp == position || tmp >= right)
	    done = True;
    }

    text.ptr = "";
    text.length = 0;
    position = tmp = left;
    position = XawTextSourceRead(ctx->text.source, position,
				 &block, right - left);
    ipos = ctx->text.insertPos;
    done = False;
    while (!done) {
	if (XawTextFormat(ctx, XawFmt8Bit)) {
	    for (i = 0; i < block.length; i++)
		if (block.ptr[i] == ' ')
		    ++count;
		else if (count == 1)
		    count = 0;
		else if (count)
		    break;
	}
	else {
	    wchar_t *wptr = (wchar_t*)block.ptr;
	    for (i = 0; i < block.length; i++)
		if (wptr[i] == _Xaw_atowc(' '))
		    ++count;
		else if (count == 1)
		    count = 0;
		else if (count)
		    break;
	}
	if (--count > 0) {
	    CHECK_SAVE();
	    if (_XawTextReplace(ctx, tmp + i - count, tmp + i, &text))
		return (False);
	    right -= count;
	    if (num_pos) {
		for (cpos = 0; cpos < num_pos; cpos++) {
		    if (tmp + i - count < pos[cpos]) {
			if (tmp + i < pos[cpos])
			    pos[cpos] -= count;
			else
			    pos[cpos] = tmp + i - count;
		    }
		}
	    }
	    else {
		if (tmp + i - count < ipos) {
		    if (tmp + i < ipos)
			ipos -= count;
		    else
			ipos = tmp + i - count;
		}
	    }
	    tmp += i - count;
	}
	else
	    tmp += i + 1;
	count = 0;
	position = XawTextSourceRead(ctx->text.source, tmp,
				     &block, right - tmp);
	if (block.length == 0 || tmp == position || tmp >= right)
	    done = True;
    }
    if (!num_pos)
	ctx->text.insertPos = ipos;

    return (True);
}

static Bool
Tabify(TextWidget ctx, XawTextPosition left, XawTextPosition right,
       XawTextPosition *pos, int num_pos, XawTextBlock *save)
{
    Bool done, zero;
    int i, cpos, count = 0, column = 0, offset = 0;
    XawTextBlock text, block;
    XawTextPosition ipos, position = left, tmp = left;
    TextSinkObject sink = (TextSinkObject)ctx->text.sink;
    short *char_tabs = sink->text_sink.char_tabs;
    int tab_count = sink->text_sink.tab_count;
    int tab_index = 0, tab_column = 0, TAB_SIZE = DEFAULT_TAB_SIZE;

    text.firstPos = 0;
    text.ptr = "\t";
    text.format = XawFmt8Bit;
    text.length = 1;

    position = XawTextSourceRead(ctx->text.source, position,
				 &block, right - left);
    ipos = ctx->text.insertPos;
    done = zero = False;
    if (tab_count)
	TAB_SIZE = *char_tabs;
    while (!done) {
	if (XawTextFormat(ctx, XawFmt8Bit)) {
	    for (i = 0; i < block.length; i++) {
		++offset;
		++column;
		if (tab_count) {
		    if (column > tab_column + char_tabs[tab_index]) {
			TAB_SIZE = tab_index < tab_count - 1 ? char_tabs[tab_index + 1] - char_tabs[tab_index] : *char_tabs;
			if (++tab_index >= tab_count) {
			    tab_column += char_tabs[tab_count - 1];
			    tab_index = 0;
			}
		    }
		}
		if (block.ptr[i] == ' ') {
		    if (++count > TAB_SIZE)
			count %= TAB_SIZE;
		    if ((tab_count && column == tab_column + char_tabs[tab_index]) ||
			(!tab_count && column % TAB_SIZE == 0)) {
			if (count % (TAB_SIZE + 1) > 1)
			    break;
			else
			    count = 0;
		    }
		}
		else {
		    if (block.ptr[i] == '\n') {
			zero = True;
			break;
		    }
		    count = 0;
		}
	    }
	}
	else {
	    wchar_t *wptr = (wchar_t*)block.ptr;
	    for (i = 0; i < block.length; i++) {
		++offset;
		++column;
		if (tab_count) {
		    if (column > tab_column + char_tabs[tab_index]) {
			TAB_SIZE = tab_index < tab_count - 1 ? char_tabs[tab_index + 1] - char_tabs[tab_index] : *char_tabs;
			if (++tab_index >= tab_count) {
			    tab_column += char_tabs[tab_count - 1];
			    tab_index = 0;
			}
		    }
		}
		if (wptr[i] == _Xaw_atowc(' ')) {
		    if (++count > TAB_SIZE)
			count %= TAB_SIZE;
		    if ((tab_count && column == tab_column + char_tabs[tab_index]) ||
			(!tab_count && column % TAB_SIZE == 0)) {
			if (count % (TAB_SIZE + 1) > 1)
			    break;
			else
			    count = 0;
		    }
		}
		else {
		    if (wptr[i] == _Xaw_atowc('\n')) {
			zero = True;
			break;
		    }
		    count = 0;
		}
	    }
	}
	count %= TAB_SIZE + 1;
	if (!zero && count > 1 && i < block.length) {
	    CHECK_SAVE();
	    if (_XawTextReplace(ctx, tmp + i - count + 1, tmp + i + 1, &text))
		return (False);
	    right -= count - 1;
	    offset -= count - 1;
	    if (num_pos) {
		for (cpos = 0; cpos < num_pos; cpos++) {
		    if (tmp + i - count + 1 < pos[cpos]) {
			if (tmp + i + 1 < pos[cpos])
			    pos[cpos] -= count;
			else
			    pos[cpos] = tmp + i - count + 1;
			++pos[cpos];
		    }
		}
	    }
	    else {
		if (tmp + i - count + 1 < ipos) {
		    if (tmp + i + 1 < ipos)
			ipos -= count;
		    else
			ipos = tmp + i - count + 1;
		    ++ipos;
		}
	    }
	}
	if (count)
	    --count;
	if (zero) {
	    count = column = 0;
	    zero = False;
	    if (tab_count) {
		tab_column = tab_index = 0;
		TAB_SIZE = *char_tabs;
	    }
	}
	else if (i < block.length)
	    count = 0;
	tmp = left + offset;
	position = XawTextSourceRead(ctx->text.source, tmp,
				     &block, right - tmp);
	if (tmp == position || tmp >= right)
	    done = True;
    }
    if (!num_pos)
	ctx->text.insertPos = ipos;

    return (True);
}

static Bool
Untabify(TextWidget ctx, XawTextPosition left, XawTextPosition right,
	 XawTextPosition *pos, int num_pos, XawTextBlock *save)
{
    Bool done, zero;
    int i, cpos, count = 0, diff = 0;
    XawTextBlock block, text;
    XawTextPosition ipos, position = left, tmp = left;
    TextSinkObject sink = (TextSinkObject)ctx->text.sink;
    short *char_tabs = sink->text_sink.char_tabs;
    int tab_count = sink->text_sink.tab_count;
    int tab_index = 0, tab_column = 0, tab_base = 0;
    static char *tabs = "        ";

    text.firstPos = 0;
    text.format = XawFmt8Bit;
    text.ptr = tabs;

    position = XawTextSourceRead(ctx->text.source, position,
				 &block, right - left);
    ipos = ctx->text.insertPos;
    done = False;
    zero = False;
    while (!done) {
	if (XawTextFormat(ctx, XawFmt8Bit))
	    for (i = 0; i < block.length; i++) {
		if (block.ptr[i] != '\t') {
		    ++count;
		    if (block.ptr[i] == '\n') {
			zero = True;
			break;
		    }
		}
		else
		    break;
	}
	else {
	    wchar_t *wptr = (wchar_t*)block.ptr;
	    for (i = 0; i < block.length; i++)
		if (wptr[i] != _Xaw_atowc('\t')) {
		    ++count;
		    if (wptr[i] != _Xaw_atowc('\n')) {
			zero = True;
			break;
		    }
		}
		else
		    break;
	}
	if (!zero && i < block.length) {
	    if (tab_count) {
		while (tab_base + tab_column <= count) {
		    for (; tab_index < tab_count; ++tab_index)
			if (tab_base + char_tabs[tab_index] > count) {
			    tab_column = char_tabs[tab_index];
			    break;
			}
		    if (tab_index >= tab_count) {
			tab_base += char_tabs[tab_count - 1];
			tab_column = tab_index = 0;
		    }
		}
		text.length = (tab_base + tab_column) - count;
		if (text.length > 8) {
		    int j;

		    text.ptr = XtMalloc(text.length);
		    for (j = 0; j < text.length; j++)
			text.ptr[j] = ' ';
		}
		else
		    text.ptr = tabs;
	    }
	    else
		text.length = DEFAULT_TAB_SIZE - (count % DEFAULT_TAB_SIZE);
	    CHECK_SAVE();
	    if (_XawTextReplace(ctx, tmp + i, tmp + i + 1, &text)) {
		if (tab_count && text.length > 8)
		    XtFree(text.ptr);
		return (False);
	    }
	    if (tab_count && text.length > 8)
		XtFree(text.ptr);
	    count += text.length;
	    right += text.length - 1;
	    if (num_pos) {
		for (cpos = 0; cpos < num_pos; cpos++) {
		    if (tmp + i < pos[cpos]) {
			if (tmp + i + 1 < pos[cpos])
			    --pos[cpos];
			else
			    pos[cpos] = tmp + i;
			pos[cpos] += text.length;
		    }
		}
	    }
	    else {
		if (tmp + i < ipos) {
		    if (tmp + i + 1 < ipos)
			--ipos;
		    else
			ipos = tmp + i;
		    ipos += text.length;
		}
	    }
	}
	tmp = left + count + diff;
	if (zero) {
	    diff += count;
	    count = 0;
	    zero = False;
	    if (tab_count)
		tab_base = tab_column = tab_index = 0;
	}
	position = XawTextSourceRead(ctx->text.source, tmp,
				     &block, right - tmp);
	if (tmp == position || tmp >= right)
	    done = True;
    }
    if (!num_pos)
	ctx->text.insertPos = ipos;

    return (True);
}

static int
FormatText(TextWidget ctx, XawTextPosition left, Bool force,
	   XawTextPosition *pos, int num_pos)
{
    char *ptr = NULL;
    Bool freepos = False, undo, paragraph = pos != NULL;
    int i, result;
    XawTextBlock block, *text;
    XawTextPosition end = ctx->text.lastPos, buf[32];
    TextSrcObject src = (TextSrcObject)ctx->text.source;
    XawTextPosition right = SrcScan(ctx->text.source, left, XawstEOL,
				    XawsdRight, 1, False);

    undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
    if (undo) {
	if (!pos) {
	    num_pos = src->textSrc.num_text;
	    pos = XawStackAlloc(sizeof(XawTextPosition) * num_pos, buf);
	    for (i = 0; i < num_pos; i++)
		pos[i] = ((TextWidget)src->textSrc.text[i])->text.insertPos;
	    freepos = True;
	}
	else
	    freepos = False;
	src->textSrc.undo_state = True;
	block.ptr = NULL;
	block.firstPos = left;
	block.length = right - left;
	text = &block;
    }
    else
	text = NULL;

    result = DoFormatText(ctx, left, force, 1, text, pos, num_pos, paragraph);
    if (undo && result == XawEditDone && block.ptr) {
	char *lbuf, *rbuf;
	unsigned llen, rlen, size;

	ptr = lbuf = block.ptr;
	llen = block.length;
	rlen = llen + (ctx->text.lastPos - end);

	block.firstPos = 0;
	block.format = _XawTextFormat(ctx);

	rbuf = _XawTextGetText(ctx, left, left + rlen);

	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
	    block.ptr = lbuf;
	    block.length = llen;
	    _XawTextReplace(ctx, left, left + rlen, &block);

	    src->textSrc.undo_state = False;
	    block.ptr = rbuf;
	    block.length = rlen;
	    _XawTextReplace(ctx, left, left + llen, &block);
	}
	else
	    src->textSrc.undo_state = False;
	XtFree(rbuf);
    }
    if (undo) {
	src->textSrc.undo_state = False;
	if (freepos) {
	    for (i = 0; i < num_pos; i++) {
		TextWidget tw = (TextWidget)src->textSrc.text[i];
		tw->text.insertPos = XawMin(XawMax(0, pos[i]), tw->text.lastPos);
	    }
	    XawStackFree(pos, buf);
	}
	if (ptr)
	    XtFree(ptr);
    }

    return (result);
}

static int
DoFormatText(TextWidget ctx, XawTextPosition left, Bool force, int level,
	     XawTextBlock *save, XawTextPosition *pos, int num_pos,
	     Bool paragraph)
{
    XawTextPosition right = SrcScan(ctx->text.source, left, XawstEOL,
				    XawsdRight, 1, False);
    XawTextPosition position, tmp, ipos;
    XawTextBlock block, text;
    char buf[128];
    wchar_t *wptr;
    int i, count, cpos;
    Bool done, force2 = force, recurse = False;

    position = XawTextSourceRead(ctx->text.source, left, &block, right - left);
    if (block.length == 0 || left >= right ||
	(level == 1 && ((XawTextFormat(ctx, XawFmt8Bit) &&
	 block.ptr[0] != ' ' &&
	 block.ptr[0] != '\t' &&
	 !isalnum(*(unsigned char*)block.ptr)) ||
	(XawTextFormat(ctx, XawFmtWide) &&
	 _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
	 _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
	 !iswalnum(*(wchar_t*)block.ptr)))))
	return (XawEditDone);

    if (level == 1 && !paragraph) {
	tmp = ctx->text.lastPos;
	if (Untabify(ctx, left, right, pos, num_pos, save) == False)
	    return (XawEditError);
	right += ctx->text.lastPos - tmp;
	position = XawTextSourceRead(ctx->text.source, left, &block,
				     right - left);
    }

    text.firstPos = 0;
    text.format = XawFmt8Bit;

    ipos = ctx->text.insertPos;
    count = 0;
    done = False;
    while (!done) {
	if (XawTextFormat(ctx, XawFmt8Bit)) {
	    for (i = 0; i < block.length; i++)
		if (block.ptr[i] == ' ')
		    ++count;
		else {
		    done = True;
		    break;
		}
	}
	else {
	    wptr = (wchar_t*)block.ptr;
	    for (i = 0; i < block.length; i++)
		if (wptr[i] == _Xaw_atowc(' '))
		    ++count;
		else {
		    done = True;
		    break;
		}
	}
	tmp = position;
	position = XawTextSourceRead(ctx->text.source, position,
				     &block, right - position);
	if (tmp == position)
	    done = True;
    }
    position = left + count;
    if (count < ctx->text.left_column) {
	int bytes = ctx->text.left_column - count;

	text.ptr = XawStackAlloc(bytes, buf);
	text.length = bytes;
	for (i = 0; i < bytes; i++)
	    text.ptr[i] = ' ';
	CHECK_SAVE();
	if (_XawTextReplace(ctx, left, left, &text)) {
	    XawStackFree(text.ptr, buf);
	    return (XawEditError);
	}
	XawStackFree(text.ptr, buf);
	right += bytes;
	if (num_pos) {
	    for (cpos = 0; cpos < num_pos; cpos++)
		if (pos[cpos] >= left)
		    pos[cpos] += bytes;
	}
	if (ipos >= left)
	    ipos += bytes;
	count += bytes;
    }

    done = False;
    if (!paragraph && level == 1
	&& ipos <= right && ipos - left > ctx->text.right_column) {
	XawTextPosition len = ctx->text.lastPos;
	int skip = ctx->text.justify == XawjustifyRight
		|| ctx->text.justify == XawjustifyCenter ?
		ctx->text.left_column : count;

	if (pos)
	    for (i = 0; i < num_pos; i++)
		if (pos[i] == ipos)
		    break;

	StripSpaces(ctx, left + skip, right, pos, num_pos, save);
	right += ctx->text.lastPos - len;
	if (pos && i < num_pos)
	    ipos = pos[i];
	else
	    ipos = ctx->text.insertPos;
	done = ipos - left > ctx->text.right_column;
	count = skip + (count == skip + 1);
    }
    if ((paragraph || done) && right - left > ctx->text.right_column) {
	position = tmp = right;
	XawTextSourceRead(ctx->text.source, position - 1, &block, 1);
	if (block.length &&
	    ((XawTextFormat(ctx, XawFmt8Bit) &&
	     block.ptr[0] == ' ') ||
	    (XawTextFormat(ctx, XawFmtWide) &&
	     _Xaw_atowc(XawSP) == *(wchar_t*)block.ptr)))
	    --position;
	while (position - left > ctx->text.right_column) {
	    tmp = position;
	    position = SrcScan(ctx->text.source, position,
			       XawstWhiteSpace, XawsdLeft, 1, True);
	}
	if (position <= left + ctx->text.left_column)
	    position = tmp;
	if (position > left && position - left > ctx->text.left_column
	    && position != right) {
	    text.ptr = "\n";
	    text.length = 1;
	    CHECK_SAVE();
	    if (_XawTextReplace(ctx, position, position + 1, &text))
		return (XawEditError);
	    right = position;
	    recurse = True;
	    force = True;
	}
    }

    if (force) {
	if (ctx->text.justify == XawjustifyCenter)
	    count = ctx->text.right_column - (count - ctx->text.left_column);
	else
	    count = ctx->text.right_column;
	if (count > right - left)
	    count -= right - left;
	else
	    count = 0;
    }
    else
	count = 0;
    if (count > 0) {
	switch (ctx->text.justify) {
	    case XawjustifyLeft:
		break;
	    case XawjustifyRight:
	    case XawjustifyCenter:
		if (ctx->text.justify == XawjustifyCenter) {
		    int alnum = 0;

		    if (!(count & 1)) {
			XawTextSourceRead(ctx->text.source, right, &block, 1);
			if ((XawTextFormat(ctx, XawFmt8Bit)
			     && isalnum(*(unsigned char*)block.ptr)) ||
			    (XawTextFormat(ctx, XawFmtWide)
			     && iswalnum(*(wchar_t*)block.ptr)))
			    alnum = 1;
		    }
		    count = (count + alnum) >> 1;
		}
		text.ptr = XawStackAlloc(count, buf);
		text.length = count;
		for (i = 0; i < count; i++)
		    text.ptr[i] = ' ';
		CHECK_SAVE();
		if (_XawTextReplace(ctx, left, left, &text)) {
		    XawStackFree(text.ptr, buf);
		    return (XawEditError);
		}
		XawStackFree(text.ptr, buf);
		position += count;
		right += count;
		if (num_pos) {
		    for (cpos = 0; cpos < num_pos; cpos++)
			if (pos[cpos] > left)
			    pos[cpos] += count;
		}
		else if (ipos > left)
		    ipos += count;
		break;
	    case XawjustifyFull:
		i = 0;
		tmp = left;
		/*CONSTCOND*/
		while (True) {
		    tmp = SrcScan(ctx->text.source, tmp, XawstWhiteSpace,
				  XawsdRight, 1, True);
		    if (tmp < right)
			++i;
		    else
			break;
		}
		if (i) {
		    double inc, ii;
		    int bytes, steps;

		    bytes = count;
		    inc = ii = (count + .5) / (double)i;

		    steps = count;
		    text.ptr = XawStackAlloc(steps, buf);
		    for (i = 0; i < steps; i++)
			text.ptr[i] = ' ';
		    tmp = left;
		    CHECK_SAVE();
		    while (bytes) {
			steps = 1;
			while (inc + ii < 1) {
			    ++steps;
			    inc += ii;
			}
			tmp = SrcScan(ctx->text.source, tmp, XawstWhiteSpace,
				      XawsdRight, steps, True);
			if (bytes > inc)
			    text.length = (int)inc;
			else
			    text.length = bytes;
			bytes -= text.length;
			if (_XawTextReplace(ctx, tmp, tmp, &text)) {
			    XawStackFree(buf, text.ptr);
			    return (XawEditError);
			}
			if (num_pos) {
			    for (cpos = 0; cpos < num_pos; cpos++)
				if (tmp <= pos[cpos])
				    pos[cpos] += text.length;
			}
			else if (tmp <= ipos)
			    ipos += text.length;
			inc -= (int)inc;
			inc += ii;
		    }
		    position += count;
		    right += count;
		    XawStackFree(buf, text.ptr);
		}
		break;
	}
    }

    if (!num_pos)
	ctx->text.insertPos = XawMin(ipos, ctx->text.lastPos);

    return (recurse ? DoFormatText(ctx, position + 1,
				   ctx->text.justify != XawjustifyFull
				   && (force2 || paragraph),
				   ++level, save, pos, num_pos, paragraph)
		 : XawEditDone);
}
#undef CHECK_SAVE

/*ARGSUSED*/
static void
Indent(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    TextSrcObject src = (TextSrcObject)ctx->text.source;
    XawTextPosition from, to, tmp, end = 0, *pos, *posbuf[32];
    char buf[32];
    XawTextBlock text;
    int i, spaces = MULT(ctx);
    char *lbuf = NULL, *rbuf;
    unsigned llen = 0, rlen, size;
    Bool undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
    Bool format = ctx->text.auto_fill
	&& ctx->text.left_column < ctx->text.right_column;

    text.firstPos = 0;
    text.format = XawFmt8Bit;
    text.ptr = "";

    StartAction(ctx, event);

    pos = XawStackAlloc(sizeof(XawTextPosition) * src->textSrc.num_text, posbuf);
    for (i = 0; i < src->textSrc.num_text; i++)
	pos[i] = ((TextWidget)src->textSrc.text[i])->text.insertPos;

    if (!GetBlockBoundaries(ctx, &from, &to)) {
	EndAction(ctx);
	XawStackFree(pos, posbuf);
	return;
    }

    if (undo) {
	llen = to - from;
	end = ctx->text.lastPos;
	lbuf = _XawTextGetText(ctx, from, to);
	src->textSrc.undo_state = True;
    }

    tmp = ctx->text.lastPos;
    if (!Untabify(ctx, from, to, pos, src->textSrc.num_text, NULL)) {
	XBell(XtDisplay(ctx), 0);
	EndAction(ctx);
	XawStackFree(pos, posbuf);
	if (undo) {
	    src->textSrc.undo_state = True;
	    XtFree(lbuf);
	}
	return;
    }
    to += ctx->text.lastPos - tmp;

    tmp = from;

    if (spaces > 0) {
	text.ptr = XawStackAlloc(spaces, buf);
	for (i = 0; i < spaces; i++)
	    text.ptr[i] = ' ';

	text.length = spaces;
	while (tmp < to) {
	    _XawTextReplace(ctx, tmp, tmp, &text);

	    for (i = 0; i < src->textSrc.num_text; i++)
		if (tmp < pos[i])
		    pos[i] += spaces;

	    to += spaces;
	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
	}
	XawStackFree(text.ptr, buf);
    }
    else {
	int min = 32767;

	text.length = 0;
	tmp = from;

	/* find the amount of spaces to cut */
	while (tmp < to) {
	    (void)BlankLine(w, tmp, &i);
	    if (i < min)
		min = i;
	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
	}
	spaces = XawMin(-spaces, min);

	/* cut the spaces */
	tmp = from;
	while (tmp < to) {
	    _XawTextReplace(ctx, tmp, tmp + spaces, &text);

	    for (i = 0; i < src->textSrc.num_text; i++)
		if (tmp < pos[i]) {
		    if (tmp + spaces < pos[i])
			pos[i] -= spaces;
		    else
			pos[i] = tmp;
		}

	    to -= spaces;
	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
	}
    }

    if (!format)
	Tabify(ctx, from, to, pos, src->textSrc.num_text, NULL);

    if (undo) {
	rlen = llen + (ctx->text.lastPos - end);
	rbuf = _XawTextGetText(ctx, from, from + rlen);

	text.format = _XawTextFormat(ctx);
	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
	    text.ptr = lbuf;
	    text.length = llen;
	    _XawTextReplace(ctx, from, from + rlen, &text);

	    src->textSrc.undo_state = False;
	    text.ptr = rbuf;
	    text.length = rlen;
	    _XawTextReplace(ctx, from, from + llen, &text);
	}
	else
	    src->textSrc.undo_state = False;
	XtFree(lbuf);
	XtFree(rbuf);
    }

    for (i = 0; i < src->textSrc.num_text; i++) {
	TextWidget tw = (TextWidget)src->textSrc.text[i];

	tw->text.insertPos = XawMin(XawMax(0, pos[i]), tw->text.lastPos);
    }
    XawStackFree(pos, posbuf);
    ctx->text.showposition = True;

    EndAction(ctx);
}

/*ARGSUSED*/
static void
ToggleOverwrite(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    ctx->text.overwrite = !ctx->text.overwrite;

    /* call information callback */
    _XawTextSetLineAndColumnNumber(ctx, True);
}
#endif /* OLDXAW */

/*
 * Insertion Routines
 */
static int
InsertNewLineAndBackupInternal(TextWidget ctx)
{
    int count, error = XawEditDone, mult = MULT(ctx);
#ifndef OLDXAW
    XawTextPosition position;
#endif
    XawTextBlock text;
    char buf[32];

    if (mult < 0) {
	ctx->text.mult = 1;
	return (XawEditError);
    }

    text.format = _XawTextFormat(ctx);
    text.length = mult;
    text.firstPos = 0;

    if (text.format == XawFmtWide) {
	wchar_t *wptr;

	text.ptr =  XawStackAlloc(sizeof(wchar_t) * mult, buf);
	wptr = (wchar_t *)text.ptr;
	for (count = 0; count < mult; count++)
	    wptr[count] = _Xaw_atowc(XawLF);
    }
    else {
	text.ptr = XawStackAlloc(sizeof(char) * mult, buf);
	for (count = 0; count < mult; count++)
	    text.ptr[count] = XawLF;
    }

#ifndef OLDXAW
    position = SrcScan(ctx->text.source, ctx->text.insertPos,
		       XawstEOL, XawsdLeft, 1, False);
#endif
    if (_XawTextReplace(ctx, ctx->text.insertPos, ctx->text.insertPos, &text)) {
	XBell( XtDisplay(ctx), 50);
	error = XawEditError;
    }
    else {
	ctx->text.showposition = TRUE;
	ctx->text.insertPos += text.length;
    }

    XawStackFree(text.ptr, buf);

#ifndef OLDXAW
    if (ctx->text.auto_fill && error == XawEditDone)
	(void)FormatText(ctx, position, ctx->text.justify != XawjustifyFull,
			 NULL, 0);
#endif

    return (error);
}

/*ARGSUSED*/
static void
InsertNewLineAndBackup(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition insertPos = ctx->text.insertPos;

    StartAction((TextWidget)w, event);
    (void)InsertNewLineAndBackupInternal(ctx);
    ctx->text.insertPos = SrcScan(ctx->text.source, insertPos, XawstEOL,
				  XawsdRight, 1, False);
    EndAction((TextWidget)w);
}

static int
LocalInsertNewLine(TextWidget ctx, XEvent *event)
{
    int error;

    StartAction(ctx, event);
    error = InsertNewLineAndBackupInternal(ctx);
    ctx->text.from_left = -1;
    EndAction(ctx);

    return (error);
}

/*ARGSUSED*/
static void
InsertNewLine(Widget w, XEvent *event, String *p, Cardinal *n)
{
    (void)LocalInsertNewLine((TextWidget)w, event);
}

/*ARGSUSED*/
static void
InsertNewLineAndIndent(Widget w, XEvent *event, String *p, Cardinal *n)
{
    XawTextBlock text;
    XawTextPosition pos1;
    int length;
    TextWidget ctx = (TextWidget)w;
    String line_to_ip;

    StartAction(ctx, event);
    pos1 = SrcScan(ctx->text.source, ctx->text.insertPos,
		   XawstEOL, XawsdLeft, 1, False);

    line_to_ip = _XawTextGetText(ctx, pos1, ctx->text.insertPos);

    text.format = _XawTextFormat(ctx);
    text.firstPos = 0;

    if (text.format == XawFmtWide) {
	wchar_t *ptr;

	text.ptr = XtMalloc((2 + wcslen((wchar_t*)line_to_ip))
			    * sizeof(wchar_t));
	ptr = (wchar_t*)text.ptr;
	ptr[0] = _Xaw_atowc(XawLF);
	wcscpy((wchar_t*)++ptr, (wchar_t*)line_to_ip);

	length = wcslen((wchar_t*)text.ptr);
	while (length && (iswspace(*ptr) || *ptr == _Xaw_atowc(XawTAB)))
	  ptr++, length--;
	*ptr = (wchar_t)0;
	text.length = wcslen((wchar_t*)text.ptr);
    }
    else {
	char *ptr;

	length = strlen(line_to_ip);
	text.ptr = XtMalloc((2 + length) * sizeof(char));
	ptr = text.ptr;
	ptr[0] = XawLF;
	strcpy(++ptr, line_to_ip);

	length++;
	while (length && (isspace(*ptr) || (*ptr == XawTAB)))
	    ptr++, length--;
	*ptr = '\0';
	text.length = strlen(text.ptr);
    }
    XtFree(line_to_ip);

    if (_XawTextReplace(ctx,ctx->text.insertPos, ctx->text.insertPos, &text)) {
	XBell(XtDisplay(ctx), 50);
	XtFree(text.ptr);
	EndAction(ctx);
	return;
    }

    XtFree(text.ptr);
    ctx->text.from_left = -1;
    ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
				  XawstPositions, XawsdRight, text.length, True);
    EndAction(ctx);
}

/*
 * Selection Routines
 */
static void
SelectWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition l, r;

    StartAction(ctx, event);
    l = SrcScan(ctx->text.source, ctx->text.insertPos,
		XawstWhiteSpace, XawsdLeft, 1, False);
    r = SrcScan(ctx->text.source, l, XawstWhiteSpace, XawsdRight, 1, False);
    _XawTextSetSelection(ctx, l, r, params, *num_params);
    EndAction(ctx);
}

static void
SelectAll(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    StartAction(ctx, event);
    _XawTextSetSelection(ctx,zeroPosition,ctx->text.lastPos,params,*num_params);
    EndAction(ctx);
}

static void
ModifySelection(TextWidget ctx, XEvent *event,
		XawTextSelectionMode mode,
		XawTextSelectionAction action,
		String *params, Cardinal *num_params)
{
#ifndef OLDXAW
    int old_y = ctx->text.ev_y;
#endif

    StartAction(ctx, event);
    NotePosition(ctx, event);

#ifndef OLDXAW
    if (event->type == MotionNotify) {
	if (ctx->text.ev_y <= ctx->text.margin.top) {
	    if (old_y >= ctx->text.ev_y)
		XawTextScroll(ctx, -1, 0);
	}
	else if (ctx->text.ev_y >= XtHeight(ctx) - ctx->text.margin.bottom) {
	    if (old_y <= ctx->text.ev_y
		&& !IsPositionVisible(ctx, ctx->text.lastPos))
	      XawTextScroll(ctx, 1, 0);
	}
    }
#endif
    ctx->text.from_left = -1;
    _XawTextAlterSelection(ctx, mode, action, params, num_params);

    EndAction(ctx);
}

static void
SelectStart(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (!ctx->text.selection_state) {
	ctx->text.selection_state = True;
#endif
	ModifySelection(ctx, event,
			XawsmTextSelect, XawactionStart, params, num_params);
#ifndef OLDXAW
    }
#endif
}

static void
SelectAdjust(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (ctx->text.selection_state)
#endif
	ModifySelection(ctx, event, 
			XawsmTextSelect, XawactionAdjust, params, num_params);
}

static void
SelectEnd(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (ctx->text.selection_state) {
	ctx->text.selection_state = False;
#endif
	ModifySelection(ctx, event,
			XawsmTextSelect, XawactionEnd, params, num_params);
#ifndef OLDXAW
    }
#endif
}

static void
ExtendStart(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (!ctx->text.selection_state) {
	ctx->text.selection_state = True;
#endif
	ModifySelection(ctx, event,
			XawsmTextExtend, XawactionStart, params, num_params);
#ifndef OLDXAW
    }
#endif
}

static void
ExtendAdjust(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (ctx->text.selection_state)
#endif
	ModifySelection(ctx, event,
			XawsmTextExtend, XawactionAdjust, params, num_params);
}

static void
ExtendEnd(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

#ifndef OLDXAW
    if (ctx->text.selection_state) {
	ctx->text.selection_state = False;
#endif
	ModifySelection(ctx, event,
			XawsmTextExtend, XawactionEnd, params, num_params);
#ifndef OLDXAW
    }
#endif
}

static void
SelectSave(Widget  w, XEvent *event, String *params, Cardinal *num_params)
{
    int num_atoms;
    Atom *sel;
    Display *dpy = XtDisplay(w);
    Atom selections[256];

    StartAction((TextWidget)w, event);
    num_atoms = *num_params;
    if (num_atoms > 256)
	num_atoms = 256;
    for (sel=selections; --num_atoms >= 0; sel++, params++)
	*sel = XInternAtom(dpy, *params, False);
    num_atoms = *num_params;
    _XawTextSaltAwaySelection((TextWidget)w, selections, num_atoms);
    EndAction((TextWidget)w);
}

/*
 * Misc. Routines
 */
/*ARGSUSED*/
static void
SetKeyboardFocus(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    Widget shell, parent;

    shell = parent = w;
    while (parent) {
	if (XtIsShell(shell = parent))
	    break;
	parent = XtParent(parent);
    }
    XtSetKeyboardFocus(shell, w);
}

/*ARGSUSED*/
static void
RedrawDisplay(Widget w, XEvent *event, String *p, Cardinal *n)
{
    StartAction((TextWidget)w, event);
    _XawTextClearAndCenterDisplay((TextWidget)w);
    EndAction((TextWidget)w);
}

/* This is kind of a hack, but, only one text widget can have focus at
 * a time on one display. There is a problem in the implementation of the
 * text widget, the scrollbars can not be adressed via editres, since they
 * are not children of a subclass of composite.
 * The focus variable is required to make sure only one text window will
 * show a block cursor at one time.
 */
struct _focus { Display *display; Widget widget; };
static struct _focus *focus;
static Cardinal num_focus;

/*ARGSUSED*/
static void
DestroyFocusCallback(Widget w, XtPointer user_data, XtPointer call_data)
{
    struct _focus *f = (struct _focus*)(user_data);

    if (f->widget == w)
	f->widget = NULL;
}

/*ARGSUSED*/
static void
TextFocusIn(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    Bool display_caret = ctx->text.display_caret;
    int i;

    if (event->xfocus.detail == NotifyPointer)
	return;

    if (event->xfocus.send_event) {
	Window root, child;
	int rootx, rooty, x, y;
	unsigned int mask;

	if (ctx->text.hasfocus)
	    return;

	if (XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child,
			  &rootx, &rooty, &x, &y, &mask)) {
	    if (child)
		return;
	}
    }

    /* Let the input method know focus has arrived. */
    _XawImSetFocusValues(w, NULL, 0);

    if (display_caret)
	StartAction(ctx, event);
    ctx->text.hasfocus = TRUE;
    if (display_caret)
	EndAction(ctx);

    for (i = 0; i < num_focus; i++)
	if (focus[i].display == XtDisplay(w))
	    break;
    if (i >= num_focus) {
	focus = (struct _focus*)
	    XtRealloc((XtPointer)focus, sizeof(struct _focus) * (num_focus + 1));
	i = num_focus;
	focus[i].widget = NULL;
	focus[i].display = XtDisplay(w);
	num_focus++;
    }
    if (focus[i].widget != w) {
	Widget old = focus[i].widget;

	focus[i].widget = w;
	if (old != NULL) {
	    TextFocusOut(old, event, p, n);
	    /* TextFocusOut may set it to NULL */
	    focus[i].widget = w;
	}
	XtAddCallback(w, XtNdestroyCallback,
		      DestroyFocusCallback, (XtPointer)&focus[i]);
    }
}

/*ARGSUSED*/
static void
TextFocusOut(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    Bool display_caret = ctx->text.display_caret;
    Widget shell;
    Window window;
    int i, revert;

    shell = w;
    while (shell) {
	if (XtIsShell(shell))
	   break;
	shell = XtParent(shell);
    }

    for (i = 0; i < num_focus; i++)
	if (focus[i].display == XtDisplay(w))
	    break;
    XGetInputFocus(XtDisplay(w), &window, &revert);
    if ((XtWindow(shell) == window &&
	 (i < num_focus && focus[i].widget == w))
	 || event->xfocus.detail == NotifyPointer)
	return;

    if (i < num_focus && focus[i].widget) {
	XtRemoveCallback(focus[i].widget, XtNdestroyCallback,
			 DestroyFocusCallback, (XtPointer)&focus[i]);
	focus[i].widget = NULL;
    }

    /* Let the input method know focus has left.*/
    _XawImUnsetFocus(w);

    if (display_caret)
	StartAction(ctx, event);
    ctx->text.hasfocus = FALSE;
    if (display_caret)
	EndAction(ctx);
}

/*ARGSUSED*/
static void
TextEnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    if ((event->xcrossing.detail != NotifyInferior) && event->xcrossing.focus
	&& !ctx->text.hasfocus)
	_XawImSetFocusValues(w, NULL, 0);
}

/*ARGSUSED*/
static void
TextLeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    if ((event->xcrossing.detail != NotifyInferior) && event->xcrossing.focus
	&& !ctx->text.hasfocus)
	_XawImUnsetFocus(w);
}

/*
 * Function:
 *	AutoFill
 *	Arguments: ctx - The text widget.
 *
 * Description:
 *	  Breaks the line at the previous word boundry when
 *	called inside InsertChar.
 */
static void
AutoFill(TextWidget ctx)
{
    int width, height, x, line_num, max_width;
    XawTextPosition ret_pos;
    XawTextBlock text;
    XRectangle cursor;
    wchar_t wc_buf[2];

    for (line_num = 0; line_num < ctx->text.lt.lines ; line_num++)
	if (ctx->text.lt.info[line_num].position >= ctx->text.insertPos)
	    break;
    if (line_num)
	line_num--;		/* backup a line. */

    XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
    max_width = Max(0, (int)XtWidth(ctx) - RHMargins(ctx) - cursor.width);

    x = ctx->text.r_margin.left;
    XawTextSinkFindPosition(ctx->text.sink, ctx->text.lt.info[line_num].position,
			    x, max_width, True, &ret_pos,
			    &width, &height);

    if (ret_pos <= ctx->text.lt.info[line_num].position
	|| ret_pos >= ctx->text.insertPos || ret_pos < 1)
	return;

    XawTextSourceRead(ctx->text.source, ret_pos - 1, &text, 1);

    if (XawTextFormat(ctx, XawFmtWide)) {
	wc_buf[0] = *(wchar_t *)text.ptr;
	if (wc_buf[0] != _Xaw_atowc(XawSP) && wc_buf[0] != _Xaw_atowc(XawTAB))
	    /* Only eats white spaces */
	    return;

	text.format = XawFmtWide;
	text.ptr = (char *)wc_buf;
	wc_buf[0] = _Xaw_atowc(XawLF);
	wc_buf[1] = 0;
    }
    else {
	if (text.ptr[0] != XawSP && text.ptr[0] != XawTAB)
	    /* Only eats white spaces */
	    return;

	text.format = XawFmt8Bit;
	text.ptr = "\n";
    }
    text.length = 1;
    text.firstPos = 0;

    if (_XawTextReplace(ctx, ret_pos - 1, ret_pos, &text))
	XBell(XtDisplay((Widget)ctx), 0);

    if (++ctx->text.insertPos > ctx->text.lastPos)
	ctx->text.insertPos = ctx->text.lastPos;
}

/*ARGSUSED*/
static void
InsertChar(Widget w, XEvent *event, String *p, Cardinal *n)
{
    TextWidget ctx = (TextWidget)w;
    char *ptr, strbuf[128], ptrbuf[512];
    int count, error, mult = MULT(ctx);
    KeySym keysym;
    XawTextBlock text;
#ifndef OLDXAW
    Bool format = False;
#endif
    XawTextPosition from, to;

    if (XtIsSubclass (ctx->text.source, (WidgetClass) multiSrcObjectClass))
	text.length = _XawImWcLookupString(w, &event->xkey, (wchar_t*)strbuf,
					   sizeof(strbuf), &keysym);
    else
	text.length = _XawLookupString(w, (XKeyEvent*)event, strbuf,
				       sizeof(strbuf), &keysym);

    if (text.length == 0)
	return;

    if (mult < 0) {
	ctx->text.mult = 1;
	return;
    }

    text.format = _XawTextFormat(ctx);
    if (text.format == XawFmtWide) {
	text.ptr = ptr = XawStackAlloc(sizeof(wchar_t) * text.length
				       * mult, ptrbuf);
	for (count = 0; count < mult; count++) {
	    memcpy((char*)ptr, (char *)strbuf, sizeof(wchar_t) * text.length);
	    ptr += sizeof(wchar_t) * text.length;
	}
#ifndef OLDXAW
	if (mult == 1)
	    format = ctx->text.left_column < ctx->text.right_column;
#endif
    }
    else {	/* == XawFmt8Bit */
	text.ptr = ptr = XawStackAlloc(text.length * mult, ptrbuf);
	for (count = 0; count < mult; count++) {
	    strncpy(ptr, strbuf, text.length);
	    ptr += text.length;
	}
#ifndef OLDXAW
	if (mult == 1)
	    format = ctx->text.left_column < ctx->text.right_column;
#endif
    }

    text.length = text.length * mult;
    text.firstPos = 0;

    StartAction(ctx, event);
#ifndef OLDXAW
    if (mult == 1)
	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
#endif

    from = ctx->text.insertPos;
#ifndef OLDXAW
    if (ctx->text.overwrite) {
	XawTextPosition tmp;

	to = from + mult;
	tmp = SrcScan(ctx->text.source, from, XawstEOL, XawsdRight, 1, False);
	if (to > tmp)
	    to = tmp;
    }
    else
#endif
	to = from;

    error = _XawTextReplace(ctx, from , to, &text);

    if (error == XawEditDone) {
	ctx->text.from_left = -1;
	ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
				      XawstPositions, XawsdRight,
				      text.length, True);
	if (ctx->text.auto_fill) {
#ifndef OLDXAW
	    if (format)
		(void)FormatText(ctx, SrcScan(ctx->text.source,
					      ctx->text.insertPos, XawstEOL,
					      XawsdLeft, 1, False), False,
					      NULL, 0);
	    else
#endif
		AutoFill(ctx);
	}
    }
    else
	XBell(XtDisplay(ctx), 50);

    XawStackFree(text.ptr, ptrbuf);
    EndAction(ctx);

    if (error == XawEditDone && text.format == XawFmt8Bit && text.length == 1
	&& (text.ptr[0] == ')' || text.ptr[0] == ']' || text.ptr[0] == '}')
	&& ctx->text.display_caret) {
	static struct timeval tmval = {0, 500000};
	fd_set fds;
	Widget source = ctx->text.source;
	XawTextPosition insertPos = ctx->text.insertPos, pos, tmp, last;
	char left, right = text.ptr[0];
	int level = 0;
	XtAppContext app_context = XtWidgetToApplicationContext(w);

	left = right == ')' ? '(' : right == ']' ? '[' : '{';

	last = insertPos - 1;
	do {
	    text.ptr[0] = left;
	    pos = XawTextSourceSearch(source, last, XawsdLeft, &text);
	    if (pos == XawTextSearchError || !IsPositionVisible(ctx, pos))
		return;
	    text.ptr[0] = right;
	    tmp = pos;
	    do {
		tmp = XawTextSourceSearch(source, tmp, XawsdRight, &text);
		if (tmp == XawTextSearchError)
		    return;
		if (tmp <= last)
		    ++level;
	    } while (++tmp <= last);
	    --level;
	    last = pos;
	} while (level);

	StartAction(ctx, NULL);
#ifndef OLDXAW
	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
#endif
	ctx->text.insertPos = pos;
	EndAction(ctx);

	XSync(XtDisplay(w), False);
	while (XtAppPending(app_context) & XtIMXEvent) {
	    XEvent ev;
	    if (! XtAppPeekEvent(app_context, &ev))
		break;
	    if (ev.type == KeyPress || ev.type == ButtonPress)
		break;
	    XtAppProcessEvent(app_context, XtIMXEvent);
	}
	FD_ZERO(&fds);
	FD_SET(ConnectionNumber(XtDisplay(w)), &fds);
	(void)select(FD_SETSIZE, &fds, NULL, NULL, &tmval);
	if (tmval.tv_usec != 500000)
	    usleep(40000);

	StartAction(ctx, NULL);
#ifndef OLDXAW
	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
#endif
	ctx->text.insertPos = insertPos;
	EndAction(ctx);
    }
}

/* IfHexConvertHexElseReturnParam() - called by InsertString
 *
 * i18n requires the ability to specify multiple characters in a hexa-
 * decimal string at once.  Since Insert was already too long, I made
 * this a seperate routine.
 *
 * A legal hex string in MBNF: '0' 'x' ( HEX-DIGIT HEX-DIGIT )+ '\0'
 *
 * WHEN:    the passed param is a legal hex string
 * RETURNS: a pointer to that converted, null terminated hex string;
 *	    len_return holds the character count of conversion result
 *
 * WHEN:    the passed param is not a legal hex string:
 * RETURNS: the parameter passed;
 *	    len_return holds the char count of param.
 *
 * NOTE:    In neither case will there be strings to free. */
static char *
IfHexConvertHexElseReturnParam(char *param, int *len_return)
{
    char *p;	    	/* steps through param char by char */
    char c;	    	/* holds the character pointed to by p */
    int ind;		/* steps through hexval buffer char by char */
    static char hexval[XawTextActionMaxHexChars];
    Boolean first_digit;

    /* reject if it doesn't begin with 0x and at least one more character. */
    if ((param[0] != '0') || (param[1] != 'x') || (param[2] == '\0')) {
	*len_return = strlen(param);
	return(param);
    }

    /* Skip the 0x; go character by character shifting and adding. */
    first_digit = True;
    ind = 0;
    hexval[ind] = '\0';

    for (p = param+2; (c = *p) != '\0'; p++) {
	hexval[ind] *= 16;
	if (c >= '0' && c <= '9')
	    hexval[ind] += c - '0';
	else if (c >= 'a' && c <= 'f')
	    hexval[ind] += c - 'a' + 10;
	else if (c >= 'A' && c <= 'F')
	    hexval[ind] += c - 'A' + 10;
	else
	    break;

	/* If we didn't break in preceding line, it was a good hex char. */
	if (first_digit)
	    first_digit = False;
	else {
	    first_digit = True;
	    if (++ind < XawTextActionMaxHexChars)
		hexval[ind] = '\0';
	    else {
		*len_return = strlen(param);
		return(param);
	    }
	}
    }

    /* We quit the above loop becasue we hit a non hex.  If that char is \0... */
    if ((c == '\0') && first_digit) {
	*len_return = strlen(hexval);
	return (hexval);       /* ...it was a legal hex string, so return it */
    }

    /* Else, there were non-hex chars or odd digit count, so... */

    *len_return = strlen(param);
    return (param);			   /* ...return the verbatim string. */
}

/* InsertString() - action
 *
 * Mostly rewritten for R6 i18n.
 *
 * Each parameter, in turn, will be insert at the inputPos
 * and the inputPos advances to the insertion's end.
 *
 * The exception is that parameters composed of the two
 * characters 0x, followed only by an even number of
 * hexadecimal digits will be converted to characters */
/*ARGSUSED*/
static void
InsertString(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    XtAppContext app_con = XtWidgetToApplicationContext(w);
    XawTextBlock text;
    int i;

    text.firstPos = 0;
    text.format = _XawTextFormat(ctx);

    StartAction(ctx, event);
    for (i = *num_params; i; i--, params++) {	/* DO FOR EACH PARAMETER */
	text.ptr = IfHexConvertHexElseReturnParam(*params, &text.length);

	if (text.length == 0)
	    continue;

	if (XawTextFormat(ctx, XawFmtWide)) {	/* convert to WC */
	    int temp_len;

	    text.ptr = (char*)_XawTextMBToWC(XtDisplay(w), text.ptr,
					     &text.length);

	    if (text.ptr == NULL) {	  /* conversion error */
		XtAppWarningMsg(app_con,
				"insertString", "textAction", "XawError",
				"insert-string()'s parameter contents "
				"not legal in this locale.",
				NULL, NULL);
		ParameterError(w, *params);
		continue;
	   }

	    /* Double check that the new input is legal: try to convert to MB. */

	    temp_len = text.length;	 /* _XawTextWCToMB's 3rd arg is in_out */
	    if (_XawTextWCToMB(XtDisplay(w), (wchar_t*)text.ptr, &temp_len)
		== NULL) {
		XtAppWarningMsg( app_con,
				 "insertString", "textAction", "XawError",
				 "insert-string()'s parameter contents "
				 "not legal in this locale.",
				 NULL, NULL);
		ParameterError(w, *params);
		continue;
	    }
	} /* convert to WC */

	if (_XawTextReplace(ctx, ctx->text.insertPos,
			    ctx->text.insertPos, &text)) {
	    XBell(XtDisplay(ctx), 50);
	    EndAction(ctx);
	    return;
	}

	ctx->text.from_left = -1;
	/* Advance insertPos to the end of the string we just inserted. */
	ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
				       XawstPositions, XawsdRight, text.length,
				      True);

    } /* DO FOR EACH PARAMETER */

    EndAction(ctx);
}

/* DisplayCaret() - action
 * 
 * The parameter list should contain one boolean value.  If the
 * argument is true, the cursor will be displayed.  If false, not.
 *
 * The exception is that EnterNotify and LeaveNotify events may
 * have a second argument, "always".  If they do not, the cursor
 * is only affected if the focus member of the event is true.	*/
static void
DisplayCaret(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    Bool display_caret = True;

    if	((event->type == EnterNotify || event->type == LeaveNotify)
	 && ((*num_params >= 2) && (strcmp(params[1], "always") == 0))
	 && (!event->xcrossing.focus))
	return;

    if (*num_params > 0) {	/* default arg is "True" */
	XrmValue from, to;
	from.size = strlen(from.addr = params[0]);
	XtConvert(w, XtRString, &from, XtRBoolean, &to);

	if (to.addr != NULL)
	    display_caret = *(Boolean*)to.addr;
	if (ctx->text.display_caret == display_caret)
	    return;
    }
    StartAction(ctx, event);
    ctx->text.display_caret = display_caret;
    EndAction(ctx);
}

#ifndef OLDXAW
static void
Numeric(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    if (ctx->text.numeric) {
	long mult = ctx->text.mult;

	if (*num_params != 1 || strlen(params[0]) != 1
	    || (!isdigit(params[0][0])
		&& (params[0][0] != '-' || mult != 0))) {
	    char err_buf[256];

	    if (event && (event->type == KeyPress || event->type == KeyRelease)
		&& params[0][0] == '-') {
		InsertChar(w, event, params, num_params);
		return;
	    }
	    XmuSnprintf(err_buf, sizeof(err_buf),
			"numeric: Invalid argument%s'%s'",
			*num_params ? ", " : "", *num_params ? params[0] : "");
	    XtAppWarning(XtWidgetToApplicationContext(w), err_buf);
	    ctx->text.numeric = False;
	    ctx->text.mult = 1;
	    return;
	}
	if (params[0][0] == '-') {
	    ctx->text.mult = 32767;
	    return;
	}
	else if (mult == 32767) {
	    mult = ctx->text.mult = - (params[0][0] - '0');
	    return;
	}
	else {
	    mult = mult * 10 + (params[0][0] - '0') * (mult < 0 ? -1 : 1);
	    ctx->text.mult = ctx->text.mult * 10 + (params[0][0] - '0') *
			     (mult < 0 ? -1 : 1);
	}
	if (mult != ctx->text.mult || mult >= 32767) {	/* checks for overflow */
	    XBell(XtDisplay(w), 0);
	    ctx->text.mult = 1;
	    ctx->text.numeric = False;
	    return;
	}
    }
    else
	InsertChar(w, event, params, num_params);
}

/*ARGSUSED*/
static void
KeyboardReset(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;

    ctx->text.numeric = False;
    ctx->text.mult = 1;

    (void)_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);

    if (ctx->text.kill_ring_ptr) {
	--ctx->text.kill_ring_ptr->refcount;
	ctx->text.kill_ring_ptr = NULL;
    }
    ctx->text.kill_ring = 0;

    XBell(XtDisplay(w), 0);
}
#endif /* OLDXAW */

/* Multiply() - action
 *
 * The parameter list may contain either a number or the string 'Reset'.
 *
 * A number will multiply the current multiplication factor by that number.
 * Many of the text widget actions will will perform n actions, where n is
 * the multiplication factor.
 *
 * The string reset will reset the mutiplication factor to 1. */
/*ARGSUSED*/
static void
Multiply(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    int mult;

    if (*num_params != 1) {
	XtAppError(XtWidgetToApplicationContext(w),
		   "Xaw Text Widget: multiply() takes exactly one argument.");
	XBell(XtDisplay(w), 0);
	return;
    }

    if ((params[0][0] == 'r') || (params[0][0] == 'R')) {
	XBell(XtDisplay(w), 0);
#ifndef OLDXAW
	ctx->text.numeric = False;
#endif
	ctx->text.mult = 1;
	return;
    }

#ifndef OLDXAW
    if (params[0][0] == 's' || params[0][0] == 'S') {
	ctx->text.numeric = True;
 	ctx->text.mult = 0;
	return;
    }
    else
#endif
	if ((mult = atoi(params[0])) == 0) {
	char buf[BUFSIZ];

	XmuSnprintf(buf, sizeof(buf),
		    "%s %s", "Xaw Text Widget: multiply() argument",
		    "must be a number greater than zero, or 'Reset'.");
	XtAppError(XtWidgetToApplicationContext(w), buf);
	XBell(XtDisplay(w), 50);
	return;
    }

    ctx->text.mult *= mult;
}

/* StripOutOldCRs() - called from FormRegion
 *
 * removes CRs in widget ctx, from from to to.
 *
 * RETURNS: the new ending location (we may add some characters),
 * or XawReplaceError if the widget can't be written to. */
static XawTextPosition
StripOutOldCRs(TextWidget ctx, XawTextPosition from, XawTextPosition to,
	       XawTextPosition *pos, int num_pos)
{
    XawTextPosition startPos, endPos, eop_begin, eop_end, temp;
    Widget src = ctx->text.source;
    XawTextBlock text;
    char *buf;
    static wchar_t wc_two_spaces[3];
    int idx;

    /* Initialize our TextBlock with two spaces. */
    text.firstPos = 0;
    text.format = _XawTextFormat(ctx);
    if (text.format == XawFmt8Bit)
      text.ptr= "  ";
    else {
	wc_two_spaces[0] = _Xaw_atowc(XawSP);
	wc_two_spaces[1] = _Xaw_atowc(XawSP);
	wc_two_spaces[2] = 0;
	text.ptr = (char*)wc_two_spaces;
    }

    /* Strip out CR's. */
    eop_begin = eop_end = startPos = endPos = from;

    /* CONSTCOND */
    while (TRUE) {
	endPos=SrcScan(src, startPos, XawstEOL, XawsdRight, 1, False);

	temp = SrcScan(src, endPos, XawstWhiteSpace, XawsdLeft, 1, False);
	temp = SrcScan(src, temp,   XawstWhiteSpace, XawsdRight,1, False);

	if (temp > startPos)
	    endPos = temp;

	if (endPos >= to)
	    break;

	if (endPos >= eop_begin) {
	    startPos = eop_end;
	    eop_begin=SrcScan(src, startPos, XawstParagraph,
			      XawsdRight, 1,False);
	    eop_end = SrcScan(src, startPos, XawstParagraph,
			      XawsdRight, 1, True);
	}
	else {
	    XawTextPosition periodPos, next_word;
	    int i, len;

	    periodPos = SrcScan(src, endPos, XawstPositions,
				XawsdLeft, 1, True);
	    next_word = SrcScan(src, endPos, XawstWhiteSpace,
				XawsdRight, 1, False);

	    len = next_word - periodPos;

	    text.length = 1;
	    buf = _XawTextGetText(ctx, periodPos, next_word);
	    if (text.format == XawFmtWide) {
		if (periodPos < endPos && ((wchar_t*)buf)[0] == _Xaw_atowc('.'))
		  text.length++;
	    }
	    else
		if (periodPos < endPos && buf[0] == '.')
		    text.length++;	  /* Put in two spaces. */

	    /*
	     * Remove all extra spaces.
	     */
	    for (i = 1 ; i < len; i++) 
		if (text.format ==  XawFmtWide) {
		    if (!iswspace(((wchar_t*)buf)[i]) || ((periodPos + i) >= to))
			break;
		}
		else if (!isspace(buf[i]) || (periodPos + i) >= to)
		    break;
      
	    XtFree(buf);

	    to -= (i - text.length - 1);
	    startPos = SrcScan(src, periodPos, XawstPositions,
			       XawsdRight, i, True);
	    if (_XawTextReplace(ctx, endPos, startPos, &text) != XawEditDone)
		return (XawReplaceError);

	    for (idx = 0; idx < num_pos; idx++) {
		if (endPos < pos[idx]) {
		    if (startPos < pos[idx])
			pos[idx] -= startPos - endPos;
		    else
			pos[idx] = endPos;
		    pos[idx] += text.length;
		}
	    }

	    startPos -= i - text.length;
	}
    }

    return (to);
}

/* InsertNewCRs() - called from FormRegion
 *
 * inserts new CRs for FormRegion, thus for FormParagraph action */
static void
InsertNewCRs(TextWidget ctx, XawTextPosition from, XawTextPosition to,
	     XawTextPosition *pos, int num_pos)
{
    XawTextPosition startPos, endPos, space, eol;
    XawTextBlock text;
    int i, width, height, len, wwidth, idx;
    char *buf;
    static wchar_t wide_CR[2];

    text.firstPos = 0;
    text.length = 1;
    text.format = _XawTextFormat(ctx);

    if (text.format == XawFmt8Bit)
	text.ptr = "\n";
    else {
	wide_CR[0] = _Xaw_atowc(XawLF);
	wide_CR[1] = 0;
	text.ptr = (char*)wide_CR;
    }

    startPos = from;

    wwidth = (int)XtWidth(ctx) - (int)HMargins(ctx);
    if (ctx->text.wrap != XawtextWrapNever) {
	XRectangle cursor;

	XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
	wwidth -= (int)cursor.width;
    }
    wwidth = XawMax(0, wwidth);

    /* CONSTCOND */
    while (TRUE) {
	XawTextSinkFindPosition(ctx->text.sink, startPos,
				(int)ctx->text.r_margin.left, wwidth,
				True, &eol, &width, &height);
	if (eol == startPos)
	    ++eol;
	if (eol >= to)
	    break;

	eol = SrcScan(ctx->text.source, eol, XawstPositions,
		      XawsdLeft, 1, True);
	space = SrcScan(ctx->text.source, eol, XawstWhiteSpace,
			XawsdRight,1, True);

	startPos = endPos = eol;
	if (eol == space) 
	    return;

	len = (int)(space - eol);
	buf = _XawTextGetText(ctx, eol, space);
	for (i = 0 ; i < len ; i++)
	    if (text.format == XawFmtWide) {
		if (!iswspace(((wchar_t*)buf)[i]))
		    break;
	    }
	    else if (!isspace(buf[i]))
		break;

	to -= (i - 1);
	endPos = SrcScan(ctx->text.source, endPos,
			 XawstPositions, XawsdRight, i, True);
	XtFree(buf);

	if (_XawTextReplace(ctx, startPos, endPos, &text))
	    return;

	for (idx = 0; idx < num_pos; idx++) {
	    if (startPos < pos[idx]) {
		if (endPos < pos[idx])
		    pos[idx] -= endPos - startPos;
		else
		    pos[idx] = startPos;
		pos[idx] += text.length;
	    }
	}

	startPos = SrcScan(ctx->text.source, startPos,
			   XawstPositions, XawsdRight, 1, True);
    }
}

/* FormRegion() - called by FormParagraph
 *
 * oversees the work of paragraph-forming a region
 *
 * Return:
 *	XawEditDone if successful, or XawReplaceError
 */
static int
FormRegion(TextWidget ctx, XawTextPosition from, XawTextPosition to,
	   XawTextPosition *pos, int num_pos)
{
#ifndef OLDXAW
    Bool format = ctx->text.auto_fill
	&& ctx->text.left_column < ctx->text.right_column;
#endif

    if (from >= to)
	return (XawEditDone);

#ifndef OLDXAW
    if (format) {
	XawTextPosition len = ctx->text.lastPos;
	int inc = 0;

	if (ctx->text.justify == XawjustifyLeft ||
	    ctx->text.justify == XawjustifyFull) {
	    Untabify(ctx, from, to, pos, num_pos, NULL);
	    to += ctx->text.lastPos - len;
	    len = ctx->text.insertPos;
	    (void)BlankLine((Widget)ctx, from, &inc);
	    if (from + inc >= to)
		return (XawEditDone);
	}
	if (!StripSpaces(ctx, from + inc, to, pos, num_pos, NULL))
	    return (XawReplaceError);
	to += ctx->text.lastPos - len;

	FormatText(ctx, from, ctx->text.justify != XawjustifyFull, pos, num_pos);
    }
    else {
#endif
	if ((to = StripOutOldCRs(ctx, from, to, pos, num_pos)) == XawReplaceError)
	    return (XawReplaceError);
	InsertNewCRs(ctx, from, to, pos, num_pos);
#ifndef OLDXAW
    }
#endif
    ctx->text.from_left = -1;

    return (XawEditDone);
}

#ifndef OLDXAW
static Bool
BlankLine(Widget w, XawTextPosition pos, int *blanks_return)
{
    int i, blanks = 0;
    XawTextBlock block;
    Widget src = XawTextGetSource(w);
    XawTextPosition l = SrcScan(src, pos, XawstEOL, XawsdLeft, 1, False);
    XawTextPosition r = SrcScan(src, pos, XawstEOL, XawsdRight, 1, False);

    while (l < r) {
	l = XawTextSourceRead(src, l, &block, r - l);
	if (block.length == 0) {
	    if (blanks_return)
		*blanks_return = blanks;
	    return (True);
	}
	if (XawTextFormat((TextWidget)w, XawFmt8Bit)) {
	    for (i = 0; i < block.length; i++, blanks++)
		if (block.ptr[i] != ' ' &&
		    block.ptr[i] != '\t') {
		    if (blanks_return)
			*blanks_return = blanks;
		    return (block.ptr[i] == '\n');
		}
	}
	else if (XawTextFormat((TextWidget)w, XawFmtWide)) {
	    for (i = 0; i < block.length; i++, blanks++)
		if (_Xaw_atowc(XawSP) != ((wchar_t*)block.ptr)[i] &&
		    _Xaw_atowc(XawTAB) != ((wchar_t*)block.ptr)[i]) {
		    if (blanks_return)
			*blanks_return = blanks;
		    return (_Xaw_atowc(XawLF) == ((wchar_t*)block.ptr)[i]);
		}
	}
    }

    return (True);
}

static Bool
GetBlockBoundaries(TextWidget ctx,
		   XawTextPosition *from_return, XawTextPosition *to_return)
{
    XawTextPosition from, to;

    if (ctx->text.auto_fill && ctx->text.left_column < ctx->text.right_column) {
	if (ctx->text.s.left != ctx->text.s.right) {
	    from = SrcScan(ctx->text.source,
			   XawMin(ctx->text.s.left, ctx->text.s.right),
			   XawstEOL, XawsdLeft, 1, False);
	    to   = SrcScan(ctx->text.source,
			   XawMax(ctx->text.s.right, ctx->text.s.right),
			   XawstEOL, XawsdRight, 1, False);
	}
	else {
	    XawTextBlock block;
	    XawTextPosition tmp;
	    Bool first;

	    from = to = ctx->text.insertPos;

	    /* find from position */
	    first = True;
	    while (1) {
		tmp = from;
		from = SrcScan(ctx->text.source, from, XawstEOL, XawsdLeft,
			       1 + !first, False);
		XawTextSourceRead(ctx->text.source, from, &block, 1);
		if (block.length == 0 ||
		    (XawTextFormat(ctx, XawFmt8Bit) &&
		     block.ptr[0] != ' ' &&
		     block.ptr[0] != '\t' &&
		     !isalnum(*(unsigned char*)block.ptr)) ||
		    (XawTextFormat(ctx, XawFmtWide) &&
		     _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
		     _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
		     !iswalnum(*(wchar_t*)block.ptr)) ||
		    BlankLine((Widget)ctx, from, NULL)) {
		    from = tmp;
		    break;
		}
		if (from == tmp && !first)
		    break;
		first = False;
	    }
	    if (first)
		return (False);

	    /* find to position */
	    first = True;
	    while (1) {
		tmp = to;
		to = SrcScan(ctx->text.source, to, XawstEOL, XawsdRight,
			     1 + !first, False);
		XawTextSourceRead(ctx->text.source, to + (to < ctx->text.lastPos),
				  &block, 1);
		if (block.length == 0 ||
		    (XawTextFormat(ctx, XawFmt8Bit) &&
		     block.ptr[0] != ' ' &&
		     block.ptr[0] != '\t' &&
		     !isalnum(*(unsigned char*)block.ptr)) ||
		    (XawTextFormat(ctx, XawFmtWide) &&
		     _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
		     _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
		     !iswalnum(*(wchar_t*)block.ptr)) ||
		    BlankLine((Widget)ctx, to, NULL))
		    break;
		if (to == tmp && !first)
		    break;
		first = False;
	    }
	}
    }
    else {
	from = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
		       XawsdLeft, 1, False);
	if (BlankLine((Widget)ctx, from, NULL))
	    return (False);
	from = SrcScan(ctx->text.source, from, XawstParagraph,
		       XawsdLeft, 1, False);
	if (BlankLine((Widget)ctx, from, NULL))
	    from = SrcScan(ctx->text.source, from, XawstEOL,
			   XawsdRight, 1, True);
	to = SrcScan(ctx->text.source, from, XawstParagraph,
			XawsdRight, 1, False);
    }

    if (from < to) {
	*from_return = from;
	*to_return = to;
	return (True);
    }

    return (False);
}
#endif /* OLDXAW */

/* FormParagraph() - action
 *
 * removes and reinserts CRs to maximize line length without clipping */
/*ARGSUSED*/
static void
FormParagraph(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition from, to, buf[32], *pos;
#ifndef OLDXAW
    XawTextPosition endPos = 0;
    char *lbuf = NULL, *rbuf;
    TextSrcObject src = (TextSrcObject)ctx->text.source;
    Cardinal i;
    Bool undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
#endif

    StartAction(ctx, event);

#ifndef OLDXAW
    pos = XawStackAlloc(sizeof(XawTextPosition) * src->textSrc.num_text, buf);
    for (i = 0; i < src->textSrc.num_text; i++)
	pos[i] = ((TextWidget)src->textSrc.text[i])->text.old_insert;
#else
    pos = buf;
    *pos = ctx->text.old_insert;
#endif

#ifndef OLDXAW
    if (!GetBlockBoundaries(ctx, &from, &to)) {
	EndAction(ctx);
	XawStackFree(pos, buf);
	return;
    }

    if (undo) {
	src->textSrc.undo_state = True;
	lbuf = _XawTextGetText(ctx, from, to);
	endPos = ctx->text.lastPos;
    }

    if (FormRegion(ctx, from, to, pos, src->textSrc.num_text) == XawReplaceError) {
#else
    from =  SrcScan(ctx->text.source, ctx->text.insertPos,
		    XawstParagraph, XawsdLeft, 1, False);
    to  =   SrcScan(ctx->text.source, from,
		    XawstParagraph, XawsdRight, 1, False);

    if (FormRegion(ctx, from, to, pos, 1) == XawReplaceError) {
#endif
	XawStackFree(pos, buf);
	XBell(XtDisplay(w), 0);
#ifndef OLDXAW
	if (undo) {
	    src->textSrc.undo_state = False;
	    XtFree(lbuf);
	}
#endif
    }
#ifndef OLDXAW
    else if (undo) {
	/* makes the form-paragraph only one undo/redo step */
	unsigned llen, rlen, size;
	XawTextBlock block;

	llen = to - from;
	rlen = llen + (ctx->text.lastPos - endPos);

	block.firstPos = 0;
	block.format = _XawTextFormat(ctx);

	rbuf = _XawTextGetText(ctx, from, from + rlen);

	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
	    block.ptr = lbuf;
	    block.length = llen;
	    _XawTextReplace(ctx, from, from + rlen, &block);

	    src->textSrc.undo_state = False;
	    block.ptr = rbuf;
	    block.length = rlen;
	    _XawTextReplace(ctx, from, from + llen, &block);
	}
	else
	    src->textSrc.undo_state = False;
	XtFree(lbuf);
	XtFree(rbuf);
    }

    for (i = 0; i < src->textSrc.num_text; i++) {
	TextWidget tw = (TextWidget)src->textSrc.text[i];

	tw->text.old_insert = tw->text.insertPos = pos[i];
	_XawTextBuildLineTable(tw, SrcScan((Widget)src, tw->text.lt.top, XawstEOL,
			       XawsdLeft, 1, False), False);
	tw->text.clear_to_eol = True;
    }
#else
    ctx->text.old_insert = ctx->text.insertPos = *pos;
    _XawTextBuildLineTable(ctx, SrcScan(ctx->text.source, ctx->text.lt.top,
			   XawstEOL, XawsdLeft, 1, False), False);
    ctx->text.clear_to_eol = True;
#endif
    XawStackFree(pos, buf);
    ctx->text.showposition = True;

    EndAction(ctx);
}

/* TransposeCharacters() - action
 *
 * Swaps the character to the left of the mark
 * with the character to the right of the mark */
/*ARGSUSED*/
static void
TransposeCharacters(Widget w, XEvent *event,
		    String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    XawTextPosition start, end;
    XawTextBlock text;
    char *buf;
    int i, mult = MULT(ctx);

    if (mult < 0) {
	ctx->text.mult = 1;
	return;
    }

    StartAction(ctx, event);

    /* Get bounds. */

    start = SrcScan(ctx->text.source, ctx->text.insertPos, XawstPositions,
		    XawsdLeft, 1, True);
    end = SrcScan(ctx->text.source, ctx->text.insertPos, XawstPositions,
		  XawsdRight, mult, True);

    /* Make sure we aren't at the very beginning or end of the buffer. */

    if (start == ctx->text.insertPos || end == ctx->text.insertPos) {
	XBell(XtDisplay(w), 0);   /* complain. */
	EndAction(ctx);
	return;
    }

    ctx->text.from_left = -1;
    ctx->text.insertPos = end;

    text.firstPos = 0;
    text.format = _XawTextFormat(ctx);

    /* Retrieve text and swap the characters. */
    if (text.format == XawFmtWide) {
	wchar_t wc;
	wchar_t *wbuf;

	wbuf = (wchar_t*)_XawTextGetText(ctx, start, end);
	text.length = wcslen(wbuf);
	wc = wbuf[0];
	for (i = 1; i < text.length; i++)
	    wbuf[i - 1] = wbuf[i];
	wbuf[i - 1] = wc;
	buf = (char*)wbuf; /* so that it gets assigned and freed */
    }
    else {	/* thus text.format == XawFmt8Bit */
	char c;

	buf = _XawTextGetText(ctx, start, end);
	text.length = strlen(buf);
	c = buf[0];
	for (i = 1; i < text.length; i++)
	    buf[i - 1] = buf[i];
	buf[i - 1] = c;
    }

    text.ptr = buf;

    /* Store new text in source. */

    if (_XawTextReplace (ctx, start, end, &text))
	XBell(XtDisplay(w), 0);
    XtFree((char *)buf);
    EndAction(ctx);
}

#ifndef OLDXAW
/*ARGSUSED*/
static void
Undo(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    TextWidget ctx = (TextWidget)w;
    int mul = MULT(ctx);
    Bool toggle = False;

    if (mul < 0) {
	toggle = True;
	_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);
	ctx->text.mult = mul = -mul;
    }

    StartAction(ctx, event);
    for (; mul; --mul)
	if (!_XawTextSrcUndo((TextSrcObject)ctx->text.source, &ctx->text.insertPos))
	    break;
    ctx->text.showposition = True;

    if (toggle)
	_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);
    EndAction(ctx);
}
#endif

/* NoOp() - action
 * This action performs no action, and allows the user or
 * application programmer to unbind a translation.
 *
 * Note: If the parameter list contains the string "RingBell" then
 *	 this action will ring the bell.
 */
/*ARGSUSED*/
static void
NoOp(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    if (*num_params != 1)
	return;

    switch(params[0][0]) {
	case 'R':
	case 'r':
	    XBell(XtDisplay(w), 0);
	    /*FALLTROUGH*/
	default:
	    break;
    }
}

/* Reconnect() - action
 * This reconnects to the input method.  The user will typically call
 * this action if/when connection has been severed, or when the app
 * was started up before an IM was started up
 */
/*ARGSUSED*/
static void
Reconnect(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    _XawImReconnect(w);
}

#define	CAPITALIZE	1
#define	DOWNCASE	2
#define UPCASE		3

#ifdef NO_LIBC_I18N
static int
ToLower(int ch)
{
    char buf[2];

    *buf = ch;
    XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf));

    return (*buf);
}

static int
ToUpper(int ch)
{
    char buf[2];

    *buf = ch;
    XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf));

    return (*buf);
}

static int
IsAlnum(int ch)
{
    return ((ch >= '0' && ch <= '9') || ToUpper(ch) != ch || ToLower(ch) != ch);
}

static int
IsLower(int ch)
{
    char upbuf[2];
    char lobuf[2];

    *upbuf = *lobuf = ch;
    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));

    return (*lobuf != *upbuf && ch == *lobuf);
}

static int
IsUpper(int ch)
{
    char upbuf[2];
    char lobuf[2];

    *upbuf = *lobuf = ch;
    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));

    return (*lobuf != *upbuf && ch == *upbuf);
}
#else
#define	ToLower	tolower
#define ToUpper	toupper
#define IsAlnum isalnum
#define IsLower islower
#define IsUpper isupper
#endif

static void
CaseProc(Widget w, XEvent *event, int cmd)
{
    TextWidget ctx = (TextWidget)w;
    short mul = MULT(ctx);
    XawTextPosition left, right;
    XawTextBlock block;
    Bool changed = False;
    unsigned char ch, mb[sizeof(wchar_t)];
    int i, count;

    if (mul > 0)
	right = SrcScan(ctx->text.source, left = ctx->text.insertPos,
			XawstAlphaNumeric, XawsdRight, mul, False);
    else
	left = SrcScan(ctx->text.source, right = ctx->text.insertPos,
		       XawstAlphaNumeric, XawsdLeft, 1 + -mul, False);
    block.firstPos = 0;
    block.format = _XawTextFormat(ctx);
    block.length = right - left;
    block.ptr = _XawTextGetText(ctx, left, right);

    count = 0;
    if (block.format == XawFmt8Bit)
	for (i = 0; i < block.length; i++) {
	    if (!IsAlnum(*mb = (unsigned char)block.ptr[i]))
		count = 0;
	    else if (++count == 1 || cmd != CAPITALIZE) {
		ch = cmd == DOWNCASE ? ToLower(*mb) : ToUpper(*mb);
		if (ch != *mb) {
		    changed = True;
		    block.ptr[i] = ch;
		}
	    }
	    else if (cmd == CAPITALIZE) {
		if ((ch = ToLower(*mb)) != *mb) {
		    changed = True;
		    block.ptr[i] = ch;
		}
	    }
	}
    else
	for (i = 0; i < block.length; i++) {
	    wctomb((char*)mb, ((wchar_t*)block.ptr)[i]);
	    if (!IsAlnum(*mb))
		count = 0;
	    else if (++count == 1 || cmd != CAPITALIZE) {
		ch = cmd == DOWNCASE ? ToLower(*mb) : ToUpper(*mb);
		if (ch != *mb) {
		    changed = True;
		    ((wchar_t*)block.ptr)[i] = _Xaw_atowc(ch);
		}
	    }
	    else if (cmd == CAPITALIZE) {
		if ((ch = ToLower(*mb)) != *mb) {
		    changed = True;
		    ((wchar_t*)block.ptr)[i] = _Xaw_atowc(ch);
		}
	    }
	}

    StartAction(ctx, event);
    if (changed && _XawTextReplace(ctx, left, right, &block) != XawEditDone)
	XBell(XtDisplay(ctx), 0);
    ctx->text.insertPos = right;
    EndAction(ctx);

    XtFree(block.ptr);
}

/*ARGSUSED*/
static void
CapitalizeWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    CaseProc(w, event, CAPITALIZE);
}

/*ARGSUSED*/
static void
DowncaseWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    CaseProc(w, event, DOWNCASE);
}

/*ARGSUSED*/
static void
UpcaseWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    CaseProc(w, event, UPCASE);
}
#undef CAPITALIZE
#undef DOWNCASE
#undef UPCASE

XtActionsRec _XawTextActionsTable[] = {
  /* motion */
  {"forward-character",		MoveForwardChar},
  {"backward-character",	MoveBackwardChar},
  {"forward-word",		MoveForwardWord},
  {"backward-word",		MoveBackwardWord},
  {"forward-paragraph",		MoveForwardParagraph},
  {"backward-paragraph",	MoveBackwardParagraph},
  {"beginning-of-line",		MoveToLineStart},
  {"end-of-line",		MoveToLineEnd},
  {"next-line",			MoveNextLine},
  {"previous-line",		MovePreviousLine},
  {"next-page",			MoveNextPage},
  {"previous-page",		MovePreviousPage},
  {"beginning-of-file",		MoveBeginningOfFile},
  {"end-of-file",		MoveEndOfFile},
  {"scroll-one-line-up",	ScrollOneLineUp},
  {"scroll-one-line-down",	ScrollOneLineDown},

  /* delete */
  {"delete-next-character",	DeleteForwardChar},
  {"delete-previous-character",	DeleteBackwardChar},
  {"delete-next-word",		DeleteForwardWord},
  {"delete-previous-word",	DeleteBackwardWord},
  {"delete-selection",		DeleteCurrentSelection},
  {"delete",			Delete},

  /* kill */
  {"kill-word",			KillForwardWord},
  {"backward-kill-word",	KillBackwardWord},
  {"kill-selection",		KillCurrentSelection},
  {"kill-to-end-of-line",	KillToEndOfLine},
  {"kill-to-end-of-paragraph",	KillToEndOfParagraph},

  /* new line */
  {"newline-and-indent",	InsertNewLineAndIndent},
  {"newline-and-backup",	InsertNewLineAndBackup},
  {"newline",			InsertNewLine},

  /* selection */
  {"select-word",		SelectWord},
  {"select-all",		SelectAll},
  {"select-start",		SelectStart},
  {"select-adjust",		SelectAdjust},
  {"select-end",		SelectEnd},
  {"select-save",		SelectSave},
  {"extend-start",		ExtendStart},
  {"extend-adjust",		ExtendAdjust},
  {"extend-end", 		ExtendEnd},
  {"insert-selection",		InsertSelection},

  /* miscellaneous */
  {"redraw-display",		RedrawDisplay},
  {"insert-file",		_XawTextInsertFile},
  {"search",			_XawTextSearch},
  {"insert-char",		InsertChar},
  {"insert-string",		InsertString},
  {"focus-in",			TextFocusIn},
  {"focus-out",			TextFocusOut},
  {"enter-window",		TextEnterWindow},
  {"leave-window",		TextLeaveWindow},
  {"display-caret",		DisplayCaret},
  {"multiply",			Multiply},
  {"form-paragraph",		FormParagraph},
  {"transpose-characters",	TransposeCharacters},
  {"set-keyboard-focus",	SetKeyboardFocus},
#ifndef OLDXAW
  {"numeric",			Numeric},
  {"undo",			Undo},
  {"keyboard-reset",		KeyboardReset},
  {"kill-ring-yank",		KillRingYank},
  {"toggle-overwrite",		ToggleOverwrite},
  {"indent",			Indent},
#endif
  {"no-op",			NoOp},

  /* case transformations */
  {"capitalize-word",		CapitalizeWord},
  {"downcase-word",		DowncaseWord},
  {"upcase-word",		UpcaseWord},

  /* action to bind translations for text dialogs */
  {"InsertFileAction",		_XawTextInsertFileAction},
  {"DoSearchAction",		_XawTextDoSearchAction},
  {"DoReplaceAction",		_XawTextDoReplaceAction},
  {"SetField",			_XawTextSetField},
  {"PopdownSearchAction",	_XawTextPopdownSearchAction},

  /* reconnect to Input Method */
  {"reconnect-im",		Reconnect} /* Li Yuhong, Omron KK, 1991 */
};

Cardinal _XawTextActionsTableCount = XtNumber(_XawTextActionsTable);
