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