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 1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTextTag.c,v 1.3 92/07/28 15:38:59 ouster Exp $ SPRITE (Berkeley)";
28 * Information used for parsing tag configuration information:
31 static Tk_ConfigSpec tagConfigSpecs
[] = {
32 {TK_CONFIG_BORDER
, "-background", (char *) NULL
, (char *) NULL
,
33 (char *) NULL
, Tk_Offset(TkTextTag
, border
), TK_CONFIG_NULL_OK
},
34 {TK_CONFIG_BITMAP
, "-bgstipple", (char *) NULL
, (char *) NULL
,
35 (char *) NULL
, Tk_Offset(TkTextTag
, bgStipple
), TK_CONFIG_NULL_OK
},
36 {TK_CONFIG_PIXELS
, "-borderwidth", (char *) NULL
, (char *) NULL
,
37 "0", Tk_Offset(TkTextTag
, borderWidth
), TK_CONFIG_DONT_SET_DEFAULT
},
38 {TK_CONFIG_BITMAP
, "-fgstipple", (char *) NULL
, (char *) NULL
,
39 (char *) NULL
, Tk_Offset(TkTextTag
, fgStipple
), TK_CONFIG_NULL_OK
},
40 {TK_CONFIG_FONT
, "-font", (char *) NULL
, (char *) NULL
,
41 (char *) NULL
, Tk_Offset(TkTextTag
, fontPtr
), TK_CONFIG_NULL_OK
},
42 {TK_CONFIG_COLOR
, "-foreground", (char *) NULL
, (char *) NULL
,
43 (char *) NULL
, Tk_Offset(TkTextTag
, fgColor
), TK_CONFIG_NULL_OK
},
44 {TK_CONFIG_RELIEF
, "-relief", (char *) NULL
, (char *) NULL
,
45 "flat", Tk_Offset(TkTextTag
, relief
), TK_CONFIG_DONT_SET_DEFAULT
},
46 {TK_CONFIG_BOOLEAN
, "-underline", (char *) NULL
, (char *) NULL
,
47 "false", Tk_Offset(TkTextTag
, underline
), TK_CONFIG_DONT_SET_DEFAULT
},
48 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
54 * The following definition specifies the maximum number of characters
55 * needed in a string to hold a position specifier.
61 * Forward declarations for procedures defined later in this file:
64 static void ChangeTagPriority
_ANSI_ARGS_((TkText
*textPtr
,
65 TkTextTag
*tagPtr
, int prio
));
66 static TkTextTag
* FindTag
_ANSI_ARGS_((Tcl_Interp
*interp
,
67 TkText
*textPtr
, char *tagName
));
68 static void SortTags
_ANSI_ARGS_((int numTags
,
69 TkTextTag
**tagArrayPtr
));
70 static int TagSortProc
_ANSI_ARGS_((CONST VOID
*first
,
72 static void TextDoEvent
_ANSI_ARGS_((TkText
*textPtr
,
76 *--------------------------------------------------------------
80 * This procedure is invoked to process the "tag" options of
81 * the widget command for text widgets. See the user documentation
82 * for details on what it does.
85 * A standard Tcl result.
88 * See the user documentation.
90 *--------------------------------------------------------------
95 register TkText
*textPtr
, /* Information about text widget. */
96 Tcl_Interp
*interp
, /* Current interpreter. */
97 int argc
, /* Number of arguments. */
98 char **argv
/* Argument strings. Someone else has already
99 * parsed this command enough to know that
100 * argv[1] is "tag". */
103 int length
, line1
, ch1
, line2
, ch2
, i
, addTag
;
106 register TkTextTag
*tagPtr
;
109 Tcl_AppendResult(interp
, "wrong # args: should be \"",
110 argv
[0], " tag option ?arg arg ...?\"", (char *) NULL
);
114 length
= strlen(argv
[2]);
115 if ((c
== 'a') && (strncmp(argv
[2], "add", length
) == 0)) {
120 if ((argc
!= 5) && (argc
!= 6)) {
121 Tcl_AppendResult(interp
, "wrong # args: should be \"",
122 argv
[0], " tag ", fullOption
, " tagName index1 ?index2?\"",
126 tagPtr
= TkTextCreateTag(textPtr
, argv
[3]);
127 if (TkTextGetIndex(interp
, textPtr
, argv
[4], &line1
, &ch1
) != TCL_OK
) {
131 if (TkTextGetIndex(interp
, textPtr
, argv
[5], &line2
, &ch2
)
139 if (TK_TAG_AFFECTS_DISPLAY(tagPtr
)) {
140 TkTextRedrawTag(textPtr
, line1
, ch1
, line2
, ch2
, tagPtr
, !addTag
);
142 TkBTreeTag(textPtr
->tree
, line1
, ch1
, line2
, ch2
, 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 if (addTag
&& textPtr
->exportSelection
152 && !(textPtr
->flags
& GOT_SELECTION
)) {
153 Tk_OwnSelection(textPtr
->tkwin
, TkTextLostSelection
,
154 (ClientData
) textPtr
);
155 textPtr
->flags
|= GOT_SELECTION
;
157 textPtr
->selOffset
= -1;
159 } else if ((c
== 'b') && (strncmp(argv
[2], "bind", length
) == 0)) {
160 if ((argc
< 4) || (argc
> 6)) {
161 Tcl_AppendResult(interp
, "wrong # args: should be \"",
162 argv
[0], " tag bind tagName ?sequence? ?command?\"",
166 tagPtr
= TkTextCreateTag(textPtr
, argv
[3]);
169 * Make a binding table if the widget doesn't already have
173 if (textPtr
->bindingTable
== NULL
) {
174 textPtr
->bindingTable
= Tk_CreateBindingTable(interp
);
181 if (argv
[5][0] == 0) {
182 return Tk_DeleteBinding(interp
, textPtr
->bindingTable
,
183 (ClientData
) tagPtr
, argv
[4]);
185 if (argv
[5][0] == '+') {
189 mask
= Tk_CreateBinding(interp
, textPtr
->bindingTable
,
190 (ClientData
) tagPtr
, argv
[4], argv
[5], append
);
194 if (mask
& ~(ButtonMotionMask
|Button1MotionMask
|Button2MotionMask
195 |Button3MotionMask
|Button4MotionMask
|Button5MotionMask
196 |ButtonPressMask
|ButtonReleaseMask
|EnterWindowMask
197 |LeaveWindowMask
|KeyPressMask
|KeyReleaseMask
198 |PointerMotionMask
)) {
199 Tk_DeleteBinding(interp
, textPtr
->bindingTable
,
200 (ClientData
) tagPtr
, argv
[4]);
201 Tcl_ResetResult(interp
);
202 Tcl_AppendResult(interp
, "requested illegal events; ",
203 "only key, button, motion, and enter/leave ",
204 "events may be used", (char *) NULL
);
207 } else if (argc
== 5) {
210 command
= Tk_GetBinding(interp
, textPtr
->bindingTable
,
211 (ClientData
) tagPtr
, argv
[4]);
212 if (command
== NULL
) {
215 interp
->result
= command
;
217 Tk_GetAllBindings(interp
, textPtr
->bindingTable
,
218 (ClientData
) tagPtr
);
220 } else if ((c
== 'c') && (strncmp(argv
[2], "configure", length
) == 0)) {
222 Tcl_AppendResult(interp
, "wrong # args: should be \"",
223 argv
[0], " tag configure tagName ?option? ?value? ",
224 "?option value ...?\"", (char *) NULL
);
227 tagPtr
= TkTextCreateTag(textPtr
, argv
[3]);
229 return Tk_ConfigureInfo(interp
, textPtr
->tkwin
, tagConfigSpecs
,
230 (char *) tagPtr
, (char *) NULL
, 0);
231 } else if (argc
== 5) {
232 return Tk_ConfigureInfo(interp
, textPtr
->tkwin
, tagConfigSpecs
,
233 (char *) tagPtr
, argv
[4], 0);
237 result
= Tk_ConfigureWidget(interp
, textPtr
->tkwin
, tagConfigSpecs
,
238 argc
-4, argv
+4, (char *) tagPtr
, 0);
240 * If the "sel" tag was changed, be sure to mirror information
241 * from the tag back into the text widget record. NOTE: we
242 * don't have to free up information in the widget record
243 * before overwriting it, because it was mirrored in the tag
244 * and hence freed when the tag field was overwritten.
247 if (tagPtr
== textPtr
->selTagPtr
) {
248 textPtr
->selBorder
= tagPtr
->border
;
249 textPtr
->selBorderWidth
= tagPtr
->borderWidth
;
250 textPtr
->selFgColorPtr
= tagPtr
->fgColor
;
252 TkTextRedrawTag(textPtr
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
256 } else if ((c
== 'd') && (strncmp(argv
[2], "delete", length
) == 0)) {
260 Tcl_AppendResult(interp
, "wrong # args: should be \"",
261 argv
[0], " tag delete tagName tagName ...\"",
265 for (i
= 3; i
< argc
; i
++) {
266 hPtr
= Tcl_FindHashEntry(&textPtr
->tagTable
, argv
[i
]);
270 tagPtr
= (TkTextTag
*) Tcl_GetHashValue(hPtr
);
271 if (tagPtr
== textPtr
->selTagPtr
) {
272 interp
->result
= "can't delete selection tag";
275 if (TK_TAG_AFFECTS_DISPLAY(tagPtr
)) {
276 TkTextRedrawTag(textPtr
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
279 TkBTreeTag(textPtr
->tree
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
281 Tcl_DeleteHashEntry(hPtr
);
282 if (textPtr
->bindingTable
!= NULL
) {
283 Tk_DeleteAllBindings(textPtr
->bindingTable
,
284 (ClientData
) tagPtr
);
288 * Update the tag priorities to reflect the deletion of this tag.
291 ChangeTagPriority(textPtr
, tagPtr
, textPtr
->numTags
-1);
292 textPtr
->numTags
-= 1;
293 TkTextFreeTag(tagPtr
);
295 } else if ((c
== 'l') && (strncmp(argv
[2], "lower", length
) == 0)) {
299 if ((argc
!= 4) && (argc
!= 5)) {
300 Tcl_AppendResult(interp
, "wrong # args: should be \"",
301 argv
[0], " tag lower tagName ?belowThis?\"",
305 tagPtr
= FindTag(interp
, textPtr
, argv
[3]);
306 if (tagPtr
== NULL
) {
310 tagPtr2
= FindTag(interp
, textPtr
, argv
[4]);
311 if (tagPtr2
== NULL
) {
314 if (tagPtr
->priority
< tagPtr2
->priority
) {
315 prio
= tagPtr2
->priority
- 1;
317 prio
= tagPtr2
->priority
;
322 ChangeTagPriority(textPtr
, tagPtr
, prio
);
323 TkTextRedrawTag(textPtr
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
325 } else if ((c
== 'n') && (strncmp(argv
[2], "names", length
) == 0)
327 TkTextTag
**arrayPtr
;
331 if ((argc
!= 3) && (argc
!= 4)) {
332 Tcl_AppendResult(interp
, "wrong # args: should be \"",
333 argv
[0], " tag names ?index?\"",
338 Tcl_HashSearch search
;
341 arrayPtr
= (TkTextTag
**) ckalloc((unsigned)
342 (textPtr
->numTags
* sizeof(TkTextTag
*)));
343 for (i
= 0, hPtr
= Tcl_FirstHashEntry(&textPtr
->tagTable
, &search
);
344 hPtr
!= NULL
; i
++, hPtr
= Tcl_NextHashEntry(&search
)) {
345 arrayPtr
[i
] = (TkTextTag
*) Tcl_GetHashValue(hPtr
);
347 arraySize
= textPtr
->numTags
;
349 if (TkTextGetIndex(interp
, textPtr
, argv
[3], &line1
, &ch1
)
353 linePtr
= TkBTreeFindLine(textPtr
->tree
, line1
);
354 if (linePtr
== NULL
) {
357 arrayPtr
= TkBTreeGetTags(textPtr
->tree
, linePtr
, ch1
, &arraySize
);
358 if (arrayPtr
== NULL
) {
362 SortTags(arraySize
, arrayPtr
);
363 for (i
= 0; i
< arraySize
; i
++) {
364 tagPtr
= arrayPtr
[i
];
365 Tcl_AppendElement(interp
, tagPtr
->name
, 0);
367 ckfree((char *) arrayPtr
);
368 } else if ((c
== 'n') && (strncmp(argv
[2], "nextrange", length
) == 0)
370 TkTextSearch tSearch
;
371 char position
[POS_CHARS
];
373 if ((argc
!= 5) && (argc
!= 6)) {
374 Tcl_AppendResult(interp
, "wrong # args: should be \"",
375 argv
[0], " tag nextrange tagName index1 ?index2?\"",
379 tagPtr
= FindTag((Tcl_Interp
*) NULL
, textPtr
, argv
[3]);
380 if (tagPtr
== NULL
) {
383 if (TkTextGetIndex(interp
, textPtr
, argv
[4], &line1
, &ch1
) != TCL_OK
) {
387 line2
= TkBTreeNumLines(textPtr
->tree
);
389 } else if (TkTextGetIndex(interp
, textPtr
, argv
[5], &line2
, &ch2
)
395 * The search below is a bit tricky. Rather than use the B-tree
396 * facilities to stop the search at line2.ch2, let it search up
397 * until the end of the file but check for a position past line2.ch2
398 * ourselves. The reason for doing it this way is that we only
399 * care whether the *start* of the range is before line2.ch2; once
400 * we find the start, we don't want TkBTreeNextTag to abort the
401 * search because the end of the range is after line2.ch2.
404 TkBTreeStartSearch(textPtr
->tree
, line1
, ch1
,
405 TkBTreeNumLines(textPtr
->tree
), 0, tagPtr
, &tSearch
);
406 if (!TkBTreeNextTag(&tSearch
)) {
409 if (!TkBTreeCharTagged(tSearch
.linePtr
, tSearch
.ch1
, tagPtr
)) {
410 if (!TkBTreeNextTag(&tSearch
)) {
414 if ((tSearch
.line1
> line2
) || ((tSearch
.line1
== line2
)
415 && (tSearch
.ch1
>= ch2
))) {
418 TkTextPrintIndex(tSearch
.line1
, tSearch
.ch1
, position
);
419 Tcl_AppendElement(interp
, position
, 0);
420 TkBTreeNextTag(&tSearch
);
421 TkTextPrintIndex(tSearch
.line1
, tSearch
.ch1
, position
);
422 Tcl_AppendElement(interp
, position
, 0);
423 } else if ((c
== 'r') && (strncmp(argv
[2], "raise", length
) == 0)
428 if ((argc
!= 4) && (argc
!= 5)) {
429 Tcl_AppendResult(interp
, "wrong # args: should be \"",
430 argv
[0], " tag raise tagName ?aboveThis?\"",
434 tagPtr
= FindTag(interp
, textPtr
, argv
[3]);
435 if (tagPtr
== NULL
) {
439 tagPtr2
= FindTag(interp
, textPtr
, argv
[4]);
440 if (tagPtr2
== NULL
) {
443 if (tagPtr
->priority
<= tagPtr2
->priority
) {
444 prio
= tagPtr2
->priority
;
446 prio
= tagPtr2
->priority
+ 1;
449 prio
= textPtr
->numTags
-1;
451 ChangeTagPriority(textPtr
, tagPtr
, prio
);
452 TkTextRedrawTag(textPtr
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
454 } else if ((c
== 'r') && (strncmp(argv
[2], "ranges", length
) == 0)
456 TkTextSearch tSearch
;
457 char position
[POS_CHARS
];
460 Tcl_AppendResult(interp
, "wrong # args: should be \"",
461 argv
[0], " tag ranges tagName\"", (char *) NULL
);
464 tagPtr
= FindTag((Tcl_Interp
*) NULL
, textPtr
, argv
[3]);
465 if (tagPtr
== NULL
) {
468 TkBTreeStartSearch(textPtr
->tree
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
469 0, tagPtr
, &tSearch
);
470 while (TkBTreeNextTag(&tSearch
)) {
471 TkTextPrintIndex(tSearch
.line1
, tSearch
.ch1
, position
);
472 Tcl_AppendElement(interp
, position
, 0);
474 } else if ((c
== 'r') && (strncmp(argv
[2], "remove", length
) == 0)
476 fullOption
= "remove";
480 Tcl_AppendResult(interp
, "bad tag option \"", argv
[2],
481 "\": must be add, bind, configure, delete, lower, ",
482 "names, nextrange, raise, ranges, or remove",
490 *----------------------------------------------------------------------
494 * Find the record describing a tag within a given text widget,
495 * creating a new record if one doesn't already exist.
498 * The return value is a pointer to the TkTextTag record for tagName.
501 * A new tag record is created if there isn't one already defined
504 *----------------------------------------------------------------------
509 TkText
*textPtr
, /* Widget in which tag is being used. */
510 char *tagName
/* Name of desired tag. */
513 register TkTextTag
*tagPtr
;
517 hPtr
= Tcl_CreateHashEntry(&textPtr
->tagTable
, tagName
, &new);
519 return (TkTextTag
*) Tcl_GetHashValue(hPtr
);
523 * No existing entry. Create a new one, initialize it, and add a
524 * pointer to it to the hash table entry.
527 tagPtr
= (TkTextTag
*) ckalloc(sizeof(TkTextTag
));
528 tagPtr
->name
= Tcl_GetHashKey(&textPtr
->tagTable
, hPtr
);
529 tagPtr
->priority
= textPtr
->numTags
;
530 tagPtr
->border
= NULL
;
531 tagPtr
->borderWidth
= 1;
532 tagPtr
->relief
= TK_RELIEF_FLAT
;
533 tagPtr
->bgStipple
= None
;
534 tagPtr
->fgColor
= NULL
;
535 tagPtr
->fontPtr
= NULL
;
536 tagPtr
->fgStipple
= None
;
537 tagPtr
->underline
= 0;
539 Tcl_SetHashValue(hPtr
, tagPtr
);
544 *----------------------------------------------------------------------
548 * See if tag is defined for a given widget.
551 * If tagName is defined in textPtr, a pointer to its TkTextTag
552 * structure is returned. Otherwise NULL is returned and an
553 * error message is recorded in interp->result unless interp
559 *----------------------------------------------------------------------
564 Tcl_Interp
*interp
, /* Interpreter to use for error message;
565 * if NULL, then don't record an error
567 TkText
*textPtr
, /* Widget in which tag is being used. */
568 char *tagName
/* Name of desired tag. */
573 hPtr
= Tcl_FindHashEntry(&textPtr
->tagTable
, tagName
);
575 return (TkTextTag
*) Tcl_GetHashValue(hPtr
);
577 if (interp
!= NULL
) {
578 Tcl_AppendResult(interp
, "tag \"", tagName
,
579 "\" isn't defined in text widget", (char *) NULL
);
585 *----------------------------------------------------------------------
589 * This procedure is called when a tag is deleted to free up the
590 * memory and other resources associated with the tag.
596 * Memory and other resources are freed.
598 *----------------------------------------------------------------------
603 register TkTextTag
*tagPtr
/* Tag being deleted. */
606 if (tagPtr
->border
!= None
) {
607 Tk_Free3DBorder(tagPtr
->border
);
609 if (tagPtr
->bgStipple
!= None
) {
610 Tk_FreeBitmap(tagPtr
->bgStipple
);
612 if (tagPtr
->fgColor
!= None
) {
613 Tk_FreeColor(tagPtr
->fgColor
);
615 if (tagPtr
->fgStipple
!= None
) {
616 Tk_FreeBitmap(tagPtr
->fgStipple
);
618 ckfree((char *) tagPtr
);
622 *----------------------------------------------------------------------
626 * This procedure sorts an array of tag pointers in increasing
627 * order of priority, optimizing for the common case where the
636 *----------------------------------------------------------------------
641 int numTags
, /* Number of tag pointers at *tagArrayPtr. */
642 TkTextTag
**tagArrayPtr
/* Pointer to array of pointers. */
646 register TkTextTag
**tagPtrPtr
;
647 TkTextTag
**maxPtrPtr
, *tmp
;
653 for (i
= numTags
-1; i
> 0; i
--, tagArrayPtr
++) {
654 maxPtrPtr
= tagPtrPtr
= tagArrayPtr
;
655 prio
= tagPtrPtr
[0]->priority
;
656 for (j
= i
, tagPtrPtr
++; j
> 0; j
--, tagPtrPtr
++) {
657 if (tagPtrPtr
[0]->priority
< prio
) {
658 prio
= tagPtrPtr
[0]->priority
;
659 maxPtrPtr
= tagPtrPtr
;
663 *maxPtrPtr
= *tagArrayPtr
;
667 qsort((VOID
*) tagArrayPtr
, numTags
, sizeof (TkTextTag
*),
673 *----------------------------------------------------------------------
677 * This procedure is called by qsort when sorting an array of
678 * tags in priority order.
681 * The return value is -1 if the first argument should be before
682 * the second element (i.e. it has lower priority), 0 if it's
683 * equivalent (this should never happen!), and 1 if it should be
684 * after the second element.
689 *----------------------------------------------------------------------
695 CONST VOID
*second
/* Elements to be compared. */
698 TkTextTag
*tagPtr1
, *tagPtr2
;
700 tagPtr1
= * (TkTextTag
**) first
;
701 tagPtr2
= * (TkTextTag
**) second
;
702 return tagPtr1
->priority
- tagPtr2
->priority
;
706 *----------------------------------------------------------------------
708 * ChangeTagPriority --
710 * This procedure changes the priority of a tag by modifying
711 * its priority and all other ones whose priority is affected
718 * Priorities may be changed for some or all of the tags in
719 * textPtr. The tags will be arranged so that there is exactly
720 * one tag at each priority level between 0 and textPtr->numTags-1,
721 * with tagPtr at priority "prio".
723 *----------------------------------------------------------------------
728 TkText
*textPtr
, /* Information about text widget. */
729 TkTextTag
*tagPtr
, /* Tag whose priority is to be
731 int prio
/* New priority for tag. */
734 int low
, high
, delta
;
735 register TkTextTag
*tagPtr2
;
737 Tcl_HashSearch search
;
742 if (prio
>= textPtr
->numTags
) {
743 prio
= textPtr
->numTags
-1;
745 if (prio
== tagPtr
->priority
) {
747 } else if (prio
< tagPtr
->priority
) {
749 high
= tagPtr
->priority
-1;
752 low
= tagPtr
->priority
+1;
756 for (hPtr
= Tcl_FirstHashEntry(&textPtr
->tagTable
, &search
);
757 hPtr
!= NULL
; hPtr
= Tcl_NextHashEntry(&search
)) {
758 tagPtr2
= (TkTextTag
*) Tcl_GetHashValue(hPtr
);
759 if ((tagPtr2
->priority
>= low
) && (tagPtr2
->priority
<= high
)) {
760 tagPtr2
->priority
+= delta
;
763 tagPtr
->priority
= prio
;
767 *--------------------------------------------------------------
771 * This procedure is invoked by the Tk dispatcher to handle
772 * events associated with bindings on items.
778 * Depends on the command invoked as part of the binding
779 * (if there was any).
781 *--------------------------------------------------------------
786 ClientData clientData
, /* Pointer to canvas structure. */
787 XEvent
*eventPtr
/* Pointer to X event that just
791 TkText
*textPtr
= (TkText
*) clientData
;
794 Tk_Preserve((ClientData
) textPtr
);
797 * This code simulates grabs for mouse buttons by refusing to
798 * pick a new current character between the time a mouse button goes
799 * down and the time when the last mouse button is released.
802 if (eventPtr
->type
== ButtonPress
) {
803 textPtr
->flags
|= BUTTON_DOWN
;
804 } else if (eventPtr
->type
== ButtonRelease
) {
807 switch (eventPtr
->xbutton
.button
) {
827 if ((eventPtr
->xbutton
.state
& (Button1Mask
|Button2Mask
828 |Button3Mask
|Button4Mask
|Button5Mask
)) == mask
) {
829 textPtr
->flags
&= ~BUTTON_DOWN
;
832 } else if ((eventPtr
->type
== EnterNotify
)
833 || (eventPtr
->type
== LeaveNotify
)) {
834 TkTextPickCurrent(textPtr
, eventPtr
);
836 } else if (eventPtr
->type
== MotionNotify
) {
837 TkTextPickCurrent(textPtr
, eventPtr
);
839 TextDoEvent(textPtr
, eventPtr
);
841 unsigned int oldState
;
843 oldState
= eventPtr
->xbutton
.state
;
844 eventPtr
->xbutton
.state
&= ~(Button1Mask
|Button2Mask
845 |Button3Mask
|Button4Mask
|Button5Mask
);
846 TkTextPickCurrent(textPtr
, eventPtr
);
847 eventPtr
->xbutton
.state
= oldState
;
851 Tk_Release((ClientData
) textPtr
);
855 *--------------------------------------------------------------
857 * TkTextPickCurrent --
859 * Find the topmost item in a canvas that contains a given
860 * location and mark the the current item. If the current
861 * item has changed, generate a fake exit event on the old
862 * current item and a fake enter event on the new current
869 * The current item for textPtr may change. If it does,
870 * then the commands associated with item entry and exit
871 * could do just about anything.
873 *--------------------------------------------------------------
878 register TkText
*textPtr
, /* Text widget in which to select
879 * current character. */
880 XEvent
*eventPtr
/* Event describing location of
881 * mouse cursor. Must be EnterWindow,
882 * LeaveWindow, ButtonRelease, or
890 * If a button is down, then don't do anything at all; we'll be
891 * called again when all buttons are up, and we can repick then.
892 * This implements a form of mouse grabbing.
895 if (textPtr
->flags
& BUTTON_DOWN
) {
900 * Save information about this event in the widget for use if we have
901 * to synthesize more enter and leave events later (e.g. because a
902 * character was deleting, causing a new character to be underneath
903 * the mouse cursor). Also translate MotionNotify events into
904 * EnterNotify events, since that's what gets reported to event
905 * handlers when the current character changes.
908 if (eventPtr
!= &textPtr
->pickEvent
) {
909 if ((eventPtr
->type
== MotionNotify
)
910 || (eventPtr
->type
== ButtonRelease
)) {
911 textPtr
->pickEvent
.xcrossing
.type
= EnterNotify
;
912 textPtr
->pickEvent
.xcrossing
.serial
= eventPtr
->xmotion
.serial
;
913 textPtr
->pickEvent
.xcrossing
.send_event
914 = eventPtr
->xmotion
.send_event
;
915 textPtr
->pickEvent
.xcrossing
.display
= eventPtr
->xmotion
.display
;
916 textPtr
->pickEvent
.xcrossing
.window
= eventPtr
->xmotion
.window
;
917 textPtr
->pickEvent
.xcrossing
.root
= eventPtr
->xmotion
.root
;
918 textPtr
->pickEvent
.xcrossing
.subwindow
= None
;
919 textPtr
->pickEvent
.xcrossing
.time
= eventPtr
->xmotion
.time
;
920 textPtr
->pickEvent
.xcrossing
.x
= eventPtr
->xmotion
.x
;
921 textPtr
->pickEvent
.xcrossing
.y
= eventPtr
->xmotion
.y
;
922 textPtr
->pickEvent
.xcrossing
.x_root
= eventPtr
->xmotion
.x_root
;
923 textPtr
->pickEvent
.xcrossing
.y_root
= eventPtr
->xmotion
.y_root
;
924 textPtr
->pickEvent
.xcrossing
.mode
= NotifyNormal
;
925 textPtr
->pickEvent
.xcrossing
.detail
= NotifyNonlinear
;
926 textPtr
->pickEvent
.xcrossing
.same_screen
927 = eventPtr
->xmotion
.same_screen
;
928 textPtr
->pickEvent
.xcrossing
.focus
= False
;
929 textPtr
->pickEvent
.xcrossing
.state
= eventPtr
->xmotion
.state
;
931 textPtr
->pickEvent
= *eventPtr
;
936 if (textPtr
->pickEvent
.type
!= LeaveNotify
) {
937 linePtr
= TkTextCharAtLoc(textPtr
, textPtr
->pickEvent
.xcrossing
.x
,
938 textPtr
->pickEvent
.xcrossing
.y
, &ch
);
942 * Simulate a LeaveNotify event on the previous current character and
943 * an EnterNotify event on the new current character. Also, move the
944 * "current" mark to its new place.
947 if (textPtr
->flags
& IN_CURRENT
) {
948 if ((linePtr
== textPtr
->currentAnnotPtr
->linePtr
)
949 && (ch
== textPtr
->currentAnnotPtr
->ch
)) {
953 if (linePtr
== NULL
) {
957 if (textPtr
->flags
& IN_CURRENT
) {
960 event
= textPtr
->pickEvent
;
961 event
.type
= LeaveNotify
;
962 TextDoEvent(textPtr
, &event
);
963 textPtr
->flags
&= ~IN_CURRENT
;
965 if (linePtr
!= NULL
) {
968 TkBTreeRemoveAnnotation(textPtr
->currentAnnotPtr
);
969 textPtr
->currentAnnotPtr
->linePtr
= linePtr
;
970 textPtr
->currentAnnotPtr
->ch
= ch
;
971 TkBTreeAddAnnotation(textPtr
->currentAnnotPtr
);
972 event
= textPtr
->pickEvent
;
973 event
.type
= EnterNotify
;
974 TextDoEvent(textPtr
, &event
);
975 textPtr
->flags
|= IN_CURRENT
;
980 *----------------------------------------------------------------------
982 * TkTextUnpickCurrent --
984 * This procedure is called when the "current" character is
985 * deleted: it synthesizes a "leave" event for the character.
991 * A binding associated with one of the tags on the current
992 * character may be triggered.
994 *----------------------------------------------------------------------
998 TkTextUnpickCurrent (
999 TkText
*textPtr
/* Text widget information. */
1002 if (textPtr
->flags
& IN_CURRENT
) {
1005 event
= textPtr
->pickEvent
;
1006 event
.type
= LeaveNotify
;
1007 TextDoEvent(textPtr
, &event
);
1008 textPtr
->flags
&= ~IN_CURRENT
;
1013 *--------------------------------------------------------------
1017 * This procedure is called to invoke binding processing
1018 * for a new event that is associated with the current character
1019 * for a text widget.
1025 * Depends on the bindings for the text.
1027 *--------------------------------------------------------------
1032 TkText
*textPtr
, /* Text widget in which event
1034 XEvent
*eventPtr
/* Real or simulated X event that
1035 * is to be processed. */
1038 TkTextTag
**tagArrayPtr
, **p1
, **p2
, *tmp
;
1041 if (textPtr
->bindingTable
== NULL
) {
1046 * Set up an array containing all of the tags that are associated
1047 * with the current character. This array will be used to look
1048 * for bindings. If there are no tags then there can't be any
1052 tagArrayPtr
= TkBTreeGetTags(textPtr
->tree
,
1053 textPtr
->currentAnnotPtr
->linePtr
, textPtr
->currentAnnotPtr
->ch
,
1060 * Sort the array of tags. SortTags sorts it backwards, so after it
1061 * returns we have to reverse the order in the array.
1064 SortTags(numTags
, tagArrayPtr
);
1065 for (p1
= tagArrayPtr
, p2
= tagArrayPtr
+ numTags
- 1;
1066 p1
< p2
; p1
++, p2
--) {
1073 * Invoke the binding system, then free up the tag array.
1076 Tk_BindEvent(textPtr
->bindingTable
, eventPtr
, textPtr
->tkwin
,
1077 numTags
, (ClientData
*) tagArrayPtr
);
1078 ckfree((char *) tagArrayPtr
);