TextSrc.c revision ab902922
1/* $Xorg: TextSrc.c,v 1.5 2001/02/09 02:03:47 xorgcvs Exp $ */
2/*
3
4Copyright 1989, 1994, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27
28/* $XFree86: xc/lib/Xaw/TextSrc.c,v 1.33 2002/09/08 02:29:47 paulo Exp $ */
29
30/*
31 * Author:  Chris Peterson, MIT X Consortium.
32 * Much code taken from X11R3 String and Disk Sources.
33 */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38#include <stdio.h>
39#include <ctype.h>
40#include <X11/IntrinsicP.h>
41#include <X11/StringDefs.h>
42#include <X11/Xfuncs.h>
43#include <X11/Xutil.h>
44#include <X11/Xmu/Atoms.h>
45#include <X11/Xmu/CharSet.h>
46#include <X11/Xaw/TextSrcP.h>
47#include <X11/Xaw/XawInit.h>
48#include "XawI18n.h"
49#include "Private.h"
50
51#ifndef OLDXAW
52#define UNDO_DEPTH	16384
53
54#define ANCHORS_DIST	4096	/* default distance between anchors */
55
56/*
57 * Types
58 */
59typedef struct {
60    XawTextPosition position;
61    char *buffer;
62    unsigned length;
63    unsigned refcount;
64    unsigned long format;
65} XawTextUndoBuffer;
66
67typedef struct _XawTextUndoList XawTextUndoList;
68struct _XawTextUndoList {
69    XawTextUndoBuffer *left, *right;
70    XawTextUndoList *undo, *redo;
71};
72
73struct _XawTextUndo {
74    XawTextUndoBuffer **undo;
75    unsigned num_undo;
76    XawTextUndoList *list, *pointer, *end_mark, *head;
77    unsigned num_list;
78    XawTextScanDirection dir;
79    XawTextUndoBuffer *l_save, *r_save;
80    XawTextUndoList *u_save;
81    XawTextUndoBuffer *l_no_change, *r_no_change;
82    int merge;
83    int erase;		/* there are two types of erases */
84};
85#endif	/* OLDXAW */
86
87/*
88 * Class Methods
89 */
90static Boolean ConvertSelection(Widget, Atom*, Atom*, Atom*, XtPointer*,
91				unsigned long*, int*);
92static XawTextPosition Read(Widget, XawTextPosition, XawTextBlock*, int);
93static int  Replace(Widget, XawTextPosition, XawTextPosition, XawTextBlock*);
94static  XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType,
95			      XawTextScanDirection, int, Bool);
96static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection,
97			      XawTextBlock*);
98static void SetSelection(Widget, XawTextPosition, XawTextPosition, Atom);
99static void XawTextSrcClassInitialize(void);
100static void XawTextSrcClassPartInitialize(WidgetClass);
101static void XawTextSrcInitialize(Widget, Widget, ArgList, Cardinal*);
102static void XawTextSrcDestroy(Widget);
103static Boolean XawTextSrcSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
104/*
105 * Prototypes
106 */
107static void CvtStringToEditMode(XrmValuePtr, Cardinal*,
108				 XrmValuePtr, XrmValuePtr);
109static Boolean CvtEditModeToString(Display*, XrmValuePtr, Cardinal*,
110				   XrmValuePtr, XrmValuePtr, XtPointer*);
111#ifndef OLDXAW
112static void FreeUndoBuffer(XawTextUndo*);
113static void UndoGC(XawTextUndo*);
114static void TellSourceChanged(TextSrcObject, XawTextPosition, XawTextPosition,
115			      XawTextBlock*, int);
116Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);
117Bool _XawTextSrcToggleUndo(TextSrcObject);
118XawTextAnchor *_XawTextSourceFindAnchor(Widget, XawTextPosition);
119
120/*
121 * External
122 */
123void _XawSourceAddText(Widget, Widget);
124void _XawSourceRemoveText(Widget, Widget, Bool);
125Bool _XawTextSourceNewLineAtEOF(Widget);
126void _XawSourceSetUndoErase(TextSrcObject, int);
127void _XawSourceSetUndoMerge(TextSrcObject, Bool);
128#endif /* OLDXAW */
129
130/*
131 * Defined in Text.c
132 */
133char *_XawTextGetText(TextWidget, XawTextPosition, XawTextPosition);
134void _XawTextSourceChanged(Widget, XawTextPosition, XawTextPosition,
135			   XawTextBlock*, int);
136
137/*
138 * Initialization
139 */
140#define offset(field) XtOffsetOf(TextSrcRec, textSrc.field)
141static XtResource resources[] = {
142  {
143    XtNeditType,
144    XtCEditType,
145    XtREditMode,
146    sizeof(XawTextEditType),
147    offset(edit_mode),
148    XtRString,
149    "read"
150  },
151#ifndef OLDXAW
152  {
153    XtNcallback,
154    XtCCallback,
155    XtRCallback,
156    sizeof(XtPointer),
157    offset(callback),
158    XtRCallback,
159    NULL
160  },
161  {
162    XtNsourceChanged,
163    XtCChanged,
164    XtRBoolean,
165    sizeof(Boolean),
166    offset(changed),
167    XtRImmediate,
168    (XtPointer)False
169  },
170  {
171    XtNenableUndo,
172    XtCUndo,
173    XtRBoolean,
174    sizeof(Boolean),
175    offset(enable_undo),
176    XtRImmediate,
177    (XtPointer)False
178  },
179  {
180    XtNpropertyCallback,
181    XtCCallback,
182    XtRCallback,
183    sizeof(XtPointer),
184    offset(property_callback),
185    XtRCallback,
186    NULL
187  },
188#endif /* OLDXAW */
189};
190#undef offset
191
192#define Superclass	(&objectClassRec)
193TextSrcClassRec textSrcClassRec = {
194  /* object */
195  {
196    (WidgetClass)Superclass,		/* superclass */
197    "TextSrc",				/* class_name */
198    sizeof(TextSrcRec),			/* widget_size */
199    XawTextSrcClassInitialize,		/* class_initialize */
200    XawTextSrcClassPartInitialize,	/* class_part_initialize */
201    False,				/* class_inited */
202    XawTextSrcInitialize,		/* initialize */
203    NULL,				/* initialize_hook */
204    NULL,				/* realize */
205    NULL,				/* actions */
206    0,					/* num_actions */
207    resources,				/* resources */
208    XtNumber(resources),		/* num_resources */
209    NULLQUARK,				/* xrm_class */
210    False,				/* compress_motion */
211    False,				/* compress_exposure */
212    False,				/* compress_enterleave */
213    False,				/* visible_interest */
214    XawTextSrcDestroy,			/* destroy */
215    NULL,				/* resize */
216    NULL,				/* expose */
217    XawTextSrcSetValues,		/* set_values */
218    NULL,				/* set_values_hook */
219    NULL,				/* set_values_almost */
220    NULL,				/* get_values_hook */
221    NULL,				/* accept_focus */
222    XtVersion,				/* version */
223    NULL,				/* callback_private */
224    NULL,				/* tm_table */
225    NULL,				/* query_geometry */
226    NULL,				/* display_accelerator */
227    NULL,				/* extension */
228  },
229  /* text_src */
230  {
231    Read,				/* Read */
232    Replace,				/* Replace */
233    Scan,				/* Scan */
234    Search,				/* Search */
235    SetSelection,			/* SetSelection */
236    ConvertSelection,			/* ConvertSelection */
237  },
238};
239
240WidgetClass textSrcObjectClass = (WidgetClass)&textSrcClassRec;
241
242static XrmQuark QRead, QAppend, QEdit;
243#ifndef OLDXAW
244static char *SrcNL = "\n";
245static wchar_t SrcWNL[2];
246#endif
247
248/*
249 * Implementation
250 */
251static void
252XawTextSrcClassInitialize(void)
253{
254    XawInitializeWidgetSet();
255
256#ifndef OLDXAW
257    SrcWNL[0] = _Xaw_atowc(XawLF);
258    SrcWNL[1] = 0;
259#endif
260    QRead   = XrmPermStringToQuark(XtEtextRead);
261    QAppend = XrmPermStringToQuark(XtEtextAppend);
262    QEdit   = XrmPermStringToQuark(XtEtextEdit);
263    XtAddConverter(XtRString, XtREditMode,   CvtStringToEditMode,   NULL, 0);
264    XtSetTypeConverter(XtREditMode, XtRString, CvtEditModeToString, NULL, 0,
265		       XtCacheNone, NULL);
266}
267
268static void
269XawTextSrcClassPartInitialize(WidgetClass wc)
270{
271    TextSrcObjectClass t_src, superC;
272
273    t_src = (TextSrcObjectClass)wc;
274    superC = (TextSrcObjectClass)t_src->object_class.superclass;
275
276    /*
277     * We don't need to check for null super since we'll get to TextSrc
278     * eventually
279     */
280    if (t_src->textSrc_class.Read == XtInheritRead)
281	t_src->textSrc_class.Read = superC->textSrc_class.Read;
282
283    if (t_src->textSrc_class.Replace == XtInheritReplace)
284	t_src->textSrc_class.Replace = superC->textSrc_class.Replace;
285
286    if (t_src->textSrc_class.Scan == XtInheritScan)
287	t_src->textSrc_class.Scan = superC->textSrc_class.Scan;
288
289    if (t_src->textSrc_class.Search == XtInheritSearch)
290	t_src->textSrc_class.Search = superC->textSrc_class.Search;
291
292    if (t_src->textSrc_class.SetSelection == XtInheritSetSelection)
293	t_src->textSrc_class.SetSelection = superC->textSrc_class.SetSelection;
294
295    if (t_src->textSrc_class.ConvertSelection == XtInheritConvertSelection)
296	t_src->textSrc_class.ConvertSelection =
297	    superC->textSrc_class.ConvertSelection;
298}
299
300/*ARGSUSED*/
301static void
302XawTextSrcInitialize(Widget request, Widget cnew,
303		     ArgList args, Cardinal *num_args)
304{
305#ifndef OLDXAW
306    TextSrcObject src = (TextSrcObject)cnew;
307
308    if (src->textSrc.enable_undo) {
309	src->textSrc.undo = (XawTextUndo*)XtCalloc(1, sizeof(XawTextUndo));
310	src->textSrc.undo->dir = XawsdLeft;
311    }
312    else
313	src->textSrc.undo = NULL;
314    src->textSrc.undo_state = False;
315    if (XtIsSubclass(XtParent(cnew), textWidgetClass)) {
316	src->textSrc.text = (WidgetList)XtMalloc(sizeof(Widget*));
317	src->textSrc.text[0] = XtParent(cnew);
318	src->textSrc.num_text = 1;
319    }
320    else {
321	src->textSrc.text = NULL;
322	src->textSrc.num_text = 0;
323    }
324
325    src->textSrc.anchors = NULL;
326    src->textSrc.num_anchors = 0;
327    (void)XawTextSourceAddAnchor(cnew, 0);
328#endif /* OLDXAW */
329}
330
331static void
332XawTextSrcDestroy(Widget w)
333{
334#ifndef OLDXAW
335    TextSrcObject src = (TextSrcObject)w;
336
337    if (src->textSrc.enable_undo) {
338	FreeUndoBuffer(src->textSrc.undo);
339	XtFree((char*)src->textSrc.undo);
340    }
341    XtFree((char*)src->textSrc.text);
342
343    if (src->textSrc.num_anchors) {
344	XawTextEntity *entity, *enext;
345	int i;
346
347	for (i = 0; i < src->textSrc.num_anchors; i++) {
348	    entity = src->textSrc.anchors[i]->entities;
349	    while (entity) {
350		enext = entity->next;
351		XtFree((XtPointer)entity);
352		entity = enext;
353	    }
354	    XtFree((XtPointer)src->textSrc.anchors[i]);
355	}
356	XtFree((XtPointer)src->textSrc.anchors);
357    }
358#endif /* OLDXAW */
359}
360
361/*ARGSUSED*/
362static Boolean
363XawTextSrcSetValues(Widget current, Widget request, Widget cnew,
364		    ArgList args, Cardinal *num_args)
365{
366#ifndef OLDXAW
367    TextSrcObject oldtw = (TextSrcObject)current;
368    TextSrcObject newtw = (TextSrcObject)cnew;
369
370    if (oldtw->textSrc.enable_undo != newtw->textSrc.enable_undo) {
371	if (newtw->textSrc.enable_undo) {
372	    newtw->textSrc.undo = (XawTextUndo*)
373		XtCalloc(1, sizeof(XawTextUndo));
374	    newtw->textSrc.undo->dir = XawsdLeft;
375	}
376	else {
377	    FreeUndoBuffer(newtw->textSrc.undo);
378	    XtFree((char*)newtw->textSrc.undo);
379	    newtw->textSrc.undo = NULL;
380	}
381    }
382    if (oldtw->textSrc.changed != newtw->textSrc.changed) {
383	if (newtw->textSrc.enable_undo) {
384	    if (newtw->textSrc.undo->list) {
385		newtw->textSrc.undo->l_no_change =
386		    newtw->textSrc.undo->list->left;
387		newtw->textSrc.undo->r_no_change =
388		    newtw->textSrc.undo->list->right;
389	    }
390	    else
391		newtw->textSrc.undo->l_no_change =
392		    newtw->textSrc.undo->r_no_change = NULL;
393	}
394    }
395#endif /* OLDXAW */
396    return (False);
397}
398
399/*
400 * Function:
401 *	Read
402 *
403 * Parameters:
404 *	w      - TextSrc Object
405 *	pos    - position of the text to retreive
406 *	text   - text block that will contain returned text
407 *	length - maximum number of characters to read
408 *
409 * Description:
410 *	This function reads the source.
411 *
412 * Returns:
413 *	The character position following the retrieved text.
414 */
415/*ARGSUSED*/
416static XawTextPosition
417Read(Widget w, XawTextPosition pos, XawTextBlock *text, int length)
418{
419    return ((XawTextPosition)0);
420}
421
422/*
423 * Function:
424 *	Replace
425 *
426 * Parameters:
427 *	src	 - Text Source Object
428 *	startPos - ends of text that will be removed
429 *	endPos	 - ""
430 *	text	 - new text to be inserted into buffer at startPos
431 *
432 * Description:
433 *	Replaces a block of text with new text.
434 */
435/*ARGSUSED*/
436static int
437Replace(Widget w, XawTextPosition startPos, XawTextPosition endPos,
438	XawTextBlock *text)
439{
440    return (XawEditError);
441}
442
443/*
444 * Function:
445 *	Scan
446 *
447 * Parameters:
448 *	w	 - TextSrc Object
449 *	position - position to start scanning
450 *	type	 - type of thing to scan for
451 *	dir	 - direction to scan
452 *	count	 - which occurance if this thing to search for
453 *		 include - whether or not to include the character found in
454 *		   the position that is returned
455 *
456 * Description:
457 *	Scans the text source for the number and type of item specified.
458 */
459/*ARGSUSED*/
460static XawTextPosition
461Scan(Widget w, XawTextPosition position, XawTextScanType type,
462     XawTextScanDirection dir, int count, Bool include)
463{
464    return ((XawTextPosition)0);
465}
466
467/*
468 * Function:
469 *	Search
470 *
471 * Parameters:
472 *	w	 - TextSource Object
473 *	position - position to start searching
474 *	dir	 - direction to search
475 *	text	 - the text block to search for
476 *
477 * Description:
478 *	Searchs the text source for the text block passed
479 */
480/*ARGSUSED*/
481static XawTextPosition
482Search(Widget w, XawTextPosition position, XawTextScanDirection dir,
483       XawTextBlock *text)
484{
485    return (XawTextSearchError);
486}
487
488/*ARGSUSED*/
489static Boolean
490ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
491		 XtPointer *value, unsigned long *length, int *format)
492{
493    return (False);
494}
495
496/*ARGSUSED*/
497static void
498SetSelection(Widget w, XawTextPosition left, XawTextPosition right,
499	     Atom selection)
500{
501}
502
503/*ARGSUSED*/
504static void
505CvtStringToEditMode(XrmValuePtr args, Cardinal *num_args,
506		    XrmValuePtr fromVal, XrmValuePtr toVal)
507{
508    static XawTextEditType editType;
509    XrmQuark	q;
510    char name[7];
511
512    XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name));
513    q = XrmStringToQuark(name);
514
515    if (q == QRead)
516	editType = XawtextRead;
517    else if (q == QAppend)
518	editType = XawtextAppend;
519    else if (q == QEdit)
520	editType = XawtextEdit;
521    else {
522	toVal->size = 0;
523	toVal->addr = NULL;
524	XtStringConversionWarning((char *)fromVal->addr, XtREditMode);
525    }
526    toVal->size = sizeof(XawTextEditType);
527    toVal->addr = (XPointer)&editType;
528}
529
530/*ARGSUSED*/
531static Boolean
532CvtEditModeToString(Display *dpy, XrmValuePtr args, Cardinal *num_args,
533		    XrmValuePtr fromVal, XrmValuePtr toVal,
534		    XtPointer *data)
535{
536    static String buffer;
537    Cardinal size;
538
539    switch (*(XawTextEditType *)fromVal->addr) {
540	case XawtextAppend:
541	case XawtextRead:
542	    buffer = XtEtextRead;
543	    break;
544	    buffer = XtEtextAppend;
545	    break;
546	case XawtextEdit:
547	    buffer = XtEtextEdit;
548	    break;
549	default:
550	    XawTypeToStringWarning(dpy, XtREditMode);
551	    toVal->addr = NULL;
552	    toVal->size = 0;
553	    return (False);
554    }
555
556    size = strlen(buffer) + 1;
557    if (toVal->addr != NULL) {
558	if (toVal->size < size) {
559	    toVal->size = size;
560	    return (False);
561	}
562	strcpy((char *)toVal->addr, buffer);
563    }
564    else
565	toVal->addr = (XPointer)buffer;
566    toVal->size = sizeof(String);
567
568    return (True);
569}
570
571#ifndef OLDXAW
572Bool
573_XawTextSourceNewLineAtEOF(Widget w)
574{
575    TextSrcObject src = (TextSrcObject)w;
576    XawTextBlock text;
577
578    text.firstPos = 0;
579    if ((text.format = src->textSrc.text_format) == XawFmt8Bit)
580	text.ptr = SrcNL;
581    else
582	text.ptr = (char*)SrcWNL;
583    text.length = 1;
584
585    return (XawTextSourceSearch(w, XawTextSourceScan(w, 0, XawstAll,
586						     XawsdRight, 1, True) - 1,
587				XawsdRight, &text) != XawTextSearchError);
588}
589
590void
591_XawSourceAddText(Widget source, Widget text)
592{
593    TextSrcObject src = (TextSrcObject)source;
594    Bool found = False;
595    Cardinal i;
596
597    for (i = 0; i < src->textSrc.num_text; i++)
598	if (src->textSrc.text[i] == text) {
599	    found = True;
600	    break;
601	}
602
603    if (!found) {
604	src->textSrc.text = (WidgetList)
605	    XtRealloc((char*)src->textSrc.text,
606		      sizeof(Widget) * (src->textSrc.num_text + 1));
607	src->textSrc.text[src->textSrc.num_text++] = text;
608    }
609}
610
611void
612_XawSourceRemoveText(Widget source, Widget text, Bool destroy)
613{
614    TextSrcObject src = (TextSrcObject)source;
615    Bool found = False;
616    Cardinal i;
617
618    if (src == NULL)
619	return;
620
621    for (i = 0; i < src->textSrc.num_text; i++)
622	if (src->textSrc.text[i] == text) {
623	    found = True;
624	    break;
625	}
626
627    if (found) {
628	if (--src->textSrc.num_text == 0) {
629	    if (destroy) {
630		XtDestroyWidget(source);
631		return;
632	    }
633	    else {
634		XtFree((char*)src->textSrc.text);
635		src->textSrc.text = NULL;	/* for realloc "magic" */
636	    }
637	}
638	else if (i < src->textSrc.num_text)
639	    memmove(&src->textSrc.text[i], &src->textSrc.text[i + 1],
640		    sizeof(Widget) * (src->textSrc.num_text - i));
641    }
642}
643#endif /* OLDXAW */
644
645/*
646 * Function:
647 *	XawTextSourceRead
648 *
649 * Parameters:
650 *	w      - TextSrc Object
651 *	pos    - position of the text to retrieve
652 *	text   - text block that will contain returned text (return)
653 *	length - maximum number of characters to read
654 *
655 * Description:
656 *	This function reads the source.
657 *
658 * Returns:
659 *	The number of characters read into the buffer
660 */
661XawTextPosition
662XawTextSourceRead(Widget w, XawTextPosition pos, XawTextBlock *text,
663		  int length)
664{
665  TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
666
667  return ((*cclass->textSrc_class.Read)(w, pos, text, length));
668}
669
670#ifndef OLDXAW
671static void
672TellSourceChanged(TextSrcObject src, XawTextPosition left,
673		  XawTextPosition right, XawTextBlock *block, int lines)
674{
675    Cardinal i;
676
677    for (i = 0; i < src->textSrc.num_text; i++)
678	_XawTextSourceChanged(src->textSrc.text[i], left, right, block, lines);
679}
680
681/*
682 * This function is required because there is no way to diferentiate
683 * if the first erase was generated by a backward-kill-char and the
684 * second by a forward-kill-char (or vice-versa) from XawTextSourceReplace.
685 * It is only possible to diferentiate after the second character is
686 * killed, but then, it is too late.
687 */
688void
689_XawSourceSetUndoErase(TextSrcObject src, int value)
690{
691    if (src && src->textSrc.enable_undo)
692	src->textSrc.undo->erase = value;
693}
694
695/*
696 * To diferentiate insert-char's separeted by cursor movements.
697 */
698void
699_XawSourceSetUndoMerge(TextSrcObject src, Bool state)
700{
701    if (src && src->textSrc.enable_undo)
702	src->textSrc.undo->merge += state ? 1 : -1;
703}
704#endif /* OLDXAW */
705
706/*
707 * Public Functions
708 */
709/*
710 * Function:
711 *	XawTextSourceReplace
712 *
713 * Parameters:
714 *	src	 - Text Source Object
715 *	startPos - ends of text that will be removed
716 *	endPos	 - ""
717 *	text	 - new text to be inserted into buffer at startPos
718 *
719 * Description:
720 *	Replaces a block of text with new text.
721 *
722 * Returns:
723 *	XawEditError or XawEditDone.
724 */
725/*ARGSUSED*/
726int
727XawTextSourceReplace(Widget w, XawTextPosition left,
728		      XawTextPosition right, XawTextBlock *block)
729{
730    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
731#ifndef OLDXAW
732    TextSrcObject src = (TextSrcObject)w;
733    XawTextUndoBuffer *l_state, *r_state;
734    XawTextUndoList *undo;
735    Bool enable_undo;
736    XawTextPosition start, end;
737    int i, error, lines = 0;
738
739    if (src->textSrc.edit_mode == XawtextRead)
740	return (XawEditError);
741
742    enable_undo = src->textSrc.enable_undo && src->textSrc.undo_state == False;
743    if (enable_undo) {
744	unsigned size, total;
745
746	if (src->textSrc.undo->l_save) {
747	    l_state = src->textSrc.undo->l_save;
748	    src->textSrc.undo->l_save = NULL;
749	}
750	else
751	    l_state = XtNew(XawTextUndoBuffer);
752	l_state->refcount = 1;
753	l_state->position = left;
754	if (left < right) {
755	    Widget ctx = NULL;
756
757	    for (i = 0; i < src->textSrc.num_text; i++)
758		if (XtIsSubclass(src->textSrc.text[i], textWidgetClass)) {
759		    ctx = src->textSrc.text[i];
760		    break;
761		}
762	    l_state->buffer = _XawTextGetText((TextWidget)ctx, left, right);
763	    l_state->length = right - left;
764	}
765	else {
766	    l_state->length = 0;
767	    l_state->buffer = NULL;
768	}
769	l_state->format = src->textSrc.text_format;
770	if (l_state->length == 1) {
771	    if (l_state->format == XawFmtWide &&
772		*(wchar_t*)l_state->buffer == *SrcWNL) {
773		XtFree(l_state->buffer);
774		l_state->buffer = (char*)SrcWNL;
775	    }
776	    else if (*l_state->buffer == '\n') {
777		XtFree(l_state->buffer);
778		l_state->buffer = SrcNL;
779	    }
780	}
781
782	if (src->textSrc.undo->r_save) {
783	    r_state = src->textSrc.undo->r_save;
784	    src->textSrc.undo->r_save = NULL;
785	}
786	else
787	    r_state = XtNew(XawTextUndoBuffer);
788	r_state->refcount = 1;
789	r_state->position = left;
790	r_state->format = block->format;
791	size = block->format == XawFmtWide ? sizeof(wchar_t) : sizeof(char);
792	total = size * block->length;
793	r_state->length = block->length;
794	r_state->buffer = NULL;
795	if (total == size) {
796	    if (r_state->format == XawFmtWide &&
797		*(wchar_t*)block->ptr == *SrcWNL)
798		r_state->buffer = (char*)SrcWNL;
799	    else if (*block->ptr == '\n')
800		r_state->buffer = SrcNL;
801	}
802	if (total && !r_state->buffer) {
803	    r_state->buffer = XtMalloc(total);
804	    memcpy(r_state->buffer, block->ptr, total);
805	}
806
807	if (src->textSrc.undo->u_save) {
808	    undo = src->textSrc.undo->u_save;
809	    src->textSrc.undo->u_save = NULL;
810	}
811	else
812	    undo = XtNew(XawTextUndoList);
813	undo->left = l_state;
814	undo->right = r_state;
815	undo->undo = src->textSrc.undo->list;
816	undo->redo = NULL;
817    }
818    else {
819	undo = NULL;
820	l_state = r_state = NULL;
821    }
822
823#define	LARGE_VALUE	262144	/* 256 K */
824    /* optimization, to avoid long delays recalculating the line number
825     * when editing huge files
826     */
827    if (left > LARGE_VALUE) {
828	start = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False);
829	for (i = 0; i < src->textSrc.num_text; i++) {
830	    TextWidget tw = (TextWidget)src->textSrc.text[i];
831
832	    if (left <= tw->text.lt.top &&
833		left + block->length - (right - left) > tw->text.lt.top)
834		_XawTextBuildLineTable(tw, start, False);
835	}
836    }
837#undef LARGE_VALUE
838
839    start = left;
840    end = right;
841    while (start < end) {
842	start = XawTextSourceScan(w, start, XawstEOL, XawsdRight, 1, True);
843	if (start <= end) {
844	    --lines;
845	    if (start == XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True)) {
846		lines += !_XawTextSourceNewLineAtEOF(w);
847		break;
848	    }
849	}
850    }
851#else
852    int error;
853#endif /* OLDXAW */
854
855    error = (*cclass->textSrc_class.Replace)(w, left, right, block);
856
857#ifndef OLDXAW
858    if (error != XawEditDone) {
859	if (enable_undo) {
860	    if (l_state->buffer) {
861		if (l_state->buffer != SrcNL && l_state->buffer != (char*)SrcWNL)
862		    XtFree(l_state->buffer);
863		l_state->buffer = NULL;
864	    }
865	     src->textSrc.undo->l_save = l_state;
866	     if (r_state->buffer) {
867		if (r_state->buffer != SrcNL && r_state->buffer != (char*)SrcWNL)
868		    XtFree(r_state->buffer);
869		r_state->buffer = NULL;
870	    }
871	    src->textSrc.undo->r_save = r_state;
872
873	    src->textSrc.undo->u_save = undo;
874	}
875    }
876    else if (enable_undo) {
877	XawTextUndoList *list = src->textSrc.undo->list;
878	XawTextUndoBuffer *unl, *lnl;
879	int erase = undo->right->length == 0 && undo->left->length == 1 && list
880		    && list->right->length == 0;
881
882	if (erase) {
883	    erase = list->left->position - 1 == undo->left->position ? -1 :
884		    list->left->position == undo->left->position ? 1 : 0;
885	    if (src->textSrc.undo->erase && erase != src->textSrc.undo->erase)
886		erase = 0;
887	    else
888		src->textSrc.undo->erase = erase;
889	}
890
891	if (erase) {
892	    unl = l_state;
893	    lnl = list->left;
894	}
895	else {
896	    unl = r_state;
897	    lnl = list ? list->right : NULL;
898	}
899
900	/* Try to merge the undo buffers */
901	if (src->textSrc.undo->merge > 0 && ((erase ||
902	     (list && ((list->left->length == 0 && undo->left->length == 0) ||
903		       (list->left->length == list->right->length &&
904			undo->left->length == 1)) &&
905	      undo->right->length == 1 &&
906	      list->right->position + list->right->length
907	      == undo->right->position))
908	    && src->textSrc.undo->pointer == list
909	    && unl->format == list->right->format
910	    && ((unl->format == XawFmt8Bit && unl->buffer[0] != XawLF) ||
911		(unl->format == XawFmtWide &&
912		 *(wchar_t*)(unl->buffer) != _Xaw_atowc(XawLF)))
913	    && ((lnl->format == XawFmt8Bit && lnl->buffer[0] != XawLF) ||
914		(lnl->format == XawFmtWide &&
915		 *(wchar_t*)(lnl->buffer) != _Xaw_atowc(XawLF))))) {
916	    unsigned size = lnl->format == XawFmtWide ?
917		sizeof(wchar_t) : sizeof(char);
918
919	    if (!erase) {
920		list->right->buffer = XtRealloc(list->right->buffer,
921						(list->right->length + 1) * size);
922		memcpy(list->right->buffer + list->right->length * size,
923		       undo->right->buffer, size);
924		++list->right->length;
925		XtFree(r_state->buffer);
926	    }
927	    else if (erase < 0) {
928		--list->left->position;
929		--list->right->position;
930	    }
931
932	    src->textSrc.undo->l_save = l_state;
933	    src->textSrc.undo->r_save = r_state;
934	    src->textSrc.undo->u_save = undo;
935
936	    if (list->left->length) {
937		list->left->buffer = XtRealloc(list->left->buffer,
938					       (list->left->length + 1) * size);
939		if (erase >= 0)
940		    memcpy(list->left->buffer + list->left->length * size,
941			   undo->left->buffer, size);
942		else {
943		    /* use memmove, since strings overlap */
944		    memmove(list->left->buffer + size, list->left->buffer,
945			    list->left->length * size);
946		    memcpy(list->left->buffer, undo->left->buffer, size);
947		}
948		++list->left->length;
949		if (l_state->buffer != SrcNL && l_state->buffer != (char*)SrcWNL)
950		    XtFree(l_state->buffer);
951	    }
952
953	    if (src->textSrc.undo->num_list >= UNDO_DEPTH)
954		UndoGC(src->textSrc.undo);
955	}
956	else {
957	    src->textSrc.undo->undo = (XawTextUndoBuffer**)
958		XtRealloc((char*)src->textSrc.undo->undo,
959			  (2 + src->textSrc.undo->num_undo)
960			  * sizeof(XawTextUndoBuffer));
961	    src->textSrc.undo->undo[src->textSrc.undo->num_undo++] = l_state;
962	    src->textSrc.undo->undo[src->textSrc.undo->num_undo++] = r_state;
963
964	    if (src->textSrc.undo->list)
965		src->textSrc.undo->list->redo = undo;
966	    else
967		src->textSrc.undo->head = undo;
968
969	    src->textSrc.undo->merge = l_state->length <= 1 &&
970				       r_state->length <= 1;
971
972	    src->textSrc.undo->list = src->textSrc.undo->pointer =
973		src->textSrc.undo->end_mark = undo;
974
975	    if (++src->textSrc.undo->num_list >= UNDO_DEPTH)
976		UndoGC(src->textSrc.undo);
977	}
978	src->textSrc.undo->dir = XawsdLeft;
979	if (!src->textSrc.changed) {
980	    src->textSrc.undo->l_no_change = src->textSrc.undo->list->right;
981	    src->textSrc.undo->r_no_change = src->textSrc.undo->list->left;
982	    src->textSrc.changed = True;
983	}
984    }
985    else if (!src->textSrc.enable_undo)
986	src->textSrc.changed = True;
987
988    if (error == XawEditDone) {
989	XawTextPropertyInfo info;
990	XawTextAnchor *anchor;
991
992	/* find anchor and index */
993	/* XXX index (i) could be returned by XawTextSourceFindAnchor
994	 * or similar function, to speed up */
995	if ((anchor = XawTextSourceFindAnchor(w, left))) {
996	    XawTextEntity *eprev, *entity, *enext;
997	    XawTextPosition offset = 0, diff = block->length - (right - left);
998
999	    for (i = 0; i < src->textSrc.num_anchors; i++)
1000		if (src->textSrc.anchors[i] == anchor)
1001		    break;
1002	    if (anchor->cache && anchor->position + anchor->cache->offset +
1003		anchor->cache->length <= left)
1004		eprev = entity = anchor->cache;
1005	    else
1006		eprev = entity = anchor->entities;
1007	    while (entity) {
1008		offset = anchor->position + entity->offset;
1009
1010		if (offset > left)
1011		    break;
1012		if (offset + entity->length > left)
1013		    break;
1014
1015		eprev = entity;
1016		entity = entity->next;
1017	    }
1018
1019	    /* try to do the right thing here (and most likely correct), but
1020	     * other code needs to check what was done */
1021
1022	    /* adjust entity length */
1023	    if (entity && offset <= left) {
1024		if (offset + entity->length < right)
1025		    entity->length = left - offset + block->length;
1026		else
1027		    entity->length += diff;
1028
1029		if (entity->length == 0) {
1030		    enext = entity->next;
1031		    eprev->next = enext;
1032		    anchor->cache = NULL;
1033		    XtFree((XtPointer)entity);
1034		    if (entity == anchor->entities) {
1035			if ((anchor->entities = enext) == NULL) {
1036			    eprev = NULL;
1037			    anchor = XawTextSourceRemoveAnchor(w, anchor);
1038			    entity = anchor ? anchor->entities : NULL;
1039			}
1040			else
1041			    eprev = entity = enext;
1042		    }
1043		    else
1044			entity = enext;
1045		}
1046		else {
1047		    eprev = entity;
1048		    entity = entity->next;
1049		}
1050	    }
1051
1052	    while (anchor) {
1053		while (entity) {
1054		    offset = anchor->position + entity->offset + entity->length;
1055
1056		    if (offset > right) {
1057			entity->length = XawMin(entity->length, offset - right);
1058			goto exit_anchor_loop;
1059		    }
1060
1061		    enext = entity->next;
1062		    if (eprev)
1063			eprev->next = enext;
1064		    XtFree((XtPointer)entity);
1065		    anchor->cache = NULL;
1066		    if (entity == anchor->entities) {
1067			eprev = NULL;
1068			if ((anchor->entities = enext) == NULL) {
1069			    if (i == 0)
1070				++i;
1071			    else if (i < --src->textSrc.num_anchors) {
1072				memmove(&src->textSrc.anchors[i],
1073					&src->textSrc.anchors[i + 1],
1074					(src->textSrc.num_anchors - i) *
1075					sizeof(XawTextAnchor*));
1076				XtFree((XtPointer)anchor);
1077			    }
1078			    if (i >= src->textSrc.num_anchors) {
1079				anchor = NULL;
1080				entity = NULL;
1081				break;
1082			    }
1083			    anchor = src->textSrc.anchors[i];
1084			    entity = anchor->entities;
1085			    continue;
1086			}
1087		    }
1088		    entity = enext;
1089		}
1090		if (i + 1 < src->textSrc.num_anchors) {
1091		    anchor = src->textSrc.anchors[++i];
1092		    entity = anchor->entities;
1093		    eprev = NULL;
1094		}
1095		else {
1096		    anchor = NULL;
1097		    break;
1098		}
1099		eprev = NULL;
1100	    }
1101
1102exit_anchor_loop:
1103	    if (anchor) {
1104		XawTextAnchor *aprev;
1105
1106		if (anchor->position >= XawMax(right, left + block->length))
1107		    anchor->position += diff;
1108		else if (anchor->position > left &&
1109			 (aprev = XawTextSourcePrevAnchor(w, anchor))) {
1110		    XawTextPosition tmp = anchor->position - aprev->position;
1111
1112		    if (diff) {
1113			while (entity) {
1114			    entity->offset += diff;
1115			    entity = entity->next;
1116			}
1117		    }
1118		    entity = anchor->entities;
1119		    while (entity) {
1120			entity->offset += tmp;
1121			entity = entity->next;
1122		    }
1123		    if ((entity = aprev->entities) == NULL)
1124			aprev->entities = anchor->entities;
1125		    else {
1126			while (entity->next)
1127			    entity = entity->next;
1128			entity->next = anchor->entities;
1129		    }
1130		    anchor->entities = NULL;
1131		    (void)XawTextSourceRemoveAnchor(w, anchor);
1132		    --i;
1133		}
1134		else if (diff) {
1135		    while (entity) {
1136			entity->offset += diff;
1137			entity = entity->next;
1138		    }
1139		}
1140	    }
1141
1142	    if (diff) {
1143		/*   The first anchor is never removed, and should
1144		 * have position 0.
1145		 *   i should be -1 if attempted to removed the first
1146		 * anchor, what can be caused when removing a chunk
1147		 * of text of the first entity.
1148		 * */
1149		if (++i == 0) {
1150		    anchor = src->textSrc.anchors[0];
1151		    eprev = entity = anchor->entities;
1152		    while (entity) {
1153			enext = entity->next;
1154			if (entity->offset + entity->length <= -diff)
1155			    XtFree((XtPointer)entity);
1156			else
1157			    break;
1158			entity = enext;
1159		    }
1160		    if (eprev != entity) {
1161			anchor->cache = NULL;
1162			if ((anchor->entities = entity) != NULL) {
1163			    if ((entity->offset += diff) < 0) {
1164				entity->length += entity->offset;
1165				entity->offset = 0;
1166			    }
1167			}
1168		    }
1169		    ++i;
1170		}
1171		for (; i < src->textSrc.num_anchors; i++)
1172		    src->textSrc.anchors[i]->position += diff;
1173	    }
1174	}
1175
1176	start = left;
1177	end = start + block->length;
1178	while (start < end) {
1179	    start = XawTextSourceScan(w, start, XawstEOL, XawsdRight, 1, True);
1180	    if (start <= end) {
1181		++lines;
1182		if (start == XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True)) {
1183		    lines -= !_XawTextSourceNewLineAtEOF(w);
1184		    break;
1185		}
1186	    }
1187	}
1188
1189	info.left = left;
1190	info.right = right;
1191	info.block = block;
1192	XtCallCallbacks(w, XtNpropertyCallback, &info);
1193
1194	TellSourceChanged(src, left, right, block, lines);
1195	/* Call callbacks, we have changed the buffer */
1196	XtCallCallbacks(w, XtNcallback,
1197			(XtPointer)((long)src->textSrc.changed));
1198    }
1199
1200#endif /* OLDXAW */
1201    return (error);
1202}
1203
1204#ifndef OLDXAW
1205Bool
1206_XawTextSrcUndo(TextSrcObject src, XawTextPosition *insert_pos)
1207{
1208    static wchar_t wnull = 0;
1209    XawTextBlock block;
1210    XawTextUndoList *list, *nlist;
1211    XawTextUndoBuffer *l_state, *r_state;
1212    Boolean changed = src->textSrc.changed;
1213
1214    if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo)
1215	return (False);
1216
1217    list = src->textSrc.undo->pointer;
1218
1219    if (src->textSrc.undo->dir == XawsdLeft) {
1220	l_state = list->right;
1221	r_state = list->left;
1222    }
1223    else {
1224	l_state = list->left;
1225	r_state = list->right;
1226    }
1227
1228    if (src->textSrc.undo->l_no_change == l_state
1229	&& src->textSrc.undo->r_no_change == r_state)
1230	src->textSrc.changed = False;
1231    else
1232	src->textSrc.changed = True;
1233
1234    block.firstPos = 0;
1235    block.length = r_state->length;
1236    block.ptr = r_state->buffer ? r_state->buffer : (char*)&wnull;
1237    block.format = r_state->format;
1238
1239    src->textSrc.undo_state = True;
1240    if (XawTextSourceReplace((Widget)src, l_state->position, l_state->position
1241			     + l_state->length, &block) != XawEditDone) {
1242	src->textSrc.undo_state = False;
1243	src->textSrc.changed = changed;
1244	return (False);
1245    }
1246    src->textSrc.undo_state = False;
1247
1248    ++l_state->refcount;
1249    ++r_state->refcount;
1250    nlist = XtNew(XawTextUndoList);
1251    nlist->left = l_state;
1252    nlist->right = r_state;
1253    nlist->undo = src->textSrc.undo->list;
1254    nlist->redo = NULL;
1255
1256    if (list == src->textSrc.undo->list)
1257	src->textSrc.undo->end_mark = nlist;
1258
1259    if (src->textSrc.undo->dir == XawsdLeft) {
1260	if (list->undo == NULL)
1261	    src->textSrc.undo->dir = XawsdRight;
1262	else
1263	    list = list->undo;
1264    }
1265    else {
1266	if (list->redo == NULL || list->redo == src->textSrc.undo->end_mark)
1267	    src->textSrc.undo->dir = XawsdLeft;
1268	else
1269	    list = list->redo;
1270    }
1271    *insert_pos = r_state->position + r_state->length;
1272    src->textSrc.undo->pointer = list;
1273    src->textSrc.undo->list->redo = nlist;
1274    src->textSrc.undo->list = nlist;
1275    src->textSrc.undo->merge = src->textSrc.undo->erase = 0;
1276
1277    if (++src->textSrc.undo->num_list >= UNDO_DEPTH)
1278	UndoGC(src->textSrc.undo);
1279
1280    return (True);
1281}
1282
1283Bool
1284_XawTextSrcToggleUndo(TextSrcObject src)
1285{
1286    if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo)
1287	return (False);
1288
1289    if (src->textSrc.undo->pointer != src->textSrc.undo->list) {
1290	if (src->textSrc.undo->dir == XawsdLeft) {
1291	    if (src->textSrc.undo->pointer->redo
1292		&& (src->textSrc.undo->pointer->redo
1293		    != src->textSrc.undo->end_mark)) {
1294		src->textSrc.undo->pointer = src->textSrc.undo->pointer->redo;
1295		src->textSrc.undo->dir = XawsdRight;
1296	    }
1297	}
1298	else {
1299	    if (src->textSrc.undo->pointer->undo
1300		&& (src->textSrc.undo->pointer != src->textSrc.undo->head)) {
1301		src->textSrc.undo->pointer = src->textSrc.undo->pointer->undo;
1302		src->textSrc.undo->dir = XawsdLeft;
1303	    }
1304	}
1305    }
1306
1307    return (True);
1308}
1309
1310static void
1311FreeUndoBuffer(XawTextUndo *undo)
1312{
1313    unsigned i;
1314    XawTextUndoList *head, *del;
1315
1316    for (i = 0; i < undo->num_undo; i++) {
1317	if (undo->undo[i]->buffer && undo->undo[i]->buffer != SrcNL &&
1318	    undo->undo[i]->buffer != (char*)SrcWNL)
1319	    XtFree(undo->undo[i]->buffer);
1320	XtFree((char*)undo->undo[i]);
1321    }
1322    XtFree((char*)undo->undo);
1323    head = undo->head;
1324
1325    del = head;
1326    while (head) {
1327	head = head->redo;
1328	XtFree((char*)del);
1329	del = head;
1330    }
1331
1332    if (undo->l_save) {
1333	XtFree((char*)undo->l_save);
1334	undo->l_save = NULL;
1335    }
1336    if (undo->r_save) {
1337	XtFree((char*)undo->r_save);
1338	undo->r_save = NULL;
1339    }
1340    if (undo->u_save) {
1341	XtFree((char*)undo->u_save);
1342	undo->u_save = NULL;
1343    }
1344
1345    undo->list = undo->pointer = undo->head = undo->end_mark = NULL;
1346    undo->l_no_change = undo->r_no_change = NULL;
1347    undo->undo = NULL;
1348    undo->dir = XawsdLeft;
1349    undo->num_undo = undo->num_list = undo->erase = undo->merge = 0;
1350}
1351
1352static void
1353UndoGC(XawTextUndo *undo)
1354{
1355    unsigned i;
1356    XawTextUndoList *head = undo->head, *redo = head->redo;
1357
1358    if (head == undo->pointer || head == undo->end_mark
1359	|| undo->l_no_change == NULL
1360	|| head->left == undo->l_no_change || head->right == undo->l_no_change)
1361      return;
1362
1363    undo->head = redo;
1364    redo->undo = NULL;
1365
1366    --head->left->refcount;
1367    if (--head->right->refcount == 0) {
1368	for (i = 0; i < undo->num_undo; i+= 2)
1369	    if (head->left == undo->undo[i] || head->left == undo->undo[i+1]) {
1370		if (head->left == undo->undo[i+1]) {
1371		    XawTextUndoBuffer *tmp = redo->left;
1372
1373		    redo->left = redo->right;
1374		    redo->right = tmp;
1375		}
1376		if (head->left->buffer && head->left->buffer != SrcNL &&
1377		    head->left->buffer != (char*)SrcWNL)
1378		    XtFree(head->left->buffer);
1379		XtFree((char*)head->left);
1380		if (head->right->buffer && head->right->buffer != SrcNL &&
1381		    head->right->buffer != (char*)SrcWNL)
1382		    XtFree(head->right->buffer);
1383		XtFree((char*)head->right);
1384
1385		undo->num_undo -= 2;
1386		memmove(&undo->undo[i], &undo->undo[i + 2],
1387			(undo->num_undo - i) * sizeof(XawTextUndoBuffer*));
1388		break;
1389	    }
1390    }
1391    XtFree((char*)head);
1392    --undo->num_list;
1393}
1394#endif /* OLDXAW */
1395
1396/*
1397 * Function:
1398 *	XawTextSourceScan
1399 *
1400 * Parameters:
1401 *	w	 - TextSrc Object
1402 *	position - position to start scanning
1403 *	type	 - type of thing to scan for
1404 *	dir	 - direction to scan
1405 *	count	 - which occurance if this thing to search for
1406 *	include  - whether or not to include the character found in
1407 *		   the position that is returned.
1408 *
1409 * Description:
1410 *	Scans the text source for the number and type of item specified.
1411 *
1412 * Returns:
1413 *	The position of the text
1414 */
1415XawTextPosition
1416XawTextSourceScan(Widget w, XawTextPosition position,
1417#if NeedWidePrototypes
1418		  int type, int dir, int count, int include
1419#else
1420		  XawTextScanType type, XawTextScanDirection dir,
1421		  int count, Boolean include
1422#endif
1423)
1424{
1425    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1426
1427    return ((*cclass->textSrc_class.Scan)
1428	    (w, position, type, dir, count, include));
1429}
1430
1431/*
1432 * Function:
1433 *	XawTextSourceSearch
1434 *
1435 * Parameters:
1436 *	w	 - TextSource Object
1437 *	position - position to start scanning
1438 *	dir	 - direction to scan
1439 *	text	 - the text block to search for.
1440 *
1441 * Returns:
1442 *	The position of the text we are searching for or XawTextSearchError.
1443 *
1444 * Description:
1445 *	Searchs the text source for the text block passed
1446 */
1447XawTextPosition
1448XawTextSourceSearch(Widget w, XawTextPosition position,
1449#if NeedWidePrototypes
1450		    int dir,
1451#else
1452		    XawTextScanDirection dir,
1453#endif
1454		    XawTextBlock *text)
1455{
1456    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1457
1458    return ((*cclass->textSrc_class.Search)(w, position, dir, text));
1459}
1460
1461/*
1462 * Function:
1463 *	XawTextSourceConvertSelection
1464 *
1465 * Parameters:
1466 *	w	  - TextSrc object
1467 *	selection - current selection atom
1468 *	target	  - current target atom
1469 *	type	  - type to conver the selection to
1470 *	value	  - return value that has been converted
1471 *	length	  - ""
1472 *	format	  - format of the returned value
1473 *
1474 * Returns:
1475 *	True if the selection has been converted
1476 */
1477Boolean
1478XawTextSourceConvertSelection(Widget w, Atom *selection, Atom *target,
1479			      Atom *type, XtPointer *value,
1480			      unsigned long *length, int *format)
1481{
1482    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1483
1484    return((*cclass->textSrc_class.ConvertSelection)
1485	   (w, selection, target, type, value, length, format));
1486}
1487
1488/*
1489 * Function:
1490 *	XawTextSourceSetSelection
1491 *
1492 * Parameters:
1493 *	w	  - TextSrc object
1494 *	left	  - bounds of the selection
1495 *	rigth	  - ""
1496 *	selection - selection atom
1497 *
1498 * Description:
1499 *	Allows special setting of the selection.
1500 */
1501void
1502XawTextSourceSetSelection(Widget w, XawTextPosition left,
1503			  XawTextPosition right, Atom selection)
1504{
1505    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1506
1507    (*cclass->textSrc_class.SetSelection)(w, left, right, selection);
1508}
1509
1510/*
1511 * External Functions for Multi Text
1512 */
1513/*
1514 * TextFormat():
1515 *	returns the format of text: FMT8BIT or FMTWIDE
1516 */
1517XrmQuark
1518_XawTextFormat(TextWidget tw)
1519{
1520    return (((TextSrcObject)(tw->text.source))->textSrc.text_format);
1521}
1522
1523/* _XawTextWCToMB():
1524 *	Convert the wchar string to external encoding
1525 *	The caller is responsible for freeing both the source and ret string
1526 *
1527 *	wstr	   - source wchar string
1528 * len_in_out - lengh of string.
1529 *		     As In, length of source wchar string, measured in wchar
1530 *		     As Out, length of returned string
1531 */
1532char *
1533_XawTextWCToMB(Display *d, wchar_t *wstr, int *len_in_out)
1534{
1535    XTextProperty textprop;
1536
1537    if (XwcTextListToTextProperty(d, (wchar_t**)&wstr, 1,
1538				XTextStyle, &textprop) < Success) {
1539	XtWarningMsg("convertError", "textSource", "XawError",
1540		     "Non-character code(s) in buffer.", NULL, NULL);
1541	*len_in_out = 0;
1542	return (NULL);
1543    }
1544    *len_in_out = textprop.nitems;
1545
1546    return ((char *)textprop.value);
1547}
1548
1549/* _XawTextMBToWC():
1550 *	Convert the string to internal processing codeset WC.
1551 *   The caller is responsible for freeing both the source and ret string.
1552 *
1553 *	str	   - source string
1554 *	len_in_out - lengh of string
1555 *		     As In, it is length of source string
1556 *		     As Out, it is length of returned string, measured in wchar
1557 */
1558wchar_t *
1559_XawTextMBToWC(Display *d, char *str, int *len_in_out)
1560{
1561    XTextProperty textprop;
1562    char *buf;
1563    wchar_t **wlist, *wstr;
1564    int count;
1565
1566    if (*len_in_out == 0)
1567	return (NULL);
1568
1569    buf = XtMalloc(*len_in_out + 1);
1570
1571    strncpy(buf, str, *len_in_out);
1572    *(buf + *len_in_out) = '\0';
1573    if (XmbTextListToTextProperty(d, &buf, 1, XTextStyle, &textprop) != Success) {
1574	XtWarningMsg("convertError", "textSource", "XawError",
1575		     "No Memory, or Locale not supported.", NULL, NULL);
1576	XtFree(buf);
1577	*len_in_out = 0;
1578	return (NULL);
1579    }
1580
1581    XtFree(buf);
1582    if (XwcTextPropertyToTextList(d, &textprop,
1583				  (wchar_t***)&wlist, &count) != Success) {
1584	XtWarningMsg("convertError", "multiSourceCreate", "XawError",
1585		     "Non-character code(s) in source.", NULL, NULL);
1586	*len_in_out = 0;
1587	return (NULL);
1588    }
1589    wstr = wlist[0];
1590    *len_in_out = wcslen(wstr);
1591    XtFree((XtPointer)wlist);
1592
1593    return (wstr);
1594}
1595
1596#ifndef OLDXAW
1597static int
1598qcmp_anchors(_Xconst void *left, _Xconst void *right)
1599{
1600    return ((*(XawTextAnchor**)left)->position -
1601	    (*(XawTextAnchor**)right)->position);
1602}
1603
1604XawTextAnchor *
1605XawTextSourceAddAnchor(Widget w, XawTextPosition position)
1606{
1607    TextSrcObject src = (TextSrcObject)w;
1608    XawTextAnchor *anchor, *panchor;
1609
1610    if ((panchor = XawTextSourceFindAnchor(w, position)) != NULL) {
1611	XawTextEntity *pentity, *entity;
1612
1613	if (position - panchor->position < ANCHORS_DIST)
1614	    return (panchor);
1615
1616	if (panchor->cache && panchor->position + panchor->cache->offset +
1617	    panchor->cache->length < position)
1618	    pentity = entity = panchor->cache;
1619	else
1620	    pentity = entity = panchor->entities;
1621
1622	while (entity && panchor->position + entity->offset +
1623	       entity->length < position) {
1624	    pentity = entity;
1625	    entity = entity->next;
1626	}
1627	if (entity) {
1628	    XawTextPosition diff;
1629
1630	    if (panchor->position + entity->offset < position)
1631		position = panchor->position + entity->offset;
1632
1633	    if (position == panchor->position)
1634		return (panchor);
1635
1636	    anchor = XtNew(XawTextAnchor);
1637	    diff = position - panchor->position;
1638
1639	    panchor->cache = NULL;
1640	    anchor->entities = entity;
1641	    if (pentity != entity)
1642		pentity->next = NULL;
1643	    else
1644		panchor->entities = NULL;
1645	    while (entity) {
1646		entity->offset -= diff;
1647		entity = entity->next;
1648	    }
1649	}
1650	else {
1651	    anchor = XtNew(XawTextAnchor);
1652	    anchor->entities = NULL;
1653	}
1654    }
1655    else {
1656	anchor = XtNew(XawTextAnchor);
1657	anchor->entities = NULL;
1658    }
1659
1660    anchor->position = position;
1661    anchor->cache = NULL;
1662
1663    src->textSrc.anchors = (XawTextAnchor**)
1664	XtRealloc((XtPointer)src->textSrc.anchors, sizeof(XawTextAnchor*) *
1665		  (src->textSrc.num_anchors + 1));
1666    src->textSrc.anchors[src->textSrc.num_anchors++] = anchor;
1667    qsort((void*)src->textSrc.anchors, src->textSrc.num_anchors,
1668	  sizeof(XawTextAnchor*), qcmp_anchors);
1669
1670    return (anchor);
1671}
1672
1673XawTextAnchor *
1674XawTextSourceFindAnchor(Widget w, XawTextPosition position)
1675{
1676    TextSrcObject src = (TextSrcObject)w;
1677    int i = 0, left, right, nmemb = src->textSrc.num_anchors;
1678    XawTextAnchor *anchor, **anchors = src->textSrc.anchors;
1679
1680    left = 0;
1681    right = nmemb - 1;
1682    while (left <= right) {
1683	anchor = anchors[i = (left + right) >> 1];
1684	if (anchor->position == position)
1685	    return (anchor);
1686	else if (position < anchor->position)
1687	    right = i - 1;
1688	else
1689	    left = i + 1;
1690    }
1691
1692    if (nmemb)
1693	return (right < 0 ? anchors[0] : anchors[right]);
1694
1695    return (NULL);
1696}
1697
1698Bool
1699XawTextSourceAnchorAndEntity(Widget w, XawTextPosition position,
1700			     XawTextAnchor **anchor_return,
1701			     XawTextEntity **entity_return)
1702{
1703    XawTextAnchor *anchor = XawTextSourceFindAnchor(w, position);
1704    XawTextEntity *pentity, *entity;
1705    XawTextPosition offset;
1706    Bool next_anchor = True, retval = False;
1707
1708    if (anchor->cache && anchor->position + anchor->cache->offset +
1709	anchor->cache->length <= position)
1710	pentity = entity = anchor->cache;
1711    else
1712	pentity = entity = anchor->entities;
1713    while (entity) {
1714	offset = anchor->position + entity->offset;
1715
1716	if (offset > position) {
1717	    retval = next_anchor = False;
1718	    break;
1719	}
1720	if (offset + entity->length > position) {
1721	    retval = True;
1722	    next_anchor = False;
1723	    break;
1724	}
1725	pentity = entity;
1726	entity = entity->next;
1727    }
1728
1729    if (next_anchor) {
1730	*anchor_return = anchor = XawTextSourceNextAnchor(w, anchor);
1731	*entity_return = anchor ? anchor->entities : NULL;
1732    }
1733    else {
1734	*anchor_return = anchor;
1735	*entity_return = retval ? entity : pentity;
1736    }
1737
1738    if (*anchor_return)
1739	(*anchor_return)->cache = *entity_return;
1740
1741    return (retval);
1742}
1743
1744XawTextAnchor *
1745XawTextSourceNextAnchor(Widget w, XawTextAnchor *anchor)
1746{
1747    int i;
1748    TextSrcObject src = (TextSrcObject)w;
1749
1750    for (i = 0; i < src->textSrc.num_anchors - 1; i++)
1751	if (src->textSrc.anchors[i] == anchor)
1752	    return (src->textSrc.anchors[i + 1]);
1753
1754    return (NULL);
1755}
1756
1757XawTextAnchor *
1758XawTextSourcePrevAnchor(Widget w, XawTextAnchor *anchor)
1759{
1760    int i;
1761    TextSrcObject src = (TextSrcObject)w;
1762
1763    for (i = src->textSrc.num_anchors - 1; i > 0; i--)
1764	if (src->textSrc.anchors[i] == anchor)
1765	    return (src->textSrc.anchors[i - 1]);
1766
1767    return (NULL);
1768}
1769
1770XawTextAnchor *
1771XawTextSourceRemoveAnchor(Widget w, XawTextAnchor *anchor)
1772{
1773    int i;
1774    TextSrcObject src = (TextSrcObject)w;
1775
1776    for (i = 0; i < src->textSrc.num_anchors; i++)
1777	if (src->textSrc.anchors[i] == anchor)
1778	    break;
1779
1780    if (i == 0)
1781	return (src->textSrc.num_anchors > 1 ? src->textSrc.anchors[1] : NULL);
1782
1783    if (i < src->textSrc.num_anchors) {
1784	XtFree((XtPointer)anchor);
1785	if (i < --src->textSrc.num_anchors) {
1786	    memmove(&src->textSrc.anchors[i],
1787		    &src->textSrc.anchors[i + 1],
1788		    (src->textSrc.num_anchors - i) *
1789		    sizeof(XawTextAnchor*));
1790
1791	    return (src->textSrc.anchors[i]);
1792	}
1793    }
1794
1795    return (NULL);
1796}
1797
1798XawTextEntity *
1799XawTextSourceAddEntity(Widget w, int type, int flags, XtPointer data,
1800		       XawTextPosition position, Cardinal length,
1801		       XrmQuark property)
1802{
1803    XawTextAnchor *next, *anchor = _XawTextSourceFindAnchor(w, position);
1804    XawTextEntity *entity, *eprev;
1805
1806    /* There is no support for zero length entities for now */
1807    if (length == 0)
1808	return (NULL);
1809
1810    if (anchor->cache && anchor->position + anchor->cache->offset +
1811	anchor->cache->length <= position)
1812	eprev = entity = anchor->cache;
1813    else
1814	eprev = entity = anchor->entities;
1815
1816    while (entity && anchor->position + entity->offset + entity->length <=
1817	   position) {
1818	eprev = entity;
1819	entity = entity->next;
1820    }
1821    if (entity && anchor->position + entity->offset < position + length) {
1822	fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n");
1823	return (NULL);
1824    }
1825
1826    next = XawTextSourceFindAnchor(w, position + length);
1827    if (next && next != anchor) {
1828	if ((entity = next->entities) != NULL) {
1829	    if (next->position + entity->offset < position + length) {
1830		fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n");
1831		return (NULL);
1832	    }
1833	}
1834	if (position + length > next->position) {
1835	    XawTextPosition diff = position + length - next->position;
1836
1837	    next->position += diff;
1838	    entity = next->entities;
1839	    while (entity) {
1840		entity->offset -= diff;
1841		entity = entity->next;
1842	    }
1843	    entity = anchor->entities;
1844	    while (entity && entity->offset < 0)
1845		entity = entity->next;
1846	    if (entity && entity->offset < 0) {
1847		if (eprev)
1848		    eprev->next = next->entities;
1849		else
1850		    anchor->entities = next->entities;
1851		if ((next->entities = entity->next) == NULL)
1852		    (void)XawTextSourceRemoveAnchor(w, next);
1853		entity->next = NULL;
1854
1855		return (XawTextSourceAddEntity(w, type, flags, data, position,
1856					       length, property));
1857	    }
1858	}
1859    }
1860
1861    /* Automatically join sequential entities if possible */
1862    if (eprev &&
1863	anchor->position + eprev->offset + eprev->length == position &&
1864	eprev->property == property && eprev->type == type &&
1865	eprev->flags == flags && eprev->data == data) {
1866	eprev->length += length;
1867	return (eprev);
1868    }
1869
1870    entity = XtNew(XawTextEntity);
1871    entity->type = type;
1872    entity->flags = flags;
1873    entity->data = data;
1874    entity->offset = position - anchor->position;
1875    entity->length = length;
1876    entity->property = property;
1877
1878    if (eprev == NULL) {
1879	anchor->entities = entity;
1880	entity->next = NULL;
1881	anchor->cache = NULL;
1882    }
1883    else if (eprev->offset > entity->offset) {
1884	anchor->cache = NULL;
1885	anchor->entities = entity;
1886	entity->next = eprev;
1887    }
1888    else {
1889	anchor->cache = eprev;
1890	entity->next = eprev->next;
1891	eprev->next = entity;
1892    }
1893
1894    return (entity);
1895}
1896
1897void
1898XawTextSourceClearEntities(Widget w, XawTextPosition left, XawTextPosition right)
1899{
1900    XawTextAnchor *anchor = XawTextSourceFindAnchor(w, left);
1901    XawTextEntity *entity, *eprev, *enext;
1902    XawTextPosition offset;
1903    int length;
1904
1905    while (anchor && anchor->entities == NULL)
1906	anchor = XawTextSourceRemoveAnchor(w, anchor);
1907
1908    if (anchor == NULL || left >= right)
1909	return;
1910
1911    if (anchor->cache && anchor->position + anchor->cache->offset +
1912	anchor->cache->length < left)
1913	eprev = entity = anchor->cache;
1914    else
1915	eprev = entity = anchor->entities;
1916
1917    /* find first entity before left position */
1918    while (anchor->position + entity->offset + entity->length < left) {
1919	eprev = entity;
1920	if ((entity = entity->next) == NULL) {
1921	    if ((anchor = XawTextSourceNextAnchor(w, anchor)) == NULL)
1922		return;
1923	    if ((eprev = entity = anchor->entities) == NULL) {
1924		fprintf(stderr, "Bad anchor found!\n");
1925		return;
1926	    }
1927	}
1928    }
1929
1930    offset = anchor->position + entity->offset;
1931    if (offset <= left) {
1932	length = XawMin(entity->length, left - offset);
1933
1934	if (length <= 0) {
1935	    enext = entity->next;
1936	    eprev->next = enext;
1937	    XtFree((XtPointer)entity);
1938	    anchor->cache = NULL;
1939	    if (entity == anchor->entities) {
1940		eprev = NULL;
1941		if ((anchor->entities = enext) == NULL) {
1942		    if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL)
1943			return;
1944		    entity = anchor->entities;
1945		}
1946		else
1947		    entity = enext;
1948	    }
1949	    else
1950		entity = enext;
1951	}
1952	else {
1953	    entity->length = length;
1954	    eprev = entity;
1955	    entity = entity->next;
1956	}
1957    }
1958
1959    /* clean everything until right position is reached */
1960    while (anchor) {
1961	while (entity) {
1962	    offset = anchor->position + entity->offset + entity->length;
1963
1964	    if (offset > right) {
1965		anchor->cache = NULL;
1966		entity->offset = XawMax(entity->offset, right - anchor->position);
1967		entity->length = XawMin(entity->length, offset - right);
1968		return;
1969	    }
1970
1971	    enext = entity->next;
1972	    if (eprev)
1973		eprev->next = enext;
1974	    XtFree((XtPointer)entity);
1975	    if (entity == anchor->entities) {
1976		eprev = anchor->cache = NULL;
1977		if ((anchor->entities = enext) == NULL) {
1978		    if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL)
1979			return;
1980		    entity = anchor->entities;
1981		    continue;
1982		}
1983	    }
1984	    entity = enext;
1985	}
1986	if (anchor)
1987	    anchor->cache = NULL;
1988	if ((anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)
1989	    entity = anchor->entities;
1990	eprev = NULL;
1991    }
1992}
1993
1994/* checks the anchors up to position, and create an appropriate anchor
1995 * at position, if required.
1996 */
1997XawTextAnchor *
1998_XawTextSourceFindAnchor(Widget w, XawTextPosition position)
1999{
2000    XawTextAnchor *anchor;
2001
2002    anchor = XawTextSourceFindAnchor(w, position);
2003
2004    position -= position % ANCHORS_DIST;
2005
2006    if (position - anchor->position >= ANCHORS_DIST)
2007	return (XawTextSourceAddAnchor(w, position));
2008
2009    return (anchor);
2010}
2011#endif
2012