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