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