summaryrefslogtreecommitdiffstats
path: root/generic/tkTextTag.c
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2004-09-10 12:13:38 (GMT)
committervincentdarley <vincentdarley>2004-09-10 12:13:38 (GMT)
commit09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9 (patch)
treec17ff6a17da4273024607033b6c1bd7bf35d2d8f /generic/tkTextTag.c
parent77f2c1e62ab0760dc6ee615d6bbcb81b11d76a6f (diff)
downloadtk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.zip
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.gz
tk-09324dada308a84a1d5ba8b14bff2a5ce8b6eaf9.tar.bz2
text widget 'peer' subcommand -- TIP#169 implementation
Diffstat (limited to 'generic/tkTextTag.c')
-rw-r--r--generic/tkTextTag.c486
1 files changed, 364 insertions, 122 deletions
diff --git a/generic/tkTextTag.c b/generic/tkTextTag.c
index b862d1b..41e61b1 100644
--- a/generic/tkTextTag.c
+++ b/generic/tkTextTag.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextTag.c,v 1.17 2003/12/10 12:57:59 vincentdarley Exp $
+ * RCS: @(#) $Id: tkTextTag.c,v 1.18 2004/09/10 12:13:42 vincentdarley Exp $
*/
#include "default.h"
@@ -91,6 +91,9 @@ static void SortTags _ANSI_ARGS_((int numTags,
TkTextTag **tagArrayPtr));
static int TagSortProc _ANSI_ARGS_((CONST VOID *first,
CONST VOID *second));
+static void TagBindEvent _ANSI_ARGS_((TkText *textPtr, XEvent *eventPtr,
+ int numTags, TkTextTag **tagArrayPtr));
+
/*
*--------------------------------------------------------------
@@ -134,7 +137,7 @@ TkTextTagCmd(textPtr, interp, objc, objv)
int i;
register TkTextTag *tagPtr;
- TkTextIndex first, last, index1, index2;
+ TkTextIndex index1, index2;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
@@ -181,7 +184,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
}
if (tagPtr->affectsDisplay) {
- TkTextRedrawTag(textPtr, &index1, &index2, tagPtr, !addTag);
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ &index1, &index2, tagPtr, !addTag);
} else {
/*
* Still need to trigger enter/leave events on tags that
@@ -195,28 +199,22 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* 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.
+ * 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) {
- XEvent event;
/*
* Send an event that the selection changed.
* This is equivalent to
* "event generate $textWidget <<Selection>>"
*/
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial =
- NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name =
- Tk_GetUid("Selection");
- Tk_HandleEvent(&event);
+ TkTextSelectionEvent(textPtr);
if (addTag && textPtr->exportSelection
&& !(textPtr->flags & GOT_SELECTION)) {
@@ -243,8 +241,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* one.
*/
- if (textPtr->bindingTable == NULL) {
- textPtr->bindingTable = Tk_CreateBindingTable(interp);
+ if (textPtr->sharedTextPtr->bindingTable == NULL) {
+ textPtr->sharedTextPtr->bindingTable = Tk_CreateBindingTable(interp);
}
if (objc == 6) {
@@ -253,15 +251,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
char *fifth = Tcl_GetString(objv[5]);
if (fifth[0] == 0) {
- return Tk_DeleteBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ 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->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]),
+ mask = Tk_CreateBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]),
fifth, append);
if (mask == 0) {
return TCL_ERROR;
@@ -271,8 +269,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
|KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
- Tk_DeleteBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ Tk_DeleteBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "requested illegal events; ",
"only key, button, motion, enter, leave, and virtual ",
@@ -282,8 +280,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
} else if (objc == 5) {
CONST char *command;
- command = Tk_GetBinding(interp, textPtr->bindingTable,
- (ClientData) tagPtr, Tcl_GetString(objv[4]));
+ command = Tk_GetBinding(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name, Tcl_GetString(objv[4]));
if (command == NULL) {
CONST char *string = Tcl_GetStringResult(interp);
@@ -302,8 +300,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
Tcl_SetResult(interp, (char *) command, TCL_STATIC);
}
} else {
- Tk_GetAllBindings(interp, textPtr->bindingTable,
- (ClientData) tagPtr);
+ Tk_GetAllBindings(interp, textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name);
}
break;
}
@@ -506,7 +504,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
* since it can't possibly have been applied to
* anything yet.
*/
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
+
+ /*
+ * 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,
+ (TkTextIndex *) NULL,
(TkTextIndex *) NULL, tagPtr, 1);
}
return result;
@@ -521,9 +527,13 @@ TkTextTagCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable,
+ 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 = (TkTextTag *) Tcl_GetHashValue(hPtr);
@@ -531,46 +541,12 @@ TkTextTagCmd(textPtr, interp, objc, objv)
continue;
}
if (tagPtr->affectsDisplay) {
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
- (TkTextIndex *) NULL, tagPtr, 1);
- }
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree),
- 0, &last),
- TkBTreeTag(&first, &last, tagPtr, 0);
-
- if (tagPtr == textPtr->selTagPtr) {
- XEvent event;
- /*
- * Send an event that the selection changed.
- * This is equivalent to
- * "event generate $textWidget <<Selection>>"
- */
-
- memset((VOID *) &event, 0, sizeof(event));
- event.xany.type = VirtualEvent;
- event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
- event.xany.send_event = False;
- event.xany.window = Tk_WindowId(textPtr->tkwin);
- event.xany.display = Tk_Display(textPtr->tkwin);
- ((XVirtualEvent *) &event)->name = Tk_GetUid("Selection");
- Tk_HandleEvent(&event);
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL,
+ (TkTextIndex *) NULL, tagPtr, 1);
}
-
+ TkTextDeleteTag(textPtr, tagPtr);
Tcl_DeleteHashEntry(hPtr);
- if (textPtr->bindingTable != NULL) {
- Tk_DeleteAllBindings(textPtr->bindingTable,
- (ClientData) tagPtr);
- }
-
- /*
- * Update the tag priorities to reflect the deletion of this tag.
- */
-
- ChangeTagPriority(textPtr, tagPtr, textPtr->numTags-1);
- textPtr->numTags -= 1;
- TkTextFreeTag(textPtr, tagPtr);
}
break;
}
@@ -600,7 +576,12 @@ TkTextTagCmd(textPtr, interp, objc, objv)
prio = 0;
}
ChangeTagPriority(textPtr, tagPtr, prio);
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
+ /*
+ * If this is the 'sel' tag, then we don't actually need
+ * to call this for all peers.
+ */
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL,
(TkTextIndex *) NULL,
tagPtr, 1);
break;
@@ -619,19 +600,22 @@ TkTextTagCmd(textPtr, interp, objc, objv)
Tcl_HashEntry *hPtr;
arrayPtr = (TkTextTag **) ckalloc((unsigned)
- (textPtr->numTags * sizeof(TkTextTag *)));
- for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable,
- &search);
- hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
+ (textPtr->sharedTextPtr->numTags * sizeof(TkTextTag *)));
+ for (i = 0,
+ hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->tagTable,
+ &search);
+ hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
arrayPtr[i] = (TkTextTag *) Tcl_GetHashValue(hPtr);
}
- arraySize = textPtr->numTags;
+ /* The 'sel' tag is not in the hash table */
+ arrayPtr[i] = textPtr->selTagPtr;
+ arraySize = textPtr->sharedTextPtr->numTags;
} else {
if (TkTextGetObjIndex(interp, textPtr, objv[3], &index1)
!= TCL_OK) {
return TCL_ERROR;
}
- arrayPtr = TkBTreeGetTags(&index1, &arraySize);
+ arrayPtr = TkBTreeGetTags(&index1, textPtr, &arraySize);
if (arrayPtr == NULL) {
return TCL_OK;
}
@@ -648,6 +632,7 @@ TkTextTagCmd(textPtr, interp, objc, objv)
break;
}
case TAG_NEXTRANGE: {
+ TkTextIndex last;
TkTextSearch tSearch;
char position[TK_POS_CHARS];
@@ -662,8 +647,9 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (TkTextGetObjIndex(interp, textPtr, objv[4], &index1) != TCL_OK) {
return TCL_ERROR;
}
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
- 0, &last);
+ 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)
@@ -716,14 +702,15 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (TkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
return TCL_OK;
}
- TkTextPrintIndex(&tSearch.curIndex, position);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
Tcl_AppendElement(interp, position);
TkBTreeNextTag(&tSearch);
- TkTextPrintIndex(&tSearch.curIndex, position);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position);
Tcl_AppendElement(interp, position);
break;
}
case TAG_PREVRANGE: {
+ TkTextIndex last;
TkTextSearch tSearch;
char position1[TK_POS_CHARS];
char position2[TK_POS_CHARS];
@@ -740,7 +727,8 @@ TkTextTagCmd(textPtr, interp, objc, objv)
return TCL_ERROR;
}
if (objc == 5) {
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &index2);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ 0, 0, &index2);
} else if (TkTextGetObjIndex(interp, textPtr, objv[5], &index2)
!= TCL_OK) {
return TCL_ERROR;
@@ -756,23 +744,69 @@ TkTextTagCmd(textPtr, interp, objc, objv)
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);
+ Tcl_AppendElement(interp, position1);
+ Tcl_AppendElement(interp, position2);
+ }
return TCL_OK;
}
if (tSearch.segPtr->typePtr == &tkTextToggleOnType) {
- TkTextPrintIndex(&tSearch.curIndex, position1);
- TkTextMakeByteIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree),
- 0, &last);
+ 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(&tSearch.curIndex, position2);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
} else {
- TkTextPrintIndex(&tSearch.curIndex, position2);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position2);
TkBTreePrevTag(&tSearch);
+ TkTextPrintIndex(textPtr, &tSearch.curIndex, position1);
if (TkTextIndexCmp(&tSearch.curIndex, &index2) < 0) {
- return TCL_OK;
+ if (textPtr->start != NULL
+ && index2.linePtr == textPtr->start
+ && index2.byteIndex == 0) {
+ /* It's ok */
+ TkTextPrintIndex(textPtr, &index2, position1);
+ } else {
+ return TCL_OK;
+ }
}
- TkTextPrintIndex(&tSearch.curIndex, position1);
}
Tcl_AppendElement(interp, position1);
Tcl_AppendElement(interp, position2);
@@ -801,17 +835,24 @@ TkTextTagCmd(textPtr, interp, objc, objv)
prio = tagPtr2->priority + 1;
}
} else {
- prio = textPtr->numTags-1;
+ prio = textPtr->sharedTextPtr->numTags-1;
}
ChangeTagPriority(textPtr, tagPtr, prio);
- TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
- tagPtr, 1);
+ /*
+ * If this is the 'sel' tag, then we don't actually need
+ * to call this for all peers.
+ */
+ TkTextRedrawTag(textPtr->sharedTextPtr, NULL,
+ (TkTextIndex *) NULL, (TkTextIndex *) 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;
@@ -820,17 +861,31 @@ TkTextTagCmd(textPtr, interp, objc, objv)
if (tagPtr == NULL) {
return TCL_OK;
}
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
+ 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(interp, listObj,
TkTextNewIndexObj(textPtr, &first));
+ count++;
}
while (TkBTreeNextTag(&tSearch)) {
Tcl_ListObjAppendElement(interp, 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(interp, listObj,
+ TkTextNewIndexObj(textPtr, &last));
}
Tcl_SetObjResult(interp, listObj);
break;
@@ -867,13 +922,30 @@ TkTextCreateTag(textPtr, tagName, newTag)
register TkTextTag *tagPtr;
Tcl_HashEntry *hPtr;
int new;
-
- hPtr = Tcl_CreateHashEntry(&textPtr->tagTable, tagName, &new);
- if (newTag != NULL) {
- *newTag = new;
- }
- if (!new) {
- return (TkTextTag *) Tcl_GetHashValue(hPtr);
+ CONST char *name;
+
+ if (!strcmp(tagName, "sel")) {
+ if (textPtr->selTagPtr != NULL) {
+ if (newTag != NULL) {
+ *newTag = 0;
+ }
+ return textPtr->selTagPtr;
+ } else {
+ if (newTag != NULL) {
+ *newTag = 1;
+ }
+ }
+ name = "sel";
+ } else {
+ hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->tagTable,
+ tagName, &new);
+ if (newTag != NULL) {
+ *newTag = new;
+ }
+ if (!new) {
+ return (TkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ name = Tcl_GetHashKey(&textPtr->sharedTextPtr->tagTable, hPtr);
}
/*
@@ -882,10 +954,11 @@ TkTextCreateTag(textPtr, tagName, newTag)
*/
tagPtr = (TkTextTag *) ckalloc(sizeof(TkTextTag));
- tagPtr->name = Tcl_GetHashKey(&textPtr->tagTable, hPtr);
+ tagPtr->name = name;
+ tagPtr->textPtr = NULL;
tagPtr->toggleCount = 0;
tagPtr->tagRootPtr = NULL;
- tagPtr->priority = textPtr->numTags;
+ tagPtr->priority = textPtr->sharedTextPtr->numTags;
tagPtr->border = NULL;
tagPtr->borderWidth = 0;
tagPtr->borderWidthPtr = NULL;
@@ -922,8 +995,13 @@ TkTextCreateTag(textPtr, tagName, newTag)
tagPtr->wrapMode = TEXT_WRAPMODE_NULL;
tagPtr->affectsDisplay = 0;
tagPtr->affectsDisplayGeometry = 0;
- textPtr->numTags++;
- Tcl_SetHashValue(hPtr, tagPtr);
+ textPtr->sharedTextPtr->numTags++;
+ if (!strcmp(tagName, "sel")) {
+ tagPtr->textPtr = textPtr;
+ textPtr->refCount++;
+ } else {
+ Tcl_SetHashValue(hPtr, tagPtr);
+ }
tagPtr->optionTable = Tk_CreateOptionTable(textPtr->interp, tagOptionSpecs);
return tagPtr;
}
@@ -956,8 +1034,14 @@ FindTag(interp, textPtr, tagName)
Tcl_Obj *tagName; /* Name of desired tag. */
{
Tcl_HashEntry *hPtr;
-
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_GetString(tagName));
+ int len;
+ CONST char *str;
+ str = Tcl_GetStringFromObj(tagName, &len);
+ if (len == 3 && !strcmp(str,"sel")) {
+ return textPtr->selTagPtr;
+ }
+ hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->tagTable,
+ Tcl_GetString(tagName));
if (hPtr != NULL) {
return (TkTextTag *) Tcl_GetHashValue(hPtr);
}
@@ -971,6 +1055,76 @@ FindTag(interp, textPtr, tagName)
/*
*----------------------------------------------------------------------
*
+ * TkTextDeleteTag --
+ *
+ * This procedure is called to carry out most actions associated
+ * with the 'tag delete' sub-command. It will remove all
+ * evidence of the tag from the B-tree, and then call
+ * TkTextFreeTag to clean up the tag structure itself.
+ *
+ * The only actions this doesn't carry out it to check if
+ * the deletion of the tag requires something to be
+ * re-displayed, and to remove the tag from the tagTable
+ * (hash table) if that is necessary (i.e. if it's not the
+ * 'sel' tag). It is expected that the caller carry out both
+ * of these actions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory and other resources are freed, the B-tree is
+ * manipulated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkTextDeleteTag(textPtr, tagPtr)
+ TkText *textPtr; /* Info about overall widget. */
+ register TkTextTag *tagPtr; /* Tag being deleted. */
+{
+ TkTextIndex first, last;
+
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, &first);
+ TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
+ TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr),
+ 0, &last),
+ TkBTreeTag(&first, &last, tagPtr, 0);
+
+ if (tagPtr == textPtr->selTagPtr) {
+ /*
+ * Send an event that the selection changed.
+ * This is equivalent to
+ * "event generate $textWidget <<Selection>>"
+ */
+ TkTextSelectionEvent(textPtr);
+ } else {
+ /*
+ * Since all peer widgets have an independent "sel" tag, we
+ * don't want removal of one sel tag to remove bindings which
+ * are still valid in other peer widgets.
+ */
+ if (textPtr->sharedTextPtr->bindingTable != NULL) {
+ Tk_DeleteAllBindings(textPtr->sharedTextPtr->bindingTable,
+ (ClientData) tagPtr->name);
+ }
+ }
+
+ /*
+ * Update the tag priorities to reflect the deletion of this tag.
+ */
+
+ ChangeTagPriority(textPtr, tagPtr,
+ textPtr->sharedTextPtr->numTags-1);
+ textPtr->sharedTextPtr->numTags -= 1;
+ TkTextFreeTag(textPtr, tagPtr);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkTextFreeTag --
*
* This procedure is called when a tag is deleted to free up the
@@ -1010,6 +1164,20 @@ TkTextFreeTag(textPtr, tagPtr)
break;
}
}
+ /*
+ * If this tag is widget-specific (peer widgets) then clean
+ * up the refCount it holds.
+ */
+ if (tagPtr->textPtr != NULL) {
+ if (textPtr != tagPtr->textPtr) {
+ Tcl_Panic("Tag being deleted from wrong widget");
+ }
+ textPtr->refCount--;
+ if (textPtr->refCount == 0) {
+ ckfree((char *) textPtr);
+ }
+ tagPtr->textPtr = NULL;
+ }
/* Finally free the tag's memory */
ckfree((char *) tagPtr);
}
@@ -1109,9 +1277,10 @@ TagSortProc(first, second)
*
* Side effects:
* Priorities may be changed for some or all of the tags in
- * textPtr. The tags will be arranged so that there is exactly
- * one tag at each priority level between 0 and textPtr->numTags-1,
- * with tagPtr at priority "prio".
+ * textPtr. The tags will be arranged so that there is exactly one
+ * tag at each priority level between 0 and
+ * textPtr->sharedTextPtr->numTags-1, with tagPtr at priority
+ * "prio".
*
*----------------------------------------------------------------------
*/
@@ -1131,8 +1300,8 @@ ChangeTagPriority(textPtr, tagPtr, prio)
if (prio < 0) {
prio = 0;
}
- if (prio >= textPtr->numTags) {
- prio = textPtr->numTags-1;
+ if (prio >= textPtr->sharedTextPtr->numTags) {
+ prio = textPtr->sharedTextPtr->numTags-1;
}
if (prio == tagPtr->priority) {
return;
@@ -1145,7 +1314,14 @@ ChangeTagPriority(textPtr, tagPtr, prio)
high = prio;
delta = -1;
}
- for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
+ /*
+ * Adjust first the 'sel' tag, then all others from the hash table
+ */
+ if ((textPtr->selTagPtr->priority >= low)
+ && (textPtr->selTagPtr->priority <= high)) {
+ textPtr->selTagPtr->priority += delta;
+ }
+ for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->tagTable, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
tagPtr2 = (TkTextTag *) Tcl_GetHashValue(hPtr);
if ((tagPtr2->priority >= low) && (tagPtr2->priority <= high)) {
@@ -1239,10 +1415,10 @@ TkTextBindProc(clientData, eventPtr)
}
TkTextPickCurrent(textPtr, eventPtr);
}
- if ((textPtr->numCurTags > 0) && (textPtr->bindingTable != NULL)
+ if ((textPtr->numCurTags > 0) && (textPtr->sharedTextPtr->bindingTable != NULL)
&& (textPtr->tkwin != NULL) && !(textPtr->flags & DESTROYED)) {
- Tk_BindEvent(textPtr->bindingTable, eventPtr, textPtr->tkwin,
- textPtr->numCurTags, (ClientData *) textPtr->curTagArrayPtr);
+ TagBindEvent(textPtr, eventPtr, textPtr->numCurTags,
+ textPtr->curTagArrayPtr);
}
if (repick) {
unsigned int oldState;
@@ -1374,7 +1550,7 @@ TkTextPickCurrent(textPtr, eventPtr)
newArrayPtr = NULL;
numNewTags = 0;
} else {
- newArrayPtr = TkBTreeGetTags(&index, &numNewTags);
+ newArrayPtr = TkBTreeGetTags(&index, textPtr, &numNewTags);
SortTags(numNewTags, newArrayPtr);
}
} else {
@@ -1421,7 +1597,7 @@ TkTextPickCurrent(textPtr, eventPtr)
oldArrayPtr = textPtr->curTagArrayPtr;
textPtr->curTagArrayPtr = newArrayPtr;
if (numOldTags != 0) {
- if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
+ if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
&& !(textPtr->flags & DESTROYED)) {
event = textPtr->pickEvent;
event.type = LeaveNotify;
@@ -1433,8 +1609,7 @@ TkTextPickCurrent(textPtr, eventPtr)
*/
event.xcrossing.detail = NotifyAncestor;
- Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
- numOldTags, (ClientData *) oldArrayPtr);
+ TagBindEvent(textPtr, &event, numOldTags, oldArrayPtr);
}
ckfree((char *) oldArrayPtr);
}
@@ -1450,14 +1625,81 @@ TkTextPickCurrent(textPtr, eventPtr)
textPtr->pickEvent.xcrossing.y, &index, &nearby);
TkTextSetMark(textPtr, "current", &index);
if (numNewTags != 0) {
- if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
+ if ((textPtr->sharedTextPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)
&& !(textPtr->flags & DESTROYED) && !nearby) {
event = textPtr->pickEvent;
event.type = EnterNotify;
event.xcrossing.detail = NotifyAncestor;
- Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
- numNewTags, (ClientData *) copyArrayPtr);
+ TagBindEvent(textPtr, &event, numNewTags, copyArrayPtr);
}
ckfree((char *) copyArrayPtr);
}
}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TagBindEvent --
+ *
+ * Trigger given events for all tags that match the
+ * relevant bindings. To handle the "sel" tag
+ * correctly in all peer widgets, we must use the
+ * name of the tags as the binding table element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Almost anything can be triggered by tag bindings,
+ * including deletion of the text widget.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+TagBindEvent(textPtr, eventPtr, numTags, tagArrayPtr)
+ TkText *textPtr; /* Text widget to fire bindings
+ * in. */
+ XEvent *eventPtr; /* What actually happened. */
+ int numTags; /* Number of relevant tags. */
+ TkTextTag **tagArrayPtr; /* Array of relevant tags. */
+{
+ #define NUM_BIND_TAGS 10
+ CONST char *nameArray[NUM_BIND_TAGS];
+ CONST char **nameArrPtr;
+ int i;
+
+ /*
+ * Try to avoid allocation unless there are lots of tags
+ */
+ if (numTags > NUM_BIND_TAGS) {
+ nameArrPtr = (CONST char**) ckalloc (numTags * sizeof(CONST char*));
+ } else {
+ nameArrPtr = nameArray;
+ }
+ /*
+ * We use tag names as keys in the hash table. We do this instead
+ * of using the actual tagPtr objects because we want one "sel" tag
+ * binding for all peer widgets, despite the fact that each has its
+ * own tagPtr object.
+ */
+ for (i = 0; i < numTags; i++) {
+ TkTextTag *tagPtr = tagArrayPtr[i];
+ if (tagPtr != NULL) {
+ nameArrPtr[i] = tagPtr->name;
+ } else {
+ /*
+ * Tag has been deleted elsewhere, and therefore nulled
+ * out in this array. Tk_BindEvent is clever enough to
+ * cope with NULLs being thrown at it.
+ */
+ nameArrPtr[i] = NULL;
+ }
+ }
+ Tk_BindEvent(textPtr->sharedTextPtr->bindingTable, eventPtr, textPtr->tkwin,
+ numTags, (ClientData *) nameArrPtr);
+
+ if (numTags > NUM_BIND_TAGS) {
+ ckfree((char*)nameArrPtr);
+ }
+}