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; }
void TkTextPickCurrent( register TkText *textPtr, /* Text widget in which to select current * character. */ XEvent *eventPtr) /* Event describing location of mouse cursor. * Must be EnterWindow, LeaveWindow, * ButtonRelease, or MotionNotify. */ { TkTextIndex index; TkTextTag **oldArrayPtr, **newArrayPtr; TkTextTag **copyArrayPtr = NULL; /* Initialization needed to prevent compiler * warning. */ int numOldTags, numNewTags, i, j, size, nearby; XEvent event; /* * If a button is down, then don't do anything at all; we'll be called * again when all buttons are up, and we can repick then. This implements * a form of mouse grabbing. */ if (textPtr->flags & BUTTON_DOWN) { if (((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) && ((eventPtr->xcrossing.mode == NotifyGrab) || (eventPtr->xcrossing.mode == NotifyUngrab))) { /* * Special case: the window is being entered or left because of a * grab or ungrab. In this case, repick after all. Furthermore, * clear BUTTON_DOWN to release the simulated grab. */ textPtr->flags &= ~BUTTON_DOWN; } else { return; } } /* * Save information about this event in the widget in case we have to * synthesize more enter and leave events later (e.g. because a character * was deleted, causing a new character to be underneath the mouse * cursor). Also translate MotionNotify events into EnterNotify events, * since that's what gets reported to event handlers when the current * character changes. */ if (eventPtr != &textPtr->pickEvent) { if ((eventPtr->type == MotionNotify) || (eventPtr->type == ButtonRelease)) { textPtr->pickEvent.xcrossing.type = EnterNotify; textPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial; textPtr->pickEvent.xcrossing.send_event = eventPtr->xmotion.send_event; textPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display; textPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window; textPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root; textPtr->pickEvent.xcrossing.subwindow = None; textPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time; textPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x; textPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y; textPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root; textPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root; textPtr->pickEvent.xcrossing.mode = NotifyNormal; textPtr->pickEvent.xcrossing.detail = NotifyNonlinear; textPtr->pickEvent.xcrossing.same_screen = eventPtr->xmotion.same_screen; textPtr->pickEvent.xcrossing.focus = False; textPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state; } else { textPtr->pickEvent = *eventPtr; } } /* * Find the new current character, then find and sort all of the tags * associated with it. */ if (textPtr->pickEvent.type != LeaveNotify) { TkTextPixelIndex(textPtr, textPtr->pickEvent.xcrossing.x, textPtr->pickEvent.xcrossing.y, &index, &nearby); if (nearby) { newArrayPtr = NULL; numNewTags = 0; } else { newArrayPtr = TkBTreeGetTags(&index, textPtr, &numNewTags); SortTags(numNewTags, newArrayPtr); } } else { newArrayPtr = NULL; numNewTags = 0; } /* * Resort the tags associated with the previous marked character (the * priorities might have changed), then make a copy of the new tags, and * compare the old tags to the copy, nullifying any tags that are present * in both groups (i.e. the tags that haven't changed). */ SortTags(textPtr->numCurTags, textPtr->curTagArrayPtr); if (numNewTags > 0) { size = numNewTags * sizeof(TkTextTag *); copyArrayPtr = ckalloc(size); memcpy(copyArrayPtr, newArrayPtr, (size_t) size); for (i = 0; i < textPtr->numCurTags; i++) { for (j = 0; j < numNewTags; j++) { if (textPtr->curTagArrayPtr[i] == copyArrayPtr[j]) { textPtr->curTagArrayPtr[i] = NULL; copyArrayPtr[j] = NULL; break; } } } } /* * Invoke the binding system with a LeaveNotify event for all of the tags * that have gone away. We have to be careful here, because it's possible * that the binding could do something (like calling tkwait) that * eventually modifies textPtr->curTagArrayPtr. To avoid problems in * situations like this, update curTagArrayPtr to its new value before * invoking any bindings, and don't use it any more here. */ numOldTags = textPtr->numCurTags; textPtr->numCurTags = numNewTags; oldArrayPtr = textPtr->curTagArrayPtr; textPtr->curTagArrayPtr = newArrayPtr; if (numOldTags != 0) { if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL) && !(textPtr->flags & DESTROYED)) { event = textPtr->pickEvent; event.type = LeaveNotify; /* * Always use a detail of NotifyAncestor. Besides being * consistent, this avoids problems where the binding code will * discard NotifyInferior events. */ event.xcrossing.detail = NotifyAncestor; TagBindEvent(textPtr, &event, numOldTags, oldArrayPtr); } ckfree(oldArrayPtr); } /* * Reset the "current" mark (be careful to recompute its location, since * it might have changed during an event binding). Then invoke the binding * system with an EnterNotify event for all of the tags that have just * appeared. */ TkTextPixelIndex(textPtr, textPtr->pickEvent.xcrossing.x, textPtr->pickEvent.xcrossing.y, &index, &nearby); TkTextSetMark(textPtr, "current", &index); if (numNewTags != 0) { if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL) && !(textPtr->flags & DESTROYED) && !nearby) { event = textPtr->pickEvent; event.type = EnterNotify; event.xcrossing.detail = NotifyAncestor; TagBindEvent(textPtr, &event, numNewTags, copyArrayPtr); } ckfree(copyArrayPtr); } }