Exemplo n.º 1
0
int
TkTextTagCmd(
    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 "tag". */
{
    static const char *const tagOptionStrings[] = {
	"add", "bind", "cget", "configure", "delete", "lower", "names",
	"nextrange", "prevrange", "raise", "ranges", "remove", NULL
    };
    enum tagOptions {
	TAG_ADD, TAG_BIND, TAG_CGET, TAG_CONFIGURE, TAG_DELETE, TAG_LOWER,
	TAG_NAMES, TAG_NEXTRANGE, TAG_PREVRANGE, TAG_RAISE, TAG_RANGES,
	TAG_REMOVE
    };
    int optionIndex, i;
    register TkTextTag *tagPtr;
    TkTextIndex index1, index2;

    if (objc < 3) {
	Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?");
	return TCL_ERROR;
    }

    if (Tcl_GetIndexFromObjStruct(interp, objv[2], tagOptionStrings,
	    sizeof(char *), "tag option", 0, &optionIndex) != TCL_OK) {
	return TCL_ERROR;
    }

    switch ((enum tagOptions)optionIndex) {
    case TAG_ADD:
    case TAG_REMOVE: {
	int addTag;

	if (((enum tagOptions)optionIndex) == TAG_ADD) {
	    addTag = 1;
	} else {
	    addTag = 0;
	}
	if (objc < 5) {
	    Tcl_WrongNumArgs(interp, 3, objv,
		    "tagName index1 ?index2 index1 index2 ...?");
	    return TCL_ERROR;
	}
	tagPtr = TkTextCreateTag(textPtr, Tcl_GetString(objv[3]), NULL);
	if (tagPtr->elide) {
		/*
		* Indices are potentially obsolete after adding or removing
		* elided character ranges, especially indices having "display"
		* or "any" submodifier, therefore increase the epoch.
		*/
		textPtr->sharedTextPtr->stateEpoch++;
	}
	for (i = 4; i < objc; i += 2) {
	    if (TkTextGetObjIndex(interp, textPtr, objv[i],
		    &index1) != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (objc > (i+1)) {
		if (TkTextGetObjIndex(interp, textPtr, objv[i+1],
			&index2) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (TkTextIndexCmp(&index1, &index2) >= 0) {
		    return TCL_OK;
		}
	    } else {
		index2 = index1;
		TkTextIndexForwChars(NULL,&index2, 1, &index2, COUNT_INDICES);
	    }

	    if (tagPtr->affectsDisplay) {
		TkTextRedrawTag(textPtr->sharedTextPtr, NULL, &index1, &index2,
			tagPtr, !addTag);
	    } else {
		/*
		 * Still need to trigger enter/leave events on tags that have
		 * changed.
		 */

		TkTextEventuallyRepick(textPtr);
	    }
	    if (TkBTreeTag(&index1, &index2, tagPtr, addTag)) {
		/*
		 * If the tag is "sel", and we actually adjusted something
		 * then grab the selection if we're supposed to export it and
		 * don't already have it.
		 *
		 * Also, invalidate partially-completed selection retrievals.
		 * We only need to check whether the tag is "sel" for this
		 * textPtr (not for other peer widget's "sel" tags) because we
		 * cannot reach this code path with a different widget's "sel"
		 * tag.
		 */

		if (tagPtr == textPtr->selTagPtr) {
		    /*
		     * Send an event that the selection changed. This is
		     * equivalent to:
		     *	   event generate $textWidget <<Selection>>
		     */

		    TkTextSelectionEvent(textPtr);

		    if (addTag && textPtr->exportSelection
			    && !(textPtr->flags & GOT_SELECTION)) {
			Tk_OwnSelection(textPtr->tkwin, XA_PRIMARY,
				TkTextLostSelection, textPtr);
			textPtr->flags |= GOT_SELECTION;
		    }
		    textPtr->abortSelections = 1;
		}
	    }
	}
	break;
    }
    case TAG_BIND:
	if ((objc < 4) || (objc > 6)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName ?sequence? ?command?");
	    return TCL_ERROR;
	}
	tagPtr = TkTextCreateTag(textPtr, Tcl_GetString(objv[3]), NULL);

	/*
	 * Make a binding table if the widget doesn't already have one.
	 */

	if (textPtr->sharedTextPtr->bindingTable == NULL) {
	    textPtr->sharedTextPtr->bindingTable =
		    Tk_CreateBindingTable(interp);
	}

	if (objc == 6) {
	    int append = 0;
	    unsigned long mask;
	    const char *fifth = Tcl_GetString(objv[5]);

	    if (fifth[0] == 0) {
		return Tk_DeleteBinding(interp,
			textPtr->sharedTextPtr->bindingTable,
			(ClientData) tagPtr->name, Tcl_GetString(objv[4]));
	    }
	    if (fifth[0] == '+') {
		fifth++;
		append = 1;
	    }
	    mask = Tk_CreateBinding(interp,
		    textPtr->sharedTextPtr->bindingTable,
		    (ClientData) tagPtr->name, Tcl_GetString(objv[4]), fifth,
		    append);
	    if (mask == 0) {
		return TCL_ERROR;
	    }
	    if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
		    |Button2MotionMask|Button3MotionMask|Button4MotionMask
		    |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
		    |EnterWindowMask|LeaveWindowMask|KeyPressMask
		    |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
		Tk_DeleteBinding(interp, textPtr->sharedTextPtr->bindingTable,
			(ClientData) tagPtr->name, Tcl_GetString(objv[4]));
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"requested illegal events; only key, button, motion,"
			" enter, leave, and virtual events may be used", -1));
		Tcl_SetErrorCode(interp, "TK", "TEXT", "TAG_BIND_EVENT",NULL);
		return TCL_ERROR;
	    }
	} else if (objc == 5) {
	    const char *command;

	    command = Tk_GetBinding(interp,
		    textPtr->sharedTextPtr->bindingTable,
		    (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
	    if (command == NULL) {
		const char *string = Tcl_GetString(Tcl_GetObjResult(interp));

		/*
		 * Ignore missing binding errors. This is a special hack that
		 * relies on the error message returned by FindSequence in
		 * tkBind.c.
		 */

		if (string[0] != '\0') {
		    return TCL_ERROR;
		}
		Tcl_ResetResult(interp);
	    } else {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(command, -1));
	    }
	} else {
	    Tk_GetAllBindings(interp, textPtr->sharedTextPtr->bindingTable,
		    (ClientData) tagPtr->name);
	}
	break;
    case TAG_CGET:
	if (objc != 5) {
	    Tcl_WrongNumArgs(interp, 1, objv, "tag cget tagName option");
	    return TCL_ERROR;
	} else {
	    Tcl_Obj *objPtr;

	    tagPtr = FindTag(interp, textPtr, objv[3]);
	    if (tagPtr == NULL) {
		return TCL_ERROR;
	    }
	    objPtr = Tk_GetOptionValue(interp, (char *) tagPtr,
		    tagPtr->optionTable, objv[4], textPtr->tkwin);
	    if (objPtr == NULL) {
		return TCL_ERROR;
	    }
	    Tcl_SetObjResult(interp, objPtr);
	    return TCL_OK;
	}
	break;
    case TAG_CONFIGURE: {
	int newTag;

	if (objc < 4) {
	    Tcl_WrongNumArgs(interp, 3, objv,
		    "tagName ?-option? ?value? ?-option value ...?");
	    return TCL_ERROR;
	}
	tagPtr = TkTextCreateTag(textPtr, Tcl_GetString(objv[3]), &newTag);
	if (objc <= 5) {
	    Tcl_Obj *objPtr = Tk_GetOptionInfo(interp, (char *) tagPtr,
		    tagPtr->optionTable,
		    (objc == 5) ? objv[4] : NULL, textPtr->tkwin);

	    if (objPtr == NULL) {
		return TCL_ERROR;
	    }
	    Tcl_SetObjResult(interp, objPtr);
	    return TCL_OK;
	} else {
	    int result = TCL_OK;

	    if (Tk_SetOptions(interp, (char *) tagPtr, tagPtr->optionTable,
		    objc-4, objv+4, textPtr->tkwin, NULL, NULL) != TCL_OK) {
		return TCL_ERROR;
	    }

	    /*
	     * Some of the configuration options, like -underline and
	     * -justify, require additional translation (this is needed
	     * because we need to distinguish a particular value of an option
	     * from "unspecified").
	     */

	    if (tagPtr->borderWidth < 0) {
		tagPtr->borderWidth = 0;
	    }
	    if (tagPtr->reliefString != NULL) {
		if (Tk_GetRelief(interp, tagPtr->reliefString,
			&tagPtr->relief) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->justifyString != NULL) {
		if (Tk_GetJustify(interp, tagPtr->justifyString,
			&tagPtr->justify) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->lMargin1String != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->lMargin1String, &tagPtr->lMargin1) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->lMargin2String != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->lMargin2String, &tagPtr->lMargin2) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->offsetString != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin, tagPtr->offsetString,
			&tagPtr->offset) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->overstrikeString != NULL) {
		if (Tcl_GetBoolean(interp, tagPtr->overstrikeString,
			&tagPtr->overstrike) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->rMarginString != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->rMarginString, &tagPtr->rMargin) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->spacing1String != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->spacing1String, &tagPtr->spacing1) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (tagPtr->spacing1 < 0) {
		    tagPtr->spacing1 = 0;
		}
	    }
	    if (tagPtr->spacing2String != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->spacing2String, &tagPtr->spacing2) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (tagPtr->spacing2 < 0) {
		    tagPtr->spacing2 = 0;
		}
	    }
	    if (tagPtr->spacing3String != NULL) {
		if (Tk_GetPixels(interp, textPtr->tkwin,
			tagPtr->spacing3String, &tagPtr->spacing3) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (tagPtr->spacing3 < 0) {
		    tagPtr->spacing3 = 0;
		}
	    }
	    if (tagPtr->tabArrayPtr != NULL) {
		ckfree(tagPtr->tabArrayPtr);
		tagPtr->tabArrayPtr = NULL;
	    }
	    if (tagPtr->tabStringPtr != NULL) {
		tagPtr->tabArrayPtr =
			TkTextGetTabs(interp, textPtr, tagPtr->tabStringPtr);
		if (tagPtr->tabArrayPtr == NULL) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->underlineString != NULL) {
		if (Tcl_GetBoolean(interp, tagPtr->underlineString,
			&tagPtr->underline) != TCL_OK) {
		    return TCL_ERROR;
		}
	    }
	    if (tagPtr->elideString != NULL) {
		if (Tcl_GetBoolean(interp, tagPtr->elideString,
			&tagPtr->elide) != TCL_OK) {
		    return TCL_ERROR;
		}

		/*
		 * Indices are potentially obsolete after changing -elide,
		 * especially those computed with "display" or "any"
		 * submodifier, therefore increase the epoch.
		 */

		textPtr->sharedTextPtr->stateEpoch++;
	    }

	    /*
	     * If the "sel" tag was changed, be sure to mirror information
	     * from the tag back into the text widget record. NOTE: we don't
	     * have to free up information in the widget record before
	     * overwriting it, because it was mirrored in the tag and hence
	     * freed when the tag field was overwritten.
	     */

	    if (tagPtr == textPtr->selTagPtr) {
		textPtr->selBorder = tagPtr->border;
		textPtr->selBorderWidth = tagPtr->borderWidth;
		textPtr->selBorderWidthPtr = tagPtr->borderWidthPtr;
		textPtr->selFgColorPtr = tagPtr->fgColor;
	    }

	    tagPtr->affectsDisplay = 0;
	    tagPtr->affectsDisplayGeometry = 0;
	    if ((tagPtr->elideString != NULL)
		    || (tagPtr->tkfont != None)
		    || (tagPtr->justifyString != NULL)
		    || (tagPtr->lMargin1String != NULL)
		    || (tagPtr->lMargin2String != NULL)
		    || (tagPtr->offsetString != NULL)
		    || (tagPtr->rMarginString != NULL)
		    || (tagPtr->spacing1String != NULL)
		    || (tagPtr->spacing2String != NULL)
		    || (tagPtr->spacing3String != NULL)
		    || (tagPtr->tabStringPtr != NULL)
		    || (tagPtr->tabStyle != TK_TEXT_TABSTYLE_NONE)
		    || (tagPtr->wrapMode != TEXT_WRAPMODE_NULL)) {
		tagPtr->affectsDisplay = 1;
		tagPtr->affectsDisplayGeometry = 1;
	    }
	    if ((tagPtr->border != NULL)
		    || (tagPtr->reliefString != NULL)
		    || (tagPtr->bgStipple != None)
		    || (tagPtr->fgColor != NULL)
		    || (tagPtr->fgStipple != None)
		    || (tagPtr->overstrikeString != NULL)
		    || (tagPtr->underlineString != NULL)) {
		tagPtr->affectsDisplay = 1;
	    }
	    if (!newTag) {
		/*
		 * This line is not necessary if this is a new tag, since it
		 * can't possibly have been applied to anything yet.
		 */

		/*
		 * VMD: If this is the 'sel' tag, then we don't need to call
		 * this for all peers, unless we actually want to synchronize
		 * sel-style changes across the peers.
		 */

		TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
			NULL, NULL, tagPtr, 1);
	    }
	    return result;
	}
	break;
    }
    case TAG_DELETE: {
	Tcl_HashEntry *hPtr;

	if (objc < 4) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName ?tagName ...?");
	    return TCL_ERROR;
	}
	for (i = 3; i < objc; i++) {
	    hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->tagTable,
		    Tcl_GetString(objv[i]));
	    if (hPtr == NULL) {
		/*
		 * Either this tag doesn't exist or it's the 'sel' tag (which
		 * is not in the hash table). Either way we don't want to
		 * delete it.
		 */

		continue;
	    }
	    tagPtr = Tcl_GetHashValue(hPtr);
	    if (tagPtr == textPtr->selTagPtr) {
		continue;
	    }
	    if (tagPtr->affectsDisplay) {
		TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
			NULL, NULL, tagPtr, 1);
	    }
	    TkTextDeleteTag(textPtr, tagPtr);
	    Tcl_DeleteHashEntry(hPtr);
	}
	break;
    }
    case TAG_LOWER: {
	TkTextTag *tagPtr2;
	int prio;

	if ((objc != 4) && (objc != 5)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName ?belowThis?");
	    return TCL_ERROR;
	}
	tagPtr = FindTag(interp, textPtr, objv[3]);
	if (tagPtr == NULL) {
	    return TCL_ERROR;
	}
	if (objc == 5) {
	    tagPtr2 = FindTag(interp, textPtr, objv[4]);
	    if (tagPtr2 == NULL) {
		return TCL_ERROR;
	    }
	    if (tagPtr->priority < tagPtr2->priority) {
		prio = tagPtr2->priority - 1;
	    } else {
		prio = tagPtr2->priority;
	    }
	} else {
	    prio = 0;
	}
	ChangeTagPriority(textPtr, tagPtr, prio);

	/*
	 * If this is the 'sel' tag, then we don't actually need to call this
	 * for all peers.
	 */

	TkTextRedrawTag(textPtr->sharedTextPtr, NULL, NULL, NULL, tagPtr, 1);
	break;
    }
    case TAG_NAMES: {
	TkTextTag **arrayPtr;
	int arraySize;
	Tcl_Obj *listObj;

	if ((objc != 3) && (objc != 4)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "?index?");
	    return TCL_ERROR;
	}
	if (objc == 3) {
	    Tcl_HashSearch search;
	    Tcl_HashEntry *hPtr;

	    arrayPtr = ckalloc(textPtr->sharedTextPtr->numTags
		    * sizeof(TkTextTag *));
	    for (i=0, hPtr = Tcl_FirstHashEntry(
		    &textPtr->sharedTextPtr->tagTable, &search);
		    hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
		arrayPtr[i] = Tcl_GetHashValue(hPtr);
	    }

	    /*
	     * The 'sel' tag is not in the hash table.
	     */

	    arrayPtr[i] = textPtr->selTagPtr;
	    arraySize = ++i;
	} else {
	    if (TkTextGetObjIndex(interp, textPtr, objv[3],
		    &index1) != TCL_OK) {
		return TCL_ERROR;
	    }
	    arrayPtr = TkBTreeGetTags(&index1, textPtr, &arraySize);
	    if (arrayPtr == NULL) {
		return TCL_OK;
	    }
	}

	SortTags(arraySize, arrayPtr);
	listObj = Tcl_NewListObj(0, NULL);

	for (i = 0; i < arraySize; i++) {
	    tagPtr = arrayPtr[i];
	    Tcl_ListObjAppendElement(interp, listObj,
		    Tcl_NewStringObj(tagPtr->name,-1));
	}
	Tcl_SetObjResult(interp, listObj);
	ckfree(arrayPtr);
	break;
    }
    case TAG_NEXTRANGE: {
	TkTextIndex last;
	TkTextSearch tSearch;
	char position[TK_POS_CHARS];
	Tcl_Obj *resultObj;

	if ((objc != 5) && (objc != 6)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName index1 ?index2?");
	    return TCL_ERROR;
	}
	tagPtr = FindTag(NULL, textPtr, objv[3]);
	if (tagPtr == NULL) {
	    return TCL_OK;
	}
	if (TkTextGetObjIndex(interp, textPtr, objv[4], &index1) != TCL_OK) {
	    return TCL_ERROR;
	}
	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
		0, &last);
	if (objc == 5) {
	    index2 = last;
	} else if (TkTextGetObjIndex(interp, textPtr, objv[5],
		&index2) != TCL_OK) {
	    return TCL_ERROR;
	}

	/*
	 * The search below is a bit tricky. Rather than use the B-tree
	 * facilities to stop the search at index2, let it search up until the
	 * end of the file but check for a position past index2 ourselves.
	 * The reason for doing it this way is that we only care whether the
	 * *start* of the range is before index2; once we find the start, we
	 * don't want TkBTreeNextTag to abort the search because the end of
	 * the range is after index2.
	 */

	TkBTreeStartSearch(&index1, &last, tagPtr, &tSearch);
	if (TkBTreeCharTagged(&index1, tagPtr)) {
	    TkTextSegment *segPtr;
	    int offset;

	    /*
	     * The first character is tagged. See if there is an on-toggle
	     * just before the character. If not, then skip to the end of this
	     * tagged range.
	     */

	    for (segPtr = index1.linePtr->segPtr, offset = index1.byteIndex;
		    offset >= 0;
		    offset -= segPtr->size, segPtr = segPtr->nextPtr) {
		if ((offset == 0) && (segPtr->typePtr == &tkTextToggleOnType)
			&& (segPtr->body.toggle.tagPtr == tagPtr)) {
		    goto gotStart;
		}
	    }
	    if (!TkBTreeNextTag(&tSearch)) {
		return TCL_OK;
	    }
	}

	/*
	 * Find the start of the tagged range.
	 */

	if (!TkBTreeNextTag(&tSearch)) {
	    return TCL_OK;
	}

    gotStart:
	if (TkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
	    return TCL_OK;
	}
	resultObj = Tcl_NewObj();
	TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
	Tcl_ListObjAppendElement(NULL, resultObj,
		Tcl_NewStringObj(position, -1));
	TkBTreeNextTag(&tSearch);
	TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
	Tcl_ListObjAppendElement(NULL, resultObj,
		Tcl_NewStringObj(position, -1));
	Tcl_SetObjResult(interp, resultObj);
	break;
    }
    case TAG_PREVRANGE: {
	TkTextIndex last;
	TkTextSearch tSearch;
	char position1[TK_POS_CHARS];
	char position2[TK_POS_CHARS];
	Tcl_Obj *resultObj;

	if ((objc != 5) && (objc != 6)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName index1 ?index2?");
	    return TCL_ERROR;
	}
	tagPtr = FindTag(NULL, textPtr, objv[3]);
	if (tagPtr == NULL) {
	    return TCL_OK;
	}
	if (TkTextGetObjIndex(interp, textPtr, objv[4], &index1) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (objc == 5) {
	    TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0,
		    &index2);
	} else if (TkTextGetObjIndex(interp, textPtr, objv[5],
		&index2) != TCL_OK) {
	    return TCL_ERROR;
	}

	/*
	 * The search below is a bit weird. The previous toggle can be either
	 * an on or off toggle. If it is an on toggle, then we need to turn
	 * around and search forward for the end toggle. Otherwise we keep
	 * searching backwards.
	 */

	TkBTreeStartSearchBack(&index1, &index2, tagPtr, &tSearch);

	if (!TkBTreePrevTag(&tSearch)) {
	    /*
	     * Special case, there may be a tag off toggle at index1, and a
	     * tag on toggle before the start of a partial peer widget. In
	     * this case we missed it.
	     */

	    if (textPtr->start != NULL && (textPtr->start == index2.linePtr)
		    && (index2.byteIndex == 0)
		    && TkBTreeCharTagged(&index2, tagPtr)
		    && (TkTextIndexCmp(&index2, &index1) < 0)) {
		/*
		 * The first character is tagged, so just add the range from
		 * the first char to the start of the range.
		 */

		TkTextPrintIndex(textPtr, &index2, position1);
		TkTextPrintIndex(textPtr, &index1, position2);
		goto gotPrevIndexPair;
	    }
	    return TCL_OK;
	}

	if (tSearch.segPtr->typePtr == &tkTextToggleOnType) {
	    TkTextPrintIndex(textPtr, &tSearch.curIndex, position1);
	    if (textPtr->start != NULL) {
		/*
		 * Make sure the first index is not before the first allowed
		 * text index in this widget.
		 */

		TkTextIndex firstIndex;

		firstIndex.linePtr = textPtr->start;
		firstIndex.byteIndex = 0;
		firstIndex.textPtr = NULL;
		if (TkTextIndexCmp(&tSearch.curIndex, &firstIndex) < 0) {
		    if (TkTextIndexCmp(&firstIndex, &index1) >= 0) {
			/*
			 * But now the new first index is actually too far
			 * along in the text, so nothing is returned.
			 */

			return TCL_OK;
		    }
		    TkTextPrintIndex(textPtr, &firstIndex, position1);
		}
	    }
	    TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
		    TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
		    0, &last);
	    TkBTreeStartSearch(&tSearch.curIndex, &last, tagPtr, &tSearch);
	    TkBTreeNextTag(&tSearch);
	    TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
	} else {
	    TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
	    TkBTreePrevTag(&tSearch);
	    TkTextPrintIndex(textPtr, &tSearch.curIndex, position1);
	    if (TkTextIndexCmp(&tSearch.curIndex, &index2) < 0) {
		if (textPtr->start != NULL && index2.linePtr == textPtr->start
			&& index2.byteIndex == 0) {
		    /* It's ok */
		    TkTextPrintIndex(textPtr, &index2, position1);
		} else {
		    return TCL_OK;
		}
	    }
	}

    gotPrevIndexPair:
	resultObj = Tcl_NewObj();
	Tcl_ListObjAppendElement(NULL, resultObj,
		Tcl_NewStringObj(position1, -1));
	Tcl_ListObjAppendElement(NULL, resultObj,
		Tcl_NewStringObj(position2, -1));
	Tcl_SetObjResult(interp, resultObj);
	break;
    }
    case TAG_RAISE: {
	TkTextTag *tagPtr2;
	int prio;

	if ((objc != 4) && (objc != 5)) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName ?aboveThis?");
	    return TCL_ERROR;
	}
	tagPtr = FindTag(interp, textPtr, objv[3]);
	if (tagPtr == NULL) {
	    return TCL_ERROR;
	}
	if (objc == 5) {
	    tagPtr2 = FindTag(interp, textPtr, objv[4]);
	    if (tagPtr2 == NULL) {
		return TCL_ERROR;
	    }
	    if (tagPtr->priority <= tagPtr2->priority) {
		prio = tagPtr2->priority;
	    } else {
		prio = tagPtr2->priority + 1;
	    }
	} else {
	    prio = textPtr->sharedTextPtr->numTags-1;
	}
	ChangeTagPriority(textPtr, tagPtr, prio);

	/*
	 * If this is the 'sel' tag, then we don't actually need to call this
	 * for all peers.
	 */

	TkTextRedrawTag(textPtr->sharedTextPtr, NULL, NULL, NULL, tagPtr, 1);
	break;
    }
    case TAG_RANGES: {
	TkTextIndex first, last;
	TkTextSearch tSearch;
	Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
	int count = 0;

	if (objc != 4) {
	    Tcl_WrongNumArgs(interp, 3, objv, "tagName");
	    return TCL_ERROR;
	}
	tagPtr = FindTag(NULL, textPtr, objv[3]);
	if (tagPtr == NULL) {
	    return TCL_OK;
	}
	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0,
		&first);
	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
		0, &last);
	TkBTreeStartSearch(&first, &last, tagPtr, &tSearch);
	if (TkBTreeCharTagged(&first, tagPtr)) {
	    Tcl_ListObjAppendElement(NULL, listObj,
		    TkTextNewIndexObj(textPtr, &first));
	    count++;
	}
	while (TkBTreeNextTag(&tSearch)) {
	    Tcl_ListObjAppendElement(NULL, listObj,
		    TkTextNewIndexObj(textPtr, &tSearch.curIndex));
	    count++;
	}
	if (count % 2 == 1) {
	    /*
	     * If a text widget uses '-end', it won't necessarily run to the
	     * end of the B-tree, and therefore the tag range might not be
	     * closed. In this case we add the end of the range.
	     */

	    Tcl_ListObjAppendElement(NULL, listObj,
		    TkTextNewIndexObj(textPtr, &last));
	}
	Tcl_SetObjResult(interp, listObj);
	break;
    }
    }
    return TCL_OK;
}
Exemplo n.º 2
0
int Sn_Syntax_Highlight(ClientData clientData, Tcl_Interp * interp, int argc, char **argv)
{
	register TkText *textPtr = NULL;
	Tcl_CmdInfo infoPtr;
	int result = TCL_OK;
	int del = 0;
	char *argv0 = argv[0];
	int wargc;
	char *wargv[10];
	char *language;
	char *idx1;
	char *idx2;
	char *text_widget;
	char endpos[TK_POS_CHARS];
	char begpos[TK_POS_CHARS];
	int val;
	int (*high_func) () = NULL;
	void (*flush_lex_scanner) () = NULL;
	void (*high_init_func) (int ms, int lineno, int charno, void (**func) (), void *idx1, void *idx2) = NULL;
	Tcl_CmdProc *text_proc;
	int update = 0;
	TkTextIndex lex_index1;
	TkTextIndex lex_index2;
	TkTextIndex lex_end_index;
	char prev_begpos[TK_POS_CHARS];
	int lex_buf_size = 0;

	eos_read = 0;

	while (argc > 1 && argv[1][0] == '-') {
		if(strncmp(argv[1], "-delete", 5) == 0) {
			del = 1;
			argc--;
			argv++;
		} else if(strncmp(argv[1], "-delall", 5) == 0) {
			del = -1;
			argc--;
			argv++;
		} else if(strncmp(argv[1], "-update", 4) == 0) {
			lex_buf_size = 256;	/* It is probably not necessary to fill */
			/* the complete lex buffer. */
			update = 1;
			argc--;
			argv++;
		} else
			break;
	}

	if(argc != 4 && argc != 5) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv0, " ?-delete? ?-delall? language text_widget index1 ?index2?", NULL);

		return TCL_ERROR;
	}

	language = argv[1];
	text_widget = argv[2];
	idx1 = argv[3];
	idx2 = argc == 5 ? argv[4] : idx1;

	LOGGER((LOGFP, "highlight lang: <%s> text: <%s> idx1: <%s> idx2: <%s>\n", language, text_widget, idx1, idx2));
	if(strcmp(language, "tcl") == 0) {
		high_func = tcl_highlight_lex;
		high_init_func = tcl_highlight_init_func;
	} else if(strcmp(language, "c++") == 0) {
		high_func = c_highlight_lex;
		high_init_func = c_highlight_init_func;
	} else if(strcmp(language, "java") == 0) {
		high_func = java_highlight_lex;
		high_init_func = java_highlight_init_func;
	} else if(strcmp(language, "chill") == 0) {
		high_func = ch_highlight_lex;
		high_init_func = ch_highlight_init_func;
	} else if(strcmp(language, "verilog") == 0) {
		high_func = verilog_highlight_lex;
		high_init_func = verilog_highlight_init_func;
	} else if(strcmp(language, "python") == 0) {
		high_func = py_highlight_lex;
		high_init_func = py_highlight_init_func;
	}

	if(!high_func) {
		return TCL_OK;
	}

	if(!Tcl_GetCommandInfo(interp, text_widget, &infoPtr)) {
		Tcl_AppendResult(interp, "wrong # \"", text_widget, "\" does not exist", (char *) NULL);
		return TCL_ERROR;
	}

	textPtr = (TkText *) infoPtr.clientData;
	Tcl_Preserve((ClientData) textPtr);
	text_proc = (Tcl_CmdProc *) infoPtr.proc;

	if(del) {
		remove_tag(textPtr, interp, text_widget, del, idx1, idx2);
	}

	TkTextGetIndex(interp, textPtr, "end", &lex_end_index);

	if(TkTextGetIndex(interp, textPtr, idx1, &lex_index1) != TCL_OK || TkTextGetIndex(interp, textPtr, idx2, &lex_index2) != TCL_OK) {
		result = TCL_ERROR;
		goto done;
	}
	if(TkTextIndexCmp(&lex_index1, &lex_index2) >= 0) {
		result = TCL_OK;
		goto done;
	}
	high_init_func(lex_buf_size, TkBTreeLineIndex(lex_index1.linePtr) + 1,
		       lex_index1.CHARINDEX, &flush_lex_scanner, (void *) &lex_index1, (void *) &lex_end_index);

	wargc = 0;
	wargv[wargc++] = text_widget;
	wargv[wargc++] = "tag";
	wargv[wargc++] = "add";
	wargv[wargc++] = NULL;
	wargv[wargc++] = begpos;
	wargv[wargc++] = endpos;

	strcpy(prev_begpos, idx1);
	while ((val = (*high_func) ()) != 0) {
		sprintf(begpos, "%d.%d", paf_high_pos.beg_lineno, paf_high_pos.beg_charno);
		sprintf(endpos, "%d.%d", paf_high_pos.end_lineno, paf_high_pos.end_charno);
		wargv[3] = high_tag_names[val];

		if(update) {
			int tag_argc = 0;
			char *tag_argv[10];
			char *p_end;
			int cou;
			int tag_off;

			tag_argv[tag_argc++] = text_widget;
			tag_argv[tag_argc++] = "tag";
			tag_argv[tag_argc++] = "prevrange";
			tag_off = tag_argc;
			tag_argv[tag_argc++] = NULL;
			tag_argv[tag_argc++] = endpos;
			for (cou = 1; cou <= PAF_HIGH_STRING; cou++) {
				tag_argv[tag_off] = high_tag_names[cou];
				Tcl_ResetResult(interp);
				TkTextTagCmd(textPtr, interp, tag_argc, tag_argv);

				p_end = strchr(interp->result, ' ');
				/* Check whether it is synchronized ! */
				if(p_end && strcmp(p_end + 1, endpos) == 0) {
					eos_read = 1;
					/* It will get flex recognized that no further
					 * input is available. */
					flush_lex_scanner();
					break;
				}
			}
			LOGGER((LOGFP, "Add %s %d %s %s\n", high_tag_names[val], val, begpos, endpos));
		}
		if(update) {
			int tag_argc = 0;
			char *tag_argv[10];
			int cou;
			int tag_off;

			tag_argv[tag_argc++] = text_widget;
			tag_argv[tag_argc++] = "tag";
			tag_argv[tag_argc++] = "remove";
			tag_off = tag_argc;
			tag_argv[tag_argc++] = NULL;
			tag_argv[tag_argc++] = prev_begpos;
			tag_argv[tag_argc++] = endpos;

			for (cou = 1; cou <= PAF_HIGH_STRING; cou++) {
				tag_argv[tag_off] = high_tag_names[cou];
				TkTextTagCmd(textPtr, interp, tag_argc, tag_argv);
			}
			strcpy(prev_begpos, endpos);
		}
		TkTextTagCmd(textPtr, interp, wargc, wargv);
	}
done:
	Tcl_Release((ClientData) textPtr);

	return result;
}