/* $Xorg: TextSrc.c,v 1.4 2000/08/17 19:45:41 cpqbld Exp $ */ /* Copyright 1989, 1994, 1998 The Open Group All Rights Reserved. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ /* $XFree86: xc/lib/Xaw/TextSrc.c,v 1.27 2001/01/30 15:03:34 paulo Exp $ */ /* * Author: Chris Peterson, MIT X Consortium. * Much code taken from X11R3 String and Disk Sources. */ #include #include #include #include #include #include #include #include #include #include #include "XawI18n.h" #include "Private.h" #ifndef OLDXAW #define UNDO_DEPTH 16384 #define ANCHORS_DIST 4096 /* default distance between anchors */ /* * Types */ typedef struct { XawTextPosition position; char *buffer; unsigned length; unsigned refcount; unsigned long format; } XawTextUndoBuffer; typedef struct _XawTextUndoList XawTextUndoList; struct _XawTextUndoList { XawTextUndoBuffer *left, *right; XawTextUndoList *undo, *redo; }; struct _XawTextUndo { XawTextUndoBuffer **undo; unsigned num_undo; XawTextUndoList *list, *pointer, *end_mark, *head; unsigned num_list; XawTextScanDirection dir; XawTextUndoBuffer *l_save, *r_save; XawTextUndoList *u_save; XawTextUndoBuffer *l_no_change, *r_no_change; int merge; int erase; /* there are two types of erases */ }; #endif /* OLDXAW */ /* * Class Methods */ static Boolean ConvertSelection(Widget, Atom*, Atom*, Atom*, XtPointer*, unsigned long*, int*); static XawTextPosition Read(Widget, XawTextPosition, XawTextBlock*, int); static int Replace(Widget, XawTextPosition, XawTextPosition, XawTextBlock*); static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType, XawTextScanDirection, int, Bool); static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection, XawTextBlock*); static void SetSelection(Widget, XawTextPosition, XawTextPosition, Atom); static void XawTextSrcClassInitialize(void); static void XawTextSrcClassPartInitialize(WidgetClass); static void XawTextSrcInitialize(Widget, Widget, ArgList, Cardinal*); static void XawTextSrcDestroy(Widget); static Boolean XawTextSrcSetValues(Widget, Widget, Widget, ArgList, Cardinal*); /* * Prototypes */ static void CvtStringToEditMode(XrmValuePtr, Cardinal*, XrmValuePtr, XrmValuePtr); static Boolean CvtEditModeToString(Display*, XrmValuePtr, Cardinal*, XrmValuePtr, XrmValuePtr, XtPointer*); #ifndef OLDXAW static void FreeUndoBuffer(XawTextUndo*); static void UndoGC(XawTextUndo*); static void TellSourceChanged(TextSrcObject, XawTextPosition, XawTextPosition, XawTextBlock*, int); Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*); Bool _XawTextSrcToggleUndo(TextSrcObject); XawTextAnchor *_XawTextSourceFindAnchor(Widget, XawTextPosition); /* * External */ void _XawSourceAddText(Widget, Widget); void _XawSourceRemoveText(Widget, Widget, Bool); Bool _XawTextSourceNewLineAtEOF(Widget); void _XawSourceSetUndoErase(TextSrcObject, int); void _XawSourceSetUndoMerge(TextSrcObject, Bool); #endif /* OLDXAW */ /* * Defined in Text.c */ char *_XawTextGetText(TextWidget, XawTextPosition, XawTextPosition); void _XawTextSourceChanged(Widget, XawTextPosition, XawTextPosition, XawTextBlock*, int); /* * Initialization */ #define offset(field) XtOffsetOf(TextSrcRec, textSrc.field) static XtResource resources[] = { { XtNeditType, XtCEditType, XtREditMode, sizeof(XawTextEditType), offset(edit_mode), XtRString, "read" }, #ifndef OLDXAW { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(callback), XtRCallback, NULL }, { XtNsourceChanged, XtCChanged, XtRBoolean, sizeof(Boolean), offset(changed), XtRImmediate, (XtPointer)False }, { XtNenableUndo, XtCUndo, XtRBoolean, sizeof(Boolean), offset(enable_undo), XtRImmediate, (XtPointer)False }, { XtNpropertyCallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(property_callback), XtRCallback, NULL }, #endif /* OLDXAW */ }; #undef offset #define Superclass (&objectClassRec) TextSrcClassRec textSrcClassRec = { /* object */ { (WidgetClass)Superclass, /* superclass */ "TextSrc", /* class_name */ sizeof(TextSrcRec), /* widget_size */ XawTextSrcClassInitialize, /* class_initialize */ XawTextSrcClassPartInitialize, /* class_part_initialize */ False, /* class_inited */ XawTextSrcInitialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ False, /* compress_motion */ False, /* compress_exposure */ False, /* compress_enterleave */ False, /* visible_interest */ XawTextSrcDestroy, /* destroy */ NULL, /* resize */ NULL, /* expose */ XawTextSrcSetValues, /* set_values */ NULL, /* set_values_hook */ NULL, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ NULL, /* display_accelerator */ NULL, /* extension */ }, /* text_src */ { Read, /* Read */ Replace, /* Replace */ Scan, /* Scan */ Search, /* Search */ SetSelection, /* SetSelection */ ConvertSelection, /* ConvertSelection */ }, }; WidgetClass textSrcObjectClass = (WidgetClass)&textSrcClassRec; static XrmQuark QRead, QAppend, QEdit; #ifndef OLDXAW static char *SrcNL = "\n"; static wchar_t SrcWNL[2]; #endif /* * Implementation */ static void XawTextSrcClassInitialize(void) { XawInitializeWidgetSet(); #ifndef OLDXAW SrcWNL[0] = _Xaw_atowc(XawLF); SrcWNL[1] = 0; #endif QRead = XrmPermStringToQuark(XtEtextRead); QAppend = XrmPermStringToQuark(XtEtextAppend); QEdit = XrmPermStringToQuark(XtEtextEdit); XtAddConverter(XtRString, XtREditMode, CvtStringToEditMode, NULL, 0); XtSetTypeConverter(XtREditMode, XtRString, CvtEditModeToString, NULL, 0, XtCacheNone, NULL); } static void XawTextSrcClassPartInitialize(WidgetClass wc) { TextSrcObjectClass t_src, superC; t_src = (TextSrcObjectClass)wc; superC = (TextSrcObjectClass)t_src->object_class.superclass; /* * We don't need to check for null super since we'll get to TextSrc * eventually */ if (t_src->textSrc_class.Read == XtInheritRead) t_src->textSrc_class.Read = superC->textSrc_class.Read; if (t_src->textSrc_class.Replace == XtInheritReplace) t_src->textSrc_class.Replace = superC->textSrc_class.Replace; if (t_src->textSrc_class.Scan == XtInheritScan) t_src->textSrc_class.Scan = superC->textSrc_class.Scan; if (t_src->textSrc_class.Search == XtInheritSearch) t_src->textSrc_class.Search = superC->textSrc_class.Search; if (t_src->textSrc_class.SetSelection == XtInheritSetSelection) t_src->textSrc_class.SetSelection = superC->textSrc_class.SetSelection; if (t_src->textSrc_class.ConvertSelection == XtInheritConvertSelection) t_src->textSrc_class.ConvertSelection = superC->textSrc_class.ConvertSelection; } /*ARGSUSED*/ static void XawTextSrcInitialize(Widget request, Widget cnew, ArgList args, Cardinal *num_args) { #ifndef OLDXAW TextSrcObject src = (TextSrcObject)cnew; if (src->textSrc.enable_undo) { src->textSrc.undo = (XawTextUndo*)XtCalloc(1, sizeof(XawTextUndo)); src->textSrc.undo->dir = XawsdLeft; } else src->textSrc.undo = NULL; src->textSrc.undo_state = False; if (XtIsSubclass(XtParent(cnew), textWidgetClass)) { src->textSrc.text = (WidgetList)XtMalloc(sizeof(Widget*)); src->textSrc.text[0] = XtParent(cnew); src->textSrc.num_text = 1; } else { src->textSrc.text = NULL; src->textSrc.num_text = 0; } src->textSrc.anchors = NULL; src->textSrc.num_anchors = 0; (void)XawTextSourceAddAnchor(cnew, 0); #endif /* OLDXAW */ } static void XawTextSrcDestroy(Widget w) { #ifndef OLDXAW TextSrcObject src = (TextSrcObject)w; if (src->textSrc.enable_undo) { FreeUndoBuffer(src->textSrc.undo); XtFree((char*)src->textSrc.undo); } XtFree((char*)src->textSrc.text); if (src->textSrc.num_anchors) { XawTextEntity *entity, *enext; int i; for (i = 0; i < src->textSrc.num_anchors; i++) { entity = src->textSrc.anchors[i]->entities; while (entity) { enext = entity->next; XtFree((XtPointer)entity); entity = enext; } XtFree((XtPointer)src->textSrc.anchors[i]); } XtFree((XtPointer)src->textSrc.anchors); } #endif /* OLDXAW */ } /*ARGSUSED*/ static Boolean XawTextSrcSetValues(Widget current, Widget request, Widget cnew, ArgList args, Cardinal *num_args) { #ifndef OLDXAW TextSrcObject oldtw = (TextSrcObject)current; TextSrcObject newtw = (TextSrcObject)cnew; if (oldtw->textSrc.enable_undo != newtw->textSrc.enable_undo) { if (newtw->textSrc.enable_undo) { newtw->textSrc.undo = (XawTextUndo*) XtCalloc(1, sizeof(XawTextUndo)); newtw->textSrc.undo->dir = XawsdLeft; } else { FreeUndoBuffer(newtw->textSrc.undo); XtFree((char*)newtw->textSrc.undo); newtw->textSrc.undo = NULL; } } if (oldtw->textSrc.changed != newtw->textSrc.changed) { if (newtw->textSrc.enable_undo) { if (newtw->textSrc.undo->list) { newtw->textSrc.undo->l_no_change = newtw->textSrc.undo->list->left; newtw->textSrc.undo->r_no_change = newtw->textSrc.undo->list->right; } else newtw->textSrc.undo->l_no_change = newtw->textSrc.undo->r_no_change = NULL; } } #endif /* OLDXAW */ return (False); } /* * Function: * Read * * Parameters: * w - TextSrc Object * pos - position of the text to retreive * text - text block that will contain returned text * length - maximum number of characters to read * * Description: * This function reads the source. */ /*ARGSUSED*/ static XawTextPosition Read(Widget w, XawTextPosition pos, XawTextBlock *text, int length) { return ((XawTextPosition)0); } /* * Function: * Replace * * Parameters: * src - Text Source Object * startPos - ends of text that will be removed * endPos - "" * text - new text to be inserted into buffer at startPos * * Description: * Replaces a block of text with new text. */ /*ARGSUSED*/ static int Replace(Widget w, XawTextPosition startPos, XawTextPosition endPos, XawTextBlock *text) { return (XawEditError); } /* * Function: * Scan * * Parameters: * w - TextSrc Object * position - position to start scanning * type - type of thing to scan for * dir - direction to scan * count - which occurance if this thing to search for * include - whether or not to include the character found in * the position that is returned * * Description: * Scans the text source for the number and type of item specified. */ /*ARGSUSED*/ static XawTextPosition Scan(Widget w, XawTextPosition position, XawTextScanType type, XawTextScanDirection dir, int count, Bool include) { return ((XawTextPosition)0); } /* * Function: * Search * * Parameters: * w - TextSource Object * position - position to start searching * dir - direction to search * text - the text block to search for * * Description: * Searchs the text source for the text block passed */ /*ARGSUSED*/ static XawTextPosition Search(Widget w, XawTextPosition position, XawTextScanDirection dir, XawTextBlock *text) { return (XawTextSearchError); } /*ARGSUSED*/ static Boolean ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type, XtPointer *value, unsigned long *length, int *format) { return (False); } /*ARGSUSED*/ static void SetSelection(Widget w, XawTextPosition left, XawTextPosition right, Atom selection) { } /*ARGSUSED*/ static void CvtStringToEditMode(XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal) { static XawTextEditType editType; XrmQuark q; char name[7]; XmuNCopyISOLatin1Lowered(name, (char *)fromVal->addr, sizeof(name)); q = XrmStringToQuark(name); if (q == QRead) editType = XawtextRead; else if (q == QAppend) editType = XawtextAppend; else if (q == QEdit) editType = XawtextEdit; else { toVal->size = 0; toVal->addr = NULL; XtStringConversionWarning((char *)fromVal->addr, XtREditMode); } toVal->size = sizeof(XawTextEditType); toVal->addr = (XPointer)&editType; } /*ARGSUSED*/ static Boolean CvtEditModeToString(Display *dpy, XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal, XtPointer *data) { static String buffer; Cardinal size; switch (*(XawTextEditType *)fromVal->addr) { case XawtextAppend: case XawtextRead: buffer = XtEtextRead; break; buffer = XtEtextAppend; break; case XawtextEdit: buffer = XtEtextEdit; break; default: XawTypeToStringWarning(dpy, XtREditMode); toVal->addr = NULL; toVal->size = 0; return (False); } size = strlen(buffer) + 1; if (toVal->addr != NULL) { if (toVal->size < size) { toVal->size = size; return (False); } strcpy((char *)toVal->addr, buffer); } else toVal->addr = (XPointer)buffer; toVal->size = sizeof(String); return (True); } #ifndef OLDXAW Bool _XawTextSourceNewLineAtEOF(Widget w) { TextSrcObject src = (TextSrcObject)w; XawTextBlock text; text.firstPos = 0; if ((text.format = src->textSrc.text_format) == XawFmt8Bit) text.ptr = SrcNL; else text.ptr = (char*)SrcWNL; text.length = 1; return (XawTextSourceSearch(w, XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True) - 1, XawsdRight, &text) != XawTextSearchError); } void _XawSourceAddText(Widget source, Widget text) { TextSrcObject src = (TextSrcObject)source; Bool found = False; Cardinal i; for (i = 0; i < src->textSrc.num_text; i++) if (src->textSrc.text[i] == text) { found = True; break; } if (!found) { src->textSrc.text = (WidgetList) XtRealloc((char*)src->textSrc.text, sizeof(Widget) * (src->textSrc.num_text + 1)); src->textSrc.text[src->textSrc.num_text++] = text; } } void _XawSourceRemoveText(Widget source, Widget text, Bool destroy) { TextSrcObject src = (TextSrcObject)source; Bool found = False; Cardinal i; if (src == NULL) return; for (i = 0; i < src->textSrc.num_text; i++) if (src->textSrc.text[i] == text) { found = True; break; } if (found) { if (--src->textSrc.num_text == 0) { if (destroy) { XtDestroyWidget(source); return; } else { XtFree((char*)src->textSrc.text); src->textSrc.text = NULL; /* for realloc "magic" */ } } else if (i < src->textSrc.num_text) memmove(&src->textSrc.text[i], &src->textSrc.text[i + 1], sizeof(Widget) * (src->textSrc.num_text - i)); } } #endif /* OLDXAW */ /* * Function: * XawTextSourceRead * * Parameters: * w - TextSrc Object * pos - position of the text to retrieve * text - text block that will contain returned text (return) * length - maximum number of characters to read * * Description: * This function reads the source. * * Returns: * The number of characters read into the buffer */ XawTextPosition XawTextSourceRead(Widget w, XawTextPosition pos, XawTextBlock *text, int length) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; return ((*cclass->textSrc_class.Read)(w, pos, text, length)); } #ifndef OLDXAW static void TellSourceChanged(TextSrcObject src, XawTextPosition left, XawTextPosition right, XawTextBlock *block, int lines) { Cardinal i; for (i = 0; i < src->textSrc.num_text; i++) _XawTextSourceChanged(src->textSrc.text[i], left, right, block, lines); } /* * This function is required because there is no way to diferentiate * if the first erase was generated by a backward-kill-char and the * second by a forward-kill-char (or vice-versa) from XawTextSourceReplace. * It is only possible to diferentiate after the second character is * killed, but then, it is too late. */ void _XawSourceSetUndoErase(TextSrcObject src, int value) { if (src && src->textSrc.enable_undo) src->textSrc.undo->erase = value; } /* * To diferentiate insert-char's separeted by cursor movements. */ void _XawSourceSetUndoMerge(TextSrcObject src, Bool state) { if (src && src->textSrc.enable_undo) src->textSrc.undo->merge += state ? 1 : -1; } #endif /* OLDXAW */ /* * Public Functions */ /* * Function: * XawTextSourceReplace * * Parameters: * src - Text Source Object * startPos - ends of text that will be removed * endPos - "" * text - new text to be inserted into buffer at startPos * * Description: * Replaces a block of text with new text. * * Returns: * XawEditError or XawEditDone. */ /*ARGSUSED*/ int XawTextSourceReplace(Widget w, XawTextPosition left, XawTextPosition right, XawTextBlock *block) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; #ifndef OLDXAW TextSrcObject src = (TextSrcObject)w; XawTextUndoBuffer *l_state, *r_state; XawTextUndoList *undo; Bool enable_undo; XawTextPosition start, end; int i, error, lines = 0; if (src->textSrc.edit_mode == XawtextRead) return (XawEditError); enable_undo = src->textSrc.enable_undo && src->textSrc.undo_state == False; if (enable_undo) { unsigned size, total; if (src->textSrc.undo->l_save) { l_state = src->textSrc.undo->l_save; src->textSrc.undo->l_save = NULL; } else l_state = XtNew(XawTextUndoBuffer); l_state->refcount = 1; l_state->position = left; if (left < right) { Widget ctx = NULL; for (i = 0; i < src->textSrc.num_text; i++) if (XtIsSubclass(src->textSrc.text[i], textWidgetClass)) { ctx = src->textSrc.text[i]; break; } l_state->buffer = _XawTextGetText((TextWidget)ctx, left, right); l_state->length = right - left; } else { l_state->length = 0; l_state->buffer = NULL; } l_state->format = src->textSrc.text_format; if (l_state->length == 1) { if (l_state->format == XawFmtWide && *(wchar_t*)l_state->buffer == *SrcWNL) { XtFree(l_state->buffer); l_state->buffer = (char*)SrcWNL; } else if (*l_state->buffer == '\n') { XtFree(l_state->buffer); l_state->buffer = SrcNL; } } if (src->textSrc.undo->r_save) { r_state = src->textSrc.undo->r_save; src->textSrc.undo->r_save = NULL; } else r_state = XtNew(XawTextUndoBuffer); r_state->refcount = 1; r_state->position = left; r_state->format = block->format; size = block->format == XawFmtWide ? sizeof(wchar_t) : sizeof(char); total = size * block->length; r_state->length = block->length; r_state->buffer = NULL; if (total == size) { if (r_state->format == XawFmtWide && *(wchar_t*)block->ptr == *SrcWNL) r_state->buffer = (char*)SrcWNL; else if (*block->ptr == '\n') r_state->buffer = SrcNL; } if (total && !r_state->buffer) { r_state->buffer = XtMalloc(total); memcpy(r_state->buffer, block->ptr, total); } if (src->textSrc.undo->u_save) { undo = src->textSrc.undo->u_save; src->textSrc.undo->u_save = NULL; } else undo = XtNew(XawTextUndoList); undo->left = l_state; undo->right = r_state; undo->undo = src->textSrc.undo->list; undo->redo = NULL; } else { undo = NULL; l_state = r_state = NULL; } #define LARGE_VALUE 262144 /* 256 K */ /* optimization, to avoid long delays recalculating the line number * when editing huge files */ if (left > LARGE_VALUE) { start = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False); for (i = 0; i < src->textSrc.num_text; i++) { TextWidget tw = (TextWidget)src->textSrc.text[i]; if (left <= tw->text.lt.top && left + block->length - (right - left) > tw->text.lt.top) _XawTextBuildLineTable(tw, start, False); } } #undef LARGE_VALUE start = left; end = right; while (start < end) { start = XawTextSourceScan(w, start, XawstEOL, XawsdRight, 1, True); if (start <= end) { --lines; if (start == XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True)) { lines += !_XawTextSourceNewLineAtEOF(w); break; } } } #else int error; #endif /* OLDXAW */ error = (*cclass->textSrc_class.Replace)(w, left, right, block); #ifndef OLDXAW if (error != XawEditDone) { if (enable_undo) { if (l_state->buffer) { if (l_state->buffer != SrcNL && l_state->buffer != (char*)SrcWNL) XtFree(l_state->buffer); l_state->buffer = NULL; } src->textSrc.undo->l_save = l_state; if (r_state->buffer) { if (r_state->buffer != SrcNL && r_state->buffer != (char*)SrcWNL) XtFree(r_state->buffer); r_state->buffer = NULL; } src->textSrc.undo->r_save = r_state; src->textSrc.undo->u_save = undo; } } else if (enable_undo) { XawTextUndoList *list = src->textSrc.undo->list; XawTextUndoBuffer *unl, *lnl; int erase = undo->right->length == 0 && undo->left->length == 1 && list && list->right->length == 0; if (erase) { erase = list->left->position - 1 == undo->left->position ? -1 : list->left->position == undo->left->position ? 1 : 0; if (src->textSrc.undo->erase && erase != src->textSrc.undo->erase) erase = 0; else src->textSrc.undo->erase = erase; } if (erase) { unl = l_state; lnl = list->left; } else { unl = r_state; lnl = list ? list->right : NULL; } /* Try to merge the undo buffers */ if (src->textSrc.undo->merge > 0 && ((erase || (list && ((list->left->length == 0 && undo->left->length == 0) || (list->left->length == list->right->length && undo->left->length == 1)) && undo->right->length == 1 && list->right->position + list->right->length == undo->right->position)) && src->textSrc.undo->pointer == list && unl->format == list->right->format && ((unl->format == XawFmt8Bit && unl->buffer[0] != XawLF) || (unl->format == XawFmtWide && *(wchar_t*)(unl->buffer) != _Xaw_atowc(XawLF))) && ((lnl->format == XawFmt8Bit && lnl->buffer[0] != XawLF) || (lnl->format == XawFmtWide && *(wchar_t*)(lnl->buffer) != _Xaw_atowc(XawLF))))) { unsigned size = lnl->format == XawFmtWide ? sizeof(wchar_t) : sizeof(char); if (!erase) { list->right->buffer = XtRealloc(list->right->buffer, (list->right->length + 1) * size); memcpy(list->right->buffer + list->right->length * size, undo->right->buffer, size); ++list->right->length; XtFree(r_state->buffer); } else if (erase < 0) { --list->left->position; --list->right->position; } src->textSrc.undo->l_save = l_state; src->textSrc.undo->r_save = r_state; src->textSrc.undo->u_save = undo; if (list->left->length) { list->left->buffer = XtRealloc(list->left->buffer, (list->left->length + 1) * size); if (erase >= 0) memcpy(list->left->buffer + list->left->length * size, undo->left->buffer, size); else { /* use memmove, since strings overlap */ memmove(list->left->buffer + size, list->left->buffer, list->left->length * size); memcpy(list->left->buffer, undo->left->buffer, size); } ++list->left->length; XtFree(l_state->buffer); } if (src->textSrc.undo->num_list >= UNDO_DEPTH) UndoGC(src->textSrc.undo); } else { src->textSrc.undo->undo = (XawTextUndoBuffer**) XtRealloc((char*)src->textSrc.undo->undo, (2 + src->textSrc.undo->num_undo) * sizeof(XawTextUndoBuffer)); src->textSrc.undo->undo[src->textSrc.undo->num_undo++] = l_state; src->textSrc.undo->undo[src->textSrc.undo->num_undo++] = r_state; if (src->textSrc.undo->list) src->textSrc.undo->list->redo = undo; else src->textSrc.undo->head = undo; src->textSrc.undo->merge = l_state->length <= 1 && r_state->length <= 1; src->textSrc.undo->list = src->textSrc.undo->pointer = src->textSrc.undo->end_mark = undo; if (++src->textSrc.undo->num_list >= UNDO_DEPTH) UndoGC(src->textSrc.undo); } src->textSrc.undo->dir = XawsdLeft; if (!src->textSrc.changed) { src->textSrc.undo->l_no_change = src->textSrc.undo->list->right; src->textSrc.undo->r_no_change = src->textSrc.undo->list->left; src->textSrc.changed = True; } } else if (!src->textSrc.enable_undo) src->textSrc.changed = True; if (error == XawEditDone) { XawTextPropertyInfo info; XawTextAnchor *anchor; /* find anchor and index */ /* XXX index (i) could be returned by XawTextSourceFindAnchor * or similar function, to speed up */ if ((anchor = XawTextSourceFindAnchor(w, left))) { XawTextEntity *eprev, *entity, *enext; XawTextPosition offset, diff = block->length - (right - left); for (i = 0; i < src->textSrc.num_anchors; i++) if (src->textSrc.anchors[i] == anchor) break; if (anchor->cache && anchor->position + anchor->cache->offset + anchor->cache->length <= left) eprev = entity = anchor->cache; else eprev = entity = anchor->entities; while (entity) { offset = anchor->position + entity->offset; if (offset > left) break; if (offset + entity->length > left) break; eprev = entity; entity = entity->next; } /* try to do the right thing here (and most likely correct), but * other code needs to check what was done */ /* adjust entity length */ if (entity && offset <= left) { if (offset + entity->length < right) entity->length = left - offset + block->length; else entity->length += diff; if (entity->length == 0) { enext = entity->next; eprev->next = enext; anchor->cache = NULL; XtFree((XtPointer)entity); if (entity == anchor->entities) { if ((anchor->entities = enext) == NULL) { eprev = NULL; anchor = XawTextSourceRemoveAnchor(w, anchor); entity = anchor ? anchor->entities : NULL; } else eprev = entity = enext; } else entity = enext; } else { eprev = entity; entity = entity->next; } } while (anchor) { while (entity) { offset = anchor->position + entity->offset + entity->length; if (offset > right) { entity->length = XawMin(entity->length, offset - right); goto exit_anchor_loop; } enext = entity->next; if (eprev) eprev->next = enext; XtFree((XtPointer)entity); anchor->cache = NULL; if (entity == anchor->entities) { eprev = NULL; if ((anchor->entities = enext) == NULL) { if (i == 0) ++i; else if (i < --src->textSrc.num_anchors) { memmove(&src->textSrc.anchors[i], &src->textSrc.anchors[i + 1], (src->textSrc.num_anchors - i) * sizeof(XawTextAnchor*)); XtFree((XtPointer)anchor); } if (i >= src->textSrc.num_anchors) { anchor = NULL; entity = NULL; break; } anchor = src->textSrc.anchors[i]; entity = anchor->entities; continue; } } entity = enext; } if (i + 1 < src->textSrc.num_anchors) { anchor = src->textSrc.anchors[++i]; entity = anchor->entities; eprev = NULL; } else break; eprev = NULL; } exit_anchor_loop: if (anchor) { XawTextAnchor *aprev; if (anchor->position >= XawMax(right, left + block->length)) anchor->position += diff; else if (anchor->position > left && (aprev = XawTextSourcePrevAnchor(w, anchor))) { XawTextPosition tmp = anchor->position - aprev->position; if (diff) { while (entity) { entity->offset += diff; entity = entity->next; } } entity = anchor->entities; while (entity) { entity->offset += tmp; entity = entity->next; } if ((entity = aprev->entities) == NULL) aprev->entities = anchor->entities; else { while (entity->next) entity = entity->next; entity->next = anchor->entities; } anchor->entities = NULL; (void)XawTextSourceRemoveAnchor(w, anchor); --i; } else if (diff) { while (entity) { entity->offset += diff; entity = entity->next; } } } if (diff) { for (++i; i < src->textSrc.num_anchors; i++) src->textSrc.anchors[i]->position += diff; } } start = left; end = start + block->length; while (start < end) { start = XawTextSourceScan(w, start, XawstEOL, XawsdRight, 1, True); if (start <= end) { ++lines; if (start == XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True)) { lines -= !_XawTextSourceNewLineAtEOF(w); break; } } } info.left = left; info.right = right; info.block = block; XtCallCallbacks(w, XtNpropertyCallback, &info); TellSourceChanged(src, left, right, block, lines); /* Call callbacks, we have changed the buffer */ XtCallCallbacks(w, XtNcallback, (XtPointer)((long)src->textSrc.changed)); } #endif /* OLDXAW */ return (error); } #ifndef OLDXAW Bool _XawTextSrcUndo(TextSrcObject src, XawTextPosition *insert_pos) { static wchar_t wnull = 0; XawTextBlock block; XawTextUndoList *list, *nlist; XawTextUndoBuffer *l_state, *r_state; Boolean changed = src->textSrc.changed; if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo) return (False); list = src->textSrc.undo->pointer; if (src->textSrc.undo->dir == XawsdLeft) { l_state = list->right; r_state = list->left; } else { l_state = list->left; r_state = list->right; } if (src->textSrc.undo->l_no_change == l_state && src->textSrc.undo->r_no_change == r_state) src->textSrc.changed = False; else src->textSrc.changed = True; block.firstPos = 0; block.length = r_state->length; block.ptr = r_state->buffer ? r_state->buffer : (char*)&wnull; block.format = r_state->format; src->textSrc.undo_state = True; if (XawTextSourceReplace((Widget)src, l_state->position, l_state->position + l_state->length, &block) != XawEditDone) { src->textSrc.undo_state = False; src->textSrc.changed = changed; return (False); } src->textSrc.undo_state = False; ++l_state->refcount; ++r_state->refcount; nlist = XtNew(XawTextUndoList); nlist->left = l_state; nlist->right = r_state; nlist->undo = src->textSrc.undo->list; nlist->redo = NULL; if (list == src->textSrc.undo->list) src->textSrc.undo->end_mark = nlist; if (src->textSrc.undo->dir == XawsdLeft) { if (list->undo == NULL) src->textSrc.undo->dir = XawsdRight; else list = list->undo; } else { if (list->redo == NULL || list->redo == src->textSrc.undo->end_mark) src->textSrc.undo->dir = XawsdLeft; else list = list->redo; } *insert_pos = r_state->position + r_state->length; src->textSrc.undo->pointer = list; src->textSrc.undo->list->redo = nlist; src->textSrc.undo->list = nlist; src->textSrc.undo->merge = src->textSrc.undo->erase = 0; if (++src->textSrc.undo->num_list >= UNDO_DEPTH) UndoGC(src->textSrc.undo); return (True); } Bool _XawTextSrcToggleUndo(TextSrcObject src) { if (!src->textSrc.enable_undo || !src->textSrc.undo->num_undo) return (False); if (src->textSrc.undo->pointer != src->textSrc.undo->list) { if (src->textSrc.undo->dir == XawsdLeft) { if (src->textSrc.undo->pointer->redo && (src->textSrc.undo->pointer->redo != src->textSrc.undo->end_mark)) { src->textSrc.undo->pointer = src->textSrc.undo->pointer->redo; src->textSrc.undo->dir = XawsdRight; } } else { if (src->textSrc.undo->pointer->undo && (src->textSrc.undo->pointer != src->textSrc.undo->head)) { src->textSrc.undo->pointer = src->textSrc.undo->pointer->undo; src->textSrc.undo->dir = XawsdLeft; } } } return (True); } static void FreeUndoBuffer(XawTextUndo *undo) { unsigned i; XawTextUndoList *head, *del; for (i = 0; i < undo->num_undo; i++) { if (undo->undo[i]->buffer && undo->undo[i]->buffer != SrcNL && undo->undo[i]->buffer != (char*)SrcWNL) XtFree(undo->undo[i]->buffer); XtFree((char*)undo->undo[i]); } XtFree((char*)undo->undo); head = undo->head; del = head; while (head) { head = head->redo; XtFree((char*)del); del = head; } if (undo->l_save) { XtFree((char*)undo->l_save); undo->l_save = NULL; } if (undo->r_save) { XtFree((char*)undo->r_save); undo->r_save = NULL; } if (undo->u_save) { XtFree((char*)undo->u_save); undo->u_save = NULL; } undo->list = undo->pointer = undo->head = undo->end_mark = NULL; undo->l_no_change = undo->r_no_change = NULL; undo->undo = NULL; undo->dir = XawsdLeft; undo->num_undo = undo->num_list = undo->erase = undo->merge = 0; } static void UndoGC(XawTextUndo *undo) { unsigned i; XawTextUndoList *head = undo->head, *redo = head->redo; if (head == undo->pointer || head == undo->end_mark || undo->l_no_change == NULL || head->left == undo->l_no_change || head->right == undo->l_no_change) return; undo->head = redo; redo->undo = NULL; --head->left->refcount; if (--head->right->refcount == 0) { for (i = 0; i < undo->num_undo; i+= 2) if (head->left == undo->undo[i] || head->left == undo->undo[i+1]) { if (head->left == undo->undo[i+1]) { XawTextUndoBuffer *tmp = redo->left; redo->left = redo->right; redo->right = tmp; } if (head->left->buffer && head->left->buffer != SrcNL && head->left->buffer != (char*)SrcWNL) XtFree(head->left->buffer); XtFree((char*)head->left); if (head->right->buffer && head->right->buffer != SrcNL && head->right->buffer != (char*)SrcWNL) XtFree(head->right->buffer); XtFree((char*)head->right); undo->num_undo -= 2; memmove(&undo->undo[i], &undo->undo[i + 2], (undo->num_undo - i) * sizeof(XawTextUndoBuffer*)); break; } } XtFree((char*)head); --undo->num_list; } #endif /* OLDXAW */ /* * Function: * XawTextSourceScan * * Parameters: * w - TextSrc Object * position - position to start scanning * type - type of thing to scan for * dir - direction to scan * count - which occurance if this thing to search for * include - whether or not to include the character found in * the position that is returned. * * Description: * Scans the text source for the number and type of item specified. * * Returns: * The position of the text */ XawTextPosition XawTextSourceScan(Widget w, XawTextPosition position, #if NeedWidePrototypes int type, int dir, int count, int include #else XawTextScanType type, XawTextScanDirection dir, int count, Boolean include #endif ) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; return ((*cclass->textSrc_class.Scan) (w, position, type, dir, count, include)); } /* * Function: * XawTextSourceSearch * * Parameters: * w - TextSource Object * position - position to start scanning * dir - direction to scan * text - the text block to search for. * * Returns: * The position of the text we are searching for or XawTextSearchError. * * Description: * Searchs the text source for the text block passed */ XawTextPosition XawTextSourceSearch(Widget w, XawTextPosition position, #if NeedWidePrototypes int dir, #else XawTextScanDirection dir, #endif XawTextBlock *text) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; return ((*cclass->textSrc_class.Search)(w, position, dir, text)); } /* * Function: * XawTextSourceConvertSelection * * Parameters: * w - TextSrc object * selection - current selection atom * target - current target atom * type - type to conver the selection to * value - return value that has been converted * length - "" * format - format of the returned value * * Returns: * True if the selection has been converted */ Boolean XawTextSourceConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type, XtPointer *value, unsigned long *length, int *format) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; return((*cclass->textSrc_class.ConvertSelection) (w, selection, target, type, value, length, format)); } /* * Function: * XawTextSourceSetSelection * * Parameters: * w - TextSrc object * left - bounds of the selection * rigth - "" * selection - selection atom * * Description: * Allows special setting of the selection. */ void XawTextSourceSetSelection(Widget w, XawTextPosition left, XawTextPosition right, Atom selection) { TextSrcObjectClass cclass = (TextSrcObjectClass)w->core.widget_class; (*cclass->textSrc_class.SetSelection)(w, left, right, selection); } /* * External Functions for Multi Text */ /* * TextFormat(): * returns the format of text: FMT8BIT or FMTWIDE */ XrmQuark _XawTextFormat(TextWidget tw) { return (((TextSrcObject)(tw->text.source))->textSrc.text_format); } /* _XawTextWCToMB(): * Convert the wchar string to external encoding * The caller is responsible for freeing both the source and ret string * * wstr - source wchar string * len_in_out - lengh of string. * As In, length of source wchar string, measured in wchar * As Out, length of returned string */ char * _XawTextWCToMB(Display *d, wchar_t *wstr, int *len_in_out) { XTextProperty textprop; if (XwcTextListToTextProperty(d, (wchar_t**)&wstr, 1, XTextStyle, &textprop) < Success) { XtWarningMsg("convertError", "textSource", "XawError", "Non-character code(s) in buffer.", NULL, NULL); *len_in_out = 0; return (NULL); } *len_in_out = textprop.nitems; return ((char *)textprop.value); } /* _XawTextMBToWC(): * Convert the string to internal processing codeset WC. * The caller is responsible for freeing both the source and ret string. * * str - source string * len_in_out - lengh of string * As In, it is length of source string * As Out, it is length of returned string, measured in wchar */ wchar_t * _XawTextMBToWC(Display *d, char *str, int *len_in_out) { XTextProperty textprop; char *buf; wchar_t **wlist, *wstr; int count; if (*len_in_out == 0) return (NULL); buf = XtMalloc(*len_in_out + 1); strncpy(buf, str, *len_in_out); *(buf + *len_in_out) = '\0'; if (XmbTextListToTextProperty(d, &buf, 1, XTextStyle, &textprop) != Success) { XtWarningMsg("convertError", "textSource", "XawError", "No Memory, or Locale not supported.", NULL, NULL); XtFree(buf); *len_in_out = 0; return (NULL); } XtFree(buf); if (XwcTextPropertyToTextList(d, &textprop, (wchar_t***)&wlist, &count) != Success) { XtWarningMsg("convertError", "multiSourceCreate", "XawError", "Non-character code(s) in source.", NULL, NULL); *len_in_out = 0; return (NULL); } wstr = wlist[0]; *len_in_out = wcslen(wstr); XtFree((XtPointer)wlist); return (wstr); } #ifndef OLDXAW static int qcmp_anchors(_Xconst void *left, _Xconst void *right) { return ((*(XawTextAnchor**)left)->position - (*(XawTextAnchor**)right)->position); } XawTextAnchor * XawTextSourceAddAnchor(Widget w, XawTextPosition position) { TextSrcObject src = (TextSrcObject)w; XawTextAnchor *anchor, *panchor; if ((panchor = XawTextSourceFindAnchor(w, position)) != NULL) { XawTextEntity *pentity, *entity; if (position - panchor->position < ANCHORS_DIST) return (panchor); anchor = XtNew(XawTextAnchor); if (panchor->cache && panchor->position + panchor->cache->offset + panchor->cache->length < position) pentity = entity = panchor->cache; else pentity = entity = panchor->entities; while (entity && panchor->position + entity->offset + entity->length < position) { pentity = entity; entity = entity->next; } if (entity) { XawTextPosition diff; if (panchor->position + entity->offset < position) position = panchor->position + entity->offset; diff = position - panchor->position; panchor->cache = NULL; anchor->entities = entity; if (pentity != entity) pentity->next = NULL; else panchor->entities = NULL; while (entity) { entity->offset -= diff; entity = entity->next; } } else anchor->entities = NULL; } else { anchor = XtNew(XawTextAnchor); anchor->entities = NULL; } anchor->position = position; anchor->cache = NULL; src->textSrc.anchors = (XawTextAnchor**) XtRealloc((XtPointer)src->textSrc.anchors, sizeof(XawTextAnchor*) * (src->textSrc.num_anchors + 1)); src->textSrc.anchors[src->textSrc.num_anchors++] = anchor; qsort((void*)src->textSrc.anchors, src->textSrc.num_anchors, sizeof(XawTextAnchor*), qcmp_anchors); return (anchor); } XawTextAnchor * XawTextSourceFindAnchor(Widget w, XawTextPosition position) { TextSrcObject src = (TextSrcObject)w; int i = 0, left, right, nmemb = src->textSrc.num_anchors; XawTextAnchor *anchor, **anchors = src->textSrc.anchors; left = 0; right = nmemb - 1; while (left <= right) { anchor = anchors[i = (left + right) >> 1]; if (anchor->position == position) return (anchor); else if (position < anchor->position) right = i - 1; else left = i + 1; } if (nmemb) return (right < 0 ? anchors[0] : anchors[right]); return (NULL); } Bool XawTextSourceAnchorAndEntity(Widget w, XawTextPosition position, XawTextAnchor **anchor_return, XawTextEntity **entity_return) { XawTextAnchor *anchor = XawTextSourceFindAnchor(w, position); XawTextEntity *pentity, *entity; XawTextPosition offset; Bool next_anchor = True, retval = False; if (anchor->cache && anchor->position + anchor->cache->offset + anchor->cache->length <= position) pentity = entity = anchor->cache; else pentity = entity = anchor->entities; while (entity) { offset = anchor->position + entity->offset; if (offset > position) { retval = next_anchor = False; break; } if (offset + entity->length > position) { retval = True; next_anchor = False; break; } pentity = entity; entity = entity->next; } if (next_anchor) { *anchor_return = anchor = XawTextSourceNextAnchor(w, anchor); *entity_return = anchor ? anchor->entities : NULL; } else { *anchor_return = anchor; *entity_return = retval ? entity : pentity; } if (*anchor_return) (*anchor_return)->cache = *entity_return; return (retval); } XawTextAnchor * XawTextSourceNextAnchor(Widget w, XawTextAnchor *anchor) { int i; TextSrcObject src = (TextSrcObject)w; for (i = 0; i < src->textSrc.num_anchors - 1; i++) if (src->textSrc.anchors[i] == anchor) return (src->textSrc.anchors[i + 1]); return (NULL); } XawTextAnchor * XawTextSourcePrevAnchor(Widget w, XawTextAnchor *anchor) { int i; TextSrcObject src = (TextSrcObject)w; for (i = src->textSrc.num_anchors - 1; i > 0; i--) if (src->textSrc.anchors[i] == anchor) return (src->textSrc.anchors[i - 1]); return (NULL); } XawTextAnchor * XawTextSourceRemoveAnchor(Widget w, XawTextAnchor *anchor) { int i; TextSrcObject src = (TextSrcObject)w; for (i = 0; i < src->textSrc.num_anchors; i++) if (src->textSrc.anchors[i] == anchor) break; if (i == 0) return (src->textSrc.num_anchors > 1 ? src->textSrc.anchors[1] : NULL); if (i < src->textSrc.num_anchors) { XtFree((XtPointer)anchor); if (i < --src->textSrc.num_anchors) { memmove(&src->textSrc.anchors[i], &src->textSrc.anchors[i + 1], (src->textSrc.num_anchors - i) * sizeof(XawTextAnchor*)); return (src->textSrc.anchors[i]); } } return (NULL); } XawTextEntity * XawTextSourceAddEntity(Widget w, int type, int flags, XtPointer data, XawTextPosition position, Cardinal length, XrmQuark property) { XawTextAnchor *next, *anchor = _XawTextSourceFindAnchor(w, position); XawTextEntity *entity, *eprev; if (anchor->cache && anchor->position + anchor->cache->offset + anchor->cache->length <= position) eprev = entity = anchor->cache; else eprev = entity = anchor->entities; while (entity && anchor->position + entity->offset + entity->length <= position) { eprev = entity; entity = entity->next; } if (entity && anchor->position + entity->offset < position + length) { fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n"); return (NULL); } next = XawTextSourceFindAnchor(w, position + length); if (next && next != anchor) { if ((entity = next->entities) != NULL) { if (next->position + entity->offset < position + length) { fprintf(stderr, "Cannot (yet) add more than one entity to same region.\n"); return (NULL); } } if (position + length > next->position) { XawTextPosition diff = position + length - next->position; next->position += diff; entity = next->entities; while (entity) { entity->offset -= diff; entity = entity->next; } entity = anchor->entities; while (entity && entity->offset < 0) entity = entity->next; if (entity && entity->offset < 0) { if (eprev) eprev->next = next->entities; else anchor->entities = next->entities; if ((next->entities = entity->next) == NULL) (void)XawTextSourceRemoveAnchor(w, next); entity->next = NULL; return (XawTextSourceAddEntity(w, type, flags, data, position, length, property)); } } } entity = XtNew(XawTextEntity); entity->type = type; entity->flags = flags; entity->data = data; entity->offset = position - anchor->position; entity->length = length; entity->property = property; if (eprev == NULL) { anchor->entities = entity; entity->next = NULL; anchor->cache = NULL; } else if (eprev->offset > entity->offset) { anchor->cache = NULL; anchor->entities = entity; entity->next = eprev; } else { anchor->cache = eprev; entity->next = eprev->next; eprev->next = entity; } return (entity); } void XawTextSourceClearEntities(Widget w, XawTextPosition left, XawTextPosition right) { XawTextAnchor *anchor = XawTextSourceFindAnchor(w, left); XawTextEntity *entity, *eprev, *enext; XawTextPosition offset; int length; while (anchor && anchor->entities == NULL) anchor = XawTextSourceRemoveAnchor(w, anchor); if (anchor == NULL || left >= right) return; if (anchor->cache && anchor->position + anchor->cache->offset + anchor->cache->length < left) eprev = entity = anchor->cache; else eprev = entity = anchor->entities; /* find first entity before left position */ while (anchor->position + entity->offset + entity->length < left) { eprev = entity; if ((entity = entity->next) == NULL) { if ((anchor = XawTextSourceNextAnchor(w, anchor)) == NULL) return; if ((eprev = entity = anchor->entities) == NULL) { fprintf(stderr, "Bad anchor found!\n"); return; } } } offset = anchor->position + entity->offset; if (offset <= left) { length = XawMin(entity->length, left - offset); if (length <= 0) { enext = entity->next; eprev->next = enext; XtFree((XtPointer)entity); anchor->cache = NULL; if (entity == anchor->entities) { eprev = NULL; if ((anchor->entities = enext) == NULL) { if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL) return; entity = anchor->entities; } else entity = enext; } else entity = enext; } else { entity->length = length; eprev = entity; entity = entity->next; } } /* clean everything until right position is reached */ while (anchor) { while (entity) { offset = anchor->position + entity->offset + entity->length; if (offset > right) { anchor->cache = NULL; entity->offset = XawMax(entity->offset, right - anchor->position); entity->length = XawMin(entity->length, offset - right); return; } enext = entity->next; if (eprev) eprev->next = enext; XtFree((XtPointer)entity); if (entity == anchor->entities) { eprev = anchor->cache = NULL; if ((anchor->entities = enext) == NULL) { if ((anchor = XawTextSourceRemoveAnchor(w, anchor)) == NULL) return; entity = anchor->entities; continue; } } entity = enext; } if (anchor) anchor->cache = NULL; if ((anchor = XawTextSourceNextAnchor(w, anchor)) != NULL) entity = anchor->entities; eprev = NULL; } } /* checks the anchors up to position, and create an appropriate anchor * at position, if required. */ XawTextAnchor * _XawTextSourceFindAnchor(Widget w, XawTextPosition position) { XawTextAnchor *anchor; anchor = XawTextSourceFindAnchor(w, position); if (position - anchor->position >= ANCHORS_DIST) return (XawTextSourceAddAnchor(w, position - (position % ANCHORS_DIST))); return (anchor); } #endif