4 * This module implements the "tag" subcommand of the widget command
5 * for text widgets, plus most of the other high-level functions
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 1995 Christian Werner
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22 * Information used for parsing tag configuration information:
25 static Ck_ConfigSpec tagConfigSpecs[] = {
26 {CK_CONFIG_ATTR, "-attributes", (char *) NULL, (char *) NULL,
27 (char *) NULL, Ck_Offset(CkTextTag, attr), 0},
28 {CK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
29 (char *) NULL, Ck_Offset(CkTextTag, bg), 0},
30 {CK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
31 (char *) NULL, Ck_Offset(CkTextTag, fg), 0},
32 {CK_CONFIG_STRING, "-justify", (char *) NULL, (char *) NULL,
33 (char *) NULL, Ck_Offset(CkTextTag, justifyString), CK_CONFIG_NULL_OK},
34 {CK_CONFIG_STRING, "-lmargin1", (char *) NULL, (char *) NULL,
35 (char *) NULL, Ck_Offset(CkTextTag, lMargin1String), CK_CONFIG_NULL_OK},
36 {CK_CONFIG_STRING, "-lmargin2", (char *) NULL, (char *) NULL,
37 (char *) NULL, Ck_Offset(CkTextTag, lMargin2String), CK_CONFIG_NULL_OK},
38 {CK_CONFIG_STRING, "-rmargin", (char *) NULL, (char *) NULL,
39 (char *) NULL, Ck_Offset(CkTextTag, rMarginString), CK_CONFIG_NULL_OK},
40 {CK_CONFIG_STRING, "-tabs", (char *) NULL, (char *) NULL,
41 (char *) NULL, Ck_Offset(CkTextTag, tabString), CK_CONFIG_NULL_OK},
42 {CK_CONFIG_UID, "-wrap", (char *) NULL, (char *) NULL,
43 (char *) NULL, Ck_Offset(CkTextTag, wrapMode),
45 {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
50 * Forward declarations for procedures defined later in this file:
53 static void ChangeTagPriority _ANSI_ARGS_((CkText *textPtr,
54 CkTextTag *tagPtr, int prio));
55 static CkTextTag * FindTag _ANSI_ARGS_((Tcl_Interp *interp,
56 CkText *textPtr, char *tagName));
57 static void SortTags _ANSI_ARGS_((int numTags,
58 CkTextTag **tagArrayPtr));
59 static int TagSortProc _ANSI_ARGS_((CONST VOID *first,
63 *--------------------------------------------------------------
67 * This procedure is invoked to process the "tag" options of
68 * the widget command for text widgets. See the user documentation
69 * for details on what it does.
72 * A standard Tcl result.
75 * See the user documentation.
77 *--------------------------------------------------------------
81 CkTextTagCmd(textPtr, interp, argc, argv)
82 register CkText *textPtr; /* Information about text widget. */
83 Tcl_Interp *interp; /* Current interpreter. */
84 int argc; /* Number of arguments. */
85 char **argv; /* Argument strings. Someone else has already
86 * parsed this command enough to know that
87 * argv[1] is "tag". */
92 register CkTextTag *tagPtr;
93 CkTextIndex first, last, index1, index2;
96 Tcl_AppendResult(interp, "wrong # args: should be \"",
97 argv[0], " tag option ?arg arg ...?\"", (char *) NULL);
101 length = strlen(argv[2]);
102 if ((c == 'a') && (strncmp(argv[2], "add", length) == 0)) {
108 Tcl_AppendResult(interp, "wrong # args: should be \"",
109 argv[0], " tag ", fullOption,
110 " tagName index1 ?index2 index1 index2 ...?\"",
114 tagPtr = CkTextCreateTag(textPtr, argv[3]);
115 for (i = 4; i < argc; i += 2) {
116 if (CkTextGetIndex(interp, textPtr, argv[i], &index1) != TCL_OK) {
120 if (CkTextGetIndex(interp, textPtr, argv[i+1], &index2)
124 if (CkTextIndexCmp(&index1, &index2) >= 0) {
129 CkTextIndexForwChars(&index2, 1, &index2);
132 if (tagPtr->affectsDisplay) {
133 CkTextRedrawTag(textPtr, &index1, &index2, tagPtr, !addTag);
136 * Still need to trigger enter/leave events on tags that
140 CkTextEventuallyRepick(textPtr);
142 CkBTreeTag(&index1, &index2, tagPtr, addTag);
145 * If the tag is "sel" then grab the selection if we're supposed
146 * to export it and don't already have it. Also, invalidate
147 * partially-completed selection retrievals.
150 if (tagPtr == textPtr->selTagPtr) {
151 textPtr->abortSelections = 1;
154 } else if ((c == 'b') && (strncmp(argv[2], "bind", length) == 0)) {
155 if ((argc < 4) || (argc > 6)) {
156 Tcl_AppendResult(interp, "wrong # args: should be \"",
157 argv[0], " tag bind tagName ?sequence? ?command?\"",
161 tagPtr = CkTextCreateTag(textPtr, argv[3]);
164 * Make a binding table if the widget doesn't already have
168 if (textPtr->bindingTable == NULL) {
169 textPtr->bindingTable = Ck_CreateBindingTable(interp);
176 if (argv[5][0] == 0) {
177 return Ck_DeleteBinding(interp, textPtr->bindingTable,
178 (ClientData) tagPtr, argv[4]);
180 if (argv[5][0] == '+') {
184 mask = Ck_CreateBinding(interp, textPtr->bindingTable,
185 (ClientData) tagPtr, argv[4], argv[5], append);
186 if (mask != TCL_OK) {
189 } else if (argc == 5) {
192 command = Ck_GetBinding(interp, textPtr->bindingTable,
193 (ClientData) tagPtr, argv[4]);
194 if (command == NULL) {
197 interp->result = command;
199 Ck_GetAllBindings(interp, textPtr->bindingTable,
200 (ClientData) tagPtr);
202 } else if ((c == 'c') && (strncmp(argv[2], "cget", length) == 0)
205 Tcl_AppendResult(interp, "wrong # args: should be \"",
206 argv[0], " tag cget tagName option\"",
210 tagPtr = FindTag(interp, textPtr, argv[3]);
211 if (tagPtr == NULL) {
214 return Ck_ConfigureValue(interp, textPtr->winPtr, tagConfigSpecs,
215 (char *) tagPtr, argv[4], 0);
216 } else if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)
219 Tcl_AppendResult(interp, "wrong # args: should be \"",
220 argv[0], " tag configure tagName ?option? ?value? ",
221 "?option value ...?\"", (char *) NULL);
224 tagPtr = CkTextCreateTag(textPtr, argv[3]);
226 return Ck_ConfigureInfo(interp, textPtr->winPtr, tagConfigSpecs,
227 (char *) tagPtr, (char *) NULL, 0);
228 } else if (argc == 5) {
229 return Ck_ConfigureInfo(interp, textPtr->winPtr, tagConfigSpecs,
230 (char *) tagPtr, argv[4], 0);
234 result = Ck_ConfigureWidget(interp, textPtr->winPtr,
235 tagConfigSpecs, argc-4, argv+4, (char *) tagPtr, 0);
237 * Some of the configuration options, like -underline
238 * and -justify, require additional translation (this is
239 * needed because we need to distinguish a particular value
240 * of an option from "unspecified").
243 if (tagPtr->justifyString != NULL) {
244 if (Ck_GetJustify(interp, tagPtr->justifyString,
245 &tagPtr->justify) != TCL_OK) {
249 if (tagPtr->lMargin1String != NULL) {
250 if (Ck_GetCoord(interp, textPtr->winPtr,
251 tagPtr->lMargin1String, &tagPtr->lMargin1) != TCL_OK) {
255 if (tagPtr->lMargin2String != NULL) {
256 if (Ck_GetCoord(interp, textPtr->winPtr,
257 tagPtr->lMargin2String, &tagPtr->lMargin2) != TCL_OK) {
261 if (tagPtr->rMarginString != NULL) {
262 if (Ck_GetCoord(interp, textPtr->winPtr,
263 tagPtr->rMarginString, &tagPtr->rMargin) != TCL_OK) {
267 if (tagPtr->tabArrayPtr != NULL) {
268 ckfree((char *) tagPtr->tabArrayPtr);
269 tagPtr->tabArrayPtr = NULL;
271 if (tagPtr->tabString != NULL) {
272 tagPtr->tabArrayPtr = CkTextGetTabs(interp, textPtr->winPtr,
274 if (tagPtr->tabArrayPtr == NULL) {
278 if ((tagPtr->wrapMode != NULL)
279 && (tagPtr->wrapMode != ckTextCharUid)
280 && (tagPtr->wrapMode != ckTextNoneUid)
281 && (tagPtr->wrapMode != ckTextWordUid)) {
282 Tcl_AppendResult(interp, "bad wrap mode \"", tagPtr->wrapMode,
283 "\": must be char, none, or word", (char *) NULL);
284 tagPtr->wrapMode = NULL;
289 * If the "sel" tag was changed, be sure to mirror information
290 * from the tag back into the text widget record. NOTE: we
291 * don't have to free up information in the widget record
292 * before overwriting it, because it was mirrored in the tag
293 * and hence freed when the tag field was overwritten.
296 if (tagPtr == textPtr->selTagPtr) {
297 textPtr->selBg = tagPtr->bg;
298 textPtr->selFg = tagPtr->fg;
299 textPtr->selAttr = tagPtr->attr;
301 tagPtr->affectsDisplay = 1;
302 CkTextRedrawTag(textPtr, (CkTextIndex *) NULL,
303 (CkTextIndex *) NULL, tagPtr, 1);
306 } else if ((c == 'd') && (strncmp(argv[2], "delete", length) == 0)) {
310 Tcl_AppendResult(interp, "wrong # args: should be \"",
311 argv[0], " tag delete tagName tagName ...\"",
315 for (i = 3; i < argc; i++) {
316 hPtr = Tcl_FindHashEntry(&textPtr->tagTable, argv[i]);
320 tagPtr = (CkTextTag *) Tcl_GetHashValue(hPtr);
321 if (tagPtr == textPtr->selTagPtr) {
324 if (tagPtr->affectsDisplay) {
325 CkTextRedrawTag(textPtr, (CkTextIndex *) NULL,
326 (CkTextIndex *) NULL, tagPtr, 1);
329 CkBTreeTag(CkTextMakeByteIndex(textPtr->tree, 0, 0, &first),
330 CkTextMakeByteIndex(textPtr->tree,
331 CkBTreeNumLines(textPtr->tree), 0, &last),
334 CkBTreeTag(CkTextMakeIndex(textPtr->tree, 0, 0, &first),
335 CkTextMakeIndex(textPtr->tree,
336 CkBTreeNumLines(textPtr->tree), 0, &last),
339 Tcl_DeleteHashEntry(hPtr);
340 if (textPtr->bindingTable != NULL) {
341 Ck_DeleteAllBindings(textPtr->bindingTable,
342 (ClientData) tagPtr);
346 * Update the tag priorities to reflect the deletion of this tag.
349 ChangeTagPriority(textPtr, tagPtr, textPtr->numTags-1);
350 textPtr->numTags -= 1;
351 CkTextFreeTag(textPtr, tagPtr);
353 } else if ((c == 'l') && (strncmp(argv[2], "lower", length) == 0)) {
357 if ((argc != 4) && (argc != 5)) {
358 Tcl_AppendResult(interp, "wrong # args: should be \"",
359 argv[0], " tag lower tagName ?belowThis?\"",
363 tagPtr = FindTag(interp, textPtr, argv[3]);
364 if (tagPtr == NULL) {
368 tagPtr2 = FindTag(interp, textPtr, argv[4]);
369 if (tagPtr2 == NULL) {
372 if (tagPtr->priority < tagPtr2->priority) {
373 prio = tagPtr2->priority - 1;
375 prio = tagPtr2->priority;
380 ChangeTagPriority(textPtr, tagPtr, prio);
381 CkTextRedrawTag(textPtr, (CkTextIndex *) NULL, (CkTextIndex *) NULL,
383 } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)
385 CkTextTag **arrayPtr;
388 if ((argc != 3) && (argc != 4)) {
389 Tcl_AppendResult(interp, "wrong # args: should be \"",
390 argv[0], " tag names ?index?\"",
395 Tcl_HashSearch search;
398 arrayPtr = (CkTextTag **) ckalloc((unsigned)
399 (textPtr->numTags * sizeof(CkTextTag *)));
400 for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
401 hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
402 arrayPtr[i] = (CkTextTag *) Tcl_GetHashValue(hPtr);
404 arraySize = textPtr->numTags;
406 if (CkTextGetIndex(interp, textPtr, argv[3], &index1)
410 arrayPtr = CkBTreeGetTags(&index1, &arraySize);
411 if (arrayPtr == NULL) {
415 SortTags(arraySize, arrayPtr);
416 for (i = 0; i < arraySize; i++) {
417 tagPtr = arrayPtr[i];
418 Tcl_AppendElement(interp, tagPtr->name);
420 ckfree((char *) arrayPtr);
421 } else if ((c == 'n') && (strncmp(argv[2], "nextrange", length) == 0)
423 CkTextSearch tSearch;
424 char position[TK_POS_CHARS];
426 if ((argc != 5) && (argc != 6)) {
427 Tcl_AppendResult(interp, "wrong # args: should be \"",
428 argv[0], " tag nextrange tagName index1 ?index2?\"",
432 tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
433 if (tagPtr == NULL) {
436 if (CkTextGetIndex(interp, textPtr, argv[4], &index1) != TCL_OK) {
440 CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
443 CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
448 } else if (CkTextGetIndex(interp, textPtr, argv[5], &index2)
454 * The search below is a bit tricky. Rather than use the B-tree
455 * facilities to stop the search at index2, let it search up
456 * until the end of the file but check for a position past index2
457 * ourselves. The reason for doing it this way is that we only
458 * care whether the *start* of the range is before index2; once
459 * we find the start, we don't want CkBTreeNextTag to abort the
460 * search because the end of the range is after index2.
463 CkBTreeStartSearch(&index1, &last, tagPtr, &tSearch);
464 if (CkBTreeCharTagged(&index1, tagPtr)) {
465 CkTextSegment *segPtr;
469 * The first character is tagged. See if there is an
470 * on-toggle just before the character. If not, then
471 * skip to the end of this tagged range.
474 for (segPtr = index1.linePtr->segPtr, offset = index1.charIndex;
476 offset -= segPtr->size, segPtr = segPtr->nextPtr) {
477 if ((offset == 0) && (segPtr->typePtr == &ckTextToggleOnType)
478 && (segPtr->body.toggle.tagPtr == tagPtr)) {
482 if (!CkBTreeNextTag(&tSearch)) {
488 * Find the start of the tagged range.
491 if (!CkBTreeNextTag(&tSearch)) {
495 if (CkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
498 CkTextPrintIndex(&tSearch.curIndex, position);
499 Tcl_AppendElement(interp, position);
500 CkBTreeNextTag(&tSearch);
501 CkTextPrintIndex(&tSearch.curIndex, position);
502 Tcl_AppendElement(interp, position);
503 } else if ((c == 'r') && (strncmp(argv[2], "raise", length) == 0)
508 if ((argc != 4) && (argc != 5)) {
509 Tcl_AppendResult(interp, "wrong # args: should be \"",
510 argv[0], " tag raise tagName ?aboveThis?\"",
514 tagPtr = FindTag(interp, textPtr, argv[3]);
515 if (tagPtr == NULL) {
519 tagPtr2 = FindTag(interp, textPtr, argv[4]);
520 if (tagPtr2 == NULL) {
523 if (tagPtr->priority <= tagPtr2->priority) {
524 prio = tagPtr2->priority;
526 prio = tagPtr2->priority + 1;
529 prio = textPtr->numTags-1;
531 ChangeTagPriority(textPtr, tagPtr, prio);
532 CkTextRedrawTag(textPtr, (CkTextIndex *) NULL, (CkTextIndex *) NULL,
534 } else if ((c == 'r') && (strncmp(argv[2], "ranges", length) == 0)
536 CkTextSearch tSearch;
537 char position[TK_POS_CHARS];
540 Tcl_AppendResult(interp, "wrong # args: should be \"",
541 argv[0], " tag ranges tagName\"", (char *) NULL);
544 tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
545 if (tagPtr == NULL) {
549 CkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
550 CkTextMakeByteIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
553 CkTextMakeIndex(textPtr->tree, 0, 0, &first);
554 CkTextMakeIndex(textPtr->tree, CkBTreeNumLines(textPtr->tree),
557 CkBTreeStartSearch(&first, &last, tagPtr, &tSearch);
558 if (CkBTreeCharTagged(&first, tagPtr)) {
559 CkTextPrintIndex(&first, position);
560 Tcl_AppendElement(interp, position);
562 while (CkBTreeNextTag(&tSearch)) {
563 CkTextPrintIndex(&tSearch.curIndex, position);
564 Tcl_AppendElement(interp, position);
566 } else if ((c == 'r') && (strncmp(argv[2], "remove", length) == 0)
568 fullOption = "remove";
572 Tcl_AppendResult(interp, "bad tag option \"", argv[2],
573 "\": must be add, bind, cget, configure, delete, lower, ",
574 "names, nextrange, raise, ranges, or remove",
582 *----------------------------------------------------------------------
586 * Find the record describing a tag within a given text widget,
587 * creating a new record if one doesn't already exist.
590 * The return value is a pointer to the CkTextTag record for tagName.
593 * A new tag record is created if there isn't one already defined
596 *----------------------------------------------------------------------
600 CkTextCreateTag(textPtr, tagName)
601 CkText *textPtr; /* Widget in which tag is being used. */
602 char *tagName; /* Name of desired tag. */
604 register CkTextTag *tagPtr;
608 hPtr = Tcl_CreateHashEntry(&textPtr->tagTable, tagName, &new);
610 return (CkTextTag *) Tcl_GetHashValue(hPtr);
614 * No existing entry. Create a new one, initialize it, and add a
615 * pointer to it to the hash table entry.
618 tagPtr = (CkTextTag *) ckalloc(sizeof(CkTextTag));
619 tagPtr->name = Tcl_GetHashKey(&textPtr->tagTable, hPtr);
620 tagPtr->priority = textPtr->numTags;
624 tagPtr->justifyString = NULL;
625 tagPtr->justify = CK_JUSTIFY_LEFT;
626 tagPtr->lMargin1String = NULL;
627 tagPtr->lMargin1 = 0;
628 tagPtr->lMargin2String = NULL;
629 tagPtr->lMargin2 = 0;
630 tagPtr->rMarginString = NULL;
632 tagPtr->tabString = NULL;
633 tagPtr->tabArrayPtr = NULL;
634 tagPtr->wrapMode = NULL;
635 tagPtr->affectsDisplay = 0;
637 Tcl_SetHashValue(hPtr, tagPtr);
642 *----------------------------------------------------------------------
646 * See if tag is defined for a given widget.
649 * If tagName is defined in textPtr, a pointer to its CkTextTag
650 * structure is returned. Otherwise NULL is returned and an
651 * error message is recorded in interp->result unless interp
657 *----------------------------------------------------------------------
661 FindTag(interp, textPtr, tagName)
662 Tcl_Interp *interp; /* Interpreter to use for error message;
663 * if NULL, then don't record an error
665 CkText *textPtr; /* Widget in which tag is being used. */
666 char *tagName; /* Name of desired tag. */
670 hPtr = Tcl_FindHashEntry(&textPtr->tagTable, tagName);
672 return (CkTextTag *) Tcl_GetHashValue(hPtr);
674 if (interp != NULL) {
675 Tcl_AppendResult(interp, "tag \"", tagName,
676 "\" isn't defined in text widget", (char *) NULL);
682 *----------------------------------------------------------------------
686 * This procedure is called when a tag is deleted to free up the
687 * memory and other resources associated with the tag.
693 * Memory and other resources are freed.
695 *----------------------------------------------------------------------
699 CkTextFreeTag(textPtr, tagPtr)
700 CkText *textPtr; /* Info about overall widget. */
701 register CkTextTag *tagPtr; /* Tag being deleted. */
703 if (tagPtr->justifyString != NULL) {
704 ckfree(tagPtr->justifyString);
706 if (tagPtr->lMargin1String != NULL) {
707 ckfree(tagPtr->lMargin1String);
709 if (tagPtr->lMargin2String != NULL) {
710 ckfree(tagPtr->lMargin2String);
712 if (tagPtr->rMarginString != NULL) {
713 ckfree(tagPtr->rMarginString);
715 if (tagPtr->tabString != NULL) {
716 ckfree(tagPtr->tabString);
718 if (tagPtr->tabArrayPtr != NULL) {
719 ckfree((char *) tagPtr->tabArrayPtr);
721 ckfree((char *) tagPtr);
725 *----------------------------------------------------------------------
729 * This procedure sorts an array of tag pointers in increasing
730 * order of priority, optimizing for the common case where the
739 *----------------------------------------------------------------------
743 SortTags(numTags, tagArrayPtr)
744 int numTags; /* Number of tag pointers at *tagArrayPtr. */
745 CkTextTag **tagArrayPtr; /* Pointer to array of pointers. */
748 register CkTextTag **tagPtrPtr;
749 CkTextTag **maxPtrPtr, *tmp;
755 for (i = numTags-1; i > 0; i--, tagArrayPtr++) {
756 maxPtrPtr = tagPtrPtr = tagArrayPtr;
757 prio = tagPtrPtr[0]->priority;
758 for (j = i, tagPtrPtr++; j > 0; j--, tagPtrPtr++) {
759 if (tagPtrPtr[0]->priority < prio) {
760 prio = tagPtrPtr[0]->priority;
761 maxPtrPtr = tagPtrPtr;
765 *maxPtrPtr = *tagArrayPtr;
769 qsort((VOID *) tagArrayPtr, (unsigned) numTags, sizeof (CkTextTag *),
775 *----------------------------------------------------------------------
779 * This procedure is called by qsort when sorting an array of
780 * tags in priority order.
783 * The return value is -1 if the first argument should be before
784 * the second element (i.e. it has lower priority), 0 if it's
785 * equivalent (this should never happen!), and 1 if it should be
786 * after the second element.
791 *----------------------------------------------------------------------
795 TagSortProc(first, second)
796 CONST VOID *first, *second; /* Elements to be compared. */
798 CkTextTag *tagPtr1, *tagPtr2;
800 tagPtr1 = * (CkTextTag **) first;
801 tagPtr2 = * (CkTextTag **) second;
802 return tagPtr1->priority - tagPtr2->priority;
806 *----------------------------------------------------------------------
808 * ChangeTagPriority --
810 * This procedure changes the priority of a tag by modifying
811 * its priority and the priorities of other tags that are affected
818 * Priorities may be changed for some or all of the tags in
819 * textPtr. The tags will be arranged so that there is exactly
820 * one tag at each priority level between 0 and textPtr->numTags-1,
821 * with tagPtr at priority "prio".
823 *----------------------------------------------------------------------
827 ChangeTagPriority(textPtr, tagPtr, prio)
828 CkText *textPtr; /* Information about text widget. */
829 CkTextTag *tagPtr; /* Tag whose priority is to be
831 int prio; /* New priority for tag. */
833 int low, high, delta;
834 register CkTextTag *tagPtr2;
836 Tcl_HashSearch search;
841 if (prio >= textPtr->numTags) {
842 prio = textPtr->numTags-1;
844 if (prio == tagPtr->priority) {
846 } else if (prio < tagPtr->priority) {
848 high = tagPtr->priority-1;
851 low = tagPtr->priority+1;
855 for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
856 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
857 tagPtr2 = (CkTextTag *) Tcl_GetHashValue(hPtr);
858 if ((tagPtr2->priority >= low) && (tagPtr2->priority <= high)) {
859 tagPtr2->priority += delta;
862 tagPtr->priority = prio;
866 *--------------------------------------------------------------
870 * This procedure is invoked by the Ck dispatcher to handle
871 * events associated with bindings on items.
877 * Depends on the command invoked as part of the binding
878 * (if there was any).
880 *--------------------------------------------------------------
884 CkTextBindProc(clientData, eventPtr)
885 ClientData clientData; /* Pointer to canvas structure. */
886 CkEvent *eventPtr; /* Pointer to X event that just
889 CkText *textPtr = (CkText *) clientData;
893 *--------------------------------------------------------------
895 * CkTextPickCurrent --
897 * Find the character containing the coordinates in an event
898 * and place the "current" mark on that character. If the
899 * "current" mark has moved then generate a fake leave event
900 * on the old current character and a fake enter event on the new
907 * The current mark for textPtr may change. If it does,
908 * then the commands associated with character entry and leave
909 * could do just about anything.
911 *--------------------------------------------------------------
915 CkTextPickCurrent(textPtr, eventPtr)
916 register CkText *textPtr; /* Text widget in which to select
917 * current character. */
918 CkEvent *eventPtr; /* Event describing location of
919 * mouse cursor. Must be EnterWindow,
920 * LeaveWindow, ButtonRelease, or