TextAction.c revision c889a3bf
1/*
2
3Copyright 1989, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30#include <stdio.h>
31#include <stdlib.h>
32#include <X11/Xos.h>		/* for select() and struct timeval */
33#include <ctype.h>
34#include <X11/IntrinsicP.h>
35#include <X11/StringDefs.h>
36#include <X11/Xatom.h>
37#include <X11/Xfuncs.h>
38#include <X11/Xutil.h>
39#include <X11/Xmu/Atoms.h>
40#include <X11/Xmu/Misc.h>
41#include <X11/Xmu/StdSel.h>
42#include <X11/Xaw/MultiSinkP.h>
43#include <X11/Xaw/MultiSrcP.h>
44#include <X11/Xaw/TextP.h>
45#include <X11/Xaw/TextSrcP.h>
46#include <X11/Xaw/XawImP.h>
47#include "Private.h"
48#include "XawI18n.h"
49
50#ifdef _WIN32
51#include <X11/Xwinsock.h>
52#endif
53
54#define SrcScan			XawTextSourceScan
55#define FindDist		XawTextSinkFindDistance
56#define FindPos			XawTextSinkFindPosition
57#define MULT(w)			(w->text.mult == 0 ? 4 :		\
58				 w->text.mult == 32767 ? -4 : w->text.mult)
59
60#define KILL_RING_APPEND	2
61#define KILL_RING_BEGIN		3
62#define KILL_RING_YANK		100
63#define KILL_RING_YANK_DONE	98
64
65#define XawTextActionMaxHexChars	100
66
67/*
68 * Prototypes
69 */
70static void _DeleteOrKill(TextWidget, XawTextPosition, XawTextPosition, Bool);
71static void _SelectionReceived(Widget, XtPointer, Atom*, Atom*, XtPointer,
72			       unsigned long*, int*);
73static void _LoseSelection(Widget, Atom*, char**, int*);
74static void AutoFill(TextWidget);
75static Boolean ConvertSelection(Widget, Atom*, Atom*, Atom*, XtPointer*,
76				unsigned long*, int*);
77static void DeleteOrKill(TextWidget, XEvent*, XawTextScanDirection,
78			 XawTextScanType, Bool, Bool);
79static void EndAction(TextWidget);
80#ifndef OLDXAW
81static Bool BlankLine(Widget, XawTextPosition, int*);
82static int DoFormatText(TextWidget, XawTextPosition, Bool, int,
83			XawTextBlock*, XawTextPosition*, int, Bool);
84static int FormatText(TextWidget, XawTextPosition, Bool,
85		      XawTextPosition*, int);
86static Bool GetBlockBoundaries(TextWidget, XawTextPosition*, XawTextPosition*);
87#endif
88static int FormRegion(TextWidget, XawTextPosition, XawTextPosition,
89		      XawTextPosition*, int);
90static void GetSelection(Widget, Time, String*, Cardinal);
91static char *IfHexConvertHexElseReturnParam(char*, int*);
92static void InsertNewCRs(TextWidget, XawTextPosition, XawTextPosition,
93			 XawTextPosition*, int);
94static int InsertNewLineAndBackupInternal(TextWidget);
95static int LocalInsertNewLine(TextWidget, XEvent*);
96static void LoseSelection(Widget, Atom*);
97static void ParameterError(Widget, String);
98static Bool MatchSelection(Atom, XawTextSelection*);
99static void ModifySelection(TextWidget, XEvent*, XawTextSelectionMode,
100			    XawTextSelectionAction, String*, Cardinal*);
101static void Move(TextWidget, XEvent*, XawTextScanDirection, XawTextScanType,
102		 Bool);
103static void NotePosition(TextWidget, XEvent*);
104static void StartAction(TextWidget, XEvent*);
105static XawTextPosition StripOutOldCRs(TextWidget, XawTextPosition,
106				      XawTextPosition, XawTextPosition*, int);
107#ifndef OLDXAW
108static Bool StripSpaces(TextWidget, XawTextPosition, XawTextPosition,
109			XawTextPosition*, int, XawTextBlock*);
110static Bool Tabify(TextWidget, XawTextPosition, XawTextPosition,
111		   XawTextPosition*, int, XawTextBlock*);
112static Bool Untabify(TextWidget, XawTextPosition, XawTextPosition,
113		     XawTextPosition*, int, XawTextBlock*);
114#endif
115
116/*
117 * Actions
118 */
119static void CapitalizeWord(Widget, XEvent*, String*, Cardinal*);
120static void DisplayCaret(Widget, XEvent*, String*, Cardinal*);
121static void Delete(Widget, XEvent*, String*, Cardinal*);
122static void DeleteBackwardChar(Widget, XEvent*, String*, Cardinal*);
123static void DeleteBackwardWord(Widget, XEvent*, String*, Cardinal*);
124static void DeleteCurrentSelection(Widget, XEvent*, String*, Cardinal*);
125static void DeleteForwardChar(Widget, XEvent*, String*, Cardinal*);
126static void DeleteForwardWord(Widget, XEvent*, String*, Cardinal*);
127static void DowncaseWord(Widget, XEvent*, String*, Cardinal*);
128static void ExtendAdjust(Widget, XEvent*, String*, Cardinal*);
129static void ExtendEnd(Widget, XEvent*, String*, Cardinal*);
130static void ExtendStart(Widget, XEvent*, String*, Cardinal*);
131static void FormParagraph(Widget, XEvent*, String*, Cardinal*);
132#ifndef OLDXAW
133static void Indent(Widget, XEvent*, String*, Cardinal*);
134#endif
135static void InsertChar(Widget, XEvent*, String*, Cardinal*);
136static void InsertNewLine(Widget, XEvent*, String*, Cardinal*);
137static void InsertNewLineAndBackup(Widget, XEvent*, String*, Cardinal*);
138static void InsertNewLineAndIndent(Widget, XEvent*, String*, Cardinal*);
139static void InsertSelection(Widget, XEvent*, String*, Cardinal*);
140static void InsertString(Widget, XEvent*, String*, Cardinal*);
141#ifndef OLDXAW
142static void KeyboardReset(Widget, XEvent*, String*, Cardinal*);
143#endif
144static void KillBackwardWord(Widget, XEvent*, String*, Cardinal*);
145static void KillCurrentSelection(Widget, XEvent*, String*, Cardinal*);
146static void KillForwardWord(Widget, XEvent*, String*, Cardinal*);
147#ifndef OLDXAW
148static void KillRingYank(Widget, XEvent*, String*, Cardinal*);
149#endif
150static void KillToEndOfLine(Widget, XEvent*, String*, Cardinal*);
151static void KillToEndOfParagraph(Widget, XEvent*, String*, Cardinal*);
152static void MoveBackwardChar(Widget, XEvent*, String*, Cardinal*);
153static void MoveBackwardWord(Widget, XEvent*, String*, Cardinal*);
154static void MoveBackwardParagraph(Widget, XEvent*, String*, Cardinal*);
155static void MoveBeginningOfFile(Widget, XEvent*, String*, Cardinal*);
156static void MoveEndOfFile(Widget, XEvent*, String*, Cardinal*);
157static void MoveForwardChar(Widget, XEvent*, String*, Cardinal*);
158static void MoveForwardWord(Widget, XEvent*, String*, Cardinal*);
159static void MoveForwardParagraph(Widget, XEvent*, String*, Cardinal*);
160static void MoveNextLine(Widget, XEvent*, String*, Cardinal*);
161static void MoveNextPage(Widget, XEvent*, String*, Cardinal*);
162static void MovePage(TextWidget, XEvent*, XawTextScanDirection);
163static void MovePreviousLine(Widget, XEvent*, String*, Cardinal*);
164static void MovePreviousPage(Widget, XEvent*, String*, Cardinal*);
165static void MoveLine(TextWidget, XEvent*, XawTextScanDirection);
166static void MoveToLineEnd(Widget, XEvent*, String*, Cardinal*);
167static void MoveToLineStart(Widget, XEvent*, String*, Cardinal*);
168static void Multiply(Widget, XEvent*, String*, Cardinal*);
169static void NoOp(Widget, XEvent*, String*, Cardinal*);
170#ifndef OLDXAW
171static void Numeric(Widget, XEvent*, String*, Cardinal*);
172#endif
173static void Reconnect(Widget, XEvent*, String*, Cardinal*);
174static void RedrawDisplay(Widget, XEvent*, String*, Cardinal*);
175static void Scroll(TextWidget, XEvent*, XawTextScanDirection);
176static void ScrollOneLineDown(Widget, XEvent*, String*, Cardinal*);
177static void ScrollOneLineUp(Widget, XEvent*, String*, Cardinal*);
178static void SelectAdjust(Widget, XEvent*, String*, Cardinal*);
179static void SelectAll(Widget, XEvent*, String*, Cardinal*);
180static void SelectEnd(Widget, XEvent*, String*, Cardinal*);
181static void SelectSave(Widget, XEvent*, String*, Cardinal*);
182static void SelectStart(Widget, XEvent*, String*, Cardinal*);
183static void SelectWord(Widget, XEvent*, String*, Cardinal*);
184static void SetKeyboardFocus(Widget, XEvent*, String*, Cardinal*);
185static void TextEnterWindow(Widget, XEvent*, String*, Cardinal*);
186static void TextFocusIn(Widget, XEvent*, String*, Cardinal*);
187static void TextFocusOut(Widget, XEvent*, String*, Cardinal*);
188static void TextLeaveWindow(Widget, XEvent*, String*, Cardinal*);
189static void TransposeCharacters(Widget, XEvent*, String*, Cardinal*);
190#ifndef OLDXAW
191static void ToggleOverwrite(Widget, XEvent*, String*, Cardinal*);
192static void Undo(Widget, XEvent*, String*, Cardinal*);
193#endif
194static void UpcaseWord(Widget, XEvent*, String*, Cardinal*);
195static void DestroyFocusCallback(Widget, XtPointer, XtPointer);
196
197/*
198 * External
199 */
200void _XawTextZapSelection(TextWidget, XEvent*, Bool);
201
202/*
203 * Defined in TextPop.c
204 */
205void _XawTextInsertFileAction(Widget, XEvent*, String*, Cardinal*);
206void _XawTextInsertFile(Widget, XEvent*, String*, Cardinal*);
207void _XawTextSearch(Widget, XEvent*, String*, Cardinal*);
208void _XawTextDoSearchAction(Widget, XEvent*, String*, Cardinal*);
209void _XawTextDoReplaceAction(Widget, XEvent*, String*, Cardinal*);
210void _XawTextSetField(Widget, XEvent*, String*, Cardinal*);
211void _XawTextPopdownSearchAction(Widget, XEvent*, String*, Cardinal*);
212
213/*
214 * These are defined in Text.c
215 */
216void _XawTextAlterSelection(TextWidget, XawTextSelectionMode,
217			    XawTextSelectionAction, String*, Cardinal*);
218void _XawTextClearAndCenterDisplay(TextWidget);
219void _XawTextExecuteUpdate(TextWidget);
220char *_XawTextGetText(TextWidget, XawTextPosition, XawTextPosition);
221void _XawTextPrepareToUpdate(TextWidget);
222int _XawTextReplace(TextWidget, XawTextPosition, XawTextPosition,
223			   XawTextBlock*);
224Atom *_XawTextSelectionList(TextWidget, String*, Cardinal);
225void _XawTextSetSelection(TextWidget, XawTextPosition, XawTextPosition,
226				 String*, Cardinal);
227void _XawTextVScroll(TextWidget, int);
228void XawTextScroll(TextWidget, int, int);
229void _XawTextSetLineAndColumnNumber(TextWidget, Bool);
230
231#ifndef OLDXAW
232/*
233 * Defined in TextSrc.c
234 */
235Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);
236Bool _XawTextSrcToggleUndo(TextSrcObject);
237void _XawSourceSetUndoErase(TextSrcObject, int);
238void _XawSourceSetUndoMerge(TextSrcObject, Bool);
239#endif /* OLDXAW */
240
241/*
242 * Initialization
243 */
244#ifndef OLDXAW
245#define MAX_KILL_RINGS	1024
246XawTextKillRing *xaw_text_kill_ring;
247static XawTextKillRing kill_ring_prev, kill_ring_null = { &kill_ring_prev, };
248static unsigned num_kill_rings;
249#endif
250
251/*
252 * Implementation
253 */
254static void
255ParameterError(Widget w, String param)
256{
257    String params[2];
258    Cardinal num_params = 2;
259    params[0] = XtName(w);
260    params[1] = param;
261
262    XtAppWarningMsg(XtWidgetToApplicationContext(w),
263		    "parameterError", "textAction", "XawError",
264		    "Widget: %s Parameter: %s",
265		    params, &num_params);
266    XBell(XtDisplay(w), 50);
267}
268
269static void
270StartAction(TextWidget ctx, XEvent *event)
271{
272#ifndef OLDXAW
273    Cardinal i;
274    TextSrcObject src = (TextSrcObject)ctx->text.source;
275
276    for (i = 0; i < src->textSrc.num_text; i++)
277	_XawTextPrepareToUpdate((TextWidget)src->textSrc.text[i]);
278    _XawSourceSetUndoMerge(src, False);
279#else
280    _XawTextPrepareToUpdate(ctx);
281#endif
282
283    if (event != NULL) {
284	switch (event->type) {
285	    case ButtonPress:
286	    case ButtonRelease:
287		ctx->text.time = event->xbutton.time;
288		break;
289	    case KeyPress:
290	    case KeyRelease:
291		ctx->text.time = event->xkey.time;
292		break;
293	    case MotionNotify:
294		ctx->text.time = event->xmotion.time;
295		break;
296	    case EnterNotify:
297	    case LeaveNotify:
298		ctx->text.time = event->xcrossing.time;
299	}
300    }
301}
302
303static void
304NotePosition(TextWidget ctx, XEvent *event)
305{
306    switch (event->type) {
307	case ButtonPress:
308	case ButtonRelease:
309	    ctx->text.ev_x = event->xbutton.x;
310	    ctx->text.ev_y = event->xbutton.y;
311	    break;
312	case KeyPress:
313	case KeyRelease: {
314	    XRectangle cursor;
315	    XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
316	    ctx->text.ev_x = cursor.x + cursor.width / 2;
317	    ctx->text.ev_y = cursor.y + cursor.height / 2;
318	}   break;
319	case MotionNotify:
320	    ctx->text.ev_x = event->xmotion.x;
321	    ctx->text.ev_y = event->xmotion.y;
322	    break;
323	case EnterNotify:
324	case LeaveNotify:
325	    ctx->text.ev_x = event->xcrossing.x;
326	    ctx->text.ev_y = event->xcrossing.y;
327    }
328}
329
330static void
331EndAction(TextWidget ctx)
332{
333#ifndef OLDXAW
334    Cardinal i;
335    TextSrcObject src = (TextSrcObject)ctx->text.source;
336
337    for (i = 0; i < src->textSrc.num_text; i++)
338	_XawTextExecuteUpdate((TextWidget)src->textSrc.text[i]);
339
340    ctx->text.mult = 1;
341    ctx->text.numeric = False;
342    if (ctx->text.kill_ring) {
343	if (--ctx->text.kill_ring == KILL_RING_YANK_DONE) {
344	    if (ctx->text.kill_ring_ptr) {
345		--ctx->text.kill_ring_ptr->refcount;
346		ctx->text.kill_ring_ptr = NULL;
347	    }
348	}
349    }
350#else
351    ctx->text.mult = 1;
352    _XawTextExecuteUpdate(ctx);
353#endif /* OLDXAW */
354}
355
356struct _SelectionList {
357    String* params;
358    Cardinal count;
359    Time time;
360    int asked;		/* which selection currently has been asked for:
361			   0 = UTF8_STRING, 1 = COMPOUND_TEXT, 2 = STRING */
362    Atom selection;	/* selection atom (normally XA_PRIMARY) */
363};
364
365/*ARGSUSED*/
366static void
367_SelectionReceived(Widget w, XtPointer client_data, Atom *selection,
368		   Atom *type, XtPointer value, unsigned long *length,
369		   int *format)
370{
371    Display *d = XtDisplay(w);
372    TextWidget ctx = (TextWidget)w;
373    XawTextBlock text;
374
375    if (*type == 0 /*XT_CONVERT_FAIL*/ || *length == 0) {
376	struct _SelectionList* list = (struct _SelectionList*)client_data;
377
378	if (list != NULL) {
379	    if (list->asked == 0) {
380		/* If we just asked for XA_UTF8_STRING and got no response,
381		   we'll ask again, this time for XA_COMPOUND_TEXT. */
382		list->asked++;
383		XtGetSelectionValue(w, list->selection, XA_COMPOUND_TEXT(d),
384				    _SelectionReceived,
385				    (XtPointer)list, list->time);
386	    } else if (list->asked == 1) {
387		/* If we just asked for XA_COMPOUND_TEXT and got no response,
388		   we'll ask again, this time for XA_STRING. */
389		list->asked++;
390		XtGetSelectionValue(w, list->selection, XA_STRING,
391				    _SelectionReceived,
392				    (XtPointer)list, list->time);
393	    } else {
394		/* We tried all possible text targets in this param.
395		   Recurse on the tail of the params list. */
396		GetSelection(w, list->time, list->params, list->count);
397		XtFree(client_data);
398	    }
399	}
400	return;
401    }
402
403    StartAction(ctx, NULL);
404    if (XawTextFormat(ctx, XawFmtWide)) {
405	XTextProperty textprop;
406	wchar_t **wlist;
407	int count;
408
409	textprop.encoding = *type;
410	textprop.value = (unsigned char *)value;
411	textprop.nitems = strlen(value);
412	textprop.format = 8;
413
414	if (XwcTextPropertyToTextList(d, &textprop, &wlist, &count)
415	    !=	Success
416	    || count < 1) {
417	    XwcFreeStringList(wlist);
418
419	    /* Notify the user on strerr and in the insertion :) */
420	    fprintf(stderr, "Xaw Text Widget: An attempt was made to insert "
421		    "an illegal selection.\n");
422
423	    textprop.value = (unsigned char *)" >> ILLEGAL SELECTION << ";
424	    textprop.nitems = strlen((char *) textprop.value);
425	    if (XwcTextPropertyToTextList(d, &textprop, &wlist, &count)
426		!=  Success
427		|| count < 1)
428		return;
429	}
430
431	XFree(value);
432	value = (XPointer)wlist[0];
433
434	*length = wcslen(wlist[0]);
435	XtFree((XtPointer)wlist);
436	text.format = XawFmtWide;
437    }
438    text.ptr = (char*)value;
439    text.firstPos = 0;
440    text.length = *length;
441    if (_XawTextReplace(ctx, ctx->text.insertPos, ctx->text.insertPos, &text)) {
442	XBell(XtDisplay(ctx), 0);
443	EndAction(ctx);
444	return;
445    }
446
447    ctx->text.from_left = -1;
448    ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
449				  XawstPositions, XawsdRight, text.length, True);
450
451    EndAction(ctx);
452    XtFree(client_data);
453    XFree(value);	/* the selection value should be freed with XFree */
454}
455
456static void
457GetSelection(Widget w, Time timev, String *params, Cardinal num_params)
458{
459    Display *d = XtDisplay(w);
460    TextWidget ctx = (TextWidget)w;
461    Atom selection;
462    int buffer;
463
464    selection = XInternAtom(XtDisplay(w), *params, False);
465    switch (selection) {
466	case XA_CUT_BUFFER0: buffer = 0; break;
467	case XA_CUT_BUFFER1: buffer = 1; break;
468	case XA_CUT_BUFFER2: buffer = 2; break;
469	case XA_CUT_BUFFER3: buffer = 3; break;
470	case XA_CUT_BUFFER4: buffer = 4; break;
471	case XA_CUT_BUFFER5: buffer = 5; break;
472	case XA_CUT_BUFFER6: buffer = 6; break;
473	case XA_CUT_BUFFER7: buffer = 7; break;
474	default:	     buffer = -1;
475    }
476    if (buffer >= 0) {
477	int nbytes;
478	unsigned long length;
479	int fmt8 = 8;
480	Atom type = XA_STRING;
481	char *line = XFetchBuffer(XtDisplay(w), &nbytes, buffer);
482
483	if ((length = nbytes) != 0L)
484	    _SelectionReceived(w, NULL, &selection, &type, line, &length, &fmt8);
485	else if (num_params > 1)
486	    GetSelection(w, timev, params+1, num_params-1);
487    }
488    else {
489	struct _SelectionList* list;
490
491	if (--num_params) {
492	    list = XtNew(struct _SelectionList);
493	    list->params = params + 1;
494	    list->count = num_params;
495	    list->time = timev;
496	    list->asked = 0;
497	    list->selection = selection;
498	}
499	else
500	    list = NULL;
501	XtGetSelectionValue(w, selection, XawTextFormat(ctx, XawFmtWide) ?
502			    XA_UTF8_STRING(d) : XA_TEXT(d),
503			    _SelectionReceived, (XtPointer)list, timev);
504    }
505}
506
507static void
508InsertSelection(Widget w, XEvent *event, String *params, Cardinal *num_params)
509{
510    StartAction((TextWidget)w, event);	/* Get Time. */
511    GetSelection(w, ((TextWidget)w)->text.time, params, *num_params);
512    EndAction((TextWidget)w);
513}
514
515/*
516 * Routines for Moving Around
517 */
518static void
519Move(TextWidget ctx, XEvent *event, XawTextScanDirection dir,
520     XawTextScanType type, Bool include)
521{
522    XawTextPosition insertPos;
523    short mult = MULT(ctx);
524
525    if (mult < 0) {
526	mult = -mult;
527	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
528    }
529
530    insertPos = SrcScan(ctx->text.source, ctx->text.insertPos,
531			type, dir, mult, include);
532
533    StartAction(ctx, event);
534
535    if (ctx->text.s.left != ctx->text.s.right)
536	XawTextUnsetSelection((Widget)ctx);
537
538#ifndef OLDXAW
539    ctx->text.numeric = False;
540#endif
541    ctx->text.mult = 1;
542    ctx->text.showposition = True;
543    ctx->text.from_left = -1;
544    ctx->text.insertPos = insertPos;
545    EndAction(ctx);
546}
547
548/*ARGSUSED*/
549static void
550MoveForwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
551{
552    Move((TextWidget)w, event, XawsdRight, XawstPositions, True);
553}
554
555/*ARGSUSED*/
556static void
557MoveBackwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
558{
559    Move((TextWidget)w, event, XawsdLeft, XawstPositions, True);
560}
561
562static void
563MoveForwardWord(Widget w, XEvent *event, String *p, Cardinal *n)
564{
565    if (*n && (p[0][0] == 'A' || p[0][0] == 'a'))
566	Move((TextWidget)w, event, XawsdRight, XawstAlphaNumeric, False);
567    else
568	Move((TextWidget)w, event, XawsdRight, XawstWhiteSpace, False);
569}
570
571static void
572MoveBackwardWord(Widget w, XEvent *event, String *p, Cardinal *n)
573{
574    if (*n && (p[0][0] == 'A' || p[0][0] == 'a'))
575	Move((TextWidget)w, event, XawsdLeft, XawstAlphaNumeric, False);
576    else
577	Move((TextWidget)w, event, XawsdLeft, XawstWhiteSpace, False);
578}
579
580static void
581MoveForwardParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
582{
583    TextWidget ctx = (TextWidget)w;
584    XawTextPosition position = ctx->text.insertPos;
585    short mult = MULT(ctx);
586
587    if (mult < 0) {
588	ctx->text.mult = -mult;
589	MoveBackwardParagraph(w, event, p, n);
590	return;
591    }
592
593    while (mult--) {
594	position = SrcScan(ctx->text.source, position,
595			   XawstEOL, XawsdRight, 1, False) - 1;
596
597	while (position == SrcScan(ctx->text.source, position,
598				   XawstEOL, XawsdRight, 1, False))
599	    if (++position > ctx->text.lastPos) {
600		mult = 0;
601		break;
602	    }
603
604	position = SrcScan(ctx->text.source, position,
605			   XawstParagraph, XawsdRight, 1, True);
606	if (position != ctx->text.lastPos)
607	    position = SrcScan(ctx->text.source, position - 1,
608			       XawstEOL, XawsdLeft, 1, False);
609	else
610	    break;
611    }
612
613    if (position != ctx->text.insertPos) {
614	XawTextUnsetSelection(w);
615	StartAction(ctx, event);
616	ctx->text.showposition = True;
617	ctx->text.from_left = -1;
618	ctx->text.insertPos = position;
619	EndAction(ctx);
620    }
621    else
622	ctx->text.mult = 1;
623}
624
625/*ARGSUSED*/
626static void
627MoveBackwardParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
628{
629    TextWidget ctx = (TextWidget)w;
630    XawTextPosition position = ctx->text.insertPos;
631    short mult = MULT(ctx);
632
633    if (mult < 0) {
634	ctx->text.mult = -mult;
635	MoveForwardParagraph(w, event, p, n);
636	return;
637    }
638
639    while (mult--) {
640	position = SrcScan(ctx->text.source, position,
641			   XawstEOL, XawsdLeft, 1, False) + 1;
642
643	while (position == SrcScan(ctx->text.source, position,
644				   XawstEOL, XawsdLeft, 1, False))
645	    if (--position < 0) {
646		mult = 0;
647		break;
648	    }
649
650	position = SrcScan(ctx->text.source, position,
651			   XawstParagraph, XawsdLeft, 1, True);
652	if (position > 0 && position < ctx->text.lastPos)
653	    ++position;
654	else
655	    break;
656    }
657
658    if (position != ctx->text.insertPos) {
659	XawTextUnsetSelection(w);
660	StartAction(ctx, event);
661	ctx->text.showposition = True;
662	ctx->text.from_left = -1;
663	ctx->text.insertPos = position;
664	EndAction(ctx);
665    }
666    else
667	ctx->text.mult = 1;
668}
669
670/*ARGSUSED*/
671static void
672MoveToLineEnd(Widget w, XEvent *event, String *p, Cardinal *n)
673{
674    Move((TextWidget)w, event, XawsdRight, XawstEOL, False);
675}
676
677/*ARGSUSED*/
678static void
679MoveToLineStart(Widget w, XEvent *event, String *p, Cardinal *n)
680{
681    Move((TextWidget)w, event, XawsdLeft, XawstEOL, False);
682}
683
684static void
685MoveLine(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
686{
687    XawTextPosition cnew, next_line, ltemp;
688    int itemp, from_left;
689    short mult = MULT(ctx);
690
691    StartAction(ctx, event);
692
693    XawTextUnsetSelection((Widget)ctx);
694
695    if (dir == XawsdLeft)
696	mult = mult == 0 ? 5 : mult + 1;
697
698    cnew = SrcScan(ctx->text.source, ctx->text.insertPos,
699		   XawstEOL, XawsdLeft, 1, False);
700
701    if (ctx->text.from_left < 0)
702	FindDist(ctx->text.sink, cnew, ctx->text.left_margin, ctx->text.insertPos,
703		 &ctx->text.from_left, &ltemp, &itemp);
704
705    cnew = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, dir,
706		   mult, (dir == XawsdRight));
707
708    next_line = SrcScan(ctx->text.source, cnew, XawstEOL, XawsdRight, 1, False);
709
710    FindPos(ctx->text.sink, cnew, ctx->text.left_margin, ctx->text.from_left,
711	    False, &ctx->text.insertPos, &from_left, &itemp);
712
713    if (from_left < ctx->text.from_left) {
714	XawTextBlock block;
715
716	XawTextSourceRead(ctx->text.source, ctx->text.insertPos, &block, 1);
717	if (block.length) {
718	    if (XawTextFormat(ctx, XawFmtWide)) {
719		if (*(wchar_t *)block.ptr == _Xaw_atowc(XawTAB))
720		    ++ctx->text.insertPos;
721	    }
722	    else if (block.ptr[0] == XawTAB)
723		++ctx->text.insertPos;
724	}
725    }
726
727    if (ctx->text.insertPos > next_line)
728	ctx->text.insertPos = next_line;
729
730    EndAction(ctx);
731}
732
733static void
734MoveNextLine(Widget w, XEvent *event, String *p, Cardinal *n)
735{
736    TextWidget ctx = (TextWidget)w;
737    short mult = MULT(ctx);
738
739    if (mult < 0) {
740	ctx->text.mult = -mult;
741	MovePreviousLine(w, event, p, n);
742	return;
743    }
744
745    if (ctx->text.insertPos < ctx->text.lastPos)
746	MoveLine(ctx, event, XawsdRight);
747    else
748	ctx->text.mult = 1;
749}
750
751static void
752MovePreviousLine(Widget w, XEvent *event, String *p, Cardinal *n)
753{
754    TextWidget ctx = (TextWidget)w;
755    short mult = MULT(ctx);
756
757    if (mult < 0) {
758	ctx->text.mult = -mult;
759	MoveNextLine(w, event, p, n);
760	return;
761    }
762
763    if (ctx->text.lt.top != 0 || (ctx->text.lt.lines > 1 &&
764	ctx->text.insertPos >= ctx->text.lt.info[1].position))
765	MoveLine(ctx, event, XawsdLeft);
766    else
767	ctx->text.mult = 1;
768}
769
770/*ARGSUSED*/
771static void
772MoveBeginningOfFile(Widget w, XEvent *event, String *p, Cardinal *n)
773{
774    Move((TextWidget)w, event, XawsdLeft, XawstAll, True);
775}
776
777/*ARGSUSED*/
778static void
779MoveEndOfFile(Widget w, XEvent *event, String *p, Cardinal *n)
780{
781    Move((TextWidget)w, event, XawsdRight, XawstAll, True);
782}
783
784static void
785Scroll(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
786{
787    short mult = MULT(ctx);
788
789    if (mult < 0) {
790	mult = -mult;
791	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
792    }
793
794    if (ctx->text.lt.lines > 1
795	&& (dir == XawsdRight
796	    || ctx->text.lastPos >= ctx->text.lt.info[1].position)) {
797	StartAction(ctx, event);
798
799	if (dir == XawsdLeft)
800	    _XawTextVScroll(ctx, mult);
801	else
802	    _XawTextVScroll(ctx, -mult);
803
804	EndAction(ctx);
805    }
806    else {
807	ctx->text.mult = 1;
808#ifndef OLDXAW
809	ctx->text.numeric = False;
810#endif
811    }
812}
813
814/*ARGSUSED*/
815static void
816ScrollOneLineUp(Widget w, XEvent *event, String *p, Cardinal *n)
817{
818    Scroll((TextWidget)w, event, XawsdLeft);
819}
820
821/*ARGSUSED*/
822static void
823ScrollOneLineDown(Widget w, XEvent *event, String *p, Cardinal *n)
824{
825    Scroll((TextWidget)w, event, XawsdRight);
826}
827
828static void
829MovePage(TextWidget ctx, XEvent *event, XawTextScanDirection dir)
830{
831    int scroll_val = 0;
832    XawTextPosition old_pos;
833
834    ctx->text.from_left = -1;
835    switch (dir) {
836	case XawsdLeft:
837	    if (ctx->text.lt.top != 0)
838		scroll_val = -Max(1, ctx->text.lt.lines - 1);
839		break;
840	case XawsdRight:
841	    if (!IsPositionVisible(ctx, Max(0, ctx->text.lastPos)))
842		scroll_val = Max(1, ctx->text.lt.lines - 1);
843	    break;
844    }
845
846    if (scroll_val)
847	XawTextScroll(ctx, scroll_val,
848		      ctx->text.left_margin - ctx->text.r_margin.left);
849
850    old_pos = ctx->text.insertPos;
851    switch (dir) {
852	case XawsdRight:
853	    if (IsPositionVisible(ctx, Max(0, ctx->text.lastPos)))
854		ctx->text.insertPos = Max(0, ctx->text.lastPos);
855	    else
856		ctx->text.insertPos = ctx->text.lt.top;
857	    if (ctx->text.insertPos < old_pos)
858		ctx->text.insertPos = SrcScan(ctx->text.source, old_pos,
859					      XawstEOL, XawsdLeft, 1, False);
860	    break;
861	case XawsdLeft:
862	    if (IsPositionVisible(ctx, 0))
863		ctx->text.insertPos = 0;
864	    else if (ctx->text.lt.lines)
865		ctx->text.insertPos =
866		    ctx->text.lt.info[ctx->text.lt.lines - 1].position;
867	    else
868		ctx->text.insertPos = ctx->text.lt.top;
869	    if (ctx->text.insertPos > old_pos)
870		ctx->text.insertPos = SrcScan(ctx->text.source, old_pos,
871					      XawstEOL, XawsdLeft, 1, False);
872	    break;
873    }
874}
875
876static void
877MoveNextPage(Widget w, XEvent *event, String *p, Cardinal *n)
878{
879    TextWidget ctx = (TextWidget)w;
880    short mult = MULT(ctx);
881
882    if (mult < 0) {
883	ctx->text.mult = -mult;
884	MovePreviousPage(w, event, p, n);
885	return;
886    }
887
888    if (ctx->text.insertPos < ctx->text.lastPos) {
889	XawTextUnsetSelection(w);
890	StartAction(ctx, event);
891	ctx->text.clear_to_eol = True;
892	while (mult-- && ctx->text.insertPos < ctx->text.lastPos)
893	    MovePage(ctx, event, XawsdRight);
894	EndAction(ctx);
895    }
896    else
897	ctx->text.mult = 1;
898}
899
900/*ARGSUSED*/
901static void
902MovePreviousPage(Widget w, XEvent *event, String *p, Cardinal *n)
903{
904    TextWidget ctx = (TextWidget)w;
905    short mult = MULT(ctx);
906
907    if (mult < 0) {
908	ctx->text.mult = -mult;
909	MoveNextPage(w, event, p, n);
910	return;
911    }
912
913    if (ctx->text.insertPos > 0) {
914	XawTextUnsetSelection(w);
915	StartAction(ctx, event);
916	ctx->text.clear_to_eol = True;
917	while (mult-- && ctx->text.insertPos > 0)
918	    MovePage(ctx, event, XawsdLeft);
919	EndAction(ctx);
920    }
921    else
922	ctx->text.mult = 1;
923}
924
925/*
926 * Delete Routines
927 */
928static Bool
929MatchSelection(Atom selection, XawTextSelection *s)
930{
931    Atom *match;
932    int count;
933
934    for (count = 0, match = s->selections; count < s->atom_count;
935	 match++, count++)
936	if (*match == selection)
937	    return (True);
938
939    return (False);
940}
941
942#define SrcCvtSel	XawTextSourceConvertSelection
943
944static Boolean
945ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
946		 XtPointer *value, unsigned long *length, int *format)
947{
948    Display *d = XtDisplay(w);
949    TextWidget ctx = (TextWidget)w;
950    Widget src = ctx->text.source;
951    XawTextEditType edit_mode;
952    Arg args[1];
953    XawTextSelectionSalt *salt = NULL;
954    XawTextSelection *s;
955
956    if (*target == XA_TARGETS(d)) {
957	Atom *targetP, *std_targets;
958	unsigned long std_length;
959
960	if (SrcCvtSel(src, selection, target, type, value, length, format))
961	    return (True);
962
963	XtSetArg(args[0], XtNeditType,&edit_mode);
964	XtGetValues(src, args, 1);
965
966	XmuConvertStandardSelection(w, ctx->text.time, selection,
967				    target, type, (XPointer *)&std_targets,
968				    &std_length, format);
969
970	*length = 7 + (edit_mode == XawtextEdit) + std_length;
971	*value = XtMalloc((unsigned)sizeof(Atom)*(*length));
972	targetP = *(Atom**)value;
973	*targetP++ = XA_STRING;
974	*targetP++ = XA_TEXT(d);
975	*targetP++ = XA_UTF8_STRING(d);
976	*targetP++ = XA_COMPOUND_TEXT(d);
977	*targetP++ = XA_LENGTH(d);
978	*targetP++ = XA_LIST_LENGTH(d);
979	*targetP++ = XA_CHARACTER_POSITION(d);
980	if (edit_mode == XawtextEdit) {
981	    *targetP++ = XA_DELETE(d);
982	}
983	memcpy((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
984	XtFree((char*)std_targets);
985	*type = XA_ATOM;
986	*format = 32;
987	return (True);
988    }
989
990    if (SrcCvtSel(src, selection, target, type, value, length, format))
991	return (True);
992
993    for (salt = ctx->text.salt2; salt; salt = salt->next)
994	if (MatchSelection (*selection, &salt->s))
995	    break;
996    if (!salt)
997	return (False);
998    s = &salt->s;
999    if (*target == XA_STRING
1000	|| *target == XA_TEXT(d)
1001	|| *target == XA_UTF8_STRING(d)
1002	|| *target == XA_COMPOUND_TEXT(d)) {
1003	if (*target == XA_TEXT(d)) {
1004	    if (XawTextFormat(ctx, XawFmtWide))
1005		*type = XA_COMPOUND_TEXT(d);
1006	    else
1007		*type = XA_STRING;
1008	}
1009	else
1010	  *type = *target;
1011
1012	/*
1013	 * If salt is True, the salt->contents stores CT string,
1014	 * its length is measured in bytes.
1015	 * Refer to _XawTextSaltAwaySelection()
1016	 *
1017	 * by Li Yuhong, Mar. 20, 1991.
1018	 */
1019	if (!salt) {
1020	    *value = (char *)_XawTextGetSTRING(ctx, s->left, s->right);
1021	    if (XawTextFormat(ctx, XawFmtWide)) {
1022		XTextProperty textprop;
1023		if (XwcTextListToTextProperty(d, (wchar_t**)value, 1,
1024					      XCompoundTextStyle, &textprop)
1025		    < Success) {
1026		    XtFree(*value);
1027		    return (False);
1028		}
1029		XtFree(*value);
1030		*value = (XtPointer)textprop.value;
1031		*length = textprop.nitems;
1032	    }
1033	    else
1034		*length = strlen(*value);
1035	}
1036	else {
1037	    *value = XtMalloc((salt->length + 1) * sizeof(unsigned char));
1038	    strcpy (*value, salt->contents);
1039	    *length = salt->length;
1040	}
1041	/* Got *value,*length, now in COMPOUND_TEXT format. */
1042	if (XawTextFormat(ctx, XawFmtWide)) {
1043	    if (*type == XA_STRING) {
1044		XTextProperty textprop;
1045		wchar_t **wlist;
1046		int count;
1047
1048		textprop.encoding = XA_COMPOUND_TEXT(d);
1049		textprop.value = (unsigned char *)*value;
1050		textprop.nitems = strlen(*value);
1051		textprop.format = 8;
1052		if (XwcTextPropertyToTextList(d, &textprop, &wlist, &count)
1053		     < Success
1054		    || count < 1) {
1055		    XtFree(*value);
1056		    return (False);
1057		}
1058		XtFree(*value);
1059		if (XwcTextListToTextProperty(d, wlist, 1, XStringStyle, &textprop)
1060		     < Success) {
1061		    XwcFreeStringList((wchar_t**)wlist);
1062		    return (False);
1063		}
1064		*value = (XtPointer)textprop.value;
1065		*length = textprop.nitems;
1066		XwcFreeStringList((wchar_t**) wlist);
1067	    }
1068	    else if (*type == XA_UTF8_STRING(d)) {
1069		XTextProperty textprop;
1070		char **list;
1071		int count;
1072
1073		textprop.encoding = XA_COMPOUND_TEXT(d);
1074		textprop.value = (unsigned char *)*value;
1075		textprop.nitems = strlen(*value);
1076		textprop.format = 8;
1077		if (Xutf8TextPropertyToTextList(d, &textprop, &list, &count)
1078		    < Success
1079		    || count < 1) {
1080		    XtFree(*value);
1081		    return (False);
1082		}
1083		XtFree(*value);
1084		*value = *list;
1085		*length = strlen(*list);
1086		XFree(list);
1087	    }
1088	}
1089	*format = 8;
1090	return (True);
1091    }
1092
1093    if (*target == XA_LIST_LENGTH(d) || *target == XA_LENGTH(d)) {
1094	long *temp;
1095
1096	temp = (long *)XtMalloc(sizeof(long));
1097	if (*target == XA_LIST_LENGTH(d))
1098	    *temp = 1L;
1099	else			/* *target == XA_LENGTH(d) */
1100	    *temp = (long)(s->right - s->left);
1101
1102	*value = (XPointer)temp;
1103	*type = XA_INTEGER;
1104	*length = 1L;
1105	*format = 32;
1106	return (True);
1107    }
1108
1109    if (*target == XA_CHARACTER_POSITION(d)) {
1110	long *temp;
1111
1112	temp = (long *) XtMalloc(2 * sizeof(long));
1113	temp[0] = (long)(s->left + 1);
1114	temp[1] = s->right;
1115	*value = (XPointer)temp;
1116	*type = XA_SPAN(d);
1117	*length = 2L;
1118	*format = 32;
1119	return (True);
1120    }
1121
1122    if (*target == XA_DELETE(d)) {
1123	if (!salt)
1124	    _XawTextZapSelection(ctx, NULL, True);
1125	*value = NULL;
1126	*type = XA_NULL(d);
1127	*length = 0;
1128	*format = 32;
1129	return (True);
1130    }
1131
1132    if (XmuConvertStandardSelection(w, ctx->text.time, selection, target, type,
1133				    (XPointer *)value, length, format))
1134	return (True);
1135
1136    return (False);
1137}
1138
1139static void
1140LoseSelection(Widget w, Atom *selection)
1141{
1142    _LoseSelection(w, selection, NULL, NULL);
1143}
1144
1145static void
1146_LoseSelection(Widget w, Atom *selection, char **contents, int *length)
1147{
1148    TextWidget ctx = (TextWidget)w;
1149    Atom *atomP;
1150    int i;
1151    XawTextSelectionSalt *salt, *prevSalt, *nextSalt;
1152
1153    prevSalt = 0;
1154    for (salt = ctx->text.salt2; salt; salt = nextSalt) {
1155	atomP = salt->s.selections;
1156	nextSalt = salt->next;
1157	for (i = 0 ; i < salt->s.atom_count; i++, atomP++)
1158	    if (*selection == *atomP)
1159		*atomP = (Atom)0;
1160
1161	while (salt->s.atom_count
1162	       && salt->s.selections[salt->s.atom_count-1] == 0)
1163	    salt->s.atom_count--;
1164
1165	/*
1166	 * Must walk the selection list in opposite order from UnsetSelection.
1167	 */
1168	atomP = salt->s.selections;
1169	for (i = 0 ; i < salt->s.atom_count; i++, atomP++)
1170	    if (*atomP == (Atom)0) {
1171		*atomP = salt->s.selections[--salt->s.atom_count];
1172
1173		while (salt->s.atom_count
1174		       && salt->s.selections[salt->s.atom_count-1] == 0)
1175		    salt->s.atom_count--;
1176	    }
1177	if (salt->s.atom_count == 0) {
1178#ifndef OLDXAW
1179	    if (contents == NULL) {
1180		XawTextKillRing *kill_ring = XtNew(XawTextKillRing);
1181
1182		kill_ring->next = xaw_text_kill_ring;
1183		kill_ring->contents = salt->contents;
1184		kill_ring->length = salt->length;
1185		kill_ring->format = XawFmt8Bit;
1186		xaw_text_kill_ring = kill_ring;
1187		kill_ring_prev.next = xaw_text_kill_ring;
1188
1189		if (++num_kill_rings > MAX_KILL_RINGS) {
1190		    XawTextKillRing *tail = NULL;
1191
1192		    while (kill_ring->next) {
1193			tail = kill_ring;
1194			kill_ring = kill_ring->next;
1195		    }
1196		    if (kill_ring->refcount == 0) {
1197			--num_kill_rings;
1198			tail->next = NULL;
1199			XtFree(kill_ring->contents);
1200			XtFree((char*)kill_ring);
1201		    }
1202		}
1203	    }
1204	    else {
1205		*contents = salt->contents;
1206		*length = salt->length;
1207	    }
1208#endif
1209	    if (prevSalt)
1210		prevSalt->next = nextSalt;
1211	    else
1212		ctx->text.salt2 = nextSalt;
1213
1214	    XtFree((char *)salt->s.selections);
1215	    XtFree((char *)salt);
1216	}
1217	else
1218	    prevSalt = salt;
1219    }
1220}
1221
1222static void
1223_DeleteOrKill(TextWidget ctx, XawTextPosition from, XawTextPosition to,
1224	      Bool kill)
1225{
1226    XawTextBlock text;
1227
1228#ifndef OLDXAW
1229    if (ctx->text.kill_ring_ptr) {
1230	--ctx->text.kill_ring_ptr->refcount;
1231	ctx->text.kill_ring_ptr = NULL;
1232    }
1233#endif
1234    if (kill && from < to) {
1235#ifndef OLDXAW
1236	Bool append = False;
1237	char *ring = NULL;
1238	XawTextPosition old_from = from;
1239#endif
1240	char *string;
1241	int size = 0, length;
1242	XawTextSelectionSalt *salt;
1243	Atom selection = XInternAtom(XtDisplay(ctx), "SECONDARY", False);
1244
1245#ifndef OLDXAW
1246	if (ctx->text.kill_ring == KILL_RING_APPEND) {
1247	    old_from = ctx->text.salt2->s.left;
1248	    append = True;
1249	}
1250	else
1251	    ctx->text.kill_ring = KILL_RING_BEGIN;
1252
1253	if (append)
1254	    _LoseSelection((Widget)ctx, &selection, &ring, &size);
1255	else
1256#endif
1257	    LoseSelection((Widget)ctx, &selection);
1258
1259	salt = (XawTextSelectionSalt*)XtMalloc(sizeof(XawTextSelectionSalt));
1260	salt->s.selections = (Atom *)XtMalloc(sizeof(Atom));
1261	salt->s.left = from;
1262	salt->s.right = to;
1263
1264	string = (char *)_XawTextGetSTRING(ctx, from, to);
1265
1266	if (XawTextFormat(ctx, XawFmtWide)) {
1267	    XTextProperty textprop;
1268
1269	    if (XwcTextListToTextProperty(XtDisplay((Widget)ctx),
1270					  (wchar_t**)(&string),
1271					  1, XCompoundTextStyle,
1272					  &textprop) <  Success) {
1273		XtFree(string);
1274		XtFree((char*)salt->s.selections);
1275		XtFree((char*)salt);
1276		return;
1277	    }
1278	    XtFree(string);
1279	    string = (char *)textprop.value;
1280	    length = textprop.nitems;
1281	}
1282	else
1283	    length = strlen(string);
1284
1285	salt->length = length + size;
1286
1287#ifndef OLDXAW
1288	if (!append)
1289	    salt->contents = string;
1290	else {
1291	    salt->contents = XtMalloc(length + size + 1);
1292	    if (from >= old_from) {
1293		strncpy(salt->contents, ring, size);
1294		salt->contents[size] = '\0';
1295		strncat(salt->contents, string, length);
1296	    }
1297	    else {
1298		strncpy(salt->contents, string, length);
1299		salt->contents[length] = '\0';
1300		strncat(salt->contents, ring, size);
1301	    }
1302	    salt->contents[length + size] = '\0';
1303	    XtFree(ring);
1304	    XtFree(string);
1305	}
1306
1307	kill_ring_prev.contents = salt->contents;
1308	kill_ring_prev.length = salt->length;
1309	kill_ring_prev.format = XawFmt8Bit;
1310#else
1311	salt->contents = string;
1312#endif
1313
1314	salt->next = ctx->text.salt2;
1315	ctx->text.salt2 = salt;
1316
1317#ifndef OLDXAW
1318	if (append)
1319	    ctx->text.kill_ring = KILL_RING_BEGIN;
1320#endif
1321
1322	salt->s.selections[0] = selection;
1323
1324	XtOwnSelection((Widget)ctx, selection, ctx->text.time,
1325		       ConvertSelection, LoseSelection, NULL);
1326	salt->s.atom_count = 1;
1327    }
1328    text.length = 0;
1329    text.firstPos = 0;
1330
1331    text.format = _XawTextFormat(ctx);
1332    text.ptr = "";
1333
1334    if (_XawTextReplace(ctx, from, to, &text)) {
1335	XBell(XtDisplay(ctx), 50);
1336	return;
1337    }
1338    ctx->text.from_left = -1;
1339    ctx->text.insertPos = from;
1340    ctx->text.showposition = TRUE;
1341}
1342
1343static void
1344DeleteOrKill(TextWidget ctx, XEvent *event, XawTextScanDirection dir,
1345	     XawTextScanType type, Bool include, Bool kill)
1346{
1347    XawTextPosition from, to;
1348    short mult = MULT(ctx);
1349
1350    if (mult < 0) {
1351	mult = -mult;
1352	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
1353    }
1354
1355    StartAction(ctx, event);
1356#ifndef OLDXAW
1357    if (mult == 1)
1358	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
1359#endif
1360    to = SrcScan(ctx->text.source, ctx->text.insertPos,
1361		 type, dir, mult, include);
1362
1363    /*
1364     * If no movement actually happened, then bump the count and try again.
1365     * This causes the character position at the very beginning and end of
1366     * a boundary to act correctly
1367     */
1368    if (to == ctx->text.insertPos)
1369	to = SrcScan(ctx->text.source, ctx->text.insertPos,
1370		     type, dir, mult + 1, include);
1371
1372    if (dir == XawsdLeft) {
1373	from = to;
1374	to = ctx->text.insertPos;
1375    }
1376    else
1377	from = ctx->text.insertPos;
1378
1379    _DeleteOrKill(ctx, from, to, kill);
1380    EndAction(ctx);
1381}
1382
1383static void
1384Delete(Widget w, XEvent *event, String *p, Cardinal *n)
1385{
1386    TextWidget ctx = (TextWidget)w;
1387
1388    if (ctx->text.s.left != ctx->text.s.right)
1389	DeleteCurrentSelection(w, event, p, n);
1390    else
1391	DeleteBackwardChar(w, event, p, n);
1392}
1393
1394static void
1395DeleteChar(Widget w, XEvent *event, XawTextScanDirection dir)
1396{
1397    TextWidget ctx = (TextWidget)w;
1398    short mul = MULT(ctx);
1399
1400    if (mul < 0) {
1401	ctx->text.mult = mul = -mul;
1402	dir = dir == XawsdLeft ? XawsdRight : XawsdLeft;
1403    }
1404    DeleteOrKill(ctx, event, dir, XawstPositions, True, False);
1405#ifndef OLDXAW
1406    if (mul == 1)
1407	_XawSourceSetUndoErase((TextSrcObject)ctx->text.source,
1408			       dir == XawsdLeft ? -1 : 1);
1409#endif
1410}
1411
1412/*ARGSUSED*/
1413static void
1414DeleteForwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
1415{
1416    DeleteChar(w, event, XawsdRight);
1417}
1418
1419/*ARGSUSED*/
1420static void
1421DeleteBackwardChar(Widget w, XEvent *event, String *p, Cardinal *n)
1422{
1423    DeleteChar(w, event, XawsdLeft);
1424}
1425
1426static void
1427DeleteForwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
1428{
1429    XawTextScanType type;
1430
1431    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
1432	type = XawstAlphaNumeric;
1433    else
1434	type = XawstWhiteSpace;
1435
1436    DeleteOrKill((TextWidget)w, event, XawsdRight, type, False, False);
1437}
1438
1439static void
1440DeleteBackwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
1441{
1442    XawTextScanType type;
1443
1444    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
1445	type = XawstAlphaNumeric;
1446    else
1447	type = XawstWhiteSpace;
1448
1449    DeleteOrKill((TextWidget)w, event, XawsdLeft, type, False, False);
1450}
1451
1452static void
1453KillForwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
1454{
1455    XawTextScanType type;
1456
1457    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
1458	type = XawstAlphaNumeric;
1459    else
1460	type = XawstWhiteSpace;
1461
1462    DeleteOrKill((TextWidget)w, event, XawsdRight, type, False, True);
1463}
1464
1465static void
1466KillBackwardWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
1467{
1468    XawTextScanType type;
1469
1470    if (*num_params && (*params[0] == 'A' || *params[0] == 'a'))
1471	type = XawstAlphaNumeric;
1472    else
1473	type = XawstWhiteSpace;
1474
1475    DeleteOrKill((TextWidget) w, event, XawsdLeft, type, False, True);
1476}
1477
1478/*ARGSUSED*/
1479static void
1480KillToEndOfLine(Widget w, XEvent *event, String *p, Cardinal *n)
1481{
1482    TextWidget ctx = (TextWidget)w;
1483    XawTextPosition end_of_line;
1484    XawTextScanDirection dir = XawsdRight;
1485    short mult = MULT(ctx);
1486
1487    if (mult < 0) {
1488	dir = XawsdLeft;
1489	mult = -mult;
1490    }
1491
1492    StartAction(ctx, event);
1493    end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
1494			  dir, mult, False);
1495    if (end_of_line == ctx->text.insertPos)
1496	end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
1497			      dir, mult, True);
1498
1499    if (dir == XawsdRight)
1500	_DeleteOrKill(ctx, ctx->text.insertPos, end_of_line, True);
1501    else
1502	_DeleteOrKill(ctx, end_of_line, ctx->text.insertPos, True);
1503    EndAction(ctx);
1504}
1505
1506/*ARGSUSED*/
1507static void
1508KillToEndOfParagraph(Widget w, XEvent *event, String *p, Cardinal *n)
1509{
1510    DeleteOrKill((TextWidget)w, event, XawsdRight, XawstParagraph, False, True);
1511}
1512
1513void
1514_XawTextZapSelection(TextWidget ctx, XEvent *event, Bool kill)
1515{
1516    StartAction(ctx, event);
1517    _DeleteOrKill(ctx, ctx->text.s.left, ctx->text.s.right, kill);
1518    EndAction(ctx);
1519}
1520
1521/*ARGSUSED*/
1522static void
1523KillCurrentSelection(Widget w, XEvent *event, String *p, Cardinal *n)
1524{
1525    _XawTextZapSelection((TextWidget) w, event, True);
1526}
1527
1528#ifndef OLDXAW
1529/*ARGSUSED*/
1530static void
1531KillRingYank(Widget w, XEvent *event, String *params, Cardinal *num_params)
1532{
1533    TextWidget ctx = (TextWidget)w;
1534    XawTextPosition insertPos = ctx->text.insertPos;
1535    Bool first_yank = False;
1536
1537    if (ctx->text.s.left != ctx->text.s.right)
1538	XawTextUnsetSelection((Widget)ctx);
1539
1540    StartAction(ctx, event);
1541
1542    if (ctx->text.kill_ring_ptr == NULL) {
1543	ctx->text.kill_ring_ptr = &kill_ring_prev;
1544	++ctx->text.kill_ring_ptr->refcount;
1545	ctx->text.s.left = ctx->text.s.right = insertPos;
1546	first_yank = True;
1547    }
1548    if (ctx->text.kill_ring_ptr) {
1549	int mul = MULT(ctx);
1550	XawTextBlock text;
1551
1552	if (!first_yank) {
1553	    if (mul < 0)
1554		mul = 1;
1555	    --ctx->text.kill_ring_ptr->refcount;
1556	    while (mul--) {
1557		if ((ctx->text.kill_ring_ptr = ctx->text.kill_ring_ptr->next) == NULL)
1558		    ctx->text.kill_ring_ptr = &kill_ring_null;
1559	    }
1560	    ++ctx->text.kill_ring_ptr->refcount;
1561	}
1562	text.firstPos = 0;
1563	text.length = ctx->text.kill_ring_ptr->length;
1564	text.ptr = ctx->text.kill_ring_ptr->contents;
1565	text.format = ctx->text.kill_ring_ptr->format;
1566
1567	if (_XawTextReplace(ctx, ctx->text.s.left, insertPos, &text) == XawEditDone) {
1568	    ctx->text.kill_ring = KILL_RING_YANK;
1569	    ctx->text.insertPos = ctx->text.s.left + text.length;
1570	}
1571    }
1572    else
1573	XBell(XtDisplay(w), 0);
1574
1575    EndAction(ctx);
1576}
1577#endif /* OLDXAW */
1578
1579/*ARGSUSED*/
1580static void
1581DeleteCurrentSelection(Widget w, XEvent *event, String *p, Cardinal *n)
1582{
1583    _XawTextZapSelection((TextWidget)w, event, False);
1584}
1585
1586#ifndef OLDXAW
1587#define CHECK_SAVE()						\
1588	if (save && !save->ptr)					\
1589	    save->ptr = _XawTextGetText(ctx, save->firstPos,	\
1590		save->firstPos + save->length)
1591static Bool
1592StripSpaces(TextWidget ctx, XawTextPosition left, XawTextPosition right,
1593	    XawTextPosition *pos, int num_pos, XawTextBlock *save)
1594{
1595    Bool done, space;
1596    int i, cpos, count = 0;
1597    XawTextBlock block, text;
1598    XawTextPosition ipos, position = left, tmp = left;
1599
1600    text.firstPos = 0;
1601    text.format = XawFmt8Bit;
1602    text.ptr = " ";
1603    text.length = 1;
1604
1605    position = XawTextSourceRead(ctx->text.source, position,
1606				 &block, right - left);
1607    done = False;
1608    space = False;
1609    /* convert tabs and returns to spaces */
1610    while (!done) {
1611	if (XawTextFormat(ctx, XawFmt8Bit)) {
1612	    for (i = 0; i < block.length; i++)
1613		if (block.ptr[i] == '\t' || block.ptr[i] == '\n') {
1614		    space = True;
1615		    break;
1616		}
1617	}
1618	else {
1619	    wchar_t *wptr = (wchar_t*)block.ptr;
1620	    for (i = 0; i < block.length; i++)
1621		if (wptr[i] == _Xaw_atowc('\t') || wptr[i] == _Xaw_atowc('\n')) {
1622		    space = True;
1623		    break;
1624		}
1625	}
1626	if (space) {
1627	    CHECK_SAVE();
1628	    if (_XawTextReplace(ctx, tmp + i, tmp + i + 1, &text))
1629		return (False);
1630	    space = False;
1631	}
1632	tmp += i;
1633	position = XawTextSourceRead(ctx->text.source, tmp,
1634				     &block, right - tmp);
1635	if (block.length == 0 || tmp == position || tmp >= right)
1636	    done = True;
1637    }
1638
1639    text.ptr = "";
1640    text.length = 0;
1641    position = tmp = left;
1642    position = XawTextSourceRead(ctx->text.source, position,
1643				 &block, right - left);
1644    ipos = ctx->text.insertPos;
1645    done = False;
1646    while (!done) {
1647	if (XawTextFormat(ctx, XawFmt8Bit)) {
1648	    for (i = 0; i < block.length; i++)
1649		if (block.ptr[i] == ' ')
1650		    ++count;
1651		else if (count == 1)
1652		    count = 0;
1653		else if (count)
1654		    break;
1655	}
1656	else {
1657	    wchar_t *wptr = (wchar_t*)block.ptr;
1658	    for (i = 0; i < block.length; i++)
1659		if (wptr[i] == _Xaw_atowc(' '))
1660		    ++count;
1661		else if (count == 1)
1662		    count = 0;
1663		else if (count)
1664		    break;
1665	}
1666	if (--count > 0) {
1667	    CHECK_SAVE();
1668	    if (_XawTextReplace(ctx, tmp + i - count, tmp + i, &text))
1669		return (False);
1670	    right -= count;
1671	    if (num_pos) {
1672		for (cpos = 0; cpos < num_pos; cpos++) {
1673		    if (tmp + i - count < pos[cpos]) {
1674			if (tmp + i < pos[cpos])
1675			    pos[cpos] -= count;
1676			else
1677			    pos[cpos] = tmp + i - count;
1678		    }
1679		}
1680	    }
1681	    else {
1682		if (tmp + i - count < ipos) {
1683		    if (tmp + i < ipos)
1684			ipos -= count;
1685		    else
1686			ipos = tmp + i - count;
1687		}
1688	    }
1689	    tmp += i - count;
1690	}
1691	else
1692	    tmp += i + 1;
1693	count = 0;
1694	position = XawTextSourceRead(ctx->text.source, tmp,
1695				     &block, right - tmp);
1696	if (block.length == 0 || tmp == position || tmp >= right)
1697	    done = True;
1698    }
1699    if (!num_pos)
1700	ctx->text.insertPos = ipos;
1701
1702    return (True);
1703}
1704
1705static Bool
1706Tabify(TextWidget ctx, XawTextPosition left, XawTextPosition right,
1707       XawTextPosition *pos, int num_pos, XawTextBlock *save)
1708{
1709    Bool done, zero;
1710    int i, cpos, count = 0, column = 0, offset = 0;
1711    XawTextBlock text, block;
1712    XawTextPosition ipos, position = left, tmp = left;
1713    TextSinkObject sink = (TextSinkObject)ctx->text.sink;
1714    short *char_tabs = sink->text_sink.char_tabs;
1715    int tab_count = sink->text_sink.tab_count;
1716    int tab_index = 0, tab_column = 0, TAB_SIZE = DEFAULT_TAB_SIZE;
1717
1718    text.firstPos = 0;
1719    text.ptr = "\t";
1720    text.format = XawFmt8Bit;
1721    text.length = 1;
1722
1723    position = XawTextSourceRead(ctx->text.source, position,
1724				 &block, right - left);
1725    ipos = ctx->text.insertPos;
1726    done = zero = False;
1727    if (tab_count)
1728	TAB_SIZE = *char_tabs;
1729    while (!done) {
1730	if (XawTextFormat(ctx, XawFmt8Bit)) {
1731	    for (i = 0; i < block.length; i++) {
1732		++offset;
1733		++column;
1734		if (tab_count) {
1735		    if (column > tab_column + char_tabs[tab_index]) {
1736			TAB_SIZE = tab_index < tab_count - 1 ? char_tabs[tab_index + 1] - char_tabs[tab_index] : *char_tabs;
1737			if (++tab_index >= tab_count) {
1738			    tab_column += char_tabs[tab_count - 1];
1739			    tab_index = 0;
1740			}
1741		    }
1742		}
1743		if (block.ptr[i] == ' ') {
1744		    if (++count > TAB_SIZE)
1745			count %= TAB_SIZE;
1746		    if ((tab_count && column == tab_column + char_tabs[tab_index]) ||
1747			(!tab_count && column % TAB_SIZE == 0)) {
1748			if (count % (TAB_SIZE + 1) > 1)
1749			    break;
1750			else
1751			    count = 0;
1752		    }
1753		}
1754		else {
1755		    if (block.ptr[i] == '\n') {
1756			zero = True;
1757			break;
1758		    }
1759		    count = 0;
1760		}
1761	    }
1762	}
1763	else {
1764	    wchar_t *wptr = (wchar_t*)block.ptr;
1765	    for (i = 0; i < block.length; i++) {
1766		++offset;
1767		++column;
1768		if (tab_count) {
1769		    if (column > tab_column + char_tabs[tab_index]) {
1770			TAB_SIZE = tab_index < tab_count - 1 ? char_tabs[tab_index + 1] - char_tabs[tab_index] : *char_tabs;
1771			if (++tab_index >= tab_count) {
1772			    tab_column += char_tabs[tab_count - 1];
1773			    tab_index = 0;
1774			}
1775		    }
1776		}
1777		if (wptr[i] == _Xaw_atowc(' ')) {
1778		    if (++count > TAB_SIZE)
1779			count %= TAB_SIZE;
1780		    if ((tab_count && column == tab_column + char_tabs[tab_index]) ||
1781			(!tab_count && column % TAB_SIZE == 0)) {
1782			if (count % (TAB_SIZE + 1) > 1)
1783			    break;
1784			else
1785			    count = 0;
1786		    }
1787		}
1788		else {
1789		    if (wptr[i] == _Xaw_atowc('\n')) {
1790			zero = True;
1791			break;
1792		    }
1793		    count = 0;
1794		}
1795	    }
1796	}
1797	count %= TAB_SIZE + 1;
1798	if (!zero && count > 1 && i < block.length) {
1799	    CHECK_SAVE();
1800	    if (_XawTextReplace(ctx, tmp + i - count + 1, tmp + i + 1, &text))
1801		return (False);
1802	    right -= count - 1;
1803	    offset -= count - 1;
1804	    if (num_pos) {
1805		for (cpos = 0; cpos < num_pos; cpos++) {
1806		    if (tmp + i - count + 1 < pos[cpos]) {
1807			if (tmp + i + 1 < pos[cpos])
1808			    pos[cpos] -= count;
1809			else
1810			    pos[cpos] = tmp + i - count + 1;
1811			++pos[cpos];
1812		    }
1813		}
1814	    }
1815	    else {
1816		if (tmp + i - count + 1 < ipos) {
1817		    if (tmp + i + 1 < ipos)
1818			ipos -= count;
1819		    else
1820			ipos = tmp + i - count + 1;
1821		    ++ipos;
1822		}
1823	    }
1824	}
1825	if (count)
1826	    --count;
1827	if (zero) {
1828	    count = column = 0;
1829	    zero = False;
1830	    if (tab_count) {
1831		tab_column = tab_index = 0;
1832		TAB_SIZE = *char_tabs;
1833	    }
1834	}
1835	else if (i < block.length)
1836	    count = 0;
1837	tmp = left + offset;
1838	position = XawTextSourceRead(ctx->text.source, tmp,
1839				     &block, right - tmp);
1840	if (tmp == position || tmp >= right)
1841	    done = True;
1842    }
1843    if (!num_pos)
1844	ctx->text.insertPos = ipos;
1845
1846    return (True);
1847}
1848
1849static Bool
1850Untabify(TextWidget ctx, XawTextPosition left, XawTextPosition right,
1851	 XawTextPosition *pos, int num_pos, XawTextBlock *save)
1852{
1853    Bool done, zero;
1854    int i, cpos, count = 0, diff = 0;
1855    XawTextBlock block, text;
1856    XawTextPosition ipos, position = left, tmp = left;
1857    TextSinkObject sink = (TextSinkObject)ctx->text.sink;
1858    short *char_tabs = sink->text_sink.char_tabs;
1859    int tab_count = sink->text_sink.tab_count;
1860    int tab_index = 0, tab_column = 0, tab_base = 0;
1861    static char *tabs = "        ";
1862
1863    text.firstPos = 0;
1864    text.format = XawFmt8Bit;
1865    text.ptr = tabs;
1866
1867    position = XawTextSourceRead(ctx->text.source, position,
1868				 &block, right - left);
1869    ipos = ctx->text.insertPos;
1870    done = False;
1871    zero = False;
1872    while (!done) {
1873	if (XawTextFormat(ctx, XawFmt8Bit))
1874	    for (i = 0; i < block.length; i++) {
1875		if (block.ptr[i] != '\t') {
1876		    ++count;
1877		    if (block.ptr[i] == '\n') {
1878			zero = True;
1879			break;
1880		    }
1881		}
1882		else
1883		    break;
1884	}
1885	else {
1886	    wchar_t *wptr = (wchar_t*)block.ptr;
1887	    for (i = 0; i < block.length; i++)
1888		if (wptr[i] != _Xaw_atowc('\t')) {
1889		    ++count;
1890		    if (wptr[i] != _Xaw_atowc('\n')) {
1891			zero = True;
1892			break;
1893		    }
1894		}
1895		else
1896		    break;
1897	}
1898	if (!zero && i < block.length) {
1899	    if (tab_count) {
1900		while (tab_base + tab_column <= count) {
1901		    for (; tab_index < tab_count; ++tab_index)
1902			if (tab_base + char_tabs[tab_index] > count) {
1903			    tab_column = char_tabs[tab_index];
1904			    break;
1905			}
1906		    if (tab_index >= tab_count) {
1907			tab_base += char_tabs[tab_count - 1];
1908			tab_column = tab_index = 0;
1909		    }
1910		}
1911		text.length = (tab_base + tab_column) - count;
1912		if (text.length > 8) {
1913		    int j;
1914
1915		    text.ptr = XtMalloc(text.length);
1916		    for (j = 0; j < text.length; j++)
1917			text.ptr[j] = ' ';
1918		}
1919		else
1920		    text.ptr = tabs;
1921	    }
1922	    else
1923		text.length = DEFAULT_TAB_SIZE - (count % DEFAULT_TAB_SIZE);
1924	    CHECK_SAVE();
1925	    if (_XawTextReplace(ctx, tmp + i, tmp + i + 1, &text)) {
1926		if (tab_count && text.length > 8)
1927		    XtFree(text.ptr);
1928		return (False);
1929	    }
1930	    if (tab_count && text.length > 8)
1931		XtFree(text.ptr);
1932	    count += text.length;
1933	    right += text.length - 1;
1934	    if (num_pos) {
1935		for (cpos = 0; cpos < num_pos; cpos++) {
1936		    if (tmp + i < pos[cpos]) {
1937			if (tmp + i + 1 < pos[cpos])
1938			    --pos[cpos];
1939			else
1940			    pos[cpos] = tmp + i;
1941			pos[cpos] += text.length;
1942		    }
1943		}
1944	    }
1945	    else {
1946		if (tmp + i < ipos) {
1947		    if (tmp + i + 1 < ipos)
1948			--ipos;
1949		    else
1950			ipos = tmp + i;
1951		    ipos += text.length;
1952		}
1953	    }
1954	}
1955	tmp = left + count + diff;
1956	if (zero) {
1957	    diff += count;
1958	    count = 0;
1959	    zero = False;
1960	    if (tab_count)
1961		tab_base = tab_column = tab_index = 0;
1962	}
1963	position = XawTextSourceRead(ctx->text.source, tmp,
1964				     &block, right - tmp);
1965	if (tmp == position || tmp >= right)
1966	    done = True;
1967    }
1968    if (!num_pos)
1969	ctx->text.insertPos = ipos;
1970
1971    return (True);
1972}
1973
1974static int
1975FormatText(TextWidget ctx, XawTextPosition left, Bool force,
1976	   XawTextPosition *pos, int num_pos)
1977{
1978    char *ptr = NULL;
1979    Bool freepos = False, undo, paragraph = pos != NULL;
1980    int i, result;
1981    XawTextBlock block, *text;
1982    XawTextPosition end = ctx->text.lastPos, buf[32];
1983    TextSrcObject src = (TextSrcObject)ctx->text.source;
1984    XawTextPosition right = SrcScan(ctx->text.source, left, XawstEOL,
1985				    XawsdRight, 1, False);
1986
1987    undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
1988    if (undo) {
1989	if (!pos) {
1990	    num_pos = src->textSrc.num_text;
1991	    pos = XawStackAlloc(sizeof(XawTextPosition) * num_pos, buf);
1992	    for (i = 0; i < num_pos; i++)
1993		pos[i] = ((TextWidget)src->textSrc.text[i])->text.insertPos;
1994	    freepos = True;
1995	}
1996	else
1997	    freepos = False;
1998	src->textSrc.undo_state = True;
1999	block.ptr = NULL;
2000	block.firstPos = left;
2001	block.length = right - left;
2002	text = &block;
2003    }
2004    else
2005	text = NULL;
2006
2007    result = DoFormatText(ctx, left, force, 1, text, pos, num_pos, paragraph);
2008    if (undo && result == XawEditDone && block.ptr) {
2009	char *lbuf, *rbuf;
2010	unsigned llen, rlen, size;
2011
2012	ptr = lbuf = block.ptr;
2013	llen = block.length;
2014	rlen = llen + (ctx->text.lastPos - end);
2015
2016	block.firstPos = 0;
2017	block.format = _XawTextFormat(ctx);
2018
2019	rbuf = _XawTextGetText(ctx, left, left + rlen);
2020
2021	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
2022	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
2023	    block.ptr = lbuf;
2024	    block.length = llen;
2025	    _XawTextReplace(ctx, left, left + rlen, &block);
2026
2027	    src->textSrc.undo_state = False;
2028	    block.ptr = rbuf;
2029	    block.length = rlen;
2030	    _XawTextReplace(ctx, left, left + llen, &block);
2031	}
2032	else
2033	    src->textSrc.undo_state = False;
2034	XtFree(rbuf);
2035    }
2036    if (undo) {
2037	src->textSrc.undo_state = False;
2038	if (freepos) {
2039	    for (i = 0; i < num_pos; i++) {
2040		TextWidget tw = (TextWidget)src->textSrc.text[i];
2041		tw->text.insertPos = XawMin(XawMax(0, pos[i]), tw->text.lastPos);
2042	    }
2043	    XawStackFree(pos, buf);
2044	}
2045	if (ptr)
2046	    XtFree(ptr);
2047    }
2048
2049    return (result);
2050}
2051
2052static int
2053DoFormatText(TextWidget ctx, XawTextPosition left, Bool force, int level,
2054	     XawTextBlock *save, XawTextPosition *pos, int num_pos,
2055	     Bool paragraph)
2056{
2057    XawTextPosition right = SrcScan(ctx->text.source, left, XawstEOL,
2058				    XawsdRight, 1, False);
2059    XawTextPosition position, tmp, ipos;
2060    XawTextBlock block, text;
2061    char buf[128];
2062    wchar_t *wptr;
2063    int i, count, cpos;
2064    Bool done, force2 = force, recurse = False;
2065
2066    position = XawTextSourceRead(ctx->text.source, left, &block, right - left);
2067    if (block.length == 0 || left >= right ||
2068	(level == 1 && ((XawTextFormat(ctx, XawFmt8Bit) &&
2069	 block.ptr[0] != ' ' &&
2070	 block.ptr[0] != '\t' &&
2071	 !isalnum(*(unsigned char*)block.ptr)) ||
2072	(XawTextFormat(ctx, XawFmtWide) &&
2073	 _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
2074	 _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
2075	 !iswalnum(*(wchar_t*)block.ptr)))))
2076	return (XawEditDone);
2077
2078    if (level == 1 && !paragraph) {
2079	tmp = ctx->text.lastPos;
2080	if (Untabify(ctx, left, right, pos, num_pos, save) == False)
2081	    return (XawEditError);
2082	right += ctx->text.lastPos - tmp;
2083	position = XawTextSourceRead(ctx->text.source, left, &block,
2084				     right - left);
2085    }
2086
2087    text.firstPos = 0;
2088    text.format = XawFmt8Bit;
2089
2090    ipos = ctx->text.insertPos;
2091    count = 0;
2092    done = False;
2093    while (!done) {
2094	if (XawTextFormat(ctx, XawFmt8Bit)) {
2095	    for (i = 0; i < block.length; i++)
2096		if (block.ptr[i] == ' ')
2097		    ++count;
2098		else {
2099		    done = True;
2100		    break;
2101		}
2102	}
2103	else {
2104	    wptr = (wchar_t*)block.ptr;
2105	    for (i = 0; i < block.length; i++)
2106		if (wptr[i] == _Xaw_atowc(' '))
2107		    ++count;
2108		else {
2109		    done = True;
2110		    break;
2111		}
2112	}
2113	tmp = position;
2114	position = XawTextSourceRead(ctx->text.source, position,
2115				     &block, right - position);
2116	if (tmp == position)
2117	    done = True;
2118    }
2119    position = left + count;
2120    if (count < ctx->text.left_column) {
2121	int bytes = ctx->text.left_column - count;
2122
2123	text.ptr = XawStackAlloc(bytes, buf);
2124	text.length = bytes;
2125	for (i = 0; i < bytes; i++)
2126	    text.ptr[i] = ' ';
2127	CHECK_SAVE();
2128	if (_XawTextReplace(ctx, left, left, &text)) {
2129	    XawStackFree(text.ptr, buf);
2130	    return (XawEditError);
2131	}
2132	XawStackFree(text.ptr, buf);
2133	right += bytes;
2134	if (num_pos) {
2135	    for (cpos = 0; cpos < num_pos; cpos++)
2136		if (pos[cpos] >= left)
2137		    pos[cpos] += bytes;
2138	}
2139	if (ipos >= left)
2140	    ipos += bytes;
2141	count += bytes;
2142    }
2143
2144    done = False;
2145    if (!paragraph && level == 1
2146	&& ipos <= right && ipos - left > ctx->text.right_column) {
2147	XawTextPosition len = ctx->text.lastPos;
2148	int skip = ctx->text.justify == XawjustifyRight
2149		|| ctx->text.justify == XawjustifyCenter ?
2150		ctx->text.left_column : count;
2151
2152	if (pos)
2153	    for (i = 0; i < num_pos; i++)
2154		if (pos[i] == ipos)
2155		    break;
2156
2157	StripSpaces(ctx, left + skip, right, pos, num_pos, save);
2158	right += ctx->text.lastPos - len;
2159	if (pos && i < num_pos)
2160	    ipos = pos[i];
2161	else
2162	    ipos = ctx->text.insertPos;
2163	done = ipos - left > ctx->text.right_column;
2164	count = skip + (count == skip + 1);
2165    }
2166    if ((paragraph || done) && right - left > ctx->text.right_column) {
2167	position = tmp = right;
2168	XawTextSourceRead(ctx->text.source, position - 1, &block, 1);
2169	if (block.length &&
2170	    ((XawTextFormat(ctx, XawFmt8Bit) &&
2171	     block.ptr[0] == ' ') ||
2172	    (XawTextFormat(ctx, XawFmtWide) &&
2173	     _Xaw_atowc(XawSP) == *(wchar_t*)block.ptr)))
2174	    --position;
2175	while (position - left > ctx->text.right_column) {
2176	    tmp = position;
2177	    position = SrcScan(ctx->text.source, position,
2178			       XawstWhiteSpace, XawsdLeft, 1, True);
2179	}
2180	if (position <= left + ctx->text.left_column)
2181	    position = tmp;
2182	if (position > left && position - left > ctx->text.left_column
2183	    && position != right) {
2184	    text.ptr = "\n";
2185	    text.length = 1;
2186	    CHECK_SAVE();
2187	    if (_XawTextReplace(ctx, position, position + 1, &text))
2188		return (XawEditError);
2189	    right = position;
2190	    recurse = True;
2191	    force = True;
2192	}
2193    }
2194
2195    if (force) {
2196	if (ctx->text.justify == XawjustifyCenter)
2197	    count = ctx->text.right_column - (count - ctx->text.left_column);
2198	else
2199	    count = ctx->text.right_column;
2200	if (count > right - left)
2201	    count -= right - left;
2202	else
2203	    count = 0;
2204    }
2205    else
2206	count = 0;
2207    if (count > 0) {
2208	switch (ctx->text.justify) {
2209	    case XawjustifyLeft:
2210		break;
2211	    case XawjustifyRight:
2212	    case XawjustifyCenter:
2213		if (ctx->text.justify == XawjustifyCenter) {
2214		    int alnum = 0;
2215
2216		    if (!(count & 1)) {
2217			XawTextSourceRead(ctx->text.source, right, &block, 1);
2218			if ((XawTextFormat(ctx, XawFmt8Bit)
2219			     && isalnum(*(unsigned char*)block.ptr)) ||
2220			    (XawTextFormat(ctx, XawFmtWide)
2221			     && iswalnum(*(wchar_t*)block.ptr)))
2222			    alnum = 1;
2223		    }
2224		    count = (count + alnum) >> 1;
2225		}
2226		text.ptr = XawStackAlloc(count, buf);
2227		text.length = count;
2228		for (i = 0; i < count; i++)
2229		    text.ptr[i] = ' ';
2230		CHECK_SAVE();
2231		if (_XawTextReplace(ctx, left, left, &text)) {
2232		    XawStackFree(text.ptr, buf);
2233		    return (XawEditError);
2234		}
2235		XawStackFree(text.ptr, buf);
2236		position += count;
2237		right += count;
2238		if (num_pos) {
2239		    for (cpos = 0; cpos < num_pos; cpos++)
2240			if (pos[cpos] > left)
2241			    pos[cpos] += count;
2242		}
2243		else if (ipos > left)
2244		    ipos += count;
2245		break;
2246	    case XawjustifyFull:
2247		i = 0;
2248		tmp = left;
2249		/*CONSTCOND*/
2250		while (True) {
2251		    tmp = SrcScan(ctx->text.source, tmp, XawstWhiteSpace,
2252				  XawsdRight, 1, True);
2253		    if (tmp < right)
2254			++i;
2255		    else
2256			break;
2257		}
2258		if (i) {
2259		    double inc, ii;
2260		    int bytes, steps;
2261
2262		    bytes = count;
2263		    inc = ii = (count + .5) / (double)i;
2264
2265		    steps = count;
2266		    text.ptr = XawStackAlloc(steps, buf);
2267		    for (i = 0; i < steps; i++)
2268			text.ptr[i] = ' ';
2269		    tmp = left;
2270		    CHECK_SAVE();
2271		    while (bytes) {
2272			steps = 1;
2273			while (inc + ii < 1) {
2274			    ++steps;
2275			    inc += ii;
2276			}
2277			tmp = SrcScan(ctx->text.source, tmp, XawstWhiteSpace,
2278				      XawsdRight, steps, True);
2279			if (bytes > inc)
2280			    text.length = (int)inc;
2281			else
2282			    text.length = bytes;
2283			bytes -= text.length;
2284			if (_XawTextReplace(ctx, tmp, tmp, &text)) {
2285			    XawStackFree(text.ptr, buf);
2286			    return (XawEditError);
2287			}
2288			if (num_pos) {
2289			    for (cpos = 0; cpos < num_pos; cpos++)
2290				if (tmp <= pos[cpos])
2291				    pos[cpos] += text.length;
2292			}
2293			else if (tmp <= ipos)
2294			    ipos += text.length;
2295			inc -= (int)inc;
2296			inc += ii;
2297		    }
2298		    position += count;
2299		    right += count;
2300		    XawStackFree(text.ptr, buf);
2301		}
2302		break;
2303	}
2304    }
2305
2306    if (!num_pos)
2307	ctx->text.insertPos = XawMin(ipos, ctx->text.lastPos);
2308
2309    return (recurse ? DoFormatText(ctx, position + 1,
2310				   ctx->text.justify != XawjustifyFull
2311				   && (force2 || paragraph),
2312				   ++level, save, pos, num_pos, paragraph)
2313		 : XawEditDone);
2314}
2315#undef CHECK_SAVE
2316
2317/*ARGSUSED*/
2318static void
2319Indent(Widget w, XEvent *event, String *params, Cardinal *num_params)
2320{
2321    TextWidget ctx = (TextWidget)w;
2322    TextSrcObject src = (TextSrcObject)ctx->text.source;
2323    XawTextPosition from, to, tmp, end = 0, *pos, *posbuf[32];
2324    char buf[32];
2325    XawTextBlock text;
2326    int i, spaces = MULT(ctx);
2327    char *lbuf = NULL, *rbuf;
2328    unsigned llen = 0, rlen, size;
2329    Bool undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
2330    Bool format = ctx->text.auto_fill
2331	&& ctx->text.left_column < ctx->text.right_column;
2332
2333    text.firstPos = 0;
2334    text.format = XawFmt8Bit;
2335    text.ptr = "";
2336
2337    StartAction(ctx, event);
2338
2339    pos = XawStackAlloc(sizeof(XawTextPosition) * src->textSrc.num_text, posbuf);
2340    for (i = 0; i < src->textSrc.num_text; i++)
2341	pos[i] = ((TextWidget)src->textSrc.text[i])->text.insertPos;
2342
2343    if (!GetBlockBoundaries(ctx, &from, &to)) {
2344	EndAction(ctx);
2345	XawStackFree(pos, posbuf);
2346	return;
2347    }
2348
2349    if (undo) {
2350	llen = to - from;
2351	end = ctx->text.lastPos;
2352	lbuf = _XawTextGetText(ctx, from, to);
2353	src->textSrc.undo_state = True;
2354    }
2355
2356    tmp = ctx->text.lastPos;
2357    if (!Untabify(ctx, from, to, pos, src->textSrc.num_text, NULL)) {
2358	XBell(XtDisplay(ctx), 0);
2359	EndAction(ctx);
2360	XawStackFree(pos, posbuf);
2361	if (undo) {
2362	    src->textSrc.undo_state = True;
2363	    XtFree(lbuf);
2364	}
2365	return;
2366    }
2367    to += ctx->text.lastPos - tmp;
2368
2369    tmp = from;
2370
2371    if (spaces > 0) {
2372	text.ptr = XawStackAlloc(spaces, buf);
2373	for (i = 0; i < spaces; i++)
2374	    text.ptr[i] = ' ';
2375
2376	text.length = spaces;
2377	while (tmp < to) {
2378	    _XawTextReplace(ctx, tmp, tmp, &text);
2379
2380	    for (i = 0; i < src->textSrc.num_text; i++)
2381		if (tmp < pos[i])
2382		    pos[i] += spaces;
2383
2384	    to += spaces;
2385	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
2386	}
2387	XawStackFree(text.ptr, buf);
2388    }
2389    else {
2390	int min = 32767;
2391
2392	text.length = 0;
2393	tmp = from;
2394
2395	/* find the amount of spaces to cut */
2396	while (tmp < to) {
2397	    (void)BlankLine(w, tmp, &i);
2398	    if (i < min)
2399		min = i;
2400	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
2401	}
2402	spaces = XawMin(-spaces, min);
2403
2404	/* cut the spaces */
2405	tmp = from;
2406	while (tmp < to) {
2407	    _XawTextReplace(ctx, tmp, tmp + spaces, &text);
2408
2409	    for (i = 0; i < src->textSrc.num_text; i++)
2410		if (tmp < pos[i]) {
2411		    if (tmp + spaces < pos[i])
2412			pos[i] -= spaces;
2413		    else
2414			pos[i] = tmp;
2415		}
2416
2417	    to -= spaces;
2418	    tmp = SrcScan(ctx->text.source, tmp, XawstEOL, XawsdRight, 1, True);
2419	}
2420    }
2421
2422    if (!format)
2423	Tabify(ctx, from, to, pos, src->textSrc.num_text, NULL);
2424
2425    if (undo) {
2426	rlen = llen + (ctx->text.lastPos - end);
2427	rbuf = _XawTextGetText(ctx, from, from + rlen);
2428
2429	text.format = _XawTextFormat(ctx);
2430	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
2431	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
2432	    text.ptr = lbuf;
2433	    text.length = llen;
2434	    _XawTextReplace(ctx, from, from + rlen, &text);
2435
2436	    src->textSrc.undo_state = False;
2437	    text.ptr = rbuf;
2438	    text.length = rlen;
2439	    _XawTextReplace(ctx, from, from + llen, &text);
2440	}
2441	else
2442	    src->textSrc.undo_state = False;
2443	XtFree(lbuf);
2444	XtFree(rbuf);
2445    }
2446
2447    for (i = 0; i < src->textSrc.num_text; i++) {
2448	TextWidget tw = (TextWidget)src->textSrc.text[i];
2449
2450	tw->text.insertPos = XawMin(XawMax(0, pos[i]), tw->text.lastPos);
2451    }
2452    XawStackFree(pos, posbuf);
2453    ctx->text.showposition = True;
2454
2455    EndAction(ctx);
2456}
2457
2458/*ARGSUSED*/
2459static void
2460ToggleOverwrite(Widget w, XEvent *event, String *params, Cardinal *num_params)
2461{
2462    TextWidget ctx = (TextWidget)w;
2463
2464    ctx->text.overwrite = !ctx->text.overwrite;
2465
2466    /* call information callback */
2467    _XawTextSetLineAndColumnNumber(ctx, True);
2468}
2469#endif /* OLDXAW */
2470
2471/*
2472 * Insertion Routines
2473 */
2474static int
2475InsertNewLineAndBackupInternal(TextWidget ctx)
2476{
2477    int count, error = XawEditDone, mult = MULT(ctx);
2478#ifndef OLDXAW
2479    XawTextPosition position;
2480#endif
2481    XawTextBlock text;
2482    char buf[32];
2483
2484    if (mult < 0) {
2485	ctx->text.mult = 1;
2486	return (XawEditError);
2487    }
2488
2489    text.format = _XawTextFormat(ctx);
2490    text.length = mult;
2491    text.firstPos = 0;
2492
2493    if (text.format == XawFmtWide) {
2494	wchar_t *wptr;
2495
2496	text.ptr =  XawStackAlloc(sizeof(wchar_t) * mult, buf);
2497	wptr = (wchar_t *)text.ptr;
2498	for (count = 0; count < mult; count++)
2499	    wptr[count] = _Xaw_atowc(XawLF);
2500    }
2501    else {
2502	text.ptr = XawStackAlloc(sizeof(char) * mult, buf);
2503	for (count = 0; count < mult; count++)
2504	    text.ptr[count] = XawLF;
2505    }
2506
2507#ifndef OLDXAW
2508    position = SrcScan(ctx->text.source, ctx->text.insertPos,
2509		       XawstEOL, XawsdLeft, 1, False);
2510#endif
2511    if (_XawTextReplace(ctx, ctx->text.insertPos, ctx->text.insertPos, &text)) {
2512	XBell( XtDisplay(ctx), 50);
2513	error = XawEditError;
2514    }
2515    else {
2516	ctx->text.showposition = TRUE;
2517	ctx->text.insertPos += text.length;
2518    }
2519
2520    XawStackFree(text.ptr, buf);
2521
2522#ifndef OLDXAW
2523    if (ctx->text.auto_fill && error == XawEditDone)
2524	(void)FormatText(ctx, position, ctx->text.justify != XawjustifyFull,
2525			 NULL, 0);
2526#endif
2527
2528    return (error);
2529}
2530
2531/*ARGSUSED*/
2532static void
2533InsertNewLineAndBackup(Widget w, XEvent *event, String *p, Cardinal *n)
2534{
2535    TextWidget ctx = (TextWidget)w;
2536    XawTextPosition insertPos = ctx->text.insertPos;
2537
2538    StartAction((TextWidget)w, event);
2539    (void)InsertNewLineAndBackupInternal(ctx);
2540    ctx->text.insertPos = SrcScan(ctx->text.source, insertPos, XawstEOL,
2541				  XawsdRight, 1, False);
2542    EndAction((TextWidget)w);
2543}
2544
2545static int
2546LocalInsertNewLine(TextWidget ctx, XEvent *event)
2547{
2548    int error;
2549
2550    StartAction(ctx, event);
2551    error = InsertNewLineAndBackupInternal(ctx);
2552    ctx->text.from_left = -1;
2553    EndAction(ctx);
2554
2555    return (error);
2556}
2557
2558/*ARGSUSED*/
2559static void
2560InsertNewLine(Widget w, XEvent *event, String *p, Cardinal *n)
2561{
2562    (void)LocalInsertNewLine((TextWidget)w, event);
2563}
2564
2565/*ARGSUSED*/
2566static void
2567InsertNewLineAndIndent(Widget w, XEvent *event, String *p, Cardinal *n)
2568{
2569    XawTextBlock text;
2570    XawTextPosition pos1;
2571    int length;
2572    TextWidget ctx = (TextWidget)w;
2573    String line_to_ip;
2574
2575    StartAction(ctx, event);
2576    pos1 = SrcScan(ctx->text.source, ctx->text.insertPos,
2577		   XawstEOL, XawsdLeft, 1, False);
2578
2579    line_to_ip = _XawTextGetText(ctx, pos1, ctx->text.insertPos);
2580
2581    text.format = _XawTextFormat(ctx);
2582    text.firstPos = 0;
2583
2584    if (text.format == XawFmtWide) {
2585	wchar_t *ptr;
2586
2587	text.ptr = XtMalloc((2 + wcslen((wchar_t*)line_to_ip))
2588			    * sizeof(wchar_t));
2589	ptr = (wchar_t*)text.ptr;
2590	ptr[0] = _Xaw_atowc(XawLF);
2591	wcscpy((wchar_t*)++ptr, (wchar_t*)line_to_ip);
2592
2593	length = wcslen((wchar_t*)text.ptr);
2594	while (length && (iswspace(*ptr) || *ptr == _Xaw_atowc(XawTAB)))
2595	  ptr++, length--;
2596	*ptr = (wchar_t)0;
2597	text.length = wcslen((wchar_t*)text.ptr);
2598    }
2599    else {
2600	char *ptr;
2601
2602	length = strlen(line_to_ip);
2603	text.ptr = XtMalloc((2 + length) * sizeof(char));
2604	ptr = text.ptr;
2605	ptr[0] = XawLF;
2606	strcpy(++ptr, line_to_ip);
2607
2608	length++;
2609	while (length && (isspace(*ptr) || (*ptr == XawTAB)))
2610	    ptr++, length--;
2611	*ptr = '\0';
2612	text.length = strlen(text.ptr);
2613    }
2614    XtFree(line_to_ip);
2615
2616    if (_XawTextReplace(ctx,ctx->text.insertPos, ctx->text.insertPos, &text)) {
2617	XBell(XtDisplay(ctx), 50);
2618	XtFree(text.ptr);
2619	EndAction(ctx);
2620	return;
2621    }
2622
2623    XtFree(text.ptr);
2624    ctx->text.from_left = -1;
2625    ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
2626				  XawstPositions, XawsdRight, text.length, True);
2627    EndAction(ctx);
2628}
2629
2630/*
2631 * Selection Routines
2632 */
2633static void
2634SelectWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
2635{
2636    TextWidget ctx = (TextWidget)w;
2637    XawTextPosition l, r;
2638
2639    StartAction(ctx, event);
2640    l = SrcScan(ctx->text.source, ctx->text.insertPos,
2641		XawstWhiteSpace, XawsdLeft, 1, False);
2642    r = SrcScan(ctx->text.source, l, XawstWhiteSpace, XawsdRight, 1, False);
2643    _XawTextSetSelection(ctx, l, r, params, *num_params);
2644    EndAction(ctx);
2645}
2646
2647static void
2648SelectAll(Widget w, XEvent *event, String *params, Cardinal *num_params)
2649{
2650    TextWidget ctx = (TextWidget)w;
2651
2652    StartAction(ctx, event);
2653    _XawTextSetSelection(ctx,zeroPosition,ctx->text.lastPos,params,*num_params);
2654    EndAction(ctx);
2655}
2656
2657static void
2658ModifySelection(TextWidget ctx, XEvent *event,
2659		XawTextSelectionMode mode,
2660		XawTextSelectionAction action,
2661		String *params, Cardinal *num_params)
2662{
2663#ifndef OLDXAW
2664    int old_y = ctx->text.ev_y;
2665#endif
2666
2667    StartAction(ctx, event);
2668    NotePosition(ctx, event);
2669
2670#ifndef OLDXAW
2671    if (event->type == MotionNotify) {
2672	if (ctx->text.ev_y <= ctx->text.margin.top) {
2673	    if (old_y >= ctx->text.ev_y)
2674		XawTextScroll(ctx, -1, 0);
2675	}
2676	else if (ctx->text.ev_y >= XtHeight(ctx) - ctx->text.margin.bottom) {
2677	    if (old_y <= ctx->text.ev_y
2678		&& !IsPositionVisible(ctx, ctx->text.lastPos))
2679	      XawTextScroll(ctx, 1, 0);
2680	}
2681    }
2682#endif
2683    ctx->text.from_left = -1;
2684    _XawTextAlterSelection(ctx, mode, action, params, num_params);
2685
2686    EndAction(ctx);
2687}
2688
2689static void
2690SelectStart(Widget w, XEvent *event, String *params, Cardinal *num_params)
2691{
2692    TextWidget ctx = (TextWidget)w;
2693
2694#ifndef OLDXAW
2695    if (!ctx->text.selection_state) {
2696	ctx->text.selection_state = True;
2697#endif
2698	ModifySelection(ctx, event,
2699			XawsmTextSelect, XawactionStart, params, num_params);
2700#ifndef OLDXAW
2701    }
2702#endif
2703}
2704
2705static void
2706SelectAdjust(Widget w, XEvent *event, String *params, Cardinal *num_params)
2707{
2708    TextWidget ctx = (TextWidget)w;
2709
2710#ifndef OLDXAW
2711    if (ctx->text.selection_state)
2712#endif
2713	ModifySelection(ctx, event,
2714			XawsmTextSelect, XawactionAdjust, params, num_params);
2715}
2716
2717static void
2718SelectEnd(Widget w, XEvent *event, String *params, Cardinal *num_params)
2719{
2720    TextWidget ctx = (TextWidget)w;
2721
2722#ifndef OLDXAW
2723    if (ctx->text.selection_state) {
2724	ctx->text.selection_state = False;
2725#endif
2726	ModifySelection(ctx, event,
2727			XawsmTextSelect, XawactionEnd, params, num_params);
2728#ifndef OLDXAW
2729    }
2730#endif
2731}
2732
2733static void
2734ExtendStart(Widget w, XEvent *event, String *params, Cardinal *num_params)
2735{
2736    TextWidget ctx = (TextWidget)w;
2737
2738#ifndef OLDXAW
2739    if (!ctx->text.selection_state) {
2740	ctx->text.selection_state = True;
2741#endif
2742	ModifySelection(ctx, event,
2743			XawsmTextExtend, XawactionStart, params, num_params);
2744#ifndef OLDXAW
2745    }
2746#endif
2747}
2748
2749static void
2750ExtendAdjust(Widget w, XEvent *event, String *params, Cardinal *num_params)
2751{
2752    TextWidget ctx = (TextWidget)w;
2753
2754#ifndef OLDXAW
2755    if (ctx->text.selection_state)
2756#endif
2757	ModifySelection(ctx, event,
2758			XawsmTextExtend, XawactionAdjust, params, num_params);
2759}
2760
2761static void
2762ExtendEnd(Widget w, XEvent *event, String *params, Cardinal *num_params)
2763{
2764    TextWidget ctx = (TextWidget)w;
2765
2766#ifndef OLDXAW
2767    if (ctx->text.selection_state) {
2768	ctx->text.selection_state = False;
2769#endif
2770	ModifySelection(ctx, event,
2771			XawsmTextExtend, XawactionEnd, params, num_params);
2772#ifndef OLDXAW
2773    }
2774#endif
2775}
2776
2777static void
2778SelectSave(Widget  w, XEvent *event, String *params, Cardinal *num_params)
2779{
2780    int num_atoms;
2781    Atom *sel;
2782    Display *dpy = XtDisplay(w);
2783    Atom selections[256];
2784
2785    StartAction((TextWidget)w, event);
2786    num_atoms = *num_params;
2787    if (num_atoms > 256)
2788	num_atoms = 256;
2789    for (sel=selections; --num_atoms >= 0; sel++, params++)
2790	*sel = XInternAtom(dpy, *params, False);
2791    num_atoms = *num_params;
2792    _XawTextSaltAwaySelection((TextWidget)w, selections, num_atoms);
2793    EndAction((TextWidget)w);
2794}
2795
2796/*
2797 * Misc. Routines
2798 */
2799/*ARGSUSED*/
2800static void
2801SetKeyboardFocus(Widget w, XEvent *event, String *params, Cardinal *num_params)
2802{
2803    Widget shell, parent;
2804
2805    shell = parent = w;
2806    while (parent) {
2807	if (XtIsShell(shell = parent))
2808	    break;
2809	parent = XtParent(parent);
2810    }
2811    XtSetKeyboardFocus(shell, w);
2812}
2813
2814/*ARGSUSED*/
2815static void
2816RedrawDisplay(Widget w, XEvent *event, String *p, Cardinal *n)
2817{
2818    StartAction((TextWidget)w, event);
2819    _XawTextClearAndCenterDisplay((TextWidget)w);
2820    EndAction((TextWidget)w);
2821}
2822
2823/* This is kind of a hack, but, only one text widget can have focus at
2824 * a time on one display. There is a problem in the implementation of the
2825 * text widget, the scrollbars can not be adressed via editres, since they
2826 * are not children of a subclass of composite.
2827 * The focus variable is required to make sure only one text window will
2828 * show a block cursor at one time.
2829 */
2830struct _focus { Display *display; Widget widget; };
2831static struct _focus *focus;
2832static Cardinal num_focus;
2833
2834/*ARGSUSED*/
2835static void
2836DestroyFocusCallback(Widget w, XtPointer user_data, XtPointer call_data)
2837{
2838    struct _focus *f = (struct _focus*)(user_data);
2839
2840    if (f->widget == w)
2841	f->widget = NULL;
2842}
2843
2844/*ARGSUSED*/
2845static void
2846TextFocusIn(Widget w, XEvent *event, String *p, Cardinal *n)
2847{
2848    TextWidget ctx = (TextWidget)w;
2849    Bool display_caret = ctx->text.display_caret;
2850    int i;
2851
2852    if (event->xfocus.detail == NotifyPointer)
2853	return;
2854
2855    if (event->xfocus.send_event) {
2856	Window root, child;
2857	int rootx, rooty, x, y;
2858	unsigned int mask;
2859
2860	if (ctx->text.hasfocus)
2861	    return;
2862
2863	if (XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child,
2864			  &rootx, &rooty, &x, &y, &mask)) {
2865	    if (child)
2866		return;
2867	}
2868    }
2869
2870    /* Let the input method know focus has arrived. */
2871    _XawImSetFocusValues(w, NULL, 0);
2872
2873    if (display_caret)
2874	StartAction(ctx, event);
2875    ctx->text.hasfocus = TRUE;
2876    if (display_caret)
2877	EndAction(ctx);
2878
2879    for (i = 0; i < num_focus; i++)
2880	if (focus[i].display == XtDisplay(w))
2881	    break;
2882    if (i >= num_focus) {
2883	focus = (struct _focus*)
2884	    XtRealloc((XtPointer)focus, sizeof(struct _focus) * (num_focus + 1));
2885	i = num_focus;
2886	focus[i].widget = NULL;
2887	focus[i].display = XtDisplay(w);
2888	num_focus++;
2889    }
2890    if (focus[i].widget != w) {
2891	Widget old = focus[i].widget;
2892
2893	focus[i].widget = w;
2894	if (old != NULL) {
2895	    TextFocusOut(old, event, p, n);
2896	    /* TextFocusOut may set it to NULL */
2897	    focus[i].widget = w;
2898	}
2899	XtAddCallback(w, XtNdestroyCallback,
2900		      DestroyFocusCallback, (XtPointer)&focus[i]);
2901    }
2902}
2903
2904/*ARGSUSED*/
2905static void
2906TextFocusOut(Widget w, XEvent *event, String *p, Cardinal *n)
2907{
2908    TextWidget ctx = (TextWidget)w;
2909    Bool display_caret = ctx->text.display_caret;
2910    Widget shell;
2911    Window window;
2912    int i, revert;
2913
2914    shell = w;
2915    while (shell) {
2916	if (XtIsShell(shell))
2917	   break;
2918	shell = XtParent(shell);
2919    }
2920
2921    for (i = 0; i < num_focus; i++)
2922	if (focus[i].display == XtDisplay(w))
2923	    break;
2924    XGetInputFocus(XtDisplay(w), &window, &revert);
2925    if ((XtWindow(shell) == window &&
2926	 (i < num_focus && focus[i].widget == w))
2927	 || event->xfocus.detail == NotifyPointer)
2928	return;
2929
2930    if (i < num_focus && focus[i].widget) {
2931	XtRemoveCallback(focus[i].widget, XtNdestroyCallback,
2932			 DestroyFocusCallback, (XtPointer)&focus[i]);
2933	focus[i].widget = NULL;
2934    }
2935
2936    /* Let the input method know focus has left.*/
2937    _XawImUnsetFocus(w);
2938
2939    if (display_caret)
2940	StartAction(ctx, event);
2941    ctx->text.hasfocus = FALSE;
2942    if (display_caret)
2943	EndAction(ctx);
2944}
2945
2946/*ARGSUSED*/
2947static void
2948TextEnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
2949{
2950    TextWidget ctx = (TextWidget)w;
2951
2952    if ((event->xcrossing.detail != NotifyInferior) && event->xcrossing.focus
2953	&& !ctx->text.hasfocus)
2954	_XawImSetFocusValues(w, NULL, 0);
2955}
2956
2957/*ARGSUSED*/
2958static void
2959TextLeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
2960{
2961    TextWidget ctx = (TextWidget)w;
2962
2963    if ((event->xcrossing.detail != NotifyInferior) && event->xcrossing.focus
2964	&& !ctx->text.hasfocus)
2965	_XawImUnsetFocus(w);
2966}
2967
2968/*
2969 * Function:
2970 *	AutoFill
2971 *	Arguments: ctx - The text widget.
2972 *
2973 * Description:
2974 *	  Breaks the line at the previous word boundry when
2975 *	called inside InsertChar.
2976 */
2977static void
2978AutoFill(TextWidget ctx)
2979{
2980    int width, height, x, line_num, max_width;
2981    XawTextPosition ret_pos;
2982    XawTextBlock text;
2983    XRectangle cursor;
2984    wchar_t wc_buf[2];
2985
2986    for (line_num = 0; line_num < ctx->text.lt.lines ; line_num++)
2987	if (ctx->text.lt.info[line_num].position >= ctx->text.insertPos)
2988	    break;
2989    if (line_num)
2990	line_num--;		/* backup a line. */
2991
2992    XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
2993    max_width = Max(0, (int)XtWidth(ctx) - RHMargins(ctx) - cursor.width);
2994
2995    x = ctx->text.r_margin.left;
2996    XawTextSinkFindPosition(ctx->text.sink, ctx->text.lt.info[line_num].position,
2997			    x, max_width, True, &ret_pos,
2998			    &width, &height);
2999
3000    if (ret_pos <= ctx->text.lt.info[line_num].position
3001	|| ret_pos >= ctx->text.insertPos || ret_pos < 1)
3002	return;
3003
3004    XawTextSourceRead(ctx->text.source, ret_pos - 1, &text, 1);
3005
3006    if (XawTextFormat(ctx, XawFmtWide)) {
3007	wc_buf[0] = *(wchar_t *)text.ptr;
3008	if (wc_buf[0] != _Xaw_atowc(XawSP) && wc_buf[0] != _Xaw_atowc(XawTAB))
3009	    /* Only eats white spaces */
3010	    return;
3011
3012	text.format = XawFmtWide;
3013	text.ptr = (char *)wc_buf;
3014	wc_buf[0] = _Xaw_atowc(XawLF);
3015	wc_buf[1] = 0;
3016    }
3017    else {
3018	if (text.ptr[0] != XawSP && text.ptr[0] != XawTAB)
3019	    /* Only eats white spaces */
3020	    return;
3021
3022	text.format = XawFmt8Bit;
3023	text.ptr = "\n";
3024    }
3025    text.length = 1;
3026    text.firstPos = 0;
3027
3028    if (_XawTextReplace(ctx, ret_pos - 1, ret_pos, &text))
3029	XBell(XtDisplay((Widget)ctx), 0);
3030
3031    if (++ctx->text.insertPos > ctx->text.lastPos)
3032	ctx->text.insertPos = ctx->text.lastPos;
3033}
3034
3035/*ARGSUSED*/
3036static void
3037InsertChar(Widget w, XEvent *event, String *p, Cardinal *n)
3038{
3039    TextWidget ctx = (TextWidget)w;
3040    char *ptr, strbuf[128], ptrbuf[512];
3041    int count, error, mult = MULT(ctx);
3042    KeySym keysym;
3043    XawTextBlock text;
3044#ifndef OLDXAW
3045    Bool format = False;
3046#endif
3047    XawTextPosition from, to;
3048
3049    if (XtIsSubclass (ctx->text.source, (WidgetClass) multiSrcObjectClass))
3050	text.length = _XawImWcLookupString(w, &event->xkey, (wchar_t*)strbuf,
3051					   sizeof(strbuf), &keysym);
3052    else
3053	text.length = _XawLookupString(w, (XKeyEvent*)event, strbuf,
3054				       sizeof(strbuf), &keysym);
3055
3056    if (text.length == 0)
3057	return;
3058
3059    if (mult < 0) {
3060	ctx->text.mult = 1;
3061	return;
3062    }
3063
3064    text.format = _XawTextFormat(ctx);
3065    if (text.format == XawFmtWide) {
3066	text.ptr = ptr = XawStackAlloc(sizeof(wchar_t) * text.length
3067				       * mult, ptrbuf);
3068	for (count = 0; count < mult; count++) {
3069	    memcpy((char*)ptr, (char *)strbuf, sizeof(wchar_t) * text.length);
3070	    ptr += sizeof(wchar_t) * text.length;
3071	}
3072#ifndef OLDXAW
3073	if (mult == 1)
3074	    format = ctx->text.left_column < ctx->text.right_column;
3075#endif
3076    }
3077    else {	/* == XawFmt8Bit */
3078	text.ptr = ptr = XawStackAlloc(text.length * mult, ptrbuf);
3079	for (count = 0; count < mult; count++) {
3080	    strncpy(ptr, strbuf, text.length);
3081	    ptr += text.length;
3082	}
3083#ifndef OLDXAW
3084	if (mult == 1)
3085	    format = ctx->text.left_column < ctx->text.right_column;
3086#endif
3087    }
3088
3089    text.length = text.length * mult;
3090    text.firstPos = 0;
3091
3092    StartAction(ctx, event);
3093#ifndef OLDXAW
3094    if (mult == 1)
3095	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
3096#endif
3097
3098    from = ctx->text.insertPos;
3099#ifndef OLDXAW
3100    if (ctx->text.overwrite) {
3101	XawTextPosition tmp;
3102
3103	to = from + mult;
3104	tmp = SrcScan(ctx->text.source, from, XawstEOL, XawsdRight, 1, False);
3105	if (to > tmp)
3106	    to = tmp;
3107    }
3108    else
3109#endif
3110	to = from;
3111
3112    error = _XawTextReplace(ctx, from , to, &text);
3113
3114    if (error == XawEditDone) {
3115	ctx->text.from_left = -1;
3116	ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
3117				      XawstPositions, XawsdRight,
3118				      text.length, True);
3119	if (ctx->text.auto_fill) {
3120#ifndef OLDXAW
3121	    if (format)
3122		(void)FormatText(ctx, SrcScan(ctx->text.source,
3123					      ctx->text.insertPos, XawstEOL,
3124					      XawsdLeft, 1, False), False,
3125					      NULL, 0);
3126	    else
3127#endif
3128		AutoFill(ctx);
3129	}
3130    }
3131    else
3132	XBell(XtDisplay(ctx), 50);
3133
3134    XawStackFree(text.ptr, ptrbuf);
3135    EndAction(ctx);
3136
3137    if (error == XawEditDone && text.format == XawFmt8Bit && text.length == 1
3138	&& (text.ptr[0] == ')' || text.ptr[0] == ']' || text.ptr[0] == '}')
3139	&& ctx->text.display_caret) {
3140	static struct timeval tmval = {0, 500000};
3141	fd_set fds;
3142	Widget source = ctx->text.source;
3143	XawTextPosition insertPos = ctx->text.insertPos, pos, tmp, last;
3144	char left, right = text.ptr[0];
3145	int level = 0;
3146	XtAppContext app_context = XtWidgetToApplicationContext(w);
3147
3148	left = right == ')' ? '(' : right == ']' ? '[' : '{';
3149
3150	last = insertPos - 1;
3151	do {
3152	    text.ptr[0] = left;
3153	    pos = XawTextSourceSearch(source, last, XawsdLeft, &text);
3154	    if (pos == XawTextSearchError || !IsPositionVisible(ctx, pos))
3155		return;
3156	    text.ptr[0] = right;
3157	    tmp = pos;
3158	    do {
3159		tmp = XawTextSourceSearch(source, tmp, XawsdRight, &text);
3160		if (tmp == XawTextSearchError)
3161		    return;
3162		if (tmp <= last)
3163		    ++level;
3164	    } while (++tmp <= last);
3165	    --level;
3166	    last = pos;
3167	} while (level);
3168
3169	StartAction(ctx, NULL);
3170#ifndef OLDXAW
3171	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
3172#endif
3173	ctx->text.insertPos = pos;
3174	EndAction(ctx);
3175
3176	XSync(XtDisplay(w), False);
3177	while (XtAppPending(app_context) & XtIMXEvent) {
3178	    XEvent ev;
3179	    if (! XtAppPeekEvent(app_context, &ev))
3180		break;
3181	    if (ev.type == KeyPress || ev.type == ButtonPress)
3182		break;
3183	    XtAppProcessEvent(app_context, XtIMXEvent);
3184	}
3185	FD_ZERO(&fds);
3186	FD_SET(ConnectionNumber(XtDisplay(w)), &fds);
3187	(void)select(FD_SETSIZE, &fds, NULL, NULL, &tmval);
3188	if (tmval.tv_usec != 500000)
3189	    usleep(40000);
3190
3191	StartAction(ctx, NULL);
3192#ifndef OLDXAW
3193	_XawSourceSetUndoMerge((TextSrcObject)ctx->text.source, True);
3194#endif
3195	ctx->text.insertPos = insertPos;
3196	EndAction(ctx);
3197    }
3198}
3199
3200/* IfHexConvertHexElseReturnParam() - called by InsertString
3201 *
3202 * i18n requires the ability to specify multiple characters in a hexa-
3203 * decimal string at once.  Since Insert was already too long, I made
3204 * this a seperate routine.
3205 *
3206 * A legal hex string in MBNF: '0' 'x' ( HEX-DIGIT HEX-DIGIT )+ '\0'
3207 *
3208 * WHEN:    the passed param is a legal hex string
3209 * RETURNS: a pointer to that converted, null terminated hex string;
3210 *	    len_return holds the character count of conversion result
3211 *
3212 * WHEN:    the passed param is not a legal hex string:
3213 * RETURNS: the parameter passed;
3214 *	    len_return holds the char count of param.
3215 *
3216 * NOTE:    In neither case will there be strings to free. */
3217static char *
3218IfHexConvertHexElseReturnParam(char *param, int *len_return)
3219{
3220    char *p;		/* steps through param char by char */
3221    char c;		/* holds the character pointed to by p */
3222    int ind;		/* steps through hexval buffer char by char */
3223    static char hexval[XawTextActionMaxHexChars];
3224    Boolean first_digit;
3225
3226    /* reject if it doesn't begin with 0x and at least one more character. */
3227    if ((param[0] != '0') || (param[1] != 'x') || (param[2] == '\0')) {
3228	*len_return = strlen(param);
3229	return(param);
3230    }
3231
3232    /* Skip the 0x; go character by character shifting and adding. */
3233    first_digit = True;
3234    ind = 0;
3235    hexval[ind] = '\0';
3236
3237    for (p = param+2; (c = *p) != '\0'; p++) {
3238	hexval[ind] *= 16;
3239	if (c >= '0' && c <= '9')
3240	    hexval[ind] += c - '0';
3241	else if (c >= 'a' && c <= 'f')
3242	    hexval[ind] += c - 'a' + 10;
3243	else if (c >= 'A' && c <= 'F')
3244	    hexval[ind] += c - 'A' + 10;
3245	else
3246	    break;
3247
3248	/* If we didn't break in preceding line, it was a good hex char. */
3249	if (first_digit)
3250	    first_digit = False;
3251	else {
3252	    first_digit = True;
3253	    if (++ind < XawTextActionMaxHexChars)
3254		hexval[ind] = '\0';
3255	    else {
3256		*len_return = strlen(param);
3257		return(param);
3258	    }
3259	}
3260    }
3261
3262    /* We quit the above loop becasue we hit a non hex.  If that char is \0... */
3263    if ((c == '\0') && first_digit) {
3264	*len_return = strlen(hexval);
3265	return (hexval);       /* ...it was a legal hex string, so return it */
3266    }
3267
3268    /* Else, there were non-hex chars or odd digit count, so... */
3269
3270    *len_return = strlen(param);
3271    return (param);			   /* ...return the verbatim string. */
3272}
3273
3274/* InsertString() - action
3275 *
3276 * Mostly rewritten for R6 i18n.
3277 *
3278 * Each parameter, in turn, will be insert at the inputPos
3279 * and the inputPos advances to the insertion's end.
3280 *
3281 * The exception is that parameters composed of the two
3282 * characters 0x, followed only by an even number of
3283 * hexadecimal digits will be converted to characters */
3284/*ARGSUSED*/
3285static void
3286InsertString(Widget w, XEvent *event, String *params, Cardinal *num_params)
3287{
3288    TextWidget ctx = (TextWidget)w;
3289    XtAppContext app_con = XtWidgetToApplicationContext(w);
3290    XawTextBlock text;
3291    int i;
3292
3293    text.firstPos = 0;
3294    text.format = _XawTextFormat(ctx);
3295
3296    StartAction(ctx, event);
3297    for (i = *num_params; i; i--, params++) {	/* DO FOR EACH PARAMETER */
3298	text.ptr = IfHexConvertHexElseReturnParam(*params, &text.length);
3299
3300	if (text.length == 0)
3301	    continue;
3302
3303	if (XawTextFormat(ctx, XawFmtWide)) {	/* convert to WC */
3304	    int temp_len;
3305
3306	    text.ptr = (char*)_XawTextMBToWC(XtDisplay(w), text.ptr,
3307					     &text.length);
3308
3309	    if (text.ptr == NULL) {	  /* conversion error */
3310		XtAppWarningMsg(app_con,
3311				"insertString", "textAction", "XawError",
3312				"insert-string()'s parameter contents "
3313				"not legal in this locale.",
3314				NULL, NULL);
3315		ParameterError(w, *params);
3316		continue;
3317	   }
3318
3319	    /* Double check that the new input is legal: try to convert to MB. */
3320
3321	    temp_len = text.length;	 /* _XawTextWCToMB's 3rd arg is in_out */
3322	    if (_XawTextWCToMB(XtDisplay(w), (wchar_t*)text.ptr, &temp_len)
3323		== NULL) {
3324		XtAppWarningMsg( app_con,
3325				 "insertString", "textAction", "XawError",
3326				 "insert-string()'s parameter contents "
3327				 "not legal in this locale.",
3328				 NULL, NULL);
3329		ParameterError(w, *params);
3330		continue;
3331	    }
3332	} /* convert to WC */
3333
3334	if (_XawTextReplace(ctx, ctx->text.insertPos,
3335			    ctx->text.insertPos, &text)) {
3336	    XBell(XtDisplay(ctx), 50);
3337	    EndAction(ctx);
3338	    return;
3339	}
3340
3341	ctx->text.from_left = -1;
3342	/* Advance insertPos to the end of the string we just inserted. */
3343	ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.old_insert,
3344				       XawstPositions, XawsdRight, text.length,
3345				      True);
3346
3347    } /* DO FOR EACH PARAMETER */
3348
3349    EndAction(ctx);
3350}
3351
3352/* DisplayCaret() - action
3353 *
3354 * The parameter list should contain one boolean value.  If the
3355 * argument is true, the cursor will be displayed.  If false, not.
3356 *
3357 * The exception is that EnterNotify and LeaveNotify events may
3358 * have a second argument, "always".  If they do not, the cursor
3359 * is only affected if the focus member of the event is true.	*/
3360static void
3361DisplayCaret(Widget w, XEvent *event, String *params, Cardinal *num_params)
3362{
3363    TextWidget ctx = (TextWidget)w;
3364    Bool display_caret = True;
3365
3366    if	((event->type == EnterNotify || event->type == LeaveNotify)
3367	 && ((*num_params >= 2) && (strcmp(params[1], "always") == 0))
3368	 && (!event->xcrossing.focus))
3369	return;
3370
3371    if (*num_params > 0) {	/* default arg is "True" */
3372	XrmValue from, to;
3373	from.size = strlen(from.addr = params[0]);
3374	XtConvert(w, XtRString, &from, XtRBoolean, &to);
3375
3376	if (to.addr != NULL)
3377	    display_caret = *(Boolean*)to.addr;
3378	if (ctx->text.display_caret == display_caret)
3379	    return;
3380    }
3381    StartAction(ctx, event);
3382    ctx->text.display_caret = display_caret;
3383    EndAction(ctx);
3384}
3385
3386#ifndef OLDXAW
3387static void
3388Numeric(Widget w, XEvent *event, String *params, Cardinal *num_params)
3389{
3390    TextWidget ctx = (TextWidget)w;
3391
3392    if (ctx->text.numeric) {
3393	long mult = ctx->text.mult;
3394
3395	if (*num_params != 1 || strlen(params[0]) != 1
3396	    || (!isdigit(params[0][0])
3397		&& (params[0][0] != '-' || mult != 0))) {
3398	    char err_buf[256];
3399
3400	    if (event && (event->type == KeyPress || event->type == KeyRelease)
3401		&& params[0][0] == '-') {
3402		InsertChar(w, event, params, num_params);
3403		return;
3404	    }
3405	    snprintf(err_buf, sizeof(err_buf),
3406		     "numeric: Invalid argument%s'%s'",
3407		     *num_params ? ", " : "",
3408		     *num_params ? params[0] : "");
3409	    XtAppWarning(XtWidgetToApplicationContext(w), err_buf);
3410	    ctx->text.numeric = False;
3411	    ctx->text.mult = 1;
3412	    return;
3413	}
3414	if (params[0][0] == '-') {
3415	    ctx->text.mult = 32767;
3416	    return;
3417	}
3418	else if (mult == 32767) {
3419	    mult = ctx->text.mult = - (params[0][0] - '0');
3420	    return;
3421	}
3422	else {
3423	    mult = mult * 10 + (params[0][0] - '0') * (mult < 0 ? -1 : 1);
3424	    ctx->text.mult = ctx->text.mult * 10 + (params[0][0] - '0') *
3425			     (mult < 0 ? -1 : 1);
3426	}
3427	if (mult != ctx->text.mult || mult >= 32767) {	/* checks for overflow */
3428	    XBell(XtDisplay(w), 0);
3429	    ctx->text.mult = 1;
3430	    ctx->text.numeric = False;
3431	    return;
3432	}
3433    }
3434    else
3435	InsertChar(w, event, params, num_params);
3436}
3437
3438/*ARGSUSED*/
3439static void
3440KeyboardReset(Widget w, XEvent *event, String *params, Cardinal *num_params)
3441{
3442    TextWidget ctx = (TextWidget)w;
3443
3444    ctx->text.numeric = False;
3445    ctx->text.mult = 1;
3446
3447    (void)_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);
3448
3449    if (ctx->text.kill_ring_ptr) {
3450	--ctx->text.kill_ring_ptr->refcount;
3451	ctx->text.kill_ring_ptr = NULL;
3452    }
3453    ctx->text.kill_ring = 0;
3454
3455    XBell(XtDisplay(w), 0);
3456}
3457#endif /* OLDXAW */
3458
3459/* Multiply() - action
3460 *
3461 * The parameter list may contain either a number or the string 'Reset'.
3462 *
3463 * A number will multiply the current multiplication factor by that number.
3464 * Many of the text widget actions will will perform n actions, where n is
3465 * the multiplication factor.
3466 *
3467 * The string reset will reset the mutiplication factor to 1. */
3468/*ARGSUSED*/
3469static void
3470Multiply(Widget w, XEvent *event, String *params, Cardinal *num_params)
3471{
3472    TextWidget ctx = (TextWidget)w;
3473    int mult;
3474
3475    if (*num_params != 1) {
3476	XtAppError(XtWidgetToApplicationContext(w),
3477		   "Xaw Text Widget: multiply() takes exactly one argument.");
3478	XBell(XtDisplay(w), 0);
3479	return;
3480    }
3481
3482    if ((params[0][0] == 'r') || (params[0][0] == 'R')) {
3483	XBell(XtDisplay(w), 0);
3484#ifndef OLDXAW
3485	ctx->text.numeric = False;
3486#endif
3487	ctx->text.mult = 1;
3488	return;
3489    }
3490
3491#ifndef OLDXAW
3492    if (params[0][0] == 's' || params[0][0] == 'S') {
3493	ctx->text.numeric = True;
3494	ctx->text.mult = 0;
3495	return;
3496    }
3497    else
3498#endif
3499	if ((mult = atoi(params[0])) == 0) {
3500	char buf[BUFSIZ];
3501
3502	snprintf(buf, sizeof(buf),
3503		 "Xaw Text Widget: multiply() argument "
3504		 "must be a number greater than zero, or 'Reset'.");
3505	XtAppError(XtWidgetToApplicationContext(w), buf);
3506	XBell(XtDisplay(w), 50);
3507	return;
3508    }
3509
3510    ctx->text.mult *= mult;
3511}
3512
3513/* StripOutOldCRs() - called from FormRegion
3514 *
3515 * removes CRs in widget ctx, from from to to.
3516 *
3517 * RETURNS: the new ending location (we may add some characters),
3518 * or XawReplaceError if the widget can't be written to. */
3519static XawTextPosition
3520StripOutOldCRs(TextWidget ctx, XawTextPosition from, XawTextPosition to,
3521	       XawTextPosition *pos, int num_pos)
3522{
3523    XawTextPosition startPos, endPos, eop_begin, eop_end, temp;
3524    Widget src = ctx->text.source;
3525    XawTextBlock text;
3526    char *buf;
3527    static wchar_t wc_two_spaces[3];
3528    int idx;
3529
3530    /* Initialize our TextBlock with two spaces. */
3531    text.firstPos = 0;
3532    text.format = _XawTextFormat(ctx);
3533    if (text.format == XawFmt8Bit)
3534      text.ptr= "  ";
3535    else {
3536	wc_two_spaces[0] = _Xaw_atowc(XawSP);
3537	wc_two_spaces[1] = _Xaw_atowc(XawSP);
3538	wc_two_spaces[2] = 0;
3539	text.ptr = (char*)wc_two_spaces;
3540    }
3541
3542    /* Strip out CR's. */
3543    eop_begin = eop_end = startPos = endPos = from;
3544
3545    /* CONSTCOND */
3546    while (TRUE) {
3547	endPos=SrcScan(src, startPos, XawstEOL, XawsdRight, 1, False);
3548
3549	temp = SrcScan(src, endPos, XawstWhiteSpace, XawsdLeft, 1, False);
3550	temp = SrcScan(src, temp,   XawstWhiteSpace, XawsdRight,1, False);
3551
3552	if (temp > startPos)
3553	    endPos = temp;
3554
3555	if (endPos >= to)
3556	    break;
3557
3558	if (endPos >= eop_begin) {
3559	    startPos = eop_end;
3560	    eop_begin=SrcScan(src, startPos, XawstParagraph,
3561			      XawsdRight, 1,False);
3562	    eop_end = SrcScan(src, startPos, XawstParagraph,
3563			      XawsdRight, 1, True);
3564	}
3565	else {
3566	    XawTextPosition periodPos, next_word;
3567	    int i, len;
3568
3569	    periodPos = SrcScan(src, endPos, XawstPositions,
3570				XawsdLeft, 1, True);
3571	    next_word = SrcScan(src, endPos, XawstWhiteSpace,
3572				XawsdRight, 1, False);
3573
3574	    len = next_word - periodPos;
3575
3576	    text.length = 1;
3577	    buf = _XawTextGetText(ctx, periodPos, next_word);
3578	    if (text.format == XawFmtWide) {
3579		if (periodPos < endPos && ((wchar_t*)buf)[0] == _Xaw_atowc('.'))
3580		  text.length++;
3581	    }
3582	    else
3583		if (periodPos < endPos && buf[0] == '.')
3584		    text.length++;	  /* Put in two spaces. */
3585
3586	    /*
3587	     * Remove all extra spaces.
3588	     */
3589	    for (i = 1 ; i < len; i++)
3590		if (text.format ==  XawFmtWide) {
3591		    if (!iswspace(((wchar_t*)buf)[i]) || ((periodPos + i) >= to))
3592			break;
3593		}
3594		else if (!isspace(buf[i]) || (periodPos + i) >= to)
3595		    break;
3596
3597	    XtFree(buf);
3598
3599	    to -= (i - text.length - 1);
3600	    startPos = SrcScan(src, periodPos, XawstPositions,
3601			       XawsdRight, i, True);
3602	    if (_XawTextReplace(ctx, endPos, startPos, &text) != XawEditDone)
3603		return (XawReplaceError);
3604
3605	    for (idx = 0; idx < num_pos; idx++) {
3606		if (endPos < pos[idx]) {
3607		    if (startPos < pos[idx])
3608			pos[idx] -= startPos - endPos;
3609		    else
3610			pos[idx] = endPos;
3611		    pos[idx] += text.length;
3612		}
3613	    }
3614
3615	    startPos -= i - text.length;
3616	}
3617    }
3618
3619    return (to);
3620}
3621
3622/* InsertNewCRs() - called from FormRegion
3623 *
3624 * inserts new CRs for FormRegion, thus for FormParagraph action */
3625static void
3626InsertNewCRs(TextWidget ctx, XawTextPosition from, XawTextPosition to,
3627	     XawTextPosition *pos, int num_pos)
3628{
3629    XawTextPosition startPos, endPos, space, eol;
3630    XawTextBlock text;
3631    int i, width, height, len, wwidth, idx;
3632    char *buf;
3633    static wchar_t wide_CR[2];
3634
3635    text.firstPos = 0;
3636    text.length = 1;
3637    text.format = _XawTextFormat(ctx);
3638
3639    if (text.format == XawFmt8Bit)
3640	text.ptr = "\n";
3641    else {
3642	wide_CR[0] = _Xaw_atowc(XawLF);
3643	wide_CR[1] = 0;
3644	text.ptr = (char*)wide_CR;
3645    }
3646
3647    startPos = from;
3648
3649    wwidth = (int)XtWidth(ctx) - (int)HMargins(ctx);
3650    if (ctx->text.wrap != XawtextWrapNever) {
3651	XRectangle cursor;
3652
3653	XawTextSinkGetCursorBounds(ctx->text.sink, &cursor);
3654	wwidth -= (int)cursor.width;
3655    }
3656    wwidth = XawMax(0, wwidth);
3657
3658    /* CONSTCOND */
3659    while (TRUE) {
3660	XawTextSinkFindPosition(ctx->text.sink, startPos,
3661				(int)ctx->text.r_margin.left, wwidth,
3662				True, &eol, &width, &height);
3663	if (eol == startPos)
3664	    ++eol;
3665	if (eol >= to)
3666	    break;
3667
3668	eol = SrcScan(ctx->text.source, eol, XawstPositions,
3669		      XawsdLeft, 1, True);
3670	space = SrcScan(ctx->text.source, eol, XawstWhiteSpace,
3671			XawsdRight,1, True);
3672
3673	startPos = endPos = eol;
3674	if (eol == space)
3675	    return;
3676
3677	len = (int)(space - eol);
3678	buf = _XawTextGetText(ctx, eol, space);
3679	for (i = 0 ; i < len ; i++)
3680	    if (text.format == XawFmtWide) {
3681		if (!iswspace(((wchar_t*)buf)[i]))
3682		    break;
3683	    }
3684	    else if (!isspace(buf[i]))
3685		break;
3686
3687	to -= (i - 1);
3688	endPos = SrcScan(ctx->text.source, endPos,
3689			 XawstPositions, XawsdRight, i, True);
3690	XtFree(buf);
3691
3692	if (_XawTextReplace(ctx, startPos, endPos, &text))
3693	    return;
3694
3695	for (idx = 0; idx < num_pos; idx++) {
3696	    if (startPos < pos[idx]) {
3697		if (endPos < pos[idx])
3698		    pos[idx] -= endPos - startPos;
3699		else
3700		    pos[idx] = startPos;
3701		pos[idx] += text.length;
3702	    }
3703	}
3704
3705	startPos = SrcScan(ctx->text.source, startPos,
3706			   XawstPositions, XawsdRight, 1, True);
3707    }
3708}
3709
3710/* FormRegion() - called by FormParagraph
3711 *
3712 * oversees the work of paragraph-forming a region
3713 *
3714 * Return:
3715 *	XawEditDone if successful, or XawReplaceError
3716 */
3717static int
3718FormRegion(TextWidget ctx, XawTextPosition from, XawTextPosition to,
3719	   XawTextPosition *pos, int num_pos)
3720{
3721#ifndef OLDXAW
3722    Bool format = ctx->text.auto_fill
3723	&& ctx->text.left_column < ctx->text.right_column;
3724#endif
3725
3726    if (from >= to)
3727	return (XawEditDone);
3728
3729#ifndef OLDXAW
3730    if (format) {
3731	XawTextPosition len = ctx->text.lastPos;
3732	int inc = 0;
3733
3734	if (ctx->text.justify == XawjustifyLeft ||
3735	    ctx->text.justify == XawjustifyFull) {
3736	    Untabify(ctx, from, to, pos, num_pos, NULL);
3737	    to += ctx->text.lastPos - len;
3738	    len = ctx->text.insertPos;
3739	    (void)BlankLine((Widget)ctx, from, &inc);
3740	    if (from + inc >= to)
3741		return (XawEditDone);
3742	}
3743	if (!StripSpaces(ctx, from + inc, to, pos, num_pos, NULL))
3744	    return (XawReplaceError);
3745	to += ctx->text.lastPos - len;
3746
3747	FormatText(ctx, from, ctx->text.justify != XawjustifyFull, pos, num_pos);
3748    }
3749    else {
3750#endif
3751	if ((to = StripOutOldCRs(ctx, from, to, pos, num_pos)) == XawReplaceError)
3752	    return (XawReplaceError);
3753	InsertNewCRs(ctx, from, to, pos, num_pos);
3754#ifndef OLDXAW
3755    }
3756#endif
3757    ctx->text.from_left = -1;
3758
3759    return (XawEditDone);
3760}
3761
3762#ifndef OLDXAW
3763static Bool
3764BlankLine(Widget w, XawTextPosition pos, int *blanks_return)
3765{
3766    int i, blanks = 0;
3767    XawTextBlock block;
3768    Widget src = XawTextGetSource(w);
3769    XawTextPosition l = SrcScan(src, pos, XawstEOL, XawsdLeft, 1, False);
3770    XawTextPosition r = SrcScan(src, pos, XawstEOL, XawsdRight, 1, False);
3771
3772    while (l < r) {
3773	l = XawTextSourceRead(src, l, &block, r - l);
3774	if (block.length == 0) {
3775	    if (blanks_return)
3776		*blanks_return = blanks;
3777	    return (True);
3778	}
3779	if (XawTextFormat((TextWidget)w, XawFmt8Bit)) {
3780	    for (i = 0; i < block.length; i++, blanks++)
3781		if (block.ptr[i] != ' ' &&
3782		    block.ptr[i] != '\t') {
3783		    if (blanks_return)
3784			*blanks_return = blanks;
3785		    return (block.ptr[i] == '\n');
3786		}
3787	}
3788	else if (XawTextFormat((TextWidget)w, XawFmtWide)) {
3789	    for (i = 0; i < block.length; i++, blanks++)
3790		if (_Xaw_atowc(XawSP) != ((wchar_t*)block.ptr)[i] &&
3791		    _Xaw_atowc(XawTAB) != ((wchar_t*)block.ptr)[i]) {
3792		    if (blanks_return)
3793			*blanks_return = blanks;
3794		    return (_Xaw_atowc(XawLF) == ((wchar_t*)block.ptr)[i]);
3795		}
3796	}
3797    }
3798
3799    return (True);
3800}
3801
3802static Bool
3803GetBlockBoundaries(TextWidget ctx,
3804		   XawTextPosition *from_return, XawTextPosition *to_return)
3805{
3806    XawTextPosition from, to;
3807
3808    if (ctx->text.auto_fill && ctx->text.left_column < ctx->text.right_column) {
3809	if (ctx->text.s.left != ctx->text.s.right) {
3810	    from = SrcScan(ctx->text.source,
3811			   XawMin(ctx->text.s.left, ctx->text.s.right),
3812			   XawstEOL, XawsdLeft, 1, False);
3813	    to   = SrcScan(ctx->text.source,
3814			   XawMax(ctx->text.s.right, ctx->text.s.right),
3815			   XawstEOL, XawsdRight, 1, False);
3816	}
3817	else {
3818	    XawTextBlock block;
3819	    XawTextPosition tmp;
3820	    Bool first;
3821
3822	    from = to = ctx->text.insertPos;
3823
3824	    /* find from position */
3825	    first = True;
3826	    while (1) {
3827		tmp = from;
3828		from = SrcScan(ctx->text.source, from, XawstEOL, XawsdLeft,
3829			       1 + !first, False);
3830		XawTextSourceRead(ctx->text.source, from, &block, 1);
3831		if (block.length == 0 ||
3832		    (XawTextFormat(ctx, XawFmt8Bit) &&
3833		     block.ptr[0] != ' ' &&
3834		     block.ptr[0] != '\t' &&
3835		     !isalnum(*(unsigned char*)block.ptr)) ||
3836		    (XawTextFormat(ctx, XawFmtWide) &&
3837		     _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
3838		     _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
3839		     !iswalnum(*(wchar_t*)block.ptr)) ||
3840		    BlankLine((Widget)ctx, from, NULL)) {
3841		    from = tmp;
3842		    break;
3843		}
3844		if (from == tmp && !first)
3845		    break;
3846		first = False;
3847	    }
3848	    if (first)
3849		return (False);
3850
3851	    /* find to position */
3852	    first = True;
3853	    while (1) {
3854		tmp = to;
3855		to = SrcScan(ctx->text.source, to, XawstEOL, XawsdRight,
3856			     1 + !first, False);
3857		XawTextSourceRead(ctx->text.source, to + (to < ctx->text.lastPos),
3858				  &block, 1);
3859		if (block.length == 0 ||
3860		    (XawTextFormat(ctx, XawFmt8Bit) &&
3861		     block.ptr[0] != ' ' &&
3862		     block.ptr[0] != '\t' &&
3863		     !isalnum(*(unsigned char*)block.ptr)) ||
3864		    (XawTextFormat(ctx, XawFmtWide) &&
3865		     _Xaw_atowc(XawSP) != *(wchar_t*)block.ptr &&
3866		     _Xaw_atowc(XawTAB) != *(wchar_t*)block.ptr &&
3867		     !iswalnum(*(wchar_t*)block.ptr)) ||
3868		    BlankLine((Widget)ctx, to, NULL))
3869		    break;
3870		if (to == tmp && !first)
3871		    break;
3872		first = False;
3873	    }
3874	}
3875    }
3876    else {
3877	from = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL,
3878		       XawsdLeft, 1, False);
3879	if (BlankLine((Widget)ctx, from, NULL))
3880	    return (False);
3881	from = SrcScan(ctx->text.source, from, XawstParagraph,
3882		       XawsdLeft, 1, False);
3883	if (BlankLine((Widget)ctx, from, NULL))
3884	    from = SrcScan(ctx->text.source, from, XawstEOL,
3885			   XawsdRight, 1, True);
3886	to = SrcScan(ctx->text.source, from, XawstParagraph,
3887			XawsdRight, 1, False);
3888    }
3889
3890    if (from < to) {
3891	*from_return = from;
3892	*to_return = to;
3893	return (True);
3894    }
3895
3896    return (False);
3897}
3898#endif /* OLDXAW */
3899
3900/* FormParagraph() - action
3901 *
3902 * removes and reinserts CRs to maximize line length without clipping */
3903/*ARGSUSED*/
3904static void
3905FormParagraph(Widget w, XEvent *event, String *params, Cardinal *num_params)
3906{
3907    TextWidget ctx = (TextWidget)w;
3908    XawTextPosition from, to, buf[32], *pos;
3909#ifndef OLDXAW
3910    XawTextPosition endPos = 0;
3911    char *lbuf = NULL, *rbuf;
3912    TextSrcObject src = (TextSrcObject)ctx->text.source;
3913    Cardinal i;
3914    Bool undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
3915#endif
3916
3917    StartAction(ctx, event);
3918
3919#ifndef OLDXAW
3920    pos = XawStackAlloc(sizeof(XawTextPosition) * src->textSrc.num_text, buf);
3921    for (i = 0; i < src->textSrc.num_text; i++)
3922	pos[i] = ((TextWidget)src->textSrc.text[i])->text.old_insert;
3923#else
3924    pos = buf;
3925    *pos = ctx->text.old_insert;
3926#endif
3927
3928#ifndef OLDXAW
3929    if (!GetBlockBoundaries(ctx, &from, &to)) {
3930	EndAction(ctx);
3931	XawStackFree(pos, buf);
3932	return;
3933    }
3934
3935    if (undo) {
3936	src->textSrc.undo_state = True;
3937	lbuf = _XawTextGetText(ctx, from, to);
3938	endPos = ctx->text.lastPos;
3939    }
3940
3941    if (FormRegion(ctx, from, to, pos, src->textSrc.num_text) == XawReplaceError) {
3942	XawStackFree(pos, buf);
3943	pos = buf;
3944#else
3945    from =  SrcScan(ctx->text.source, ctx->text.insertPos,
3946		    XawstParagraph, XawsdLeft, 1, False);
3947    to  =   SrcScan(ctx->text.source, from,
3948		    XawstParagraph, XawsdRight, 1, False);
3949
3950    if (FormRegion(ctx, from, to, pos, 1) == XawReplaceError) {
3951#endif
3952	XBell(XtDisplay(w), 0);
3953#ifndef OLDXAW
3954	if (undo) {
3955	    src->textSrc.undo_state = False;
3956	    XtFree(lbuf);
3957	}
3958#endif
3959    }
3960#ifndef OLDXAW
3961    else if (undo) {
3962	/* makes the form-paragraph only one undo/redo step */
3963	unsigned llen, rlen, size;
3964	XawTextBlock block;
3965
3966	llen = to - from;
3967	rlen = llen + (ctx->text.lastPos - endPos);
3968
3969	block.firstPos = 0;
3970	block.format = _XawTextFormat(ctx);
3971
3972	rbuf = _XawTextGetText(ctx, from, from + rlen);
3973
3974	size = XawTextFormat(ctx, XawFmtWide) ? sizeof(wchar_t) : sizeof(char);
3975	if (llen != rlen || memcmp(lbuf, rbuf, llen * size)) {
3976	    block.ptr = lbuf;
3977	    block.length = llen;
3978	    _XawTextReplace(ctx, from, from + rlen, &block);
3979
3980	    src->textSrc.undo_state = False;
3981	    block.ptr = rbuf;
3982	    block.length = rlen;
3983	    _XawTextReplace(ctx, from, from + llen, &block);
3984	}
3985	else
3986	    src->textSrc.undo_state = False;
3987	XtFree(lbuf);
3988	XtFree(rbuf);
3989    }
3990
3991    for (i = 0; i < src->textSrc.num_text; i++) {
3992	TextWidget tw = (TextWidget)src->textSrc.text[i];
3993
3994	tw->text.old_insert = tw->text.insertPos = pos[i];
3995	_XawTextBuildLineTable(tw, SrcScan((Widget)src, tw->text.lt.top, XawstEOL,
3996			       XawsdLeft, 1, False), False);
3997	tw->text.clear_to_eol = True;
3998    }
3999    XawStackFree(pos, buf);
4000#else
4001    ctx->text.old_insert = ctx->text.insertPos = *pos;
4002    _XawTextBuildLineTable(ctx, SrcScan(ctx->text.source, ctx->text.lt.top,
4003			   XawstEOL, XawsdLeft, 1, False), False);
4004    ctx->text.clear_to_eol = True;
4005#endif
4006    ctx->text.showposition = True;
4007
4008    EndAction(ctx);
4009}
4010
4011/* TransposeCharacters() - action
4012 *
4013 * Swaps the character to the left of the mark
4014 * with the character to the right of the mark */
4015/*ARGSUSED*/
4016static void
4017TransposeCharacters(Widget w, XEvent *event,
4018		    String *params, Cardinal *num_params)
4019{
4020    TextWidget ctx = (TextWidget)w;
4021    XawTextPosition start, end;
4022    XawTextBlock text;
4023    char *buf;
4024    int i, mult = MULT(ctx);
4025
4026    if (mult < 0) {
4027	ctx->text.mult = 1;
4028	return;
4029    }
4030
4031    StartAction(ctx, event);
4032
4033    /* Get bounds. */
4034
4035    start = SrcScan(ctx->text.source, ctx->text.insertPos, XawstPositions,
4036		    XawsdLeft, 1, True);
4037    end = SrcScan(ctx->text.source, ctx->text.insertPos, XawstPositions,
4038		  XawsdRight, mult, True);
4039
4040    /* Make sure we aren't at the very beginning or end of the buffer. */
4041
4042    if (start == ctx->text.insertPos || end == ctx->text.insertPos) {
4043	XBell(XtDisplay(w), 0);   /* complain. */
4044	EndAction(ctx);
4045	return;
4046    }
4047
4048    ctx->text.from_left = -1;
4049    ctx->text.insertPos = end;
4050
4051    text.firstPos = 0;
4052    text.format = _XawTextFormat(ctx);
4053
4054    /* Retrieve text and swap the characters. */
4055    if (text.format == XawFmtWide) {
4056	wchar_t wc;
4057	wchar_t *wbuf;
4058
4059	wbuf = (wchar_t*)_XawTextGetText(ctx, start, end);
4060	text.length = wcslen(wbuf);
4061	wc = wbuf[0];
4062	for (i = 1; i < text.length; i++)
4063	    wbuf[i - 1] = wbuf[i];
4064	wbuf[i - 1] = wc;
4065	buf = (char*)wbuf; /* so that it gets assigned and freed */
4066    }
4067    else {	/* thus text.format == XawFmt8Bit */
4068	char c;
4069
4070	buf = _XawTextGetText(ctx, start, end);
4071	text.length = strlen(buf);
4072	c = buf[0];
4073	for (i = 1; i < text.length; i++)
4074	    buf[i - 1] = buf[i];
4075	buf[i - 1] = c;
4076    }
4077
4078    text.ptr = buf;
4079
4080    /* Store new text in source. */
4081
4082    if (_XawTextReplace (ctx, start, end, &text))
4083	XBell(XtDisplay(w), 0);
4084    XtFree((char *)buf);
4085    EndAction(ctx);
4086}
4087
4088#ifndef OLDXAW
4089/*ARGSUSED*/
4090static void
4091Undo(Widget w, XEvent *event, String *params, Cardinal *num_params)
4092{
4093    TextWidget ctx = (TextWidget)w;
4094    int mul = MULT(ctx);
4095    Bool toggle = False;
4096
4097    if (mul < 0) {
4098	toggle = True;
4099	_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);
4100	ctx->text.mult = mul = -mul;
4101    }
4102
4103    StartAction(ctx, event);
4104    for (; mul; --mul)
4105	if (!_XawTextSrcUndo((TextSrcObject)ctx->text.source, &ctx->text.insertPos))
4106	    break;
4107    ctx->text.showposition = True;
4108
4109    if (toggle)
4110	_XawTextSrcToggleUndo((TextSrcObject)ctx->text.source);
4111    EndAction(ctx);
4112}
4113#endif
4114
4115/* NoOp() - action
4116 * This action performs no action, and allows the user or
4117 * application programmer to unbind a translation.
4118 *
4119 * Note: If the parameter list contains the string "RingBell" then
4120 *	 this action will ring the bell.
4121 */
4122/*ARGSUSED*/
4123static void
4124NoOp(Widget w, XEvent *event, String *params, Cardinal *num_params)
4125{
4126    if (*num_params != 1)
4127	return;
4128
4129    switch(params[0][0]) {
4130	case 'R':
4131	case 'r':
4132	    XBell(XtDisplay(w), 0);
4133	    /*FALLTROUGH*/
4134	default:
4135	    break;
4136    }
4137}
4138
4139/* Reconnect() - action
4140 * This reconnects to the input method.  The user will typically call
4141 * this action if/when connection has been severed, or when the app
4142 * was started up before an IM was started up
4143 */
4144/*ARGSUSED*/
4145static void
4146Reconnect(Widget w, XEvent *event, String *params, Cardinal *num_params)
4147{
4148    _XawImReconnect(w);
4149}
4150
4151#define	CAPITALIZE	1
4152#define	DOWNCASE	2
4153#define UPCASE		3
4154
4155#ifdef NO_LIBC_I18N
4156static int
4157ToLower(int ch)
4158{
4159    char buf[2];
4160
4161    *buf = ch;
4162    XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf));
4163
4164    return (*buf);
4165}
4166
4167static int
4168ToUpper(int ch)
4169{
4170    char buf[2];
4171
4172    *buf = ch;
4173    XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf));
4174
4175    return (*buf);
4176}
4177
4178static int
4179IsAlnum(int ch)
4180{
4181    return ((ch >= '0' && ch <= '9') || ToUpper(ch) != ch || ToLower(ch) != ch);
4182}
4183
4184static int
4185IsLower(int ch)
4186{
4187    char upbuf[2];
4188    char lobuf[2];
4189
4190    *upbuf = *lobuf = ch;
4191    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
4192    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
4193
4194    return (*lobuf != *upbuf && ch == *lobuf);
4195}
4196
4197static int
4198IsUpper(int ch)
4199{
4200    char upbuf[2];
4201    char lobuf[2];
4202
4203    *upbuf = *lobuf = ch;
4204    XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
4205    XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
4206
4207    return (*lobuf != *upbuf && ch == *upbuf);
4208}
4209#else
4210#define	ToLower	tolower
4211#define ToUpper	toupper
4212#define IsAlnum isalnum
4213#define IsLower islower
4214#define IsUpper isupper
4215#endif
4216
4217static void
4218CaseProc(Widget w, XEvent *event, int cmd)
4219{
4220    TextWidget ctx = (TextWidget)w;
4221    short mul = MULT(ctx);
4222    XawTextPosition left, right;
4223    XawTextBlock block;
4224    Bool changed = False;
4225    unsigned char ch, mb[sizeof(wchar_t)];
4226    int i, count;
4227
4228    if (mul > 0)
4229	right = SrcScan(ctx->text.source, left = ctx->text.insertPos,
4230			XawstAlphaNumeric, XawsdRight, mul, False);
4231    else
4232	left = SrcScan(ctx->text.source, right = ctx->text.insertPos,
4233		       XawstAlphaNumeric, XawsdLeft, 1 + -mul, False);
4234    block.firstPos = 0;
4235    block.format = _XawTextFormat(ctx);
4236    block.length = right - left;
4237    block.ptr = _XawTextGetText(ctx, left, right);
4238
4239    count = 0;
4240    if (block.format == XawFmt8Bit)
4241	for (i = 0; i < block.length; i++) {
4242	    if (!IsAlnum(*mb = (unsigned char)block.ptr[i]))
4243		count = 0;
4244	    else if (++count == 1 || cmd != CAPITALIZE) {
4245		ch = cmd == DOWNCASE ? ToLower(*mb) : ToUpper(*mb);
4246		if (ch != *mb) {
4247		    changed = True;
4248		    block.ptr[i] = ch;
4249		}
4250	    }
4251	    else if (cmd == CAPITALIZE) {
4252		if ((ch = ToLower(*mb)) != *mb) {
4253		    changed = True;
4254		    block.ptr[i] = ch;
4255		}
4256	    }
4257	}
4258    else
4259	for (i = 0; i < block.length; i++) {
4260	    wctomb((char*)mb, ((wchar_t*)block.ptr)[i]);
4261	    if (!IsAlnum(*mb))
4262		count = 0;
4263	    else if (++count == 1 || cmd != CAPITALIZE) {
4264		ch = cmd == DOWNCASE ? ToLower(*mb) : ToUpper(*mb);
4265		if (ch != *mb) {
4266		    changed = True;
4267		    ((wchar_t*)block.ptr)[i] = _Xaw_atowc(ch);
4268		}
4269	    }
4270	    else if (cmd == CAPITALIZE) {
4271		if ((ch = ToLower(*mb)) != *mb) {
4272		    changed = True;
4273		    ((wchar_t*)block.ptr)[i] = _Xaw_atowc(ch);
4274		}
4275	    }
4276	}
4277
4278    StartAction(ctx, event);
4279    if (changed && _XawTextReplace(ctx, left, right, &block) != XawEditDone)
4280	XBell(XtDisplay(ctx), 0);
4281    ctx->text.insertPos = right;
4282    EndAction(ctx);
4283
4284    XtFree(block.ptr);
4285}
4286
4287/*ARGSUSED*/
4288static void
4289CapitalizeWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
4290{
4291    CaseProc(w, event, CAPITALIZE);
4292}
4293
4294/*ARGSUSED*/
4295static void
4296DowncaseWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
4297{
4298    CaseProc(w, event, DOWNCASE);
4299}
4300
4301/*ARGSUSED*/
4302static void
4303UpcaseWord(Widget w, XEvent *event, String *params, Cardinal *num_params)
4304{
4305    CaseProc(w, event, UPCASE);
4306}
4307#undef CAPITALIZE
4308#undef DOWNCASE
4309#undef UPCASE
4310
4311XtActionsRec _XawTextActionsTable[] = {
4312  /* motion */
4313  {"forward-character",		MoveForwardChar},
4314  {"backward-character",	MoveBackwardChar},
4315  {"forward-word",		MoveForwardWord},
4316  {"backward-word",		MoveBackwardWord},
4317  {"forward-paragraph",		MoveForwardParagraph},
4318  {"backward-paragraph",	MoveBackwardParagraph},
4319  {"beginning-of-line",		MoveToLineStart},
4320  {"end-of-line",		MoveToLineEnd},
4321  {"next-line",			MoveNextLine},
4322  {"previous-line",		MovePreviousLine},
4323  {"next-page",			MoveNextPage},
4324  {"previous-page",		MovePreviousPage},
4325  {"beginning-of-file",		MoveBeginningOfFile},
4326  {"end-of-file",		MoveEndOfFile},
4327  {"scroll-one-line-up",	ScrollOneLineUp},
4328  {"scroll-one-line-down",	ScrollOneLineDown},
4329
4330  /* delete */
4331  {"delete-next-character",	DeleteForwardChar},
4332  {"delete-previous-character",	DeleteBackwardChar},
4333  {"delete-next-word",		DeleteForwardWord},
4334  {"delete-previous-word",	DeleteBackwardWord},
4335  {"delete-selection",		DeleteCurrentSelection},
4336  {"delete",			Delete},
4337
4338  /* kill */
4339  {"kill-word",			KillForwardWord},
4340  {"backward-kill-word",	KillBackwardWord},
4341  {"kill-selection",		KillCurrentSelection},
4342  {"kill-to-end-of-line",	KillToEndOfLine},
4343  {"kill-to-end-of-paragraph",	KillToEndOfParagraph},
4344
4345  /* new line */
4346  {"newline-and-indent",	InsertNewLineAndIndent},
4347  {"newline-and-backup",	InsertNewLineAndBackup},
4348  {"newline",			InsertNewLine},
4349
4350  /* selection */
4351  {"select-word",		SelectWord},
4352  {"select-all",		SelectAll},
4353  {"select-start",		SelectStart},
4354  {"select-adjust",		SelectAdjust},
4355  {"select-end",		SelectEnd},
4356  {"select-save",		SelectSave},
4357  {"extend-start",		ExtendStart},
4358  {"extend-adjust",		ExtendAdjust},
4359  {"extend-end", 		ExtendEnd},
4360  {"insert-selection",		InsertSelection},
4361
4362  /* miscellaneous */
4363  {"redraw-display",		RedrawDisplay},
4364  {"insert-file",		_XawTextInsertFile},
4365  {"search",			_XawTextSearch},
4366  {"insert-char",		InsertChar},
4367  {"insert-string",		InsertString},
4368  {"focus-in",			TextFocusIn},
4369  {"focus-out",			TextFocusOut},
4370  {"enter-window",		TextEnterWindow},
4371  {"leave-window",		TextLeaveWindow},
4372  {"display-caret",		DisplayCaret},
4373  {"multiply",			Multiply},
4374  {"form-paragraph",		FormParagraph},
4375  {"transpose-characters",	TransposeCharacters},
4376  {"set-keyboard-focus",	SetKeyboardFocus},
4377#ifndef OLDXAW
4378  {"numeric",			Numeric},
4379  {"undo",			Undo},
4380  {"keyboard-reset",		KeyboardReset},
4381  {"kill-ring-yank",		KillRingYank},
4382  {"toggle-overwrite",		ToggleOverwrite},
4383  {"indent",			Indent},
4384#endif
4385  {"no-op",			NoOp},
4386
4387  /* case transformations */
4388  {"capitalize-word",		CapitalizeWord},
4389  {"downcase-word",		DowncaseWord},
4390  {"upcase-word",		UpcaseWord},
4391
4392  /* action to bind translations for text dialogs */
4393  {"InsertFileAction",		_XawTextInsertFileAction},
4394  {"DoSearchAction",		_XawTextDoSearchAction},
4395  {"DoReplaceAction",		_XawTextDoReplaceAction},
4396  {"SetField",			_XawTextSetField},
4397  {"PopdownSearchAction",	_XawTextPopdownSearchAction},
4398
4399  /* reconnect to Input Method */
4400  {"reconnect-im",		Reconnect} /* Li Yuhong, Omron KK, 1991 */
4401};
4402
4403Cardinal _XawTextActionsTableCount = XtNumber(_XawTextActionsTable);
4404