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