TkTextSegment * TkTextSetMark( TkText *textPtr, /* Text widget in which to create mark. */ const char *name, /* Name of mark to set. */ TkTextIndex *indexPtr) /* Where to set mark. */ { Tcl_HashEntry *hPtr = NULL; TkTextSegment *markPtr; TkTextIndex insertIndex; int isNew, widgetSpecific; if (!strcmp(name, "insert")) { widgetSpecific = 1; markPtr = textPtr->insertMarkPtr; isNew = (markPtr == NULL ? 1 : 0); } else if (!strcmp(name, "current")) { widgetSpecific = 2; markPtr = textPtr->currentMarkPtr; isNew = (markPtr == NULL ? 1 : 0); } else { widgetSpecific = 0; hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name, &isNew); markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); } if (!isNew) { /* * If this is the insertion point that's being moved, be sure to force * a display update at the old position. Also, don't let the insertion * cursor be after the final newline of the file. */ if (markPtr == textPtr->insertMarkPtr) { TkTextIndex index, index2; TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); TkTextIndexForwChars(NULL,&index, 1, &index2, COUNT_INDICES); /* * While we wish to redisplay, no heights have changed, so no need * to call TkTextInvalidateLineMetrics. */ TkTextChanged(NULL, textPtr, &index, &index2); if (TkBTreeLinesTo(textPtr, indexPtr->linePtr) == TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) { TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex, COUNT_INDICES); indexPtr = &insertIndex; } } TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); } else { markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE); markPtr->typePtr = &tkTextRightMarkType; markPtr->size = 0; markPtr->body.mark.textPtr = textPtr; markPtr->body.mark.linePtr = indexPtr->linePtr; markPtr->body.mark.hPtr = hPtr; if (widgetSpecific == 0) { Tcl_SetHashValue(hPtr, markPtr); } else if (widgetSpecific == 1) { textPtr->insertMarkPtr = markPtr; } else { textPtr->currentMarkPtr = markPtr; } } TkBTreeLinkSegment(markPtr, indexPtr); /* * If the mark is the insertion cursor, then update the screen at the * mark's new location. */ if (markPtr == textPtr->insertMarkPtr) { TkTextIndex index2; TkTextIndexForwChars(NULL,indexPtr, 1, &index2, COUNT_INDICES); /* * While we wish to redisplay, no heights have changed, so no need to * call TkTextInvalidateLineMetrics */ TkTextChanged(NULL, textPtr, indexPtr, &index2); } return markPtr; }
int TkTextMarkCmd( register TkText *textPtr, /* Information about text widget. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. Someone else has already * parsed this command enough to know that * objv[1] is "mark". */ { Tcl_HashEntry *hPtr; TkTextSegment *markPtr; Tcl_HashSearch search; TkTextIndex index; const Tk_SegType *newTypePtr; int optionIndex; static const char *markOptionStrings[] = { "gravity", "names", "next", "previous", "set", "unset", NULL }; enum markOptions { MARK_GRAVITY, MARK_NAMES, MARK_NEXT, MARK_PREVIOUS, MARK_SET, MARK_UNSET }; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], markOptionStrings, "mark option", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum markOptions) optionIndex) { case MARK_GRAVITY: { char c; int length; char *str; if (objc < 4 || objc > 5) { Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?"); return TCL_ERROR; } str = Tcl_GetStringFromObj(objv[3],&length); if (length == 6 && !strcmp(str, "insert")) { markPtr = textPtr->insertMarkPtr; } else if (length == 7 && !strcmp(str, "current")) { markPtr = textPtr->currentMarkPtr; } else { hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str); if (hPtr == NULL) { Tcl_AppendResult(interp, "there is no mark named \"", Tcl_GetString(objv[3]), "\"", NULL); return TCL_ERROR; } markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); } if (objc == 4) { if (markPtr->typePtr == &tkTextRightMarkType) { Tcl_SetResult(interp, "right", TCL_STATIC); } else { Tcl_SetResult(interp, "left", TCL_STATIC); } return TCL_OK; } str = Tcl_GetStringFromObj(objv[4],&length); c = str[0]; if ((c == 'l') && (strncmp(str, "left", (unsigned)length) == 0)) { newTypePtr = &tkTextLeftMarkType; } else if ((c == 'r') && (strncmp(str, "right", (unsigned)length) == 0)) { newTypePtr = &tkTextRightMarkType; } else { Tcl_AppendResult(interp, "bad mark gravity \"", str, "\": must be left or right", NULL); return TCL_ERROR; } TkTextMarkSegToIndex(textPtr, markPtr, &index); TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); markPtr->typePtr = newTypePtr; TkBTreeLinkSegment(markPtr, &index); break; } case MARK_NAMES: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } Tcl_AppendElement(interp, "insert"); Tcl_AppendElement(interp, "current"); for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr)); } break; case MARK_NEXT: if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index"); return TCL_ERROR; } return MarkFindNext(interp, textPtr, Tcl_GetString(objv[3])); case MARK_PREVIOUS: if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "index"); return TCL_ERROR; } return MarkFindPrev(interp, textPtr, Tcl_GetString(objv[3])); case MARK_SET: if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "markName index"); return TCL_ERROR; } if (TkTextGetObjIndex(interp, textPtr, objv[4], &index) != TCL_OK) { return TCL_ERROR; } TkTextSetMark(textPtr, Tcl_GetString(objv[3]), &index); return TCL_OK; case MARK_UNSET: { int i; for (i = 3; i < objc; i++) { hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, Tcl_GetString(objv[i])); if (hPtr != NULL) { markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); /* * Special case not needed with peer widgets. */ if ((markPtr == textPtr->insertMarkPtr) || (markPtr == textPtr->currentMarkPtr)) { continue; } TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); Tcl_DeleteHashEntry(hPtr); ckfree((char *) markPtr); } } break; } } return TCL_OK; }
int TkTextWindowCmd( register TkText *textPtr, /* Information about text widget. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. Someone else has already * parsed this command enough to know that * objv[1] is "window". */ { int optionIndex; static const char *const windOptionStrings[] = { "cget", "configure", "create", "names", NULL }; enum windOptions { WIND_CGET, WIND_CONFIGURE, WIND_CREATE, WIND_NAMES }; register TkTextSegment *ewPtr; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], windOptionStrings, "window option", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum windOptions) optionIndex) { case WIND_CGET: { TkTextIndex index; TkTextSegment *ewPtr; Tcl_Obj *objPtr; TkTextEmbWindowClient *client; if (objc != 5) { Tcl_WrongNumArgs(interp, 3, objv, "index option"); return TCL_ERROR; } if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { return TCL_ERROR; } ewPtr = TkTextIndexToSeg(&index, NULL); if (ewPtr->typePtr != &tkTextEmbWindowType) { Tcl_AppendResult(interp, "no embedded window at index \"", Tcl_GetString(objv[3]), "\"", NULL); return TCL_ERROR; } /* * Copy over client specific value before querying. */ client = EmbWinGetClient(textPtr, ewPtr); if (client != NULL) { ewPtr->body.ew.tkwin = client->tkwin; } else { ewPtr->body.ew.tkwin = NULL; } objPtr = Tk_GetOptionValue(interp, (char *) &ewPtr->body.ew, ewPtr->body.ew.optionTable, objv[4], textPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } Tcl_SetObjResult(interp, objPtr); return TCL_OK; } case WIND_CONFIGURE: { TkTextIndex index; TkTextSegment *ewPtr; if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "index ?-option value ...?"); return TCL_ERROR; } if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { return TCL_ERROR; } ewPtr = TkTextIndexToSeg(&index, NULL); if (ewPtr->typePtr != &tkTextEmbWindowType) { Tcl_AppendResult(interp, "no embedded window at index \"", Tcl_GetString(objv[3]), "\"", NULL); return TCL_ERROR; } if (objc <= 5) { TkTextEmbWindowClient *client; Tcl_Obj *objPtr; /* * Copy over client specific value before querying. */ client = EmbWinGetClient(textPtr, ewPtr); if (client != NULL) { ewPtr->body.ew.tkwin = client->tkwin; } else { ewPtr->body.ew.tkwin = NULL; } objPtr = Tk_GetOptionInfo(interp, (char *) &ewPtr->body.ew, ewPtr->body.ew.optionTable, (objc == 5) ? objv[4] : NULL, textPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } Tcl_SetObjResult(interp, objPtr); return TCL_OK; } else { TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index); /* * It's probably not true that all window configuration can change * the line height, so we could be more efficient here and only * call this when necessary. */ TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL, index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY); return EmbWinConfigure(textPtr, ewPtr, objc-4, objv+4); } } case WIND_CREATE: { TkTextIndex index; int lineIndex; TkTextEmbWindowClient *client; int res; /* * Add a new window. Find where to put the new window, and mark that * position for redisplay. */ if (objc < 4) { Tcl_WrongNumArgs(interp, 3, objv, "index ?-option value ...?"); return TCL_ERROR; } if (TkTextGetObjIndex(interp, textPtr, objv[3], &index) != TCL_OK) { return TCL_ERROR; } /* * Don't allow insertions on the last (dummy) line of the text. */ lineIndex = TkBTreeLinesTo(textPtr, index.linePtr); if (lineIndex == TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) { lineIndex--; TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, lineIndex, 1000000, &index); } /* * Create the new window segment and initialize it. */ ewPtr = (TkTextSegment *) ckalloc(EW_SEG_SIZE); ewPtr->typePtr = &tkTextEmbWindowType; ewPtr->size = 1; ewPtr->body.ew.sharedTextPtr = textPtr->sharedTextPtr; ewPtr->body.ew.linePtr = NULL; ewPtr->body.ew.tkwin = NULL; ewPtr->body.ew.create = NULL; ewPtr->body.ew.align = ALIGN_CENTER; ewPtr->body.ew.padX = ewPtr->body.ew.padY = 0; ewPtr->body.ew.stretch = 0; ewPtr->body.ew.optionTable = Tk_CreateOptionTable(interp, optionSpecs); client = (TkTextEmbWindowClient *) ckalloc(sizeof(TkTextEmbWindowClient)); client->next = NULL; client->textPtr = textPtr; client->tkwin = NULL; client->chunkCount = 0; client->displayed = 0; client->parent = ewPtr; ewPtr->body.ew.clients = client; /* * Link the segment into the text widget, then configure it (delete it * again if the configuration fails). */ TkTextChanged(textPtr->sharedTextPtr, NULL, &index, &index); TkBTreeLinkSegment(ewPtr, &index); res = EmbWinConfigure(textPtr, ewPtr, objc-4, objv+4); client->tkwin = ewPtr->body.ew.tkwin; if (res != TCL_OK) { TkTextIndex index2; TkTextIndexForwChars(NULL, &index, 1, &index2, COUNT_INDICES); TkBTreeDeleteIndexRange(textPtr->sharedTextPtr->tree, &index, &index2); return TCL_ERROR; } TkTextInvalidateLineMetrics(textPtr->sharedTextPtr, NULL, index.linePtr, 0, TK_TEXT_INVALIDATE_ONLY); break; } case WIND_NAMES: { Tcl_HashSearch search; Tcl_HashEntry *hPtr; if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->windowTable, &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr)); } break; } } return TCL_OK; }