diff options
Diffstat (limited to 'lisp/xedit.c')
-rw-r--r-- | lisp/xedit.c | 1636 |
1 files changed, 1636 insertions, 0 deletions
diff --git a/lisp/xedit.c b/lisp/xedit.c new file mode 100644 index 0000000..9ef7f8f --- /dev/null +++ b/lisp/xedit.c @@ -0,0 +1,1636 @@ +/* + * Copyright (c) 2002 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * 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 XFREE86 PROJECT 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 XFree86 Project 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 + * XFree86 Project. + * + * Author: Paulo César Pereira de Andrade + */ + +/* $XFree86: xc/programs/xedit/lisp/xedit.c,v 1.24 2003/01/13 03:57:59 paulo Exp $ */ + +#include "../xedit.h" +#include <X11/Xaw/TextSrcP.h> /* Needs some private definitions */ +#include <X11/Xaw/TextSinkP.h> /* Also needs private definitions... */ +#include <X11/Xmu/Xmu.h> +#define XEDIT_LISP_PRIVATE +#include "xedit.h" +#include <signal.h> + +/* Initialize to enter lisp */ +#define LISP_SETUP() \ + int lisp__running = lisp__data.running + +/* XXX Maybe should use ualarm or better, setitimer, but one + * second seens good enough to check for interrupts */ + +#define ENABLE_SIGALRM() \ + old_sigalrm = signal(SIGALRM, SigalrmHandler); \ + alarm(1) + +#define DISABLE_SIGALRM() \ + alarm(0); \ + signal(SIGALRM, old_sigalrm) + +/* Enter lisp */ +#define LISP_ENTER() \ + if (!lisp__running) { \ + lisp__data.running = 1; \ + XFlush(XtDisplay(textwindow)); \ + ENABLE_SIGALRM(); \ + if (sigsetjmp(lisp__data.jmp, 1) != 0) { \ + DISABLE_SIGALRM(); \ + lisp__data.running = 0; \ + return; \ + } \ + } + +/* Leave lisp */ +#define LISP_LEAVE() \ + if (!lisp__running) { \ + DISABLE_SIGALRM(); \ + LispTopLevel(); \ + lisp__data.running = 0; \ + } + +/* + * Types + */ +typedef struct { + XawTextPosition left, right; + XrmQuark property; +} EntityInfo; + +/* + * Prototypes + */ +static Bool ControlGPredicate(Display*, XEvent*, XPointer); +static ssize_t WriteToStdout(int, const void*, size_t); +static ssize_t WriteToStderr(int, const void*, size_t); +static ssize_t WrapWrite(Widget, const void*, size_t); +static void XeditUpdateModeInfos(void); +static void XeditPrint(Widget, LispObj*, int); +static void XeditInteractiveCallback(Widget, XtPointer, XtPointer); +static void XeditIndentationCallback(Widget, XtPointer, XtPointer); +static LispObj *XeditCharAt(LispBuiltin*, int); +static LispObj *XeditSearch(LispBuiltin*, XawTextScanDirection); + +/* + * Initialization + */ +#ifdef SIGNALRETURNSINT +static int (*old_sigalrm)(int); +#else +static void (*old_sigalrm)(int); +#endif + +EditModeInfo *mode_infos; +Cardinal num_mode_infos; + +static LispObj *Oauto_modes, *Oauto_mode, *Osyntax_highlight, *Osyntable_indent; + +/* Just to make calling interactive reparse easier */ +static LispObj interactive_arguments[4]; + +static LispObj *justify_modes[4]; +static LispObj *wrap_modes[3]; +static LispObj *scan_types[5]; +static LispObj *scan_directions[2]; +static LispObj execute_stream; +static LispString execute_string; +static LispObj result_stream; +static LispString result_string; +static XawTextPropertyList **property_lists; +static Cardinal num_property_lists; + +/* Some hacks to (at lest try to) avoid problems reentering Xlib while + * testing for user interrupts */ +static volatile int disable_timeout, request_timeout; + +extern int pagesize; + +static LispBuiltin xeditbuiltins[] = { + {LispFunction, Xedit_AddEntity, "add-entity offset length identifier"}, + {LispFunction, Xedit_AutoFill, "auto-fill &optional value"}, + {LispFunction, Xedit_Background, "background &optional color"}, + {LispFunction, Xedit_CharAfter, "char-after &optional offset"}, + {LispFunction, Xedit_CharBefore, "char-before &optional offset"}, + {LispFunction, Xedit_ClearEntities, "clear-entities left right"}, + {LispFunction, Xedit_ConvertPropertyList, "convert-property-list name definition"}, + {LispFunction, Xedit_Font, "font &optional font"}, + {LispFunction, Xedit_Foreground, "foreground &optional color"}, + {LispFunction, Xedit_GotoChar, "goto-char offset"}, + {LispFunction, Xedit_HorizontalScrollbar, "horizontal-scrollbar &optional state"}, + {LispFunction, Xedit_Insert, "insert text"}, + {LispFunction, Xedit_Justification, "justification &optional value"}, + {LispFunction, Xedit_LeftColumn, "left-column &optional left"}, + {LispFunction, Xedit_Point, "point"}, + {LispFunction, Xedit_PointMax, "point-max"}, + {LispFunction, Xedit_PointMin, "point-min"}, + {LispFunction, Xedit_PropertyList, "property-list &optional value"}, + {LispFunction, Xedit_ReadText, "read-text offset length"}, + {LispFunction, Xedit_ReplaceText, "replace-text left right text"}, + {LispFunction, Xedit_RightColumn, "right-column &optional right"}, + {LispFunction, Xedit_Scan, "scan offset type direction &key count include"}, + {LispFunction, Xedit_SearchBackward, "search-backward string &optional offset ignore-case"}, + {LispFunction, Xedit_SearchForward, "search-forward string &optional offset ignore-case"}, + {LispFunction, Xedit_VerticalScrollbar, "vertical-scrollbar &optional state"}, + {LispFunction, Xedit_WrapMode, "wrap-mode &optional value"}, + + /* This should be available from elsewhere at some time... */ + {LispFunction, Xedit_XrmStringToQuark, "xrm-string-to-quark string"}, +}; + +/* + * Implementation + */ +/*ARGUSED*/ +static Bool +ControlGPredicate(Display *display, XEvent *event, XPointer arguments) +{ + char buffer[2]; + + return ((event->type == KeyPress || event->type == KeyRelease) && + (event->xkey.state & ControlMask) && + XLookupString(&(event->xkey), buffer, sizeof(buffer), NULL, NULL) && + buffer[0] == '\a'); +} + +/*ARGSUSED*/ +static +#ifdef SIGNALRETURNSINT +int +#else +void +#endif +SigalrmHandler(int signum) +{ + XEvent event; + + if (disable_timeout) { + request_timeout = 1; + return; + } + + /* Check if user pressed C-g */ + if (XCheckIfEvent(XtDisplay(textwindow), &event, ControlGPredicate, NULL)) { + XPutBackEvent(XtDisplay(textwindow), &event); + alarm(0); + /* Tell a signal was received, print message for SIGINT */ + LispSignal(SIGINT); + } + else + alarm(1); +#ifdef SIGNALRETURNSINT + return (0); +#endif +} + +static ssize_t +WrapWrite(Widget output, const void *buffer, size_t nbytes) +{ + XawTextBlock block; + XawTextPosition position; + + disable_timeout = 1; + position = XawTextGetInsertionPoint(output); + block.firstPos = 0; + block.format = FMT8BIT; + block.length = nbytes; + block.ptr = (String)buffer; + XawTextReplace(output, position, position, &block); + XawTextSetInsertionPoint(output, position + block.length); + disable_timeout = 0; + + if (request_timeout) { + XFlush(XtDisplay(output)); + request_timeout = 0; + SigalrmHandler(SIGALRM); + } + + return ((ssize_t)nbytes); +} + +static ssize_t +WriteToStdout(int fd, const void *buffer, size_t nbytes) +{ + return (WrapWrite(textwindow, buffer, nbytes)); +} + +static ssize_t +WriteToStderr(int fd, const void *buffer, size_t nbytes) +{ + return (WrapWrite(messwidget, buffer, nbytes)); +} + +void +LispXeditInitialize(void) +{ + int i; + char *string; + LispObj *xedit, *list, *savepackage; + + LispSetFileWrite(Stdout, WriteToStdout); + LispSetFileWrite(Stderr, WriteToStderr); + + justify_modes[0] = KEYWORD("LEFT"); + justify_modes[1] = KEYWORD("RIGHT"); + justify_modes[2] = KEYWORD("CENTER"); + justify_modes[3] = KEYWORD("FULL"); + + wrap_modes[0] = KEYWORD("NEVER"); + wrap_modes[1] = KEYWORD("LINE"); + wrap_modes[2] = KEYWORD("WORD"); + + scan_types[0] = KEYWORD("POSITIONS"); + scan_types[1] = KEYWORD("WHITE-SPACE"); + scan_types[2] = KEYWORD("EOL"); + scan_types[3] = KEYWORD("PARAGRAPH"); + scan_types[4] = KEYWORD("ALL"); + scan_types[5] = KEYWORD("ALPHA-NUMERIC"); + + scan_directions[0] = justify_modes[0]; + scan_directions[1] = justify_modes[1]; + + /* Remember value of current package */ + savepackage = PACKAGE; + + /* Create the XEDIT package */ + xedit = LispNewPackage(STRING("XEDIT"), NIL); + + /* Update list of packages */ + PACK = CONS(xedit, PACK); + + /* Temporarily switch to the XEDIT package */ + lisp__data.pack = lisp__data.savepack = xedit->data.package.package; + PACKAGE = xedit; + + /* Add XEDIT builtin functions */ + for (i = 0; i < sizeof(xeditbuiltins) / sizeof(xeditbuiltins[0]); i++) + LispAddBuiltinFunction(&xeditbuiltins[i]); + + /* Create these objects in the xedit package */ + Oauto_modes = STATIC_ATOM("*AUTO-MODES*"); + Oauto_mode = STATIC_ATOM("AUTO-MODE"); + Osyntax_highlight = STATIC_ATOM("SYNTAX-HIGHLIGHT"); + Osyntable_indent = STATIC_ATOM("SYNTABLE-INDENT"); + + /* Import symbols from the LISP and EXT packages */ + for (list = PACK; CONSP(list); list = CDR(list)) { + string = THESTR(CAR(list)->data.package.name); + if (strcmp(string, "LISP") == 0 || strcmp(string, "EXT") == 0) + LispUsePackage(CAR(list)); + } + + /* Restore previous package */ + lisp__data.pack = savepackage->data.package.package; + PACKAGE = savepackage; + + /* Initialize helper static objects used when executing expressions */ + execute_stream.type = LispStream_t; + execute_stream.data.stream.source.string = &execute_string; + execute_stream.data.stream.pathname = NIL; + execute_stream.data.stream.type = LispStreamString; + execute_stream.data.stream.readable = 1; + execute_stream.data.stream.writable = 0; + execute_string.output = 0; + result_stream.type = LispStream_t; + result_stream.data.stream.source.string = &result_string; + result_stream.data.stream.pathname = NIL; + result_stream.data.stream.type = LispStreamString; + result_stream.data.stream.readable = 0; + result_stream.data.stream.writable = 1; + result_string.string = XtMalloc(pagesize); + result_string.space = pagesize; + + /* Initialize interactive edition function arguments */ + /* first argument is syntax table */ + interactive_arguments[0].type = LispCons_t; + interactive_arguments[0].data.cons.cdr = &interactive_arguments[1]; + /* second argument is where to start reparsing */ + interactive_arguments[1].type = LispCons_t; + interactive_arguments[1].data.cons.cdr = &interactive_arguments[2]; + /* third argument is where to stop reparsing */ + interactive_arguments[2].type = LispCons_t; + interactive_arguments[2].data.cons.cdr = &interactive_arguments[3]; + /* fourth argument is interactive flag */ + interactive_arguments[3].type = LispCons_t; + interactive_arguments[3].data.cons.car = T; + interactive_arguments[3].data.cons.cdr = NIL; + + /* Load extra functions and data type definitions */ + EXECUTE("(require \"xedit\")"); + + + /* + * This assumes that the *auto-modes* variable is a list where every + * item has the format: + * (regexp string-desc load-file-desc . symbol-name) + * Minimal error checking is done. + */ + + if (Oauto_modes->data.atom->a_object) { + LispObj *desc, *modes = Oauto_modes->data.atom->property->value; + + for (; CONSP(modes); modes = CDR(modes)) { + list = CAR(modes); + + desc = NIL; + for (i = 0; i < 3 && CONSP(list); i++, list = CDR(list)) { + if (i == 1) + desc = CAR(list); + } + if (i == 3 && STRINGP(desc)) { + mode_infos = (EditModeInfo*) + XtRealloc((XtPointer)mode_infos, sizeof(EditModeInfo) * + (num_mode_infos + 1)); + mode_infos[num_mode_infos].desc = XtNewString(THESTR(desc)); + mode_infos[num_mode_infos].symbol = list; + mode_infos[num_mode_infos].syntax = NULL; + ++num_mode_infos; + } + } + } +} + +static void +XeditUpdateModeInfos(void) +{ + int i; + + for (i = 0; i < num_mode_infos; i++) { + if (mode_infos[i].symbol && + mode_infos[i].syntax == NULL && + XSYMBOLP(mode_infos[i].symbol) && + mode_infos[i].symbol->data.atom->a_object) + mode_infos[i].syntax = + mode_infos[i].symbol->data.atom->property->value; + } +} + +void +XeditLispExecute(Widget output, XawTextPosition left, XawTextPosition right) +{ + GC_ENTER(); + LISP_SETUP(); + int alloced, return_count; + XawTextBlock block; + XawTextPosition position; + char *string, *ptr; + LispObj *result, *code, *_cod, *returns; + + LISP_ENTER(); + + position = left; + XawTextSourceRead(XawTextGetSource(textwindow), left, &block, right - left); + if (block.length < right - left) { + alloced = 1; + string = ptr = LispMalloc(right - left); + memcpy(ptr, block.ptr, block.length); + position = left + block.length; + ptr += block.length; + for (; position < right;) { + XawTextSourceRead(XawTextGetSource(textwindow), + position, &block, right - position); + memcpy(ptr, block.ptr, block.length); + position += block.length; + ptr += block.length; + } + } + else { + alloced = 0; + string = block.ptr; + } + + execute_string.string = string; + execute_string.length = right - left; + execute_string.input = 0; + LispPushInput(&execute_stream); + _cod = COD; + result = NIL; + if ((code = LispRead()) != NULL) + result = EVAL(code); + COD = _cod; + LispPopInput(&execute_stream); + + returns = NIL; + if (RETURN_COUNT > 0) { + GC_PROTECT(result); + returns = _cod = CONS(RETURN(0), NIL); + GC_PROTECT(returns); + for (return_count = 1; return_count < RETURN_COUNT; return_count++) { + RPLACD(_cod, CONS(RETURN(return_count), NIL)); + _cod = CDR(_cod); + } + } + LispFflush(Stdout); + LispUpdateResults(code, result); + if (RETURN_COUNT >= 0) { + XeditPrint(output, result, 1); + for (; CONSP(returns); returns = CDR(returns)) + XeditPrint(output, CAR(returns), 0); + } + + if (alloced) + LispFree(string); + GC_LEAVE(); + + LISP_LEAVE(); +} + +static void +XeditPrint(Widget output, LispObj *object, int newline) +{ + XawTextBlock block; + XawTextPosition position; + + result_string.length = result_string.output = 0; + if (newline) { + position = XawTextGetInsertionPoint(output); + if (position != XawTextSourceScan(XawTextGetSource(output), + position, XawstEOL, + XawsdLeft, 1, False)) + LispSputc(&result_string, '\n'); + } + LispWriteObject(&result_stream, object); + LispSputc(&result_string, '\n'); + + position = XawTextGetInsertionPoint(output); + block.firstPos = 0; + block.format = FMT8BIT; + block.length = result_string.length; + block.ptr = result_string.string; + XawTextReplace(output, position, position, &block); + XawTextSetInsertionPoint(output, position + block.length); +} + +/* + * This function is defined here to avoid exporting all the lisp interfaces + * to the core xedit code. + */ +void +XeditLispSetEditMode(xedit_flist_item *item, LispObj *symbol) +{ + GC_ENTER(); + LISP_SETUP(); + LispObj *syntax, *name; + + item->xldata = (XeditLispData*)XtCalloc(1, sizeof(XeditLispData)); + + LISP_ENTER(); + + /* Create an object that represents the buffer filename. + * Note that the entire path is passed to the auto-mode + * function, so that directory names may be also be used + * when determining a file type. */ + name = STRING(item->filename); + GC_PROTECT(name); + + /* Call the AUTO-MODE function to check if there is a + * syntax definition for the file being loaded */ + if (symbol == NULL) + syntax = APPLY1(Oauto_mode, name); + else + syntax = APPLY2(Oauto_mode, name, symbol); + + /* Don't need the name object anymore */ + GC_LEAVE(); + + if (syntax != NIL) { + Arg arg[1]; + LispObj arguments; + XawTextPropertyList *property_list; + + item->xldata->syntax = syntax; + + /* Apply the syntax highlight to the current buffer */ + arguments.type = LispCons_t; + arguments.data.cons.car = syntax; + arguments.data.cons.cdr = NIL; + LispFuncall(Osyntax_highlight, &arguments, 1); + + /* The previous call added the property list to the widget, + * remember it when switching sources. */ + XtSetArg(arg[0], XawNtextProperties, &property_list); + XtGetValues(XawTextGetSink(textwindow), arg, 1); + item->properties = property_list; + + /* Add callback for interactive changes */ + XtAddCallback(item->source, XtNpropertyCallback, + XeditInteractiveCallback, item->xldata); + + /* Update information as a new file may have been loaded */ + XeditUpdateModeInfos(); + } + else + item->properties = NULL; + + LISP_LEAVE(); +} + +void +XeditLispUnsetEditMode(xedit_flist_item *item) +{ + if (item->xldata) { + XtRemoveCallback(item->source, XtNpropertyCallback, + XeditInteractiveCallback, item->xldata); + XtFree((XtPointer)item->xldata); + item->xldata = NULL; + } +} + +#define MAX_INFOS 32 +/* + * This callback tries to do it's best in generating correct output while + * also doing minimal work/redrawing of the screen. It probably will fail + * for some syntax-definitions, or will just not properly repaint the + * screen. In the later case, just press Ctrl+L. + * There isn't yet any command to force reparsing of some regions, and if + * the parser becomes confused, you may need to go to a line, press a space + * and undo, just to force it to reparse the line, and possibly some extra + * lines until the parser thinks the display is in sync. + * Sometimes it will repaint a lot more of text than what is being requested + * by this callback, this should be fixed at some time, as for certain cases + * it is also required some redesign in the Xaw interface. + */ +static void +XeditInteractiveCallback(Widget w, XtPointer client_data, XtPointer call_data) +{ + LISP_SETUP(); + XeditLispData *data = (XeditLispData*)client_data; + LispObj *syntax = data->syntax; + XawTextPropertyInfo *info = (XawTextPropertyInfo*)call_data; + LispObj *result, *syntable; + XawTextAnchor *anchor; + XawTextEntity *entity; + XawTextPosition first, last, left, right, begin, next, tmp, position; + int i, j, indent; + TextSrcObject src = (TextSrcObject)w; + EntityInfo oinfo[MAX_INFOS], ninfo[MAX_INFOS]; + XrmQuark props[MAX_INFOS]; + int num_oinfo, num_ninfo, num_props; + XmuScanline *clip, *oclip, *nclip; + XmuSegment segment, *seg; + + if (data->disable_highlight) + return; + + LISP_ENTER(); + + first = XawTextSourceScan(w, 0, XawstAll, XawsdLeft, 1, True); + last = XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True); + + left = info->left; + right = left + info->block->length; + + /* For now, only call the indent hook if a single character was typed */ + indent = (info->right == left) && (right == left + 1); + + /* Always reparse full lines */ + left = begin = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, False); + right = next = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 1, False); + + + /* Check properties in the modified text. If a complex nested syntax + * table was parsed, the newline has it's default property, so, while + * the newline has a property, backup a line to make sure everything is + * properly parsed. + * Maybe should limit the number of backuped lines, but if the parsing + * becomes noticeable slow, better to rethink the syntax definition. */ + while (left > first) { + position = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, True); + if (XawTextSourceAnchorAndEntity(w, position, &anchor, &entity)) + left = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False); + else + break; + } + + /* While the newline after the right position has a "hidden" property, + * keep incrementing a line to be reparsed. */ + while (right < last) { + if (XawTextSourceAnchorAndEntity(w, right, &anchor, &entity)) + right = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 2, False); + else + break; + } + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define STORE_STATE(count, info, from, to) \ + (count) = 0; \ + if ((anchor = XawTextSourceFindAnchor(w, (from))) != NULL) { \ + entity = anchor->entities; \ + /* Find first entity in the region to parse */ \ + while (entity && \ + anchor->position + entity->offset + entity->length <= \ + (from)) \ + entity = entity->next; \ + /* Loop storing information */ \ + while (entity && \ + (position = anchor->position + entity->offset) < (to)) { \ + (info)[(count)].left = MAX(position, (from)); \ + position += entity->length; \ + (info)[(count)].right = MIN(position, (to)); \ + (info)[(count)].property = entity->property; \ + /* If the changes are so complex, user need press Ctrl+L */ \ + if (++(count) >= MAX_INFOS) \ + break; \ + if ((entity = entity->next) == NULL && \ + (anchor = XawTextSourceNextAnchor(w, anchor)) != NULL) \ + entity = anchor->entities; \ + } \ + } + + /* Remember old state */ + STORE_STATE(num_oinfo, oinfo, begin, right); + + /* Reparse the lines in the modified/edited range of text */ + interactive_arguments[0].data.cons.car = syntax; + interactive_arguments[1].data.cons.car = FIXNUM(left); + interactive_arguments[2].data.cons.car = FIXNUM(right); + result = APPLY(Osyntax_highlight, &interactive_arguments[0]); + /* Indent table is the second return value */ + if (RETURN_COUNT) + syntable = RETURN(0); + else + syntable = NIL; + + /* This normally is the same value as right, but the parser may have + * continued when the syntax table stack did not finish. */ + if (FIXNUMP(result)) + right = FIXNUM_VALUE(result); + + LISP_LEAVE(); + + /* Check what have changed */ + STORE_STATE(num_ninfo, ninfo, begin, right); + + /* Initialize to redraw everything. */ + clip = XmuNewScanline(0, begin, right); + +#define CLIP_MASK(mask, from, to) \ + if ((from) < (to)) { \ + segment.x1 = (from); \ + segment.x2 = (to); \ + XmuScanlineOrSegment((mask), &segment); \ + } + + oclip = XmuNewScanline(0, 0, 0); + nclip = XmuNewScanline(0, 0, 0); + +#define CLIP_DEFAULT(mask, from, info, num_info) \ + for (tmp = (from), i = 0; i < (num_info); i++) { \ + CLIP_MASK((mask), tmp, (info)[i].left); \ + tmp = (info)[i].right; \ + } + + /* First generate masks of regions with the default property */ + CLIP_DEFAULT(oclip, begin, oinfo, num_oinfo); + CLIP_DEFAULT(nclip, begin, ninfo, num_ninfo); + + /* Store unchanged region in oclip */ + XmuScanlineAnd(oclip, nclip); + + /* Don't need to redraw the region in oclip */ + XmuScanlineXor(clip, oclip); + +#define LIST_PROPERTIES(prop, num_prop, info, num_info) \ + (num_prop) = 0; \ + for (i = 0; i < (num_info); i++) { \ + for (j = 0; j < (num_prop); j++) \ + if ((prop)[j] == (info)[i].property) \ + break; \ + if (j == (num_prop)) \ + (prop)[(num_prop)++] = (info)[i].property; \ + } + + /* Prepare to generate masks of regions of text with defined properties */ + LIST_PROPERTIES(props, num_props, oinfo, num_oinfo); + +#define CLIP_PROPERTY(mask, prop, info, num_info) \ + for (j = 0; j < (num_info); j++) { \ + if ((info)[j].property == (prop)) { \ + CLIP_MASK((mask), (info)[j].left, (info)[j].right); \ + } \ + } + + /* Only care about the old properties, new ones need to be redrawn */ + for (i = 0; i < num_props; i++) { + XrmQuark property = props[i]; + + /* Reset oclip and nclip */ + XmuScanlineXor(oclip, oclip); + XmuScanlineXor(nclip, nclip); + + /* Generate masks */ + CLIP_PROPERTY(oclip, property, oinfo, num_oinfo); + CLIP_PROPERTY(nclip, property, ninfo, num_ninfo); + + /* Store unchanged region in oclip */ + XmuScanlineAnd(oclip, nclip); + + /* Don't need to redraw the region in oclip */ + XmuScanlineXor(clip, oclip); + XmuOptimizeScanline(clip); + } + + XmuDestroyScanline(oclip); + XmuDestroyScanline(nclip); + + /* Tell Xaw that need update some regions */ + for (seg = clip->segment; seg; seg = seg->next) { + for (i = 0; i < src->textSrc.num_text; i++) + /* This really should have an exported interface... */ + _XawTextNeedsUpdating((TextWidget)(src->textSrc.text[i]), + seg->x1, seg->x2 + (seg->x2 > next)); + } + XmuDestroyScanline(clip); + + data->syntable = syntable; + /* XXX check lisp__running to know if at the toplevel parsing state */ + if (indent && syntable != NIL && !lisp__running && + /* Doing an undo, probably will need an exported interface for this + * case. Should not change the text now. */ + (!src->textSrc.enable_undo || !src->textSrc.undo_state)) + XtAddCallback(textwindow, XtNpositionCallback, + XeditIndentationCallback, data); +} + +/* + * This callback is called if the syntax table where the cursor is located + * defines an indentation function. + */ +static void +XeditIndentationCallback(Widget w, XtPointer client_data, XtPointer call_data) +{ + LISP_SETUP(); + LispObj *indentp; + XeditLispData *data = (XeditLispData*)client_data; + + data->disable_highlight = True; + XtRemoveCallback(w, XtNpositionCallback, XeditIndentationCallback, data); + + LISP_ENTER(); + + /* Get pointer to indentation function */ + indentp = APPLY1(Osyntable_indent, data->syntable); + + /* Execute indentation function */ + if (indentp != NIL) + APPLY2(indentp, data->syntax, data->syntable); + + data->disable_highlight = False; + + LISP_LEAVE(); +} + +/************************************************************************ + * Builtin functions + ************************************************************************/ +LispObj * +Xedit_AddEntity(LispBuiltin *builtin) +/* + add-entity offset length identifier + */ +{ + LispObj *offset, *length, *identifier; + + identifier = ARGUMENT(2); + length = ARGUMENT(1); + offset = ARGUMENT(0); + + CHECK_INDEX(offset); + CHECK_INDEX(length); + CHECK_LONGINT(identifier); + + return (XawTextSourceAddEntity(XawTextGetSource(textwindow), 0, 0, NULL, + FIXNUM_VALUE(offset), FIXNUM_VALUE(length), + LONGINT_VALUE(identifier)) ? T : NIL); +} + +LispObj * +Xedit_AutoFill(LispBuiltin *builtin) +/* + auto-fill &optional value + */ +{ + Arg arg[1]; + Boolean state; + + LispObj *value; + + value = ARGUMENT(0); + + if (value != UNSPEC) { + XtSetArg(arg[0], XtNautoFill, value == NIL ? False : True); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNautoFill, &state); + XtGetValues(textwindow, arg, 1); + value = state ? T : NIL; + } + + return (value); +} + +LispObj * +Xedit_Background(LispBuiltin *builtin) +/* + background &optional color + */ +{ + Pixel pixel; + Arg arg[1]; + XrmValue from, to; + + LispObj *color; + + color = ARGUMENT(0); + + if (color != UNSPEC) { + CHECK_STRING(color); + + from.size = STRLEN(color); + from.addr = (XtPointer)THESTR(color); + to.size = sizeof(Pixel); + to.addr = (XtPointer)&pixel; + + if (!XtConvertAndStore(XawTextGetSink(textwindow), + XtRString, &from, XtRPixel, &to)) + LispDestroy("cannot convert %s to Pixel", STROBJ(color)); + + XtSetArg(arg[0], XtNbackground, pixel); + XtSetValues(textwindow, arg, 1); + } + else { + from.size = sizeof(Pixel); + from.addr = (XtPointer)&pixel; + to.size = 0; + to.addr = NULL; + + XtSetArg(arg[0], XtNbackground, &pixel); + XtGetValues(XawTextGetSink(textwindow), arg, 1); + /* This cannot fail */ + XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to); + + color = STRING(to.addr); + } + + return (color); +} + +static LispObj * +XeditCharAt(LispBuiltin *builtin, int before) +{ + Widget source = XawTextGetSource(textwindow); + XawTextPosition first, point, last; + XawTextBlock block; + + LispObj *offset; + + offset = ARGUMENT(0); + if (offset != UNSPEC) { + CHECK_INDEX(offset); + } + + first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True); + if (FIXNUMP(offset)) + point = FIXNUM_VALUE(offset); + else + point = XawTextGetInsertionPoint(textwindow); + if (before && point > first) { + XawTextPosition position = + XawTextSourceScan(source, point, XawstPositions, XawsdLeft, 1, True); + + if (position < point) + point = position; + else + return (NIL); + } + last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True); + + if (point < first || point > last) + return (NIL); + + XawTextSourceRead(source, point, &block, 1); + + return (block.length ? SCHAR(*(unsigned char*)block.ptr) : NIL); +} + +LispObj * +Xedit_CharAfter(LispBuiltin *builtin) +/* + char-after &optional offset + */ +{ + return (XeditCharAt(builtin, 0)); +} + +LispObj * +Xedit_CharBefore(LispBuiltin *builtin) +/* + char-before &optional offset + */ +{ + return (XeditCharAt(builtin, 1)); +} + +LispObj * +Xedit_ClearEntities(LispBuiltin *builtin) +/* + clear-entities left right + */ +{ + LispObj *left, *right; + + right = ARGUMENT(1); + left = ARGUMENT(0); + + CHECK_INDEX(left); + CHECK_INDEX(right); + + XawTextSourceClearEntities(XawTextGetSource(textwindow), + FIXNUM_VALUE(left), FIXNUM_VALUE(right)); + + return (T); +} + +LispObj * +Xedit_ConvertPropertyList(LispBuiltin *builtin) +/* + convert-property-list name definition + */ +{ + LispObj *result; + XawTextPropertyList *property_list; + + LispObj *name, *definition; + + definition = ARGUMENT(1); + name = ARGUMENT(0); + + CHECK_STRING(name); + CHECK_STRING(definition); + + result = NIL; + property_list = XawTextSinkConvertPropertyList(THESTR(name), + THESTR(definition), + topwindow->core.screen, + topwindow->core.colormap, + topwindow->core.depth); + + if (property_list) { + Cardinal i; + + for (i = 0; i < num_property_lists; i++) + /* Check if a new property list was created */ + if (property_lists[i]->identifier == property_list->identifier) + break; + + /* Remember this pointer when asked back for it */ + if (i == num_property_lists) { + property_lists = (XawTextPropertyList**) + XtRealloc((XtPointer)property_lists, + sizeof(XawTextPropertyList) * + (num_property_lists + 1)); + property_lists[num_property_lists++] = property_list; + } + result = INTEGER(property_list->identifier); + } + + return (result); +} + +LispObj * +Xedit_Font(LispBuiltin *builtin) +/* + font &optional font + */ +{ + XFontStruct *font_struct; + Arg arg[1]; + XrmValue from, to; + + LispObj *font; + + font = ARGUMENT(0); + + if (font != UNSPEC) { + CHECK_STRING(font); + + from.size = STRLEN(font); + from.addr = (XtPointer)THESTR(font); + to.size = sizeof(XFontStruct*); + to.addr = (XtPointer)&font_struct; + + if (!XtConvertAndStore(textwindow, XtRString, &from, XtRFontStruct, &to)) + LispDestroy("cannot convert %s to FontStruct", STROBJ(font)); + + XtSetArg(arg[0], XtNfont, font_struct); + XtSetValues(textwindow, arg, 1); + } + else { + from.size = sizeof(XFontStruct*); + from.addr = (XtPointer)&font_struct; + to.size = 0; + to.addr = NULL; + + XtSetArg(arg[0], XtNfont, &font_struct); + XtGetValues(XawTextGetSink(textwindow), arg, 1); + /* This cannot fail */ + XtConvertAndStore(textwindow, XtRFontStruct, &from, XtRString, &to); + + font = STRING(to.addr); + } + + return (font); +} + +LispObj * +Xedit_Foreground(LispBuiltin *builtin) +/* + foreground &optional color + */ +{ + Pixel pixel; + Arg arg[1]; + XrmValue from, to; + + LispObj *color; + + color = ARGUMENT(0); + + if (color != UNSPEC) { + CHECK_STRING(color); + + from.size = STRLEN(color); + from.addr = (XtPointer)THESTR(color); + to.size = sizeof(Pixel); + to.addr = (XtPointer)&pixel; + + if (!XtConvertAndStore(XawTextGetSink(textwindow), + XtRString, &from, XtRPixel, &to)) + LispDestroy("cannot convert %s to Pixel", STROBJ(color)); + + XtSetArg(arg[0], XtNforeground, pixel); + XtSetValues(textwindow, arg, 1); + } + else { + from.size = sizeof(Pixel); + from.addr = (XtPointer)&pixel; + to.size = 0; + to.addr = NULL; + + XtSetArg(arg[0], XtNforeground, &pixel); + XtGetValues(XawTextGetSink(textwindow), arg, 1); + /* This cannot fail */ + XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to); + + color = STRING(to.addr); + } + + return (color); +} + +LispObj * +Xedit_GotoChar(LispBuiltin *builtin) +/* + goto-char offset + */ +{ + LispObj *offset; + XawTextPosition point; + + offset = ARGUMENT(0); + + CHECK_INDEX(offset); + XawTextSetInsertionPoint(textwindow, FIXNUM_VALUE(offset)); + point = XawTextGetInsertionPoint(textwindow); + if (point != FIXNUM_VALUE(offset)) + offset = FIXNUM(point); + + return (offset); +} + +LispObj * +Xedit_HorizontalScrollbar(LispBuiltin *builtin) +/* + horizontal-scrollbar &optional state + */ +{ + Arg arg[1]; + XawTextScrollMode scroll; + + LispObj *state; + + state = ARGUMENT(0); + + if (state != UNSPEC) { + scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways; + XtSetArg(arg[0], XtNscrollHorizontal, scroll); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNscrollHorizontal, &scroll); + XtGetValues(textwindow, arg, 1); + state = scroll == XawtextScrollAlways ? T : NIL; + } + + return (state); +} + +LispObj * +Xedit_Insert(LispBuiltin *builtin) +/* + insert text + */ +{ + XawTextPosition point = XawTextGetInsertionPoint(textwindow); + XawTextBlock block; + + LispObj *text; + + text = ARGUMENT(0); + + CHECK_STRING(text); + + block.firstPos = 0; + block.format = FMT8BIT; + block.length = STRLEN(text); + block.ptr = THESTR(text); + XawTextReplace(textwindow, point, point, &block); + XawTextSetInsertionPoint(textwindow, point + block.length); + + return (text); +} + +LispObj * +Xedit_Justification(LispBuiltin *builtin) +/* + justification &optional value + */ +{ + int i; + Arg arg[1]; + XawTextJustifyMode justify; + + LispObj *value; + + value = ARGUMENT(0); + + if (value != UNSPEC) { + for (i = 0; i < 4; i++) + if (value == justify_modes[i]) + break; + if (i >= 4) + LispDestroy("%s: argument must be " + ":LEFT, :RIGHT, :CENTER, or :FULL, not %s", + STRFUN(builtin), STROBJ(value)); + XtSetArg(arg[0], XtNjustifyMode, (XawTextJustifyMode)i); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNjustifyMode, &justify); + XtGetValues(textwindow, arg, 1); + i = (int)justify; + if (i <= 0 || i >= 4) + i = 0; + value = justify_modes[i]; + } + + return (value); +} + +LispObj * +Xedit_LeftColumn(LispBuiltin *builtin) +/* + left-column &optional left + */ +{ + short left; + Arg arg[1]; + + LispObj *oleft; + + oleft = ARGUMENT(0); + + if (oleft != UNSPEC) { + CHECK_INDEX(oleft); + if (FIXNUM_VALUE(oleft) >= 32767) + left = 32767; + else + left = FIXNUM_VALUE(oleft); + + XtSetArg(arg[0], XtNleftColumn, left); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNleftColumn, &left); + XtGetValues(textwindow, arg, 1); + + oleft = FIXNUM((long)left); + } + + return (oleft); +} + +LispObj * +Xedit_Point(LispBuiltin *builtin) +/* + point + */ +{ + return (FIXNUM(XawTextGetInsertionPoint(textwindow))); +} + +LispObj * +Xedit_PointMax(LispBuiltin *builtin) +/* + point-max + */ +{ + return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0, + XawstAll, XawsdRight, 1, True))); +} + +LispObj * +Xedit_PointMin(LispBuiltin *builtin) +/* + point-min + */ +{ + return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0, + XawstAll, XawsdLeft, 1, True))); +} + +LispObj * +Xedit_PropertyList(LispBuiltin *builtin) +/* + property-list &optional value + */ +{ + Arg arg[1]; + XawTextPropertyList *property_list; + + LispObj *value; + + value = ARGUMENT(0); + + if (value != UNSPEC) { + Cardinal i; + XrmQuark quark; + + CHECK_LONGINT(value); + property_list = NULL; + quark = LONGINT_VALUE(value); + for (i = 0; i < num_property_lists; i++) + if (property_lists[i]->identifier == quark) { + property_list = property_lists[i]; + break; + } + + if (property_list) { + XtSetArg(arg[0], XawNtextProperties, property_list); + XtSetValues(XawTextGetSink(textwindow), arg, 1); + } + else + /* Maybe should generate an error here */ + value = NIL; + } + else { + XtSetArg(arg[0], XawNtextProperties, &property_list); + XtGetValues(XawTextGetSink(textwindow), arg, 1); + if (property_list) + value = INTEGER(property_list->identifier); + } + + return (value); +} + +LispObj * +Xedit_ReadText(LispBuiltin *builtin) +/* + read-text offset length + */ +{ + XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0, + XawstAll, XawsdRight, 1, True); + XawTextPosition from, to, len; + XawTextBlock block; + char *string, *ptr; + + LispObj *offset, *length; + + length = ARGUMENT(1); + offset = ARGUMENT(0); + + CHECK_INDEX(offset); + CHECK_INDEX(length); + + from = FIXNUM_VALUE(offset); + to = from + FIXNUM_VALUE(length); + if (from > last) + from = last; + if (to > last) + to = last; + + if (from == to) + return (STRING("")); + + len = to - from; + string = LispMalloc(len); + + for (ptr = string; from < to;) { + XawTextSourceRead(XawTextGetSource(textwindow), from, &block, to - from); + memcpy(ptr, block.ptr, block.length); + ptr += block.length; + from += block.length; + } + + return (LSTRING2(string, len)); +} + +LispObj * +Xedit_ReplaceText(LispBuiltin *builtin) +/* + replace-text left right text + */ +{ + XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0, + XawstAll, XawsdRight, 1, True); + XawTextPosition left, right; + XawTextBlock block; + + LispObj *oleft, *oright, *text; + + text = ARGUMENT(2); + oright = ARGUMENT(1); + oleft = ARGUMENT(0); + + CHECK_INDEX(oleft); + CHECK_INDEX(oright); + CHECK_STRING(text); + + left = FIXNUM_VALUE(oleft); + right = FIXNUM_VALUE(oright); + if (left > last) + left = last; + if (left > right) + right = left; + else if (right > last) + right = last; + + block.firstPos = 0; + block.format = FMT8BIT; + block.length = STRLEN(text); + block.ptr = THESTR(text); + XawTextReplace(textwindow, left, right, &block); + + return (text); +} + +LispObj * +Xedit_RightColumn(LispBuiltin *builtin) +/* + right-column &optional right + */ +{ + short right; + Arg arg[1]; + + LispObj *oright; + + oright = ARGUMENT(0); + + if (oright != UNSPEC) { + CHECK_INDEX(oright); + if (FIXNUM_VALUE(oright) >= 32767) + right = 32767; + else + right = FIXNUM_VALUE(oright); + + XtSetArg(arg[0], XtNrightColumn, right); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNrightColumn, &right); + XtGetValues(textwindow, arg, 1); + + oright = FIXNUM(right); + } + + return (oright); +} + +LispObj * +Xedit_Scan(LispBuiltin *builtin) +/* + scan offset type direction &key count include + */ +{ + int i; + XawTextPosition offset; + XawTextScanType type; + XawTextScanDirection direction; + int count; + + LispObj *ooffset, *otype, *odirection, *ocount, *include; + + include = ARGUMENT(4); + if (include == UNSPEC) + include = NIL; + ocount = ARGUMENT(3); + odirection = ARGUMENT(2); + otype = ARGUMENT(1); + ooffset = ARGUMENT(0); + + CHECK_INDEX(ooffset); + offset = FIXNUM_VALUE(ooffset); + + for (i = 0; i < 2; i++) + if (odirection == scan_directions[i]) + break; + if (i >= 2) + LispDestroy("%s: direction must be " + ":LEFT or :RIGHT, not %s", + STRFUN(builtin), STROBJ(odirection)); + direction = (XawTextScanDirection)i; + + for (i = 0; i < 5; i++) + if (otype == scan_types[i]) + break; + if (i >= 5) + LispDestroy("%s: direction must be " + ":POSITIONS, :WHITE-SPACE, :EOL, " + ":PARAGRAPH, :ALL, or :ALPHA-NUMERIC, not %s", + STRFUN(builtin), STROBJ(otype)); + type = (XawTextScanType)i; + + if (ocount == UNSPEC) + count = 1; + else { + CHECK_INDEX(ocount); + count = FIXNUM_VALUE(ocount); + } + + offset = XawTextSourceScan(XawTextGetSource(textwindow), + offset, type, direction, count, + include != NIL); + + return (FIXNUM(offset)); +} + +static LispObj * +XeditSearch(LispBuiltin *builtin, XawTextScanDirection direction) +{ + XawTextBlock block; + XawTextPosition position; + + LispObj *string, *offset, *ignore_case; + + ignore_case = ARGUMENT(2); + offset = ARGUMENT(1); + string = ARGUMENT(0); + + CHECK_STRING(string); + if (offset != UNSPEC) { + CHECK_INDEX(offset); + position = FIXNUM_VALUE(offset); + } + else + position = XawTextGetInsertionPoint(textwindow); + + block.firstPos = (ignore_case != UNSPEC && ignore_case != NIL) ? 1 : 0; + block.format = FMT8BIT; + block.length = STRLEN(string); + block.ptr = THESTR(string); + position = XawTextSourceSearch(XawTextGetSource(textwindow), + position, direction, &block); + + return (position != XawTextSearchError ? FIXNUM(position) : NIL); +} + + +LispObj * +Xedit_SearchBackward(LispBuiltin *builtin) +/* + search-backward string &optional offset ignore-case + */ +{ + return (XeditSearch(builtin, XawsdLeft)); +} + +LispObj * +Xedit_SearchForward(LispBuiltin *builtin) +/* + search-forward string &optional offset ignore-case + */ +{ + return (XeditSearch(builtin, XawsdRight)); +} + +LispObj * +Xedit_VerticalScrollbar(LispBuiltin *builtin) +/* + vertical-scrollbar &optional state + */ +{ + Arg arg[1]; + XawTextScrollMode scroll; + + LispObj *state; + + state = ARGUMENT(0); + + if (state != UNSPEC) { + scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways; + XtSetArg(arg[0], XtNscrollVertical, scroll); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNscrollVertical, &scroll); + XtGetValues(textwindow, arg, 1); + state = scroll == XawtextScrollAlways ? T : NIL; + } + + return (state); +} + +LispObj * +Xedit_WrapMode(LispBuiltin *builtin) +/* + wrap-mode &optional value + */ +{ + int i; + Arg arg[1]; + XawTextWrapMode wrap; + + LispObj *value; + + value = ARGUMENT(0); + + if (value != UNSPEC) { + for (i = 0; i < 3; i++) + if (value == wrap_modes[i]) + break; + if (i >= 3) + LispDestroy("%s: argument must be " + ":NEVER, :LINE, or :WORD, not %s", + STRFUN(builtin), STROBJ(value)); + XtSetArg(arg[0], XtNwrap, (XawTextWrapMode)i); + XtSetValues(textwindow, arg, 1); + } + else { + XtSetArg(arg[0], XtNwrap, &wrap); + XtGetValues(textwindow, arg, 1); + i = (int)wrap; + if (i <= 0 || i >= 3) + i = 0; + value = wrap_modes[i]; + } + + return (value); +} + +LispObj * +Xedit_XrmStringToQuark(LispBuiltin *builtin) +/* + xrm-string-to-quark string + */ +{ + LispObj *string; + + string = ARGUMENT(0); + + CHECK_STRING(string); + + return (INTEGER(XrmStringToQuark(THESTR(string)))); +} |