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