Example #1
0
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;
}
Example #2
0
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);
    }
}