TextSrc.c revision 7a84e134
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		    break;
1097		eprev = NULL;
1098	    }
1099
1100exit_anchor_loop:
1101	    if (anchor) {
1102		XawTextAnchor *aprev;
1103
1104		if (anchor->position >= XawMax(right, left + block->length))
1105		    anchor->position += diff;
1106		else if (anchor->position > left &&
1107			 (aprev = XawTextSourcePrevAnchor(w, anchor))) {
1108		    XawTextPosition tmp = anchor->position - aprev->position;
1109
1110		    if (diff) {
1111			while (entity) {
1112			    entity->offset += diff;
1113			    entity = entity->next;
1114			}
1115		    }
1116		    entity = anchor->entities;
1117		    while (entity) {
1118			entity->offset += tmp;
1119			entity = entity->next;
1120		    }
1121		    if ((entity = aprev->entities) == NULL)
1122			aprev->entities = anchor->entities;
1123		    else {
1124			while (entity->next)
1125			    entity = entity->next;
1126			entity->next = anchor->entities;
1127		    }
1128		    anchor->entities = NULL;
1129		    (void)XawTextSourceRemoveAnchor(w, anchor);
1130		    --i;
1131		}
1132		else if (diff) {
1133		    while (entity) {
1134			entity->offset += diff;
1135			entity = entity->next;
1136		    }
1137		}
1138	    }
1139
1140	    if (diff) {
1141		for (++i; i < src->textSrc.num_anchors; i++)
1142		    src->textSrc.anchors[i]->position += diff;
1143	    }
1144	}
1145
1146	start = left;
1147	end = start + block->length;
1148	while (start < end) {
1149	    start = XawTextSourceScan(w, start, XawstEOL, XawsdRight, 1, True);
1150	    if (start <= end) {
1151		++lines;
1152		if (start == XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True)) {
1153		    lines -= !_XawTextSourceNewLineAtEOF(w);
1154		    break;
1155		}
1156	    }
1157	}
1158
1159	info.left = left;
1160	info.right = right;
1161	info.block = block;
1162	XtCallCallbacks(w, XtNpropertyCallback, &info);
1163
1164	TellSourceChanged(src, left, right, block, lines);
1165	/* Call callbacks, we have changed the buffer */
1166	XtCallCallbacks(w, XtNcallback,
1167			(XtPointer)((long)src->textSrc.changed));
1168    }
1169
1170#endif /* OLDXAW */
1171    return (error);
1172}
1173
1174#ifndef OLDXAW
1175Bool
1176_XawTextSrcUndo(TextSrcObject src, XawTextPosition *insert_pos)
1177{
1178    static wchar_t wnull = 0;
1179    XawTextBlock block;
1180    XawTextUndoList *list, *nlist;
1181    XawTextUndoBuffer *l_state, *r_state;
1182    Boolean changed = src->textSrc.changed;
1183
1184    if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo)
1185	return (False);
1186
1187    list = src->textSrc.undo->pointer;
1188
1189    if (src->textSrc.undo->dir == XawsdLeft) {
1190	l_state = list->right;
1191	r_state = list->left;
1192    }
1193    else {
1194	l_state = list->left;
1195	r_state = list->right;
1196    }
1197
1198    if (src->textSrc.undo->l_no_change == l_state
1199	&& src->textSrc.undo->r_no_change == r_state)
1200	src->textSrc.changed = False;
1201    else
1202	src->textSrc.changed = True;
1203
1204    block.firstPos = 0;
1205    block.length = r_state->length;
1206    block.ptr = r_state->buffer ? r_state->buffer : (char*)&wnull;
1207    block.format = r_state->format;
1208
1209    src->textSrc.undo_state = True;
1210    if (XawTextSourceReplace((Widget)src, l_state->position, l_state->position
1211			     + l_state->length, &block) != XawEditDone) {
1212	src->textSrc.undo_state = False;
1213	src->textSrc.changed = changed;
1214	return (False);
1215    }
1216    src->textSrc.undo_state = False;
1217
1218    ++l_state->refcount;
1219    ++r_state->refcount;
1220    nlist = XtNew(XawTextUndoList);
1221    nlist->left = l_state;
1222    nlist->right = r_state;
1223    nlist->undo = src->textSrc.undo->list;
1224    nlist->redo = NULL;
1225
1226    if (list == src->textSrc.undo->list)
1227	src->textSrc.undo->end_mark = nlist;
1228
1229    if (src->textSrc.undo->dir == XawsdLeft) {
1230	if (list->undo == NULL)
1231	    src->textSrc.undo->dir = XawsdRight;
1232	else
1233	    list = list->undo;
1234    }
1235    else {
1236	if (list->redo == NULL || list->redo == src->textSrc.undo->end_mark)
1237	    src->textSrc.undo->dir = XawsdLeft;
1238	else
1239	    list = list->redo;
1240    }
1241    *insert_pos = r_state->position + r_state->length;
1242    src->textSrc.undo->pointer = list;
1243    src->textSrc.undo->list->redo = nlist;
1244    src->textSrc.undo->list = nlist;
1245    src->textSrc.undo->merge = src->textSrc.undo->erase = 0;
1246
1247    if (++src->textSrc.undo->num_list >= UNDO_DEPTH)
1248	UndoGC(src->textSrc.undo);
1249
1250    return (True);
1251}
1252
1253Bool
1254_XawTextSrcToggleUndo(TextSrcObject src)
1255{
1256    if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo)
1257	return (False);
1258
1259    if (src->textSrc.undo->pointer != src->textSrc.undo->list) {
1260	if (src->textSrc.undo->dir == XawsdLeft) {
1261	    if (src->textSrc.undo->pointer->redo
1262		&& (src->textSrc.undo->pointer->redo
1263		    != src->textSrc.undo->end_mark)) {
1264		src->textSrc.undo->pointer = src->textSrc.undo->pointer->redo;
1265		src->textSrc.undo->dir = XawsdRight;
1266	    }
1267	}
1268	else {
1269	    if (src->textSrc.undo->pointer->undo
1270		&& (src->textSrc.undo->pointer != src->textSrc.undo->head)) {
1271		src->textSrc.undo->pointer = src->textSrc.undo->pointer->undo;
1272		src->textSrc.undo->dir = XawsdLeft;
1273	    }
1274	}
1275    }
1276
1277    return (True);
1278}
1279
1280static void
1281FreeUndoBuffer(XawTextUndo *undo)
1282{
1283    unsigned i;
1284    XawTextUndoList *head, *del;
1285
1286    for (i = 0; i < undo->num_undo; i++) {
1287	if (undo->undo[i]->buffer && undo->undo[i]->buffer != SrcNL &&
1288	    undo->undo[i]->buffer != (char*)SrcWNL)
1289	    XtFree(undo->undo[i]->buffer);
1290	XtFree((char*)undo->undo[i]);
1291    }
1292    XtFree((char*)undo->undo);
1293    head = undo->head;
1294
1295    del = head;
1296    while (head) {
1297	head = head->redo;
1298	XtFree((char*)del);
1299	del = head;
1300    }
1301
1302    if (undo->l_save) {
1303	XtFree((char*)undo->l_save);
1304	undo->l_save = NULL;
1305    }
1306    if (undo->r_save) {
1307	XtFree((char*)undo->r_save);
1308	undo->r_save = NULL;
1309    }
1310    if (undo->u_save) {
1311	XtFree((char*)undo->u_save);
1312	undo->u_save = NULL;
1313    }
1314
1315    undo->list = undo->pointer = undo->head = undo->end_mark = NULL;
1316    undo->l_no_change = undo->r_no_change = NULL;
1317    undo->undo = NULL;
1318    undo->dir = XawsdLeft;
1319    undo->num_undo = undo->num_list = undo->erase = undo->merge = 0;
1320}
1321
1322static void
1323UndoGC(XawTextUndo *undo)
1324{
1325    unsigned i;
1326    XawTextUndoList *head = undo->head, *redo = head->redo;
1327
1328    if (head == undo->pointer || head == undo->end_mark
1329	|| undo->l_no_change == NULL
1330	|| head->left == undo->l_no_change || head->right == undo->l_no_change)
1331      return;
1332
1333    undo->head = redo;
1334    redo->undo = NULL;
1335
1336    --head->left->refcount;
1337    if (--head->right->refcount == 0) {
1338	for (i = 0; i < undo->num_undo; i+= 2)
1339	    if (head->left == undo->undo[i] || head->left == undo->undo[i+1]) {
1340		if (head->left == undo->undo[i+1]) {
1341		    XawTextUndoBuffer *tmp = redo->left;
1342
1343		    redo->left = redo->right;
1344		    redo->right = tmp;
1345		}
1346		if (head->left->buffer && head->left->buffer != SrcNL &&
1347		    head->left->buffer != (char*)SrcWNL)
1348		    XtFree(head->left->buffer);
1349		XtFree((char*)head->left);
1350		if (head->right->buffer && head->right->buffer != SrcNL &&
1351		    head->right->buffer != (char*)SrcWNL)
1352		    XtFree(head->right->buffer);
1353		XtFree((char*)head->right);
1354
1355		undo->num_undo -= 2;
1356		memmove(&undo->undo[i], &undo->undo[i + 2],
1357			(undo->num_undo - i) * sizeof(XawTextUndoBuffer*));
1358		break;
1359	    }
1360    }
1361    XtFree((char*)head);
1362    --undo->num_list;
1363}
1364#endif /* OLDXAW */
1365
1366/*
1367 * Function:
1368 *	XawTextSourceScan
1369 *
1370 * Parameters:
1371 *	w	 - TextSrc Object
1372 *	position - position to start scanning
1373 *	type	 - type of thing to scan for
1374 *	dir	 - direction to scan
1375 *	count	 - which occurance if this thing to search for
1376 *	include  - whether or not to include the character found in
1377 *		   the position that is returned.
1378 *
1379 * Description:
1380 *	Scans the text source for the number and type of item specified.
1381 *
1382 * Returns:
1383 *	The position of the text
1384 */
1385XawTextPosition
1386XawTextSourceScan(Widget w, XawTextPosition position,
1387#if NeedWidePrototypes
1388		  int type, int dir, int count, int include
1389#else
1390		  XawTextScanType type, XawTextScanDirection dir,
1391		  int count, Boolean include
1392#endif
1393)
1394{
1395    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1396
1397    return ((*cclass->textSrc_class.Scan)
1398	    (w, position, type, dir, count, include));
1399}
1400
1401/*
1402 * Function:
1403 *	XawTextSourceSearch
1404 *
1405 * Parameters:
1406 *	w	 - TextSource Object
1407 *	position - position to start scanning
1408 *	dir	 - direction to scan
1409 *	text	 - the text block to search for.
1410 *
1411 * Returns:
1412 *	The position of the text we are searching for or XawTextSearchError.
1413 *
1414 * Description:
1415 *	Searchs the text source for the text block passed
1416 */
1417XawTextPosition
1418XawTextSourceSearch(Widget w, XawTextPosition position,
1419#if NeedWidePrototypes
1420		    int dir,
1421#else
1422		    XawTextScanDirection dir,
1423#endif
1424		    XawTextBlock *text)
1425{
1426    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1427
1428    return ((*cclass->textSrc_class.Search)(w, position, dir, text));
1429}
1430
1431/*
1432 * Function:
1433 *	XawTextSourceConvertSelection
1434 *
1435 * Parameters:
1436 *	w	  - TextSrc object
1437 *	selection - current selection atom
1438 *	target	  - current target atom
1439 *	type	  - type to conver the selection to
1440 *	value	  - return value that has been converted
1441 *	length	  - ""
1442 *	format	  - format of the returned value
1443 *
1444 * Returns:
1445 *	True if the selection has been converted
1446 */
1447Boolean
1448XawTextSourceConvertSelection(Widget w, Atom *selection, Atom *target,
1449			      Atom *type, XtPointer *value,
1450			      unsigned long *length, int *format)
1451{
1452    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1453
1454    return((*cclass->textSrc_class.ConvertSelection)
1455	   (w, selection, target, type, value, length, format));
1456}
1457
1458/*
1459 * Function:
1460 *	XawTextSourceSetSelection
1461 *
1462 * Parameters:
1463 *	w	  - TextSrc object
1464 *	left	  - bounds of the selection
1465 *	rigth	  - ""
1466 *	selection - selection atom
1467 *
1468 * Description:
1469 *	Allows special setting of the selection.
1470 */
1471void
1472XawTextSourceSetSelection(Widget w, XawTextPosition left,
1473			  XawTextPosition right, Atom selection)
1474{
1475    TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class;
1476
1477    (*cclass->textSrc_class.SetSelection)(w, left, right, selection);
1478}
1479
1480/*
1481 * External Functions for Multi Text
1482 */
1483/*
1484 * TextFormat():
1485 *	returns the format of text: FMT8BIT or FMTWIDE
1486 */
1487XrmQuark
1488_XawTextFormat(TextWidget tw)
1489{
1490    return (((TextSrcObject)(tw->text.source))->textSrc.text_format);
1491}
1492
1493/* _XawTextWCToMB():
1494 *	Convert the wchar string to external encoding
1495 *	The caller is responsible for freeing both the source and ret string
1496 *
1497 *	wstr	   - source wchar string
1498 * len_in_out - lengh of string.
1499 *		     As In, length of source wchar string, measured in wchar
1500 *		     As Out, length of returned string
1501 */
1502char *
1503_XawTextWCToMB(Display *d, wchar_t *wstr, int *len_in_out)
1504{
1505    XTextProperty textprop;
1506
1507    if (XwcTextListToTextProperty(d, (wchar_t**)&wstr, 1,
1508				XTextStyle, &textprop) < Success) {
1509	XtWarningMsg("convertError", "textSource", "XawError",
1510		     "Non-character code(s) in buffer.", NULL, NULL);
1511	*len_in_out = 0;
1512	return (NULL);
1513    }
1514    *len_in_out = textprop.nitems;
1515
1516    return ((char *)textprop.value);
1517}
1518
1519/* _XawTextMBToWC():
1520 *	Convert the string to internal processing codeset WC.
1521 *   The caller is responsible for freeing both the source and ret string.
1522 *
1523 *	str	   - source string
1524 *	len_in_out - lengh of string
1525 *		     As In, it is length of source string
1526 *		     As Out, it is length of returned string, measured in wchar
1527 */
1528wchar_t *
1529_XawTextMBToWC(Display *d, char *str, int *len_in_out)
1530{
1531    XTextProperty textprop;
1532    char *buf;
1533    wchar_t **wlist, *wstr;
1534    int count;
1535
1536    if (*len_in_out == 0)
1537	return (NULL);
1538
1539    buf = XtMalloc(*len_in_out + 1);
1540
1541    strncpy(buf, str, *len_in_out);
1542    *(buf + *len_in_out) = '\0';
1543    if (XmbTextListToTextProperty(d, &buf, 1, XTextStyle, &textprop) != Success) {
1544	XtWarningMsg("convertError", "textSource", "XawError",
1545		     "No Memory, or Locale not supported.", NULL, NULL);
1546	XtFree(buf);
1547	*len_in_out = 0;
1548	return (NULL);
1549    }
1550
1551    XtFree(buf);
1552    if (XwcTextPropertyToTextList(d, &textprop,
1553				  (wchar_t***)&wlist, &count) != Success) {
1554	XtWarningMsg("convertError", "multiSourceCreate", "XawError",
1555		     "Non-character code(s) in source.", NULL, NULL);
1556	*len_in_out = 0;
1557	return (NULL);
1558    }
1559    wstr = wlist[0];
1560    *len_in_out = wcslen(wstr);
1561    XtFree((XtPointer)wlist);
1562
1563    return (wstr);
1564}
1565
1566#ifndef OLDXAW
1567static int
1568qcmp_anchors(_Xconst void *left, _Xconst void *right)
1569{
1570    return ((*(XawTextAnchor**)left)->position -
1571	    (*(XawTextAnchor**)right)->position);
1572}
1573
1574XawTextAnchor *
1575XawTextSourceAddAnchor(Widget w, XawTextPosition position)
1576{
1577    TextSrcObject src = (TextSrcObject)w;
1578    XawTextAnchor *anchor, *panchor;
1579
1580    if ((panchor = XawTextSourceFindAnchor(w, position)) != NULL) {
1581	XawTextEntity *pentity, *entity;
1582
1583	if (position - panchor->position < ANCHORS_DIST)
1584	    return (panchor);
1585
1586	if (panchor->cache && panchor->position + panchor->cache->offset +
1587	    panchor->cache->length < position)
1588	    pentity = entity = panchor->cache;
1589	else
1590	    pentity = entity = panchor->entities;
1591
1592	while (entity && panchor->position + entity->offset +
1593	       entity->length < position) {
1594	    pentity = entity;
1595	    entity = entity->next;
1596	}
1597	if (entity) {
1598	    XawTextPosition diff;
1599
1600	    if (panchor->position + entity->offset < position)
1601		position = panchor->position + entity->offset;
1602
1603	    if (position == panchor->position)
1604		return (panchor);
1605
1606	    anchor = XtNew(XawTextAnchor);
1607	    diff = position - panchor->position;
1608
1609	    panchor->cache = NULL;
1610	    anchor->entities = entity;
1611	    if (pentity != entity)
1612		pentity->next = NULL;
1613	    else
1614		panchor->entities = NULL;
1615	    while (entity) {
1616		entity->offset -= diff;
1617		entity = entity->next;
1618	    }
1619	}
1620	else {
1621	    anchor = XtNew(XawTextAnchor);
1622	    anchor->entities = NULL;
1623	}
1624    }
1625    else {
1626	anchor = XtNew(XawTextAnchor);
1627	anchor->entities = NULL;
1628    }
1629
1630    anchor->position = position;
1631    anchor->cache = NULL;
1632
1633    src->textSrc.anchors = (XawTextAnchor**)
1634	XtRealloc((XtPointer)src->textSrc.anchors, sizeof(XawTextAnchor*) *
1635		  (src->textSrc.num_anchors + 1));
1636    src->textSrc.anchors[src->textSrc.num_anchors++] = anchor;
1637    qsort((void*)src->textSrc.anchors, src->textSrc.num_anchors,
1638	  sizeof(XawTextAnchor*), qcmp_anchors);
1639
1640    return (anchor);
1641}
1642
1643XawTextAnchor *
1644XawTextSourceFindAnchor(Widget w, XawTextPosition position)
1645{
1646    TextSrcObject src = (TextSrcObject)w;
1647    int i = 0, left, right, nmemb = src->textSrc.num_anchors;
1648    XawTextAnchor *anchor, **anchors = src->textSrc.anchors;
1649
1650    left = 0;
1651    right = nmemb - 1;
1652    while (left <= right) {
1653	anchor = anchors[i = (left + right) >> 1];
1654	if (anchor->position == position)
1655	    return (anchor);
1656	else if (position < anchor->position)
1657	    right = i - 1;
1658	else
1659	    left = i + 1;
1660    }
1661
1662    if (nmemb)
1663	return (right < 0 ? anchors[0] : anchors[right]);
1664
1665    return (NULL);
1666}
1667
1668Bool
1669XawTextSourceAnchorAndEntity(Widget w, XawTextPosition position,
1670			     XawTextAnchor **anchor_return,
1671			     XawTextEntity **entity_return)
1672{
1673    XawTextAnchor *anchor = XawTextSourceFindAnchor(w, position);
1674    XawTextEntity *pentity, *entity;
1675    XawTextPosition offset;
1676    Bool next_anchor = True, retval = False;
1677
1678    if (anchor->cache && anchor->position + anchor->cache->offset +
1679	anchor->cache->length <= position)
1680	pentity = entity = anchor->cache;
1681    else
1682	pentity = entity = anchor->entities;
1683    while (entity) {
1684	offset = anchor->position + entity->offset;
1685
1686	if (offset > position) {
1687	    retval = next_anchor = False;
1688	    break;
1689	}
1690	if (offset + entity->length > position) {
1691	    retval = True;
1692	    next_anchor = False;
1693	    break;
1694	}
1695	pentity = entity;
1696	entity = entity->next;
1697    }
1698
1699    if (next_anchor) {
1700	*anchor_return = anchor = XawTextSourceNextAnchor(w, anchor);
1701	*entity_return = anchor ? anchor->entities : NULL;
1702    }
1703    else {
1704	*anchor_return = anchor;
1705	*entity_return = retval ? entity : pentity;
1706    }
1707
1708    if (*anchor_return)
1709	(*anchor_return)->cache = *entity_return;
1710
1711    return (retval);
1712}
1713
1714XawTextAnchor *
1715XawTextSourceNextAnchor(Widget w, XawTextAnchor *anchor)
1716{
1717    int i;
1718    TextSrcObject src = (TextSrcObject)w;
1719
1720    for (i = 0; i < src->textSrc.num_anchors - 1; i++)
1721	if (src->textSrc.anchors[i] == anchor)
1722	    return (src->textSrc.anchors[i + 1]);
1723
1724    return (NULL);
1725}
1726
1727XawTextAnchor *
1728XawTextSourcePrevAnchor(Widget w, XawTextAnchor *anchor)
1729{
1730    int i;
1731    TextSrcObject src = (TextSrcObject)w;
1732
1733    for (i = src->textSrc.num_anchors - 1; i > 0; i--)
1734	if (src->textSrc.anchors[i] == anchor)
1735	    return (src->textSrc.anchors[i - 1]);
1736
1737    return (NULL);
1738}
1739
1740XawTextAnchor *
1741XawTextSourceRemoveAnchor(Widget w, XawTextAnchor *anchor)
1742{
1743    int i;
1744    TextSrcObject src = (TextSrcObject)w;
1745
1746    for (i = 0; i < src->textSrc.num_anchors; i++)
1747	if (src->textSrc.anchors[i] == anchor)
1748	    break;
1749
1750    if (i == 0)
1751	return (src->textSrc.num_anchors > 1 ? src->textSrc.anchors[1] : NULL);
1752
1753    if (i < src->textSrc.num_anchors) {
1754	XtFree((XtPointer)anchor);
1755	if (i < --src->textSrc.num_anchors) {
1756	    memmove(&src->textSrc.anchors[i],
1757		    &src->textSrc.anchors[i + 1],
1758		    (src->textSrc.num_anchors - i) *
1759		    sizeof(XawTextAnchor*));
1760
1761	    return (src->textSrc.anchors[i]);
1762	}
1763    }
1764
1765    return (NULL);
1766}
1767
1768XawTextEntity *
1769XawTextSourceAddEntity(Widget w, int type, int flags, XtPointer data,
1770		       XawTextPosition position, Cardinal length,
1771		       XrmQuark property)
1772{
1773    XawTextAnchor *next, *anchor = _XawTextSourceFindAnchor(w, position);
1774    XawTextEntity *entity, *eprev;
1775
1776    /* There is no support for zero length entities for now */
1777    if (length == 0)
1778	return (NULL);
1779
1780    if (anchor->cache && anchor->position + anchor->cache->offset +
1781	anchor->cache->length <= position)
1782	eprev = entity = anchor->cache;
1783    else
1784	eprev = entity = anchor->entities;
1785
1786    while (entity && anchor->position + entity->offset + entity->length <=
1787	   position) {
1788	eprev = entity;
1789	entity = entity->next;
1790    }
1791    if (entity && anchor->position + entity->offset < position + length) {
1792	fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n");
1793	return (NULL);
1794    }
1795
1796    next = XawTextSourceFindAnchor(w, position + length);
1797    if (next && next != anchor) {
1798	if ((entity = next->entities) != NULL) {
1799	    if (next->position + entity->offset < position + length) {
1800		fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n");
1801		return (NULL);
1802	    }
1803	}
1804	if (position + length > next->position) {
1805	    XawTextPosition diff = position + length - next->position;
1806
1807	    next->position += diff;
1808	    entity = next->entities;
1809	    while (entity) {
1810		entity->offset -= diff;
1811		entity = entity->next;
1812	    }
1813	    entity = anchor->entities;
1814	    while (entity && entity->offset < 0)
1815		entity = entity->next;
1816	    if (entity && entity->offset < 0) {
1817		if (eprev)
1818		    eprev->next = next->entities;
1819		else
1820		    anchor->entities = next->entities;
1821		if ((next->entities = entity->next) == NULL)
1822		    (void)XawTextSourceRemoveAnchor(w, next);
1823		entity->next = NULL;
1824
1825		return (XawTextSourceAddEntity(w, type, flags, data, position,
1826					       length, property));
1827	    }
1828	}
1829    }
1830
1831    /* Automatically join sequential entities if possible */
1832    if (eprev &&
1833	anchor->position + eprev->offset + eprev->length == position &&
1834	eprev->property == property && eprev->type == type &&
1835	eprev->flags == flags && eprev->data == data) {
1836	eprev->length += length;
1837	return (eprev);
1838    }
1839
1840    entity = XtNew(XawTextEntity);
1841    entity->type = type;
1842    entity->flags = flags;
1843    entity->data = data;
1844    entity->offset = position - anchor->position;
1845    entity->length = length;
1846    entity->property = property;
1847
1848    if (eprev == NULL) {
1849	anchor->entities = entity;
1850	entity->next = NULL;
1851	anchor->cache = NULL;
1852    }
1853    else if (eprev->offset > entity->offset) {
1854	anchor->cache = NULL;
1855	anchor->entities = entity;
1856	entity->next = eprev;
1857    }
1858    else {
1859	anchor->cache = eprev;
1860	entity->next = eprev->next;
1861	eprev->next = entity;
1862    }
1863
1864    return (entity);
1865}
1866
1867void
1868XawTextSourceClearEntities(Widget w, XawTextPosition left, XawTextPosition right)
1869{
1870    XawTextAnchor *anchor = XawTextSourceFindAnchor(w, left);
1871    XawTextEntity *entity, *eprev, *enext;
1872    XawTextPosition offset;
1873    int length;
1874
1875    while (anchor && anchor->entities == NULL)
1876	anchor = XawTextSourceRemoveAnchor(w, anchor);
1877
1878    if (anchor == NULL || left >= right)
1879	return;
1880
1881    if (anchor->cache && anchor->position + anchor->cache->offset +
1882	anchor->cache->length < left)
1883	eprev = entity = anchor->cache;
1884    else
1885	eprev = entity = anchor->entities;
1886
1887    /* find first entity before left position */
1888    while (anchor->position + entity->offset + entity->length < left) {
1889	eprev = entity;
1890	if ((entity = entity->next) == NULL) {
1891	    if ((anchor = XawTextSourceNextAnchor(w, anchor)) == NULL)
1892		return;
1893	    if ((eprev = entity = anchor->entities) == NULL) {
1894		fprintf(stderr, "Bad anchor found!\n");
1895		return;
1896	    }
1897	}
1898    }
1899
1900    offset = anchor->position + entity->offset;
1901    if (offset <= left) {
1902	length = XawMin(entity->length, left - offset);
1903
1904	if (length <= 0) {
1905	    enext = entity->next;
1906	    eprev->next = enext;
1907	    XtFree((XtPointer)entity);
1908	    anchor->cache = NULL;
1909	    if (entity == anchor->entities) {
1910		eprev = NULL;
1911		if ((anchor->entities = enext) == NULL) {
1912		    if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL)
1913			return;
1914		    entity = anchor->entities;
1915		}
1916		else
1917		    entity = enext;
1918	    }
1919	    else
1920		entity = enext;
1921	}
1922	else {
1923	    entity->length = length;
1924	    eprev = entity;
1925	    entity = entity->next;
1926	}
1927    }
1928
1929    /* clean everything until right position is reached */
1930    while (anchor) {
1931	while (entity) {
1932	    offset = anchor->position + entity->offset + entity->length;
1933
1934	    if (offset > right) {
1935		anchor->cache = NULL;
1936		entity->offset = XawMax(entity->offset, right - anchor->position);
1937		entity->length = XawMin(entity->length, offset - right);
1938		return;
1939	    }
1940
1941	    enext = entity->next;
1942	    if (eprev)
1943		eprev->next = enext;
1944	    XtFree((XtPointer)entity);
1945	    if (entity == anchor->entities) {
1946		eprev = anchor->cache = NULL;
1947		if ((anchor->entities = enext) == NULL) {
1948		    if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL)
1949			return;
1950		    entity = anchor->entities;
1951		    continue;
1952		}
1953	    }
1954	    entity = enext;
1955	}
1956	if (anchor)
1957	    anchor->cache = NULL;
1958	if ((anchor = XawTextSourceNextAnchor(w, anchor)) != NULL)
1959	    entity = anchor->entities;
1960	eprev = NULL;
1961    }
1962}
1963
1964/* checks the anchors up to position, and create an appropriate anchor
1965 * at position, if required.
1966 */
1967XawTextAnchor *
1968_XawTextSourceFindAnchor(Widget w, XawTextPosition position)
1969{
1970    XawTextAnchor *anchor;
1971
1972    anchor = XawTextSourceFindAnchor(w, position);
1973
1974    position -= position % ANCHORS_DIST;
1975
1976    if (position - anchor->position >= ANCHORS_DIST)
1977	return (XawTextSourceAddAnchor(w, position));
1978
1979    return (anchor);
1980}
1981#endif
1982