Annotation of micropolis/src/tk/tktext.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tkText.c --
                      3:  *
                      4:  *     This module provides a big chunk of the implementation of
                      5:  *     multi-line editable text widgets for Tk.  Among other things,
                      6:  *     it provides the Tcl command interfaces to text widgets and
                      7:  *     the display code.  The B-tree representation of text is
                      8:  *     implemented elsewhere.
                      9:  *
                     10:  * Copyright 1992 Regents of the University of California.
                     11:  * Permission to use, copy, modify, and distribute this
                     12:  * software and its documentation for any purpose and without
                     13:  * fee is hereby granted, provided that the above copyright
                     14:  * notice appear in all copies.  The University of California
                     15:  * makes no representations about the suitability of this
                     16:  * software for any purpose.  It is provided "as is" without
                     17:  * express or implied warranty.
                     18:  */
                     19: 
                     20: #ifndef lint
                     21: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkText.c,v 1.23 92/08/14 14:45:44 ouster Exp $ SPRITE (Berkeley)";
                     22: #endif
                     23: 
                     24: #include "default.h"
                     25: #include "tkconfig.h"
                     26: #include "tk.h"
                     27: #include "tktext.h"
                     28: 
                     29: /*
                     30:  * Information used to parse text configuration options:
                     31:  */
                     32: 
                     33: static Tk_ConfigSpec configSpecs[] = {
                     34:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                     35:        DEF_TEXT_BG_COLOR, Tk_Offset(TkText, border), TK_CONFIG_COLOR_ONLY},
                     36:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                     37:        DEF_TEXT_BG_MONO, Tk_Offset(TkText, border), TK_CONFIG_MONO_ONLY},
                     38:     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
                     39:        (char *) NULL, 0, 0},
                     40:     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
                     41:        (char *) NULL, 0, 0},
                     42:     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
                     43:        DEF_TEXT_BORDER_WIDTH, Tk_Offset(TkText, borderWidth), 0},
                     44:     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
                     45:        DEF_TEXT_CURSOR, Tk_Offset(TkText, cursor), TK_CONFIG_NULL_OK},
                     46:     {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
                     47:        "ExportSelection", DEF_TEXT_EXPORT_SELECTION,
                     48:        Tk_Offset(TkText, exportSelection), 0},
                     49:     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
                     50:        (char *) NULL, 0, 0},
                     51:     {TK_CONFIG_FONT, "-font", "font", "Font",
                     52:        DEF_TEXT_FONT, Tk_Offset(TkText, fontPtr), 0},
                     53:     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
                     54:        DEF_TEXT_FG, Tk_Offset(TkText, fgColor), 0},
                     55:     {TK_CONFIG_INT, "-height", "height", "Height",
                     56:        DEF_TEXT_HEIGHT, Tk_Offset(TkText, height), 0},
                     57:     {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
                     58:        DEF_TEXT_INSERT_BG, Tk_Offset(TkText, insertBorder), 0},
                     59:     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
                     60:        DEF_TEXT_INSERT_BD_COLOR, Tk_Offset(TkText, insertBorderWidth),
                     61:        TK_CONFIG_COLOR_ONLY},
                     62:     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
                     63:        DEF_TEXT_INSERT_BD_MONO, Tk_Offset(TkText, insertBorderWidth),
                     64:        TK_CONFIG_MONO_ONLY},
                     65:     {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
                     66:        DEF_TEXT_INSERT_OFF_TIME, Tk_Offset(TkText, insertOffTime), 0},
                     67:     {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
                     68:        DEF_TEXT_INSERT_ON_TIME, Tk_Offset(TkText, insertOnTime), 0},
                     69:     {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
                     70:        DEF_TEXT_INSERT_WIDTH, Tk_Offset(TkText, insertWidth), 0},
                     71:     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
                     72:        DEF_TEXT_PADX, Tk_Offset(TkText, padX), 0},
                     73:     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
                     74:        DEF_TEXT_PADY, Tk_Offset(TkText, padY), 0},
                     75:     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
                     76:        DEF_TEXT_RELIEF, Tk_Offset(TkText, relief), 0},
                     77:     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
                     78:        DEF_ENTRY_SELECT_COLOR, Tk_Offset(TkText, selBorder),
                     79:        TK_CONFIG_COLOR_ONLY},
                     80:     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
                     81:        DEF_TEXT_SELECT_MONO, Tk_Offset(TkText, selBorder),
                     82:        TK_CONFIG_MONO_ONLY},
                     83:     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
                     84:        DEF_TEXT_SELECT_BD_COLOR, Tk_Offset(TkText, selBorderWidth),
                     85:        TK_CONFIG_COLOR_ONLY},
                     86:     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
                     87:        DEF_TEXT_SELECT_BD_MONO, Tk_Offset(TkText, selBorderWidth),
                     88:        TK_CONFIG_MONO_ONLY},
                     89:     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
                     90:        DEF_TEXT_SELECT_FG_COLOR, Tk_Offset(TkText, selFgColorPtr),
                     91:        TK_CONFIG_COLOR_ONLY},
                     92:     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
                     93:        DEF_TEXT_SELECT_FG_MONO, Tk_Offset(TkText, selFgColorPtr),
                     94:        TK_CONFIG_MONO_ONLY},
                     95:     {TK_CONFIG_BOOLEAN, "-setgrid", "setGrid", "SetGrid",
                     96:        DEF_TEXT_SET_GRID, Tk_Offset(TkText, setGrid), 0},
                     97:     {TK_CONFIG_UID, "-state", "state", "State",
                     98:        DEF_TEXT_STATE, Tk_Offset(TkText, state), 0},
                     99:     {TK_CONFIG_INT, "-width", "width", "Width",
                    100:        DEF_TEXT_WIDTH, Tk_Offset(TkText, width), 0},
                    101:     {TK_CONFIG_UID, "-wrap", "wrap", "Wrap",
                    102:        DEF_TEXT_WRAP, Tk_Offset(TkText, wrapMode), 0},
                    103:     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
                    104:        DEF_TEXT_YSCROLL_COMMAND, Tk_Offset(TkText, yScrollCmd),
                    105:        TK_CONFIG_NULL_OK},
                    106:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    107:        (char *) NULL, 0, 0}
                    108: };
                    109: 
                    110: /*
                    111:  * The following definition specifies the maximum number of characters
                    112:  * needed in a string to hold a position specifier.
                    113:  */
                    114: 
                    115: #define POS_CHARS 30
                    116: 
                    117: /*
                    118:  * Tk_Uid's used to represent text states:
                    119:  */
                    120: 
                    121: Tk_Uid tkTextCharUid = NULL;
                    122: Tk_Uid tkTextDisabledUid = NULL;
                    123: Tk_Uid tkTextNoneUid = NULL;
                    124: Tk_Uid tkTextNormalUid = NULL;
                    125: Tk_Uid tkTextWordUid = NULL;
                    126: 
                    127: /*
                    128:  * Forward declarations for procedures defined later in this file:
                    129:  */
                    130: 
                    131: static int             ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
                    132:                            TkText *textPtr, int argc, char **argv, int flags));
                    133: static void            DeleteChars _ANSI_ARGS_((TkText *textPtr, int line1,
                    134:                            int ch1, int line2, int ch2));
                    135: static void            DestroyText _ANSI_ARGS_((ClientData clientData));
                    136: static void            InsertChars _ANSI_ARGS_((TkText *textPtr, int line,
                    137:                            int ch, char *string));
                    138: static void            TextBlinkProc _ANSI_ARGS_((ClientData clientData));
                    139: static void            TextEventProc _ANSI_ARGS_((ClientData clientData,
                    140:                            XEvent *eventPtr));
                    141: static int             TextFetchSelection _ANSI_ARGS_((ClientData clientData,
                    142:                            int offset, char *buffer, int maxBytes));
                    143: static void            TextFocusProc _ANSI_ARGS_((ClientData clientData,
                    144:                            int gotFocus));
                    145: static int             TextMarkCmd _ANSI_ARGS_((TkText *textPtr,
                    146:                            Tcl_Interp *interp, int argc, char **argv));
                    147: static int             TextScanCmd _ANSI_ARGS_((TkText *textPtr,
                    148:                            Tcl_Interp *interp, int argc, char **argv));
                    149: static int             TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
                    150:                            Tcl_Interp *interp, int argc, char **argv));
                    151: 
                    152: /*
                    153:  *--------------------------------------------------------------
                    154:  *
                    155:  * Tk_TextCmd --
                    156:  *
                    157:  *     This procedure is invoked to process the "text" Tcl command.
                    158:  *     See the user documentation for details on what it does.
                    159:  *
                    160:  * Results:
                    161:  *     A standard Tcl result.
                    162:  *
                    163:  * Side effects:
                    164:  *     See the user documentation.
                    165:  *
                    166:  *--------------------------------------------------------------
                    167:  */
                    168: 
                    169: int
                    170: Tk_TextCmd(clientData, interp, argc, argv)
                    171:     ClientData clientData;     /* Main window associated with
                    172:                                 * interpreter. */
                    173:     Tcl_Interp *interp;                /* Current interpreter. */
                    174:     int argc;                  /* Number of arguments. */
                    175:     char **argv;               /* Argument strings. */
                    176: {
                    177:     Tk_Window tkwin = (Tk_Window) clientData;
                    178:     Tk_Window new;
                    179:     register TkText *textPtr;
                    180: 
                    181:     if (argc < 2) {
                    182:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    183:                argv[0], " pathName ?options?\"", (char *) NULL);
                    184:        return TCL_ERROR;
                    185:     }
                    186: 
                    187:     /*
                    188:      * Perform once-only initialization:
                    189:      */
                    190: 
                    191:     if (tkTextNormalUid == NULL) {
                    192:        tkTextCharUid = Tk_GetUid("char");
                    193:        tkTextDisabledUid = Tk_GetUid("disabled");
                    194:        tkTextNoneUid = Tk_GetUid("none");
                    195:        tkTextNormalUid = Tk_GetUid("normal");
                    196:        tkTextWordUid = Tk_GetUid("word");
                    197:     }
                    198: 
                    199:     /*
                    200:      * Create the window.
                    201:      */
                    202: 
                    203:     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
                    204:     if (new == NULL) {
                    205:        return TCL_ERROR;
                    206:     }
                    207: 
                    208:     textPtr = (TkText *) ckalloc(sizeof(TkText));
                    209:     textPtr->tkwin = new;
                    210:     textPtr->interp = interp;
                    211:     textPtr->tree = TkBTreeCreate();
                    212:     Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);
                    213:     textPtr->numTags = 0;
                    214:     Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);
                    215:     textPtr->state = tkTextNormalUid;
                    216:     textPtr->border = NULL;
                    217:     textPtr->cursor = None;
                    218:     textPtr->fgColor = NULL;
                    219:     textPtr->fontPtr = NULL;
                    220:     textPtr->prevWidth = Tk_Width(new);
                    221:     textPtr->prevHeight = Tk_Height(new);
                    222:     textPtr->topLinePtr = NULL;
                    223: // Moved down so flags were set right.
                    224: //    TkTextCreateDInfo(textPtr);
                    225: //    TkTextSetView(textPtr, 0, 0);
                    226:     textPtr->selBorder = NULL;
                    227:     textPtr->selFgColorPtr = NULL;
                    228:     textPtr->exportSelection = 1;
                    229:     textPtr->selOffset = -1;
                    230:     textPtr->insertAnnotPtr = NULL;
                    231:     textPtr->insertBorder = NULL;
                    232:     textPtr->insertBlinkHandler = (Tk_TimerToken) NULL;
                    233:     textPtr->bindingTable = NULL;
                    234:     textPtr->pickEvent.type = LeaveNotify;
                    235:     textPtr->yScrollCmd = NULL;
                    236:     textPtr->scanMarkLine = 0;
                    237:     textPtr->scanMarkY = 0;
                    238:     textPtr->flags = 0;
                    239:     textPtr->updateTimerToken = 0; // Added by Don to optimize rapid updates.
                    240:     TkTextCreateDInfo(textPtr);
                    241:     TkTextSetView(textPtr, 0, 0);
                    242: 
                    243:     /*
                    244:      * Create the "sel" tag and the "current" and "insert" marks.
                    245:      */
                    246: 
                    247:     textPtr->selTagPtr = TkTextCreateTag(textPtr, "sel");
                    248:     textPtr->selTagPtr->relief = TK_RELIEF_RAISED;
                    249:     textPtr->currentAnnotPtr = TkTextSetMark(textPtr, "current", 0, 0);
                    250:     textPtr->insertAnnotPtr = TkTextSetMark(textPtr, "insert", 0, 0);
                    251: 
                    252:     Tk_SetClass(new, "Text");
                    253:     Tk_CreateEventHandler(textPtr->tkwin, ExposureMask|StructureNotifyMask,
                    254:            TextEventProc, (ClientData) textPtr);
                    255:     Tk_CreateEventHandler(textPtr->tkwin, KeyPressMask|KeyReleaseMask
                    256:            |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
                    257:            |LeaveWindowMask|PointerMotionMask, TkTextBindProc,
                    258:            (ClientData) textPtr);
                    259:     Tk_CreateSelHandler(textPtr->tkwin, XA_STRING, TextFetchSelection,
                    260:            (ClientData) textPtr, XA_STRING);
                    261:     Tcl_CreateCommand(interp, Tk_PathName(textPtr->tkwin),
                    262:            TextWidgetCmd, (ClientData) textPtr, (void (*)()) NULL);
                    263:     if (ConfigureText(interp, textPtr, argc-2, argv+2, 0) != TCL_OK) {
                    264:        Tk_DestroyWindow(textPtr->tkwin);
                    265:        return TCL_ERROR;
                    266:     }
                    267:     Tk_CreateFocusHandler(textPtr->tkwin, TextFocusProc, (ClientData) textPtr);
                    268:     interp->result = Tk_PathName(textPtr->tkwin);
                    269: 
                    270:     return TCL_OK;
                    271: }
                    272: 
                    273: /*
                    274:  *--------------------------------------------------------------
                    275:  *
                    276:  * TextWidgetCmd --
                    277:  *
                    278:  *     This procedure is invoked to process the Tcl command
                    279:  *     that corresponds to a text widget.  See the user
                    280:  *     documentation for details on what it does.
                    281:  *
                    282:  * Results:
                    283:  *     A standard Tcl result.
                    284:  *
                    285:  * Side effects:
                    286:  *     See the user documentation.
                    287:  *
                    288:  *--------------------------------------------------------------
                    289:  */
                    290: 
                    291: static int
                    292: TextWidgetCmd(clientData, interp, argc, argv)
                    293:     ClientData clientData;     /* Information about text widget. */
                    294:     Tcl_Interp *interp;                /* Current interpreter. */
                    295:     int argc;                  /* Number of arguments. */
                    296:     char **argv;               /* Argument strings. */
                    297: {
                    298:     register TkText *textPtr = (TkText *) clientData;
                    299:     int result = TCL_OK;
                    300:     int length;
                    301:     char c;
                    302:     int line1, line2, ch1, ch2;
                    303: 
                    304:     if (argc < 2) {
                    305:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    306:                argv[0], " option ?arg arg ...?\"", (char *) NULL);
                    307:        return TCL_ERROR;
                    308:     }
                    309:     Tk_Preserve((ClientData) textPtr);
                    310:     c = argv[1][0];
                    311:     length = strlen(argv[1]);
                    312:     if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)
                    313:            && (length >= 3)) {
                    314:        int less, equal, greater, value;
                    315:        char *p;
                    316: 
                    317:        if (argc != 5) {
                    318:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    319:                    argv[0], " compare index1 op index2\"", (char *) NULL);
                    320:            result = TCL_ERROR;
                    321:            goto done;
                    322:        }
                    323:        if ((TkTextGetIndex(interp, textPtr, argv[2], &line1, &ch1) != TCL_OK)
                    324:                || (TkTextGetIndex(interp, textPtr, argv[4], &line2, &ch2)
                    325:                != TCL_OK)) {
                    326:            result = TCL_ERROR;
                    327:            goto done;
                    328:        }
                    329:        less = equal = greater = 0;
                    330:        if (line1 < line2) {
                    331:            less = 1;
                    332:        } else if (line1 > line2) {
                    333:            greater = 1;
                    334:        } else {
                    335:            if (ch1 < ch2) {
                    336:                less = 1;
                    337:            } else if (ch1 > ch2) {
                    338:                greater = 1;
                    339:            } else {
                    340:                equal = 1;
                    341:            }
                    342:        }
                    343:        p = argv[3];
                    344:        if (p[0] == '<') {
                    345:                value = less;
                    346:            if ((p[1] == '=') && (p[2] == 0)) {
                    347:                value = less || equal;
                    348:            } else if (p[1] != 0) {
                    349:                compareError:
                    350:                Tcl_AppendResult(interp, "bad comparison operator \"",
                    351:                        argv[3], "\": must be <, <=, ==, >=, >, or !=",
                    352:                        (char *) NULL);
                    353:                result = TCL_ERROR;
                    354:                goto done;
                    355:            }
                    356:        } else if (p[0] == '>') {
                    357:                value = greater;
                    358:            if ((p[1] == '=') && (p[2] == 0)) {
                    359:                value = greater || equal;
                    360:            } else if (p[1] != 0) {
                    361:                goto compareError;
                    362:            }
                    363:        } else if ((p[0] == '=') && (p[1] == '=') && (p[2] == 0)) {
                    364:            value = equal;
                    365:        } else if ((p[0] == '!') && (p[1] == '=') && (p[2] == 0)) {
                    366:            value = !equal;
                    367:        } else {
                    368:            goto compareError;
                    369:        }
                    370:        interp->result = (value) ? "1" : "0";
                    371:     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
                    372:            && (length >= 3)) {
                    373:        if (argc == 2) {
                    374:            result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
                    375:                    (char *) textPtr, (char *) NULL, 0);
                    376:        } else if (argc == 3) {
                    377:            result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
                    378:                    (char *) textPtr, argv[2], 0);
                    379:        } else {
                    380:            result = ConfigureText(interp, textPtr, argc-2, argv+2,
                    381:                    TK_CONFIG_ARGV_ONLY);
                    382:        }
                    383:     } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)
                    384:            && (length >= 3)) {
                    385:        if (argc > 3) {
                    386:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    387:                    argv[0], " debug ?on|off?\"", (char *) NULL);
                    388:            result = TCL_ERROR;
                    389:            goto done;
                    390:        }
                    391:        if (argc == 2) {
                    392:            interp->result = (tkBTreeDebug) ? "on" : "off";
                    393:        } else {
                    394:            if (Tcl_GetBoolean(interp, argv[2], &tkBTreeDebug) != TCL_OK) {
                    395:                result = TCL_ERROR;
                    396:                goto done;
                    397:            }
                    398:        }
                    399:     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
                    400:            && (length >= 3)) {
                    401:        if ((argc != 3) && (argc != 4)) {
                    402:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    403:                    argv[0], " delete index1 ?index2?\"", (char *) NULL);
                    404:            result = TCL_ERROR;
                    405:            goto done;
                    406:        }
                    407:        if (TkTextGetIndex(interp, textPtr, argv[2], &line1, &ch1) != TCL_OK) {
                    408:            result = TCL_ERROR;
                    409:            goto done;
                    410:        }
                    411:        if (argc == 3) {
                    412:            line2 = line1;
                    413:            ch2 = ch1+1;
                    414:        } else if (TkTextGetIndex(interp, textPtr, argv[3], &line2, &ch2)
                    415:                != TCL_OK) {
                    416:            result = TCL_ERROR;
                    417:            goto done;
                    418:        }
                    419:        if (textPtr->state == tkTextNormalUid) {
                    420:            DeleteChars(textPtr, line1, ch1, line2, ch2);
                    421:        }
                    422:     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
                    423:        register TkTextLine *linePtr;
                    424: 
                    425:        if ((argc != 3) && (argc != 4)) {
                    426:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    427:                    argv[0], " get index1 ?index2?\"", (char *) NULL);
                    428:            result = TCL_ERROR;
                    429:            goto done;
                    430:        }
                    431:        if (TkTextGetIndex(interp, textPtr, argv[2], &line1, &ch1) != TCL_OK) {
                    432:            result = TCL_ERROR;
                    433:            goto done;
                    434:        }
                    435:        if (argc == 3) {
                    436:            line2 = line1;
                    437:            ch2 = ch1+1;
                    438:        } else if (TkTextGetIndex(interp, textPtr, argv[3], &line2, &ch2)
                    439:                != TCL_OK) {
                    440:            result = TCL_ERROR;
                    441:            goto done;
                    442:        }
                    443:        if (line1 < 0) {
                    444:            line1 = 0;
                    445:            ch1 = 0;
                    446:        }
                    447:        for (linePtr = TkBTreeFindLine(textPtr->tree, line1);
                    448:                (linePtr != NULL) && (line1 <= line2);
                    449:                linePtr = TkBTreeNextLine(linePtr), line1++, ch1 = 0) {
                    450:            int savedChar, last;
                    451: 
                    452:            if (line1 == line2) {
                    453:                last = ch2;
                    454:                if (last > linePtr->numBytes) {
                    455:                    last = linePtr->numBytes;
                    456:                }
                    457:            } else {
                    458:                last = linePtr->numBytes;
                    459:            }
                    460:            if (ch1 >= last) {
                    461:                continue;
                    462:            }
                    463:            savedChar = linePtr->bytes[last];
                    464:            linePtr->bytes[last] = 0;
                    465:            Tcl_AppendResult(interp, linePtr->bytes+ch1, (char *) NULL);
                    466:            linePtr->bytes[last] = savedChar;
                    467:        }
                    468:     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
                    469:            && (length >= 3)) {
                    470:        if (argc != 3) {
                    471:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    472:                    argv[0], " index index\"",
                    473:                    (char *) NULL);
                    474:            result = TCL_ERROR;
                    475:            goto done;
                    476:        }
                    477:        if (TkTextGetIndex(interp, textPtr, argv[2], &line1, &ch1) != TCL_OK) {
                    478:            result = TCL_ERROR;
                    479:            goto done;
                    480:        }
                    481:        TkTextPrintIndex(line1, ch1, interp->result);
                    482:     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
                    483:            && (length >= 3)) {
                    484:        if (argc != 4) {
                    485:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    486:                    argv[0], " insert index chars ?chars ...?\"",
                    487:                    (char *) NULL);
                    488:            result = TCL_ERROR;
                    489:            goto done;
                    490:        }
                    491:        if (TkTextGetIndex(interp, textPtr, argv[2], &line1, &ch1) != TCL_OK) {
                    492:            result = TCL_ERROR;
                    493:            goto done;
                    494:        }
                    495:        if (textPtr->state == tkTextNormalUid) {
                    496:            InsertChars(textPtr, line1, ch1, argv[3]);
                    497:        }
                    498:     } else if ((c == 'm') && (strncmp(argv[1], "mark", length) == 0)) {
                    499:        result = TextMarkCmd(textPtr, interp, argc, argv);
                    500:     } else if ((c == 's') && (strcmp(argv[1], "scan") == 0)) {
                    501:        result = TextScanCmd(textPtr, interp, argc, argv);
                    502:     } else if ((c == 't') && (strcmp(argv[1], "tag") == 0)) {
                    503:        result = TkTextTagCmd(textPtr, interp, argc, argv);
                    504:     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
                    505:        int numLines, pickPlace;
                    506: 
                    507:        if (argc < 3) {
                    508:            yviewSyntax:
                    509:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    510:                    argv[0], " yview ?-pickplace? lineNum|index\"",
                    511:                    (char *) NULL);
                    512:            result = TCL_ERROR;
                    513:            goto done;
                    514:        }
                    515:        pickPlace = 0;
                    516:        if (argv[2][0] == '-') {
                    517:            int switchLength;
                    518: 
                    519:            switchLength = strlen(argv[2]);
                    520:            if ((switchLength >= 2)
                    521:                    && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
                    522:                pickPlace = 1;
                    523:            }
                    524:        }
                    525:        if ((pickPlace+3) != argc) {
                    526:            goto yviewSyntax;
                    527:        }
                    528:        if (Tcl_GetInt(interp, argv[2+pickPlace], &line1) != TCL_OK) {
                    529:            Tcl_ResetResult(interp);
                    530:            if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
                    531:                    &line1, &ch1) != TCL_OK) {
                    532:                result = TCL_ERROR;
                    533:                goto done;
                    534:            }
                    535:        }
                    536:        numLines = TkBTreeNumLines(textPtr->tree);
                    537:        if (line1 >= numLines) {
                    538:            line1 = numLines-1;
                    539:        }
                    540:        if (line1 < 0) {
                    541:            line1 = 0;
                    542:        }
                    543:        TkTextSetView(textPtr, line1, pickPlace);
                    544:     } else {
                    545:        Tcl_AppendResult(interp, "bad option \"", argv[1],
                    546:                "\":  must be compare, configure, debug, delete, get, ",
                    547:                "index, insert, mark, scan, tag, or yview",
                    548:                (char *) NULL);
                    549:        result = TCL_ERROR;
                    550:     }
                    551: 
                    552:     done:
                    553:     Tk_Release((ClientData) textPtr);
                    554:     return result;
                    555: }
                    556: 
                    557: /*
                    558:  *----------------------------------------------------------------------
                    559:  *
                    560:  * DestroyText --
                    561:  *
                    562:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    563:  *     to clean up the internal structure of a text at a safe time
                    564:  *     (when no-one is using it anymore).
                    565:  *
                    566:  * Results:
                    567:  *     None.
                    568:  *
                    569:  * Side effects:
                    570:  *     Everything associated with the text is freed up.
                    571:  *
                    572:  *----------------------------------------------------------------------
                    573:  */
                    574: 
                    575: static void
                    576: DestroyText(clientData)
                    577:     ClientData clientData;     /* Info about text widget. */
                    578: {
                    579:     register TkText *textPtr = (TkText *) clientData;
                    580:     Tcl_HashSearch search;
                    581:     Tcl_HashEntry *hPtr;
                    582:     TkTextTag *tagPtr;
                    583: 
                    584:     TkBTreeDestroy(textPtr->tree);
                    585:     for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
                    586:            hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
                    587:        tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
                    588:        TkTextFreeTag(tagPtr);
                    589:     }
                    590:     Tcl_DeleteHashTable(&textPtr->tagTable);
                    591:     for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
                    592:            hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
                    593:        ckfree((char *) Tcl_GetHashValue(hPtr));
                    594:     }
                    595:     Tcl_DeleteHashTable(&textPtr->markTable);
                    596:     if (textPtr->border != NULL) {
                    597:        Tk_Free3DBorder(textPtr->border);
                    598:     }
                    599:     if (textPtr->cursor != None) {
                    600:        Tk_FreeCursor(textPtr->cursor);
                    601:     }
                    602:     if (textPtr->fgColor != NULL) {
                    603:        Tk_FreeColor(textPtr->fgColor);
                    604:     }
                    605:     if (textPtr->fontPtr != NULL) {
                    606:        Tk_FreeFontStruct(textPtr->fontPtr);
                    607:     }
                    608:     TkTextFreeDInfo(textPtr);
                    609: 
                    610:     /*
                    611:      * NOTE: do NOT free up selBorder or selFgColorPtr:  they are
                    612:      * duplicates of information in the "sel" tag, which was freed
                    613:      * up as part of deleting the tags above.
                    614:      */
                    615: 
                    616:     if (textPtr->insertBorder != NULL) {
                    617:        Tk_Free3DBorder(textPtr->insertBorder);
                    618:     }
                    619:     if (textPtr->insertBlinkHandler != NULL) {
                    620:        Tk_DeleteTimerHandler(textPtr->insertBlinkHandler);
                    621:     }
                    622:     if (textPtr->updateTimerToken != NULL) {
                    623:        Tk_DeleteTimerHandler(textPtr->updateTimerToken);
                    624:        textPtr->updateTimerToken = 0;
                    625:     }
                    626:     if (textPtr->bindingTable != NULL) {
                    627:        Tk_DeleteBindingTable(textPtr->bindingTable);
                    628:     }
                    629:     if (textPtr->yScrollCmd != NULL) {
                    630:        ckfree(textPtr->yScrollCmd);
                    631:     }
                    632:     ckfree((char *) textPtr);
                    633: }
                    634: 
                    635: /*
                    636:  *----------------------------------------------------------------------
                    637:  *
                    638:  * ConfigureText --
                    639:  *
                    640:  *     This procedure is called to process an argv/argc list, plus
                    641:  *     the Tk option database, in order to configure (or
                    642:  *     reconfigure) a text widget.
                    643:  *
                    644:  * Results:
                    645:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                    646:  *     returned, then interp->result contains an error message.
                    647:  *
                    648:  * Side effects:
                    649:  *     Configuration information, such as text string, colors, font,
                    650:  *     etc. get set for textPtr;  old resources get freed, if there
                    651:  *     were any.
                    652:  *
                    653:  *----------------------------------------------------------------------
                    654:  */
                    655: 
                    656: static int
                    657: ConfigureText(interp, textPtr, argc, argv, flags)
                    658:     Tcl_Interp *interp;                /* Used for error reporting. */
                    659:     register TkText *textPtr;  /* Information about widget;  may or may
                    660:                                 * not already have values for some fields. */
                    661:     int argc;                  /* Number of valid entries in argv. */
                    662:     char **argv;               /* Arguments. */
                    663:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
                    664: {
                    665:     int oldExport = textPtr->exportSelection;
                    666:     int charWidth, charHeight;
                    667: 
                    668:     if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs,
                    669:            argc, argv, (char *) textPtr, flags) != TCL_OK) {
                    670:        return TCL_ERROR;
                    671:     }
                    672: 
                    673:     /*
                    674:      * A few other options also need special processing, such as parsing
                    675:      * the geometry and setting the background from a 3-D border.
                    676:      */
                    677: 
                    678:     if ((textPtr->state != tkTextNormalUid)
                    679:            && (textPtr->state != tkTextDisabledUid)) {
                    680:        Tcl_AppendResult(interp, "bad state value \"", textPtr->state,
                    681:                "\":  must be normal or disabled", (char *) NULL);
                    682:        textPtr->state = tkTextNormalUid;
                    683:        return TCL_ERROR;
                    684:     }
                    685: 
                    686:     if ((textPtr->wrapMode != tkTextCharUid)
                    687:            && (textPtr->wrapMode != tkTextNoneUid)
                    688:            && (textPtr->wrapMode != tkTextWordUid)) {
                    689:        Tcl_AppendResult(interp, "bad wrap mode \"", textPtr->state,
                    690:                "\":  must be char, none, or word", (char *) NULL);
                    691:        textPtr->wrapMode = tkTextCharUid;
                    692:        return TCL_ERROR;
                    693:     }
                    694: 
                    695:     Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
                    696:     Tk_SetInternalBorder(textPtr->tkwin, textPtr->borderWidth);
                    697:     Tk_GeometryRequest(textPtr->tkwin, 200, 100);
                    698: 
                    699:     /*
                    700:      * Make sure that configuration options are properly mirrored
                    701:      * between the widget record and the "sel" tags.  NOTE: we don't
                    702:      * have to free up information during the mirroring;  old
                    703:      * information was freed when it was replaced in the widget
                    704:      * record.
                    705:      */
                    706: 
                    707:     textPtr->selTagPtr->border = textPtr->selBorder;
                    708:     textPtr->selTagPtr->borderWidth = textPtr->selBorderWidth;
                    709:     textPtr->selTagPtr->fgColor = textPtr->selFgColorPtr;
                    710: 
                    711:     /*
                    712:      * Claim the selection if we've suddenly started exporting it and there
                    713:      * are tagged characters.
                    714:      */
                    715: 
                    716:     if (textPtr->exportSelection && (!oldExport)) {
                    717:        TkTextSearch search;
                    718: 
                    719:        TkBTreeStartSearch(textPtr->tree, 0, 0, TkBTreeNumLines(textPtr->tree),
                    720:                0, textPtr->selTagPtr, &search);
                    721:        if (TkBTreeNextTag(&search)) {
                    722:            Tk_OwnSelection(textPtr->tkwin, TkTextLostSelection,
                    723:                    (ClientData) textPtr);
                    724:            textPtr->flags |= GOT_SELECTION;
                    725:        }
                    726:     }
                    727: 
                    728:     /*
                    729:      * Register the desired geometry for the window, and arrange for
                    730:      * the window to be redisplayed.
                    731:      */
                    732: 
                    733:     if (textPtr->width <= 0) {
                    734:        textPtr->width = 1;
                    735:     }
                    736:     if (textPtr->height <= 0) {
                    737:        textPtr->height = 1;
                    738:     }
                    739:     charWidth = XTextWidth(textPtr->fontPtr, "0", 1);
                    740:     charHeight = (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
                    741:     Tk_GeometryRequest(textPtr->tkwin,
                    742:            textPtr->width * charWidth + 2*textPtr->borderWidth
                    743:                    + 2*textPtr->padX,
                    744:            textPtr->height * charHeight + 2*textPtr->borderWidth
                    745:                    + 2*textPtr->padX);
                    746:     Tk_SetInternalBorder(textPtr->tkwin, textPtr->borderWidth);
                    747:     if (textPtr->setGrid) {
                    748:        Tk_SetGrid(textPtr->tkwin, textPtr->width, textPtr->height,
                    749:                charWidth, charHeight);
                    750:     }
                    751: 
                    752:     TkTextRelayoutWindow(textPtr);
                    753:     return TCL_OK;
                    754: }
                    755: 
                    756: /*
                    757:  *--------------------------------------------------------------
                    758:  *
                    759:  * TextEventProc --
                    760:  *
                    761:  *     This procedure is invoked by the Tk dispatcher on
                    762:  *     structure changes to a text.  For texts with 3D
                    763:  *     borders, this procedure is also invoked for exposures.
                    764:  *
                    765:  * Results:
                    766:  *     None.
                    767:  *
                    768:  * Side effects:
                    769:  *     When the window gets deleted, internal structures get
                    770:  *     cleaned up.  When it gets exposed, it is redisplayed.
                    771:  *
                    772:  *--------------------------------------------------------------
                    773:  */
                    774: 
                    775: static void
                    776: TextEventProc(clientData, eventPtr)
                    777:     ClientData clientData;     /* Information about window. */
                    778:     register XEvent *eventPtr; /* Information about event. */
                    779: {
                    780:     register TkText *textPtr = (TkText *) clientData;
                    781: 
                    782:     if (eventPtr->type == Expose) {
                    783:        TkTextRedrawRegion(textPtr, eventPtr->xexpose.x,
                    784:                eventPtr->xexpose.y, eventPtr->xexpose.width,
                    785:                eventPtr->xexpose.height);
                    786:     } else if (eventPtr->type == ConfigureNotify) {
                    787:        if ((textPtr->prevWidth != Tk_Width(textPtr->tkwin))
                    788:                || (textPtr->prevHeight != Tk_Height(textPtr->tkwin))) {
                    789:            TkTextRelayoutWindow(textPtr);
                    790:        }
                    791:     } else if (eventPtr->type == DestroyNotify) {
                    792:        Tcl_DeleteCommand(textPtr->interp, Tk_PathName(textPtr->tkwin));
                    793:        textPtr->tkwin = NULL;
                    794:        Tk_EventuallyFree((ClientData) textPtr, DestroyText);
                    795:     }
                    796: }
                    797: 
                    798: /*
                    799:  *----------------------------------------------------------------------
                    800:  *
                    801:  * InsertChars --
                    802:  *
                    803:  *     This procedure implements most of the functionality of the
                    804:  *     "insert" widget command.
                    805:  *
                    806:  * Results:
                    807:  *     None.
                    808:  *
                    809:  * Side effects:
                    810:  *     The characters in "string" get added to the text just before
                    811:  *     the character indicated by "line" and "ch".
                    812:  *
                    813:  *----------------------------------------------------------------------
                    814:  */
                    815: 
                    816: static void
                    817: InsertChars(textPtr, line, ch, string)
                    818:     TkText *textPtr;           /* Overall information about text widget. */
                    819:     int line, ch;              /* Identifies character just before which
                    820:                                 * new information is to be inserted. */
                    821:     char *string;              /* Null-terminated string containing new
                    822:                                 * information to add to text. */
                    823: {
                    824:     register TkTextLine *linePtr;
                    825: 
                    826:     /*
                    827:      * Locate the line where the insertion will occur.
                    828:      */
                    829: 
                    830:     linePtr = TkTextRoundIndex(textPtr, &line, &ch);
                    831: 
                    832:     /*
                    833:      * Notify the display module that lines are about to change, then do
                    834:      * the insertion.
                    835:      */
                    836: 
                    837:     TkTextLinesChanged(textPtr, line, line);
                    838:     TkBTreeInsertChars(textPtr->tree, linePtr, ch, string);
                    839: 
                    840:     /*
                    841:      * If the line containing the insertion point was textPtr->topLinePtr,
                    842:      * we must reset this pointer since the line structure was re-allocated.
                    843:      */
                    844: 
                    845:     if (linePtr == textPtr->topLinePtr) {
                    846:        TkTextSetView(textPtr, line, 0);
                    847:     }
                    848: 
                    849:     /*
                    850:      * Invalidate any selection retrievals in progress.
                    851:      */
                    852: 
                    853:     textPtr->selOffset = -1;
                    854: }
                    855: 
                    856: /*
                    857:  *----------------------------------------------------------------------
                    858:  *
                    859:  * DeleteChars --
                    860:  *
                    861:  *     This procedure implements most of the functionality of the
                    862:  *     "delete" widget command.
                    863:  *
                    864:  * Results:
                    865:  *     None.
                    866:  *
                    867:  * Side effects:
                    868:  *     None.
                    869:  *
                    870:  *----------------------------------------------------------------------
                    871:  */
                    872: 
                    873: static void
                    874: DeleteChars(textPtr, line1, ch1, line2, ch2)
                    875:     TkText *textPtr;           /* Overall information about text widget. */
                    876:     int line1, ch1;            /* Position of first character to delete. */
                    877:     int line2, ch2;            /* Position of character just after last
                    878:                                 * one to delete. */
                    879: {
                    880:     register TkTextLine *line1Ptr, *line2Ptr;
                    881:     int numLines, topLine;
                    882: 
                    883:     /*
                    884:      * The loop below is needed because a LeaveNotify event may be
                    885:      * generated on the current charcter if it's about to be deleted.
                    886:      * If this happens, then the bindings that trigger could modify
                    887:      * the text, invalidating the range information computed here.
                    888:      * So, go back and recompute all the range information after
                    889:      * synthesizing a leave event.
                    890:      */
                    891: 
                    892:     while (1) {
                    893: 
                    894:        /*
                    895:         * Locate the starting and ending lines for the deletion and adjust
                    896:         * the endpoints if necessary to ensure that they are within valid
                    897:         * ranges.  Adjust the deletion range if necessary to ensure that the
                    898:         * text (and each invidiual line) always ends in a newline.
                    899:         */
                    900:     
                    901:        numLines = TkBTreeNumLines(textPtr->tree);
                    902:        line1Ptr = TkTextRoundIndex(textPtr, &line1, &ch1);
                    903:        if (line2 < 0) {
                    904:            return;
                    905:        } else if (line2 >= numLines) {
                    906:            line2 = numLines-1;
                    907:            line2Ptr = TkBTreeFindLine(textPtr->tree, line2);
                    908:            ch2 = line2Ptr->numBytes;
                    909:        } else {
                    910:            line2Ptr = TkBTreeFindLine(textPtr->tree, line2);
                    911:            if (ch2 < 0) {
                    912:                ch2 = 0;
                    913:            }
                    914:        }
                    915:     
                    916:        /*
                    917:         * If the deletion range ends after the last character of a line,
                    918:         * do one of three things:
                    919:         *
                    920:         * (a) if line2Ptr isn't the last line of the text, just adjust the
                    921:         *     ending point to be just before the 0th character of the next
                    922:         *     line.
                    923:         * (b) if ch1 is at the beginning of a line, then adjust line1Ptr and
                    924:         *     ch1 to point just after the last character of the previous line.
                    925:         * (c) otherwise, adjust ch2 so the final newline isn't deleted.
                    926:         */
                    927:     
                    928:        if (ch2 >= line2Ptr->numBytes) {
                    929:            if (line2 < (numLines-1)) {
                    930:                line2++;
                    931:                line2Ptr = TkBTreeNextLine(line2Ptr);
                    932:                ch2 = 0;
                    933:            } else {
                    934:                ch2 = line2Ptr->numBytes-1;
                    935:                if ((ch1 == 0) && (line1 > 0)) {
                    936:                    line1--;
                    937:                    line1Ptr = TkBTreeFindLine(textPtr->tree, line1);
                    938:                    ch1 = line1Ptr->numBytes;
                    939:                    ch2 = line2Ptr->numBytes;
                    940:                } else {
                    941:                    ch2 = line2Ptr->numBytes-1;
                    942:                }
                    943:            }
                    944:        }
                    945: 
                    946:        if ((line1 > line2) || ((line1 == line2) && (ch1 >= ch2))) {
                    947:            return;
                    948:        }
                    949: 
                    950:        /*
                    951:         * If the current character is within the range being deleted,
                    952:         * unpick it and synthesize a leave event for its tags, then
                    953:         * go back and recompute the range ends.
                    954:         */
                    955: 
                    956:        if (!(textPtr->flags & IN_CURRENT)) {
                    957:            break;
                    958:        }
                    959:        if ((textPtr->currentAnnotPtr->linePtr == line1Ptr)
                    960:                && (textPtr->currentAnnotPtr->ch < ch1)) {
                    961:            break;
                    962:        }
                    963:        if ((textPtr->currentAnnotPtr->linePtr == line2Ptr)
                    964:                && (textPtr->currentAnnotPtr->ch >= ch2)) {
                    965:            break;
                    966:        }
                    967:        if (line2 > (line1+1)) {
                    968:            int currentLine;
                    969: 
                    970:            currentLine = TkBTreeLineIndex(textPtr->currentAnnotPtr->linePtr);
                    971:            if ((currentLine <= line1) || (currentLine >= line2)) {
                    972:                break;
                    973:            }
                    974:        }
                    975:        TkTextUnpickCurrent(textPtr);
                    976:     }
                    977: 
                    978:     /*
                    979:      * Tell the display what's about to happen so it can discard
                    980:      * obsolete display information, then do the deletion.  Also,
                    981:      * check to see if textPtr->topLinePtr is in the range of
                    982:      * characters deleted.  If so, call the display module to reset
                    983:      * it after doing the deletion.
                    984:      */
                    985: 
                    986:     topLine = TkBTreeLineIndex(textPtr->topLinePtr);
                    987:     TkTextLinesChanged(textPtr, line1, line2);
                    988:     TkBTreeDeleteChars(textPtr->tree, line1Ptr, ch1, line2Ptr, ch2);
                    989:     if ((topLine >= line1) && (topLine <= line2)) {
                    990:        numLines = TkBTreeNumLines(textPtr->tree);
                    991:        TkTextSetView(textPtr, (line1 > (numLines-1)) ? (numLines-1) : line1,
                    992:                0);
                    993:     }
                    994: 
                    995:     /*
                    996:      * Invalidate any selection retrievals in progress.
                    997:      */
                    998: 
                    999:     textPtr->selOffset = -1;
                   1000: }
                   1001: 
                   1002: /*
                   1003:  *----------------------------------------------------------------------
                   1004:  *
                   1005:  * TextFetchSelection --
                   1006:  *
                   1007:  *     This procedure is called back by Tk when the selection is
                   1008:  *     requested by someone.  It returns part or all of the selection
                   1009:  *     in a buffer provided by the caller.
                   1010:  *
                   1011:  * Results:
                   1012:  *     The return value is the number of non-NULL bytes stored
                   1013:  *     at buffer.  Buffer is filled (or partially filled) with a
                   1014:  *     NULL-terminated string containing part or all of the selection,
                   1015:  *     as given by offset and maxBytes.
                   1016:  *
                   1017:  * Side effects:
                   1018:  *     None.
                   1019:  *
                   1020:  *----------------------------------------------------------------------
                   1021:  */
                   1022: 
                   1023: static int
                   1024: TextFetchSelection(clientData, offset, buffer, maxBytes)
                   1025:     ClientData clientData;             /* Information about text widget. */
                   1026:     int offset;                                /* Offset within selection of first
                   1027:                                         * character to be returned. */
                   1028:     char *buffer;                      /* Location in which to place
                   1029:                                         * selection. */
                   1030:     int maxBytes;                      /* Maximum number of bytes to place
                   1031:                                         * at buffer, not including terminating
                   1032:                                         * NULL character. */
                   1033: {
                   1034:     register TkText *textPtr = (TkText *) clientData;
                   1035:     register TkTextLine *linePtr;
                   1036:     int count, chunkSize;
                   1037:     TkTextSearch search;
                   1038: 
                   1039:     if (!textPtr->exportSelection) {
                   1040:        return -1;
                   1041:     }
                   1042: 
                   1043:     /*
                   1044:      * Find the beginning of the next range of selected text.  Note:  if
                   1045:      * the selection is being retrieved in multiple pieces (offset != 0)
                   1046:      * and some modification has been made to the text that affects the
                   1047:      * selection (textPtr->selOffset != offset) then reject the selection
                   1048:      * request (make 'em start over again).
                   1049:      */
                   1050: 
                   1051:     if (offset == 0) {
                   1052:        textPtr->selLine = 0;
                   1053:        textPtr->selCh = 0;
                   1054:        textPtr->selOffset = 0;
                   1055:     } else if (textPtr->selOffset != offset) {
                   1056:        return 0;
                   1057:     }
                   1058:     TkBTreeStartSearch(textPtr->tree, textPtr->selLine, textPtr->selCh+1,
                   1059:            TkBTreeNumLines(textPtr->tree), 0, textPtr->selTagPtr, &search);
                   1060:     if (!TkBTreeCharTagged(search.linePtr, textPtr->selCh,
                   1061:            textPtr->selTagPtr)) {
                   1062:        if (!TkBTreeNextTag(&search)) {
                   1063:            if (offset == 0) {
                   1064:                return -1;
                   1065:            } else {
                   1066:                return 0;
                   1067:            }
                   1068:        }
                   1069:        textPtr->selLine = search.line1;
                   1070:        textPtr->selCh = search.ch1;
                   1071:     }
                   1072: 
                   1073:     /*
                   1074:      * Each iteration through the outer loop below scans one selected range.
                   1075:      * Each iteration through the nested loop scans one line in the
                   1076:      * selected range.
                   1077:      */
                   1078: 
                   1079:     count = 0;
                   1080:     while (1) {
                   1081:        linePtr = search.linePtr;
                   1082: 
                   1083:        /*
                   1084:         * Find the end of the current range of selected text.
                   1085:         */
                   1086: 
                   1087:        if (!TkBTreeNextTag(&search)) {
                   1088:            panic("TextFetchSelection couldn't find end of range");
                   1089:        }
                   1090: 
                   1091:        /*
                   1092:         * Copy information from text lines into the buffer until
                   1093:         * either we run out of space in the buffer or we get to
                   1094:         * the end of this range of text.
                   1095:         */
                   1096: 
                   1097:        while (1) {
                   1098:            chunkSize = ((linePtr == search.linePtr) ? search.ch1
                   1099:                    : linePtr->numBytes) - textPtr->selCh;
                   1100:            if (chunkSize > maxBytes) {
                   1101:                chunkSize = maxBytes;
                   1102:            }
                   1103:            memcpy((VOID *) buffer, (VOID *) (linePtr->bytes + textPtr->selCh),
                   1104:                    chunkSize);
                   1105:            buffer += chunkSize;
                   1106:            maxBytes -= chunkSize;
                   1107:            count += chunkSize;
                   1108:            textPtr->selOffset += chunkSize;
                   1109:            if (maxBytes == 0) {
                   1110:                textPtr->selCh += chunkSize;
                   1111:                goto done;
                   1112:            }
                   1113:            if (linePtr == search.linePtr) {
                   1114:                break;
                   1115:            }
                   1116:            textPtr->selCh = 0;
                   1117:            textPtr->selLine++;
                   1118:            linePtr = TkBTreeNextLine(linePtr);
                   1119:        }
                   1120: 
                   1121:        /*
                   1122:         * Find the beginning of the next range of selected text.
                   1123:         */
                   1124: 
                   1125:        if (!TkBTreeNextTag(&search)) {
                   1126:            break;
                   1127:        }
                   1128:        textPtr->selLine = search.line1;
                   1129:        textPtr->selCh = search.ch1;
                   1130:     }
                   1131: 
                   1132:     done:
                   1133:     *buffer = 0;
                   1134:     return count;
                   1135: }
                   1136: 
                   1137: /*
                   1138:  *----------------------------------------------------------------------
                   1139:  *
                   1140:  * TkTextLostSelection --
                   1141:  *
                   1142:  *     This procedure is called back by Tk when the selection is
                   1143:  *     grabbed away from a text widget.
                   1144:  *
                   1145:  * Results:
                   1146:  *     None.
                   1147:  *
                   1148:  * Side effects:
                   1149:  *     The "sel" tag is cleared from the window.
                   1150:  *
                   1151:  *----------------------------------------------------------------------
                   1152:  */
                   1153: 
                   1154: void
                   1155: TkTextLostSelection(clientData)
                   1156:     ClientData clientData;             /* Information about text widget. */
                   1157: {
                   1158:     register TkText *textPtr = (TkText *) clientData;
                   1159: 
                   1160:     if (!textPtr->exportSelection) {
                   1161:        return;
                   1162:     }
                   1163: 
                   1164:     /*
                   1165:      * Just remove the "sel" tag from everything in the widget.
                   1166:      */
                   1167: 
                   1168:     TkTextRedrawTag(textPtr, 0, 0, TkBTreeNumLines(textPtr->tree),
                   1169:            0, textPtr->selTagPtr, 1);
                   1170:     TkBTreeTag(textPtr->tree, 0, 0, TkBTreeNumLines(textPtr->tree),
                   1171:            0, textPtr->selTagPtr, 0);
                   1172:     textPtr->flags &= ~GOT_SELECTION;
                   1173: }
                   1174: 
                   1175: /*
                   1176:  *--------------------------------------------------------------
                   1177:  *
                   1178:  * TextMarkCmd --
                   1179:  *
                   1180:  *     This procedure is invoked to process the "mark" options of
                   1181:  *     the widget command for text widgets. See the user documentation
                   1182:  *     for details on what it does.
                   1183:  *
                   1184:  * Results:
                   1185:  *     A standard Tcl result.
                   1186:  *
                   1187:  * Side effects:
                   1188:  *     See the user documentation.
                   1189:  *
                   1190:  *--------------------------------------------------------------
                   1191:  */
                   1192: 
                   1193: static int
                   1194: TextMarkCmd(textPtr, interp, argc, argv)
                   1195:     register TkText *textPtr;  /* Information about text widget. */
                   1196:     Tcl_Interp *interp;                /* Current interpreter. */
                   1197:     int argc;                  /* Number of arguments. */
                   1198:     char **argv;               /* Argument strings.  Someone else has already
                   1199:                                 * parsed this command enough to know that
                   1200:                                 * argv[1] is "mark". */
                   1201: {
                   1202:     int length, line, ch, i;
                   1203:     char c;
                   1204:     Tcl_HashEntry *hPtr;
                   1205:     TkAnnotation *markPtr;
                   1206:     Tcl_HashSearch search;
                   1207: 
                   1208:     if (argc < 3) {
                   1209:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                   1210:                argv[0], " mark option ?arg arg ...?\"", (char *) NULL);
                   1211:        return TCL_ERROR;
                   1212:     }
                   1213:     c = argv[2][0];
                   1214:     length = strlen(argv[2]);
                   1215:     if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
                   1216:        if (argc != 3) {
                   1217:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                   1218:                    argv[0], " mark names\"", (char *) NULL);
                   1219:            return TCL_ERROR;
                   1220:        }
                   1221:        for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
                   1222:                hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
                   1223:            Tcl_AppendElement(interp,
                   1224:                    Tcl_GetHashKey(&textPtr->markTable, hPtr), 0);
                   1225:        }
                   1226:     } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
                   1227:        if (argc != 5) {
                   1228:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                   1229:                    argv[0], " mark set markName index\"", (char *) NULL);
                   1230:            return TCL_ERROR;
                   1231:        }
                   1232:        if (TkTextGetIndex(interp, textPtr, argv[4], &line, &ch) != TCL_OK) {
                   1233:            return TCL_ERROR;
                   1234:        }
                   1235:        TkTextSetMark(textPtr, argv[3], line, ch);
                   1236:     } else if ((c == 'u') && (strncmp(argv[2], "unset", length) == 0)) {
                   1237:        if (argc < 4) {
                   1238:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                   1239:                    argv[0], " mark unset markName ?markName ...?\"",
                   1240:                    (char *) NULL);
                   1241:            return TCL_ERROR;
                   1242:        }
                   1243:        for (i = 3; i < argc; i++) {
                   1244:            hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[i]);
                   1245:            if (hPtr != NULL) {
                   1246:                markPtr = (TkAnnotation *) Tcl_GetHashValue(hPtr);
                   1247:                if (markPtr == textPtr->insertAnnotPtr) {
                   1248:                    interp->result = "can't delete \"insert\" mark";
                   1249:                    return TCL_ERROR;
                   1250:                }
                   1251:                if (markPtr == textPtr->currentAnnotPtr) {
                   1252:                    interp->result = "can't delete \"current\" mark";
                   1253:                    return TCL_ERROR;
                   1254:                }
                   1255:                TkBTreeRemoveAnnotation(markPtr);
                   1256:                Tcl_DeleteHashEntry(hPtr);
                   1257:                ckfree((char *) markPtr);
                   1258:            }
                   1259:        }
                   1260:     } else {
                   1261:        Tcl_AppendResult(interp, "bad mark option \"", argv[2],
                   1262:                "\":  must be names, set, or unset",
                   1263:                (char *) NULL);
                   1264:        return TCL_ERROR;
                   1265:     }
                   1266:     return TCL_OK;
                   1267: }
                   1268: 
                   1269: /*
                   1270:  *----------------------------------------------------------------------
                   1271:  *
                   1272:  * TkTextSetMark --
                   1273:  *
                   1274:  *     Set a mark to a particular position, creating a new mark if
                   1275:  *     one doesn't already exist.
                   1276:  *
                   1277:  * Results:
                   1278:  *     The return value is a pointer to the mark that was just set.
                   1279:  *
                   1280:  * Side effects:
                   1281:  *     A new mark is created, or an existing mark is moved.
                   1282:  *
                   1283:  *----------------------------------------------------------------------
                   1284:  */
                   1285: 
                   1286: TkAnnotation *
                   1287: TkTextSetMark(textPtr, name, line, ch)
                   1288:     TkText *textPtr;           /* Text widget in which to create mark. */
                   1289:     char *name;                        /* Name of mark to set. */
                   1290:     int line;                  /* Index of line at which to place mark. */
                   1291:     int ch;                    /* Index of character within line at which
                   1292:                                 * to place mark. */
                   1293: {
                   1294:     Tcl_HashEntry *hPtr;
                   1295:     TkAnnotation *markPtr;
                   1296:     int new;
                   1297: 
                   1298:     hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
                   1299:     markPtr = (TkAnnotation *) Tcl_GetHashValue(hPtr);
                   1300:     if (!new) {
                   1301:        /*
                   1302:         * If this is the insertion point that's being moved, be sure
                   1303:         * to force a display update at the old position.
                   1304:         */
                   1305: 
                   1306:        if (markPtr == textPtr->insertAnnotPtr) {
                   1307:            int oldLine;
                   1308: 
                   1309:            oldLine = TkBTreeLineIndex(markPtr->linePtr);
                   1310:            TkTextLinesChanged(textPtr, oldLine, oldLine);
                   1311:        }
                   1312:        TkBTreeRemoveAnnotation(markPtr);
                   1313:     } else {
                   1314:        markPtr = (TkAnnotation *) ckalloc(sizeof(TkAnnotation));
                   1315:        markPtr->type = TK_ANNOT_MARK;
                   1316:        markPtr->info.hPtr = hPtr;
                   1317:        Tcl_SetHashValue(hPtr, markPtr);
                   1318:     }
                   1319:     if (line < 0) {
                   1320:        line = 0;
                   1321:        markPtr->ch = 0;
                   1322:     } else if (ch < 0) {
                   1323:        markPtr->ch = 0;
                   1324:     } else {
                   1325:        markPtr->ch = ch;
                   1326:     }
                   1327:     markPtr->linePtr = TkBTreeFindLine(textPtr->tree, line);
                   1328:     if (markPtr->linePtr == NULL) {
                   1329:        line = TkBTreeNumLines(textPtr->tree)-1;
                   1330:        markPtr->linePtr = TkBTreeFindLine(textPtr->tree, line);
                   1331:        markPtr->ch = markPtr->linePtr->numBytes-1;
                   1332:     } else {
                   1333:        if (markPtr->ch >= markPtr->linePtr->numBytes) {
                   1334:            TkTextLine *nextLinePtr;
                   1335: 
                   1336:            nextLinePtr = TkBTreeNextLine(markPtr->linePtr);
                   1337:            if (nextLinePtr == NULL) {
                   1338:                markPtr->ch = markPtr->linePtr->numBytes-1;
                   1339:            } else {
                   1340:                markPtr->linePtr = nextLinePtr;
                   1341:                line++;
                   1342:                markPtr->ch = 0;
                   1343:            }
                   1344:        }
                   1345:     }
                   1346:     TkBTreeAddAnnotation(markPtr);
                   1347: 
                   1348:     /*
                   1349:      * If the mark is the insertion cursor, then update the screen at the
                   1350:      * mark's new location.
                   1351:      */
                   1352: 
                   1353:     if (markPtr == textPtr->insertAnnotPtr) {
                   1354:        TkTextLinesChanged(textPtr, line, line);
                   1355:     }
                   1356:     return markPtr;
                   1357: }
                   1358: 
                   1359: /*
                   1360:  *----------------------------------------------------------------------
                   1361:  *
                   1362:  * TextBlinkProc --
                   1363:  *
                   1364:  *     This procedure is called as a timer handler to blink the
                   1365:  *     insertion cursor off and on.
                   1366:  *
                   1367:  * Results:
                   1368:  *     None.
                   1369:  *
                   1370:  * Side effects:
                   1371:  *     The cursor gets turned on or off, redisplay gets invoked,
                   1372:  *     and this procedure reschedules itself.
                   1373:  *
                   1374:  *----------------------------------------------------------------------
                   1375:  */
                   1376: 
                   1377: static void
                   1378: TextBlinkProc(clientData)
                   1379:     ClientData clientData;     /* Pointer to record describing text. */
                   1380: {
                   1381:     register TkText *textPtr = (TkText *) clientData;
                   1382:     int lineNum;
                   1383: 
                   1384:     if (!(textPtr->flags & GOT_FOCUS) || (textPtr->insertOffTime == 0)) {
                   1385:        return;
                   1386:     }
                   1387:     if (textPtr->flags & INSERT_ON) {
                   1388:        textPtr->flags &= ~INSERT_ON;
                   1389:        textPtr->insertBlinkHandler = Tk_CreateTimerHandler(
                   1390:                textPtr->insertOffTime, TextBlinkProc, (ClientData) textPtr);
                   1391:     } else {
                   1392:        textPtr->flags |= INSERT_ON;
                   1393:        textPtr->insertBlinkHandler = Tk_CreateTimerHandler(
                   1394:                textPtr->insertOnTime, TextBlinkProc, (ClientData) textPtr);
                   1395:     }
                   1396:     lineNum = TkBTreeLineIndex(textPtr->insertAnnotPtr->linePtr);
                   1397:     TkTextLinesChanged(textPtr, lineNum, lineNum);
                   1398: }
                   1399: 
                   1400: /*
                   1401:  *----------------------------------------------------------------------
                   1402:  *
                   1403:  * TextFocusProc --
                   1404:  *
                   1405:  *     This procedure is called whenever the entry gets or loses the
                   1406:  *     input focus.  It's also called whenever the window is reconfigured
                   1407:  *     while it has the focus.
                   1408:  *
                   1409:  * Results:
                   1410:  *     None.
                   1411:  *
                   1412:  * Side effects:
                   1413:  *     The cursor gets turned on or off.
                   1414:  *
                   1415:  *----------------------------------------------------------------------
                   1416:  */
                   1417: 
                   1418: static void
                   1419: TextFocusProc(clientData, gotFocus)
                   1420:     ClientData clientData;     /* Pointer to structure describing text. */
                   1421:     int gotFocus;              /* 1 means window is getting focus, 0 means
                   1422:                                 * it's losing it. */
                   1423: {
                   1424:     register TkText *textPtr = (TkText *) clientData;
                   1425:     int lineNum;
                   1426: 
                   1427:     Tk_DeleteTimerHandler(textPtr->insertBlinkHandler);
                   1428:     if (gotFocus) {
                   1429:        textPtr->flags |= GOT_FOCUS | INSERT_ON;
                   1430:        if (textPtr->insertOffTime != 0) {
                   1431:            textPtr->insertBlinkHandler = Tk_CreateTimerHandler(
                   1432:                    textPtr->insertOnTime, TextBlinkProc,
                   1433:                    (ClientData) textPtr);
                   1434:        }
                   1435:     } else {
                   1436:        textPtr->flags &= ~(GOT_FOCUS | INSERT_ON);
                   1437:        textPtr->insertBlinkHandler = (Tk_TimerToken) NULL;
                   1438:     }
                   1439:     lineNum = TkBTreeLineIndex(textPtr->insertAnnotPtr->linePtr);
                   1440:     TkTextLinesChanged(textPtr, lineNum, lineNum);
                   1441: }
                   1442: 
                   1443: /*
                   1444:  *--------------------------------------------------------------
                   1445:  *
                   1446:  * TextScanCmd --
                   1447:  *
                   1448:  *     This procedure is invoked to process the "scan" options of
                   1449:  *     the widget command for text widgets. See the user documentation
                   1450:  *     for details on what it does.
                   1451:  *
                   1452:  * Results:
                   1453:  *     A standard Tcl result.
                   1454:  *
                   1455:  * Side effects:
                   1456:  *     See the user documentation.
                   1457:  *
                   1458:  *--------------------------------------------------------------
                   1459:  */
                   1460: 
                   1461: static int
                   1462: TextScanCmd(textPtr, interp, argc, argv)
                   1463:     register TkText *textPtr;  /* Information about text widget. */
                   1464:     Tcl_Interp *interp;                /* Current interpreter. */
                   1465:     int argc;                  /* Number of arguments. */
                   1466:     char **argv;               /* Argument strings.  Someone else has already
                   1467:                                 * parsed this command enough to know that
                   1468:                                 * argv[1] is "tag". */
                   1469: {
                   1470:     int length, y, line, lastLine;
                   1471:     char c;
                   1472: 
                   1473:     if (argc != 4) {
                   1474:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                   1475:                argv[0], " scan mark|dragto y\"", (char *) NULL);
                   1476:        return TCL_ERROR;
                   1477:     }
                   1478:     if (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) {
                   1479:        return TCL_ERROR;
                   1480:     }
                   1481:     c = argv[2][0];
                   1482:     length = strlen(argv[2]);
                   1483:     if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
                   1484:        /*
                   1485:         * Amplify the difference between the current y position and the
                   1486:         * mark position to compute how many lines up or down the view
                   1487:         * should shift, then update the mark position to correspond to
                   1488:         * the new view.  If we run off the top or bottom of the text,
                   1489:         * reset the mark point so that the current position continues
                   1490:         * to correspond to the edge of the window.  This means that the
                   1491:         * picture will start dragging as soon as the mouse reverses
                   1492:         * direction (without this reset, might have to slide mouse a
                   1493:         * long ways back before the picture starts moving again).
                   1494:         */
                   1495: 
                   1496:        line = textPtr->scanMarkLine + (10*(textPtr->scanMarkY - y))
                   1497:                / (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
                   1498:        lastLine = TkBTreeNumLines(textPtr->tree) - 1;
                   1499:        if (line < 0) {
                   1500:            textPtr->scanMarkLine = line = 0;
                   1501:            textPtr->scanMarkY = y;
                   1502:        } else if (line > lastLine) {
                   1503:            textPtr->scanMarkLine = line = lastLine;
                   1504:            textPtr->scanMarkY = y;
                   1505:        }
                   1506:        TkTextSetView(textPtr, line, 0);
                   1507:     } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
                   1508:        textPtr->scanMarkLine = TkBTreeLineIndex(textPtr->topLinePtr);
                   1509:        textPtr->scanMarkY = y;
                   1510:     } else {
                   1511:        Tcl_AppendResult(interp, "bad scan option \"", argv[2],
                   1512:                "\":  must be mark or dragto", (char *) NULL);
                   1513:        return TCL_ERROR;
                   1514:     }
                   1515:     return TCL_OK;
                   1516: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.