/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tktexttag.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tktexttag.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (hide annotations) (download)
Sat Nov 5 10:54:17 2016 UTC (7 years, 8 months ago) by dashley
File MIME type: text/plain
File size: 44009 byte(s)
License and property (keyword) changes.
1 dashley 69 /* $Header$ */
2 dashley 25
3     /*
4     * tkTextTag.c --
5     *
6     * This module implements the "tag" subcommand of the widget command
7     * for text widgets, plus most of the other high-level functions
8     * related to tags.
9     *
10     * Copyright (c) 1992-1994 The Regents of the University of California.
11     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12     *
13     * See the file "license.terms" for information on usage and redistribution
14     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15     *
16     * RCS: @(#) $Id: tktexttag.c,v 1.1.1.1 2001/06/13 05:10:53 dtashley Exp $
17     */
18    
19     #include "default.h"
20     #include "tkPort.h"
21     #include "tkInt.h"
22     #include "tkText.h"
23    
24     static Tk_ConfigSpec tagConfigSpecs[] = {
25     {TK_CONFIG_BORDER, "-background", (char *) NULL, (char *) NULL,
26     (char *) NULL, Tk_Offset(TkTextTag, border), TK_CONFIG_NULL_OK},
27     {TK_CONFIG_BITMAP, "-bgstipple", (char *) NULL, (char *) NULL,
28     (char *) NULL, Tk_Offset(TkTextTag, bgStipple), TK_CONFIG_NULL_OK},
29     {TK_CONFIG_STRING, "-borderwidth", (char *) NULL, (char *) NULL,
30     "0", Tk_Offset(TkTextTag, bdString),
31     TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
32     {TK_CONFIG_STRING, "-elide", (char *) NULL, (char *) NULL,
33     "0", Tk_Offset(TkTextTag, elideString),
34     TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
35     {TK_CONFIG_BITMAP, "-fgstipple", (char *) NULL, (char *) NULL,
36     (char *) NULL, Tk_Offset(TkTextTag, fgStipple), TK_CONFIG_NULL_OK},
37     {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
38     (char *) NULL, Tk_Offset(TkTextTag, tkfont), TK_CONFIG_NULL_OK},
39     {TK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
40     (char *) NULL, Tk_Offset(TkTextTag, fgColor), TK_CONFIG_NULL_OK},
41     {TK_CONFIG_STRING, "-justify", (char *) NULL, (char *) NULL,
42     (char *) NULL, Tk_Offset(TkTextTag, justifyString), TK_CONFIG_NULL_OK},
43     {TK_CONFIG_STRING, "-lmargin1", (char *) NULL, (char *) NULL,
44     (char *) NULL, Tk_Offset(TkTextTag, lMargin1String), TK_CONFIG_NULL_OK},
45     {TK_CONFIG_STRING, "-lmargin2", (char *) NULL, (char *) NULL,
46     (char *) NULL, Tk_Offset(TkTextTag, lMargin2String), TK_CONFIG_NULL_OK},
47     {TK_CONFIG_STRING, "-offset", (char *) NULL, (char *) NULL,
48     (char *) NULL, Tk_Offset(TkTextTag, offsetString), TK_CONFIG_NULL_OK},
49     {TK_CONFIG_STRING, "-overstrike", (char *) NULL, (char *) NULL,
50     (char *) NULL, Tk_Offset(TkTextTag, overstrikeString),
51     TK_CONFIG_NULL_OK},
52     {TK_CONFIG_STRING, "-relief", (char *) NULL, (char *) NULL,
53     (char *) NULL, Tk_Offset(TkTextTag, reliefString), TK_CONFIG_NULL_OK},
54     {TK_CONFIG_STRING, "-rmargin", (char *) NULL, (char *) NULL,
55     (char *) NULL, Tk_Offset(TkTextTag, rMarginString), TK_CONFIG_NULL_OK},
56     {TK_CONFIG_STRING, "-spacing1", (char *) NULL, (char *) NULL,
57     (char *) NULL, Tk_Offset(TkTextTag, spacing1String), TK_CONFIG_NULL_OK},
58     {TK_CONFIG_STRING, "-spacing2", (char *) NULL, (char *) NULL,
59     (char *) NULL, Tk_Offset(TkTextTag, spacing2String), TK_CONFIG_NULL_OK},
60     {TK_CONFIG_STRING, "-spacing3", (char *) NULL, (char *) NULL,
61     (char *) NULL, Tk_Offset(TkTextTag, spacing3String), TK_CONFIG_NULL_OK},
62     {TK_CONFIG_STRING, "-tabs", (char *) NULL, (char *) NULL,
63     (char *) NULL, Tk_Offset(TkTextTag, tabString), TK_CONFIG_NULL_OK},
64     {TK_CONFIG_STRING, "-underline", (char *) NULL, (char *) NULL,
65     (char *) NULL, Tk_Offset(TkTextTag, underlineString),
66     TK_CONFIG_NULL_OK},
67     {TK_CONFIG_CUSTOM, "-wrap", (char *) NULL, (char *) NULL,
68     (char *) NULL, Tk_Offset(TkTextTag, wrapMode),
69     TK_CONFIG_NULL_OK,
70     &textWrapModeOption},
71     {TK_CONFIG_END,
72     (char *) NULL,
73     (char *) NULL,
74     (char *) NULL,
75     (char *) NULL,
76     0,
77     0}
78     };
79    
80     /*
81     * Forward declarations for procedures defined later in this file:
82     */
83    
84     static void ChangeTagPriority _ANSI_ARGS_((TkText *textPtr,
85     TkTextTag *tagPtr, int prio));
86     static TkTextTag * FindTag _ANSI_ARGS_((Tcl_Interp *interp,
87     TkText *textPtr, char *tagName));
88     static void SortTags _ANSI_ARGS_((int numTags,
89     TkTextTag **tagArrayPtr));
90     static int TagSortProc _ANSI_ARGS_((CONST VOID *first,
91     CONST VOID *second));
92    
93     /*
94     *--------------------------------------------------------------
95     *
96     * TkTextTagCmd --
97     *
98     * This procedure is invoked to process the "tag" options of
99     * the widget command for text widgets. See the user documentation
100     * for details on what it does.
101     *
102     * Results:
103     * A standard Tcl result.
104     *
105     * Side effects:
106     * See the user documentation.
107     *
108     *--------------------------------------------------------------
109     */
110    
111     int
112     TkTextTagCmd(textPtr, interp, argc, argv)
113     register TkText *textPtr; /* Information about text widget. */
114     Tcl_Interp *interp; /* Current interpreter. */
115     int argc; /* Number of arguments. */
116     char **argv; /* Argument strings. Someone else has already
117     * parsed this command enough to know that
118     * argv[1] is "tag". */
119     {
120     int c, i, addTag;
121     size_t length;
122     char *fullOption;
123     register TkTextTag *tagPtr;
124     TkTextIndex first, last, index1, index2;
125    
126     if (argc < 3) {
127     Tcl_AppendResult(interp, "wrong # args: should be \"",
128     argv[0], " tag option ?arg arg ...?\"", (char *) NULL);
129     return TCL_ERROR;
130     }
131     c = argv[2][0];
132     length = strlen(argv[2]);
133     if ((c == 'a') && (strncmp(argv[2], "add", length) == 0)) {
134     fullOption = "add";
135     addTag = 1;
136    
137     addAndRemove:
138     if (argc < 5) {
139     Tcl_AppendResult(interp, "wrong # args: should be \"",
140     argv[0], " tag ", fullOption,
141     " tagName index1 ?index2 index1 index2 ...?\"",
142     (char *) NULL);
143     return TCL_ERROR;
144     }
145     tagPtr = TkTextCreateTag(textPtr, argv[3]);
146     for (i = 4; i < argc; i += 2) {
147     if (TkTextGetIndex(interp, textPtr, argv[i], &index1) != TCL_OK) {
148     return TCL_ERROR;
149     }
150     if (argc > (i+1)) {
151     if (TkTextGetIndex(interp, textPtr, argv[i+1], &index2)
152     != TCL_OK) {
153     return TCL_ERROR;
154     }
155     if (TkTextIndexCmp(&index1, &index2) >= 0) {
156     return TCL_OK;
157     }
158     } else {
159     index2 = index1;
160     TkTextIndexForwChars(&index2, 1, &index2);
161     }
162    
163     if (tagPtr->affectsDisplay) {
164     TkTextRedrawTag(textPtr, &index1, &index2, tagPtr, !addTag);
165     } else {
166     /*
167     * Still need to trigger enter/leave events on tags that
168     * have changed.
169     */
170    
171     TkTextEventuallyRepick(textPtr);
172     }
173     TkBTreeTag(&index1, &index2, tagPtr, addTag);
174    
175     /*
176     * If the tag is "sel" then grab the selection if we're supposed
177     * to export it and don't already have it. Also, invalidate
178     * partially-completed selection retrievals.
179     */
180    
181     if (tagPtr == textPtr->selTagPtr) {
182     if (addTag && textPtr->exportSelection
183     && !(textPtr->flags & GOT_SELECTION)) {
184     Tk_OwnSelection(textPtr->tkwin, XA_PRIMARY,
185     TkTextLostSelection, (ClientData) textPtr);
186     textPtr->flags |= GOT_SELECTION;
187     }
188     textPtr->abortSelections = 1;
189     }
190     }
191     } else if ((c == 'b') && (strncmp(argv[2], "bind", length) == 0)) {
192     if ((argc < 4) || (argc > 6)) {
193     Tcl_AppendResult(interp, "wrong # args: should be \"",
194     argv[0], " tag bind tagName ?sequence? ?command?\"",
195     (char *) NULL);
196     return TCL_ERROR;
197     }
198     tagPtr = TkTextCreateTag(textPtr, argv[3]);
199    
200     /*
201     * Make a binding table if the widget doesn't already have
202     * one.
203     */
204    
205     if (textPtr->bindingTable == NULL) {
206     textPtr->bindingTable = Tk_CreateBindingTable(interp);
207     }
208    
209     if (argc == 6) {
210     int append = 0;
211     unsigned long mask;
212    
213     if (argv[5][0] == 0) {
214     return Tk_DeleteBinding(interp, textPtr->bindingTable,
215     (ClientData) tagPtr, argv[4]);
216     }
217     if (argv[5][0] == '+') {
218     argv[5]++;
219     append = 1;
220     }
221     mask = Tk_CreateBinding(interp, textPtr->bindingTable,
222     (ClientData) tagPtr, argv[4], argv[5], append);
223     if (mask == 0) {
224     return TCL_ERROR;
225     }
226     if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
227     |Button2MotionMask|Button3MotionMask|Button4MotionMask
228     |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
229     |EnterWindowMask|LeaveWindowMask|KeyPressMask
230     |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
231     Tk_DeleteBinding(interp, textPtr->bindingTable,
232     (ClientData) tagPtr, argv[4]);
233     Tcl_ResetResult(interp);
234     Tcl_AppendResult(interp, "requested illegal events; ",
235     "only key, button, motion, enter, leave, and virtual ",
236     "events may be used", (char *) NULL);
237     return TCL_ERROR;
238     }
239     } else if (argc == 5) {
240     char *command;
241    
242     command = Tk_GetBinding(interp, textPtr->bindingTable,
243     (ClientData) tagPtr, argv[4]);
244     if (command == NULL) {
245     char *string = Tcl_GetStringResult(interp);
246    
247     /*
248     * Ignore missing binding errors. This is a special hack
249     * that relies on the error message returned by FindSequence
250     * in tkBind.c.
251     */
252    
253     if (string[0] != '\0') {
254     return TCL_ERROR;
255     } else {
256     Tcl_ResetResult(interp);
257     }
258     } else {
259     Tcl_SetResult(interp, command, TCL_STATIC);
260     }
261     } else {
262     Tk_GetAllBindings(interp, textPtr->bindingTable,
263     (ClientData) tagPtr);
264     }
265     } else if ((c == 'c') && (strncmp(argv[2], "cget", length) == 0)
266     && (length >= 2)) {
267     if (argc != 5) {
268     Tcl_AppendResult(interp, "wrong # args: should be \"",
269     argv[0], " tag cget tagName option\"",
270     (char *) NULL);
271     return TCL_ERROR;
272     }
273     tagPtr = FindTag(interp, textPtr, argv[3]);
274     if (tagPtr == NULL) {
275     return TCL_ERROR;
276     }
277     return Tk_ConfigureValue(interp, textPtr->tkwin, tagConfigSpecs,
278     (char *) tagPtr, argv[4], 0);
279     } else if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)
280     && (length >= 2)) {
281     if (argc < 4) {
282     Tcl_AppendResult(interp, "wrong # args: should be \"",
283     argv[0], " tag configure tagName ?option? ?value? ",
284     "?option value ...?\"", (char *) NULL);
285     return TCL_ERROR;
286     }
287     tagPtr = TkTextCreateTag(textPtr, argv[3]);
288     if (argc == 4) {
289     return Tk_ConfigureInfo(interp, textPtr->tkwin, tagConfigSpecs,
290     (char *) tagPtr, (char *) NULL, 0);
291     } else if (argc == 5) {
292     return Tk_ConfigureInfo(interp, textPtr->tkwin, tagConfigSpecs,
293     (char *) tagPtr, argv[4], 0);
294     } else {
295     int result;
296    
297     result = Tk_ConfigureWidget(interp, textPtr->tkwin, tagConfigSpecs,
298     argc-4, argv+4, (char *) tagPtr, 0);
299     /*
300     * Some of the configuration options, like -underline
301     * and -justify, require additional translation (this is
302     * needed because we need to distinguish a particular value
303     * of an option from "unspecified").
304     */
305    
306     if (tagPtr->bdString != NULL) {
307     if (Tk_GetPixels(interp, textPtr->tkwin, tagPtr->bdString,
308     &tagPtr->borderWidth) != TCL_OK) {
309     return TCL_ERROR;
310     }
311     if (tagPtr->borderWidth < 0) {
312     tagPtr->borderWidth = 0;
313     }
314     }
315     if (tagPtr->reliefString != NULL) {
316     if (Tk_GetRelief(interp, tagPtr->reliefString,
317     &tagPtr->relief) != TCL_OK) {
318     return TCL_ERROR;
319     }
320     }
321     if (tagPtr->justifyString != NULL) {
322     if (Tk_GetJustify(interp, tagPtr->justifyString,
323     &tagPtr->justify) != TCL_OK) {
324     return TCL_ERROR;
325     }
326     }
327     if (tagPtr->lMargin1String != NULL) {
328     if (Tk_GetPixels(interp, textPtr->tkwin,
329     tagPtr->lMargin1String, &tagPtr->lMargin1) != TCL_OK) {
330     return TCL_ERROR;
331     }
332     }
333     if (tagPtr->lMargin2String != NULL) {
334     if (Tk_GetPixels(interp, textPtr->tkwin,
335     tagPtr->lMargin2String, &tagPtr->lMargin2) != TCL_OK) {
336     return TCL_ERROR;
337     }
338     }
339     if (tagPtr->offsetString != NULL) {
340     if (Tk_GetPixels(interp, textPtr->tkwin, tagPtr->offsetString,
341     &tagPtr->offset) != TCL_OK) {
342     return TCL_ERROR;
343     }
344     }
345     if (tagPtr->overstrikeString != NULL) {
346     if (Tcl_GetBoolean(interp, tagPtr->overstrikeString,
347     &tagPtr->overstrike) != TCL_OK) {
348     return TCL_ERROR;
349     }
350     }
351     if (tagPtr->rMarginString != NULL) {
352     if (Tk_GetPixels(interp, textPtr->tkwin,
353     tagPtr->rMarginString, &tagPtr->rMargin) != TCL_OK) {
354     return TCL_ERROR;
355     }
356     }
357     if (tagPtr->spacing1String != NULL) {
358     if (Tk_GetPixels(interp, textPtr->tkwin,
359     tagPtr->spacing1String, &tagPtr->spacing1) != TCL_OK) {
360     return TCL_ERROR;
361     }
362     if (tagPtr->spacing1 < 0) {
363     tagPtr->spacing1 = 0;
364     }
365     }
366     if (tagPtr->spacing2String != NULL) {
367     if (Tk_GetPixels(interp, textPtr->tkwin,
368     tagPtr->spacing2String, &tagPtr->spacing2) != TCL_OK) {
369     return TCL_ERROR;
370     }
371     if (tagPtr->spacing2 < 0) {
372     tagPtr->spacing2 = 0;
373     }
374     }
375     if (tagPtr->spacing3String != NULL) {
376     if (Tk_GetPixels(interp, textPtr->tkwin,
377     tagPtr->spacing3String, &tagPtr->spacing3) != TCL_OK) {
378     return TCL_ERROR;
379     }
380     if (tagPtr->spacing3 < 0) {
381     tagPtr->spacing3 = 0;
382     }
383     }
384     if (tagPtr->tabArrayPtr != NULL) {
385     ckfree((char *) tagPtr->tabArrayPtr);
386     tagPtr->tabArrayPtr = NULL;
387     }
388     if (tagPtr->tabString != NULL) {
389     tagPtr->tabArrayPtr = TkTextGetTabs(interp, textPtr->tkwin,
390     tagPtr->tabString);
391     if (tagPtr->tabArrayPtr == NULL) {
392     return TCL_ERROR;
393     }
394     }
395     if (tagPtr->underlineString != NULL) {
396     if (Tcl_GetBoolean(interp, tagPtr->underlineString,
397     &tagPtr->underline) != TCL_OK) {
398     return TCL_ERROR;
399     }
400     }
401     if (tagPtr->elideString != NULL) {
402     if (Tcl_GetBoolean(interp, tagPtr->elideString,
403     &tagPtr->elide) != TCL_OK) {
404     return TCL_ERROR;
405     }
406     }
407    
408     /*
409     * If the "sel" tag was changed, be sure to mirror information
410     * from the tag back into the text widget record. NOTE: we
411     * don't have to free up information in the widget record
412     * before overwriting it, because it was mirrored in the tag
413     * and hence freed when the tag field was overwritten.
414     */
415    
416     if (tagPtr == textPtr->selTagPtr) {
417     textPtr->selBorder = tagPtr->border;
418     textPtr->selBdString = tagPtr->bdString;
419     textPtr->selFgColorPtr = tagPtr->fgColor;
420     }
421     tagPtr->affectsDisplay = 0;
422     if ((tagPtr->border != NULL)
423     || (tagPtr->bdString != NULL)
424     || (tagPtr->reliefString != NULL)
425     || (tagPtr->bgStipple != None)
426     || (tagPtr->fgColor != NULL) || (tagPtr->tkfont != None)
427     || (tagPtr->fgStipple != None)
428     || (tagPtr->justifyString != NULL)
429     || (tagPtr->lMargin1String != NULL)
430     || (tagPtr->lMargin2String != NULL)
431     || (tagPtr->offsetString != NULL)
432     || (tagPtr->overstrikeString != NULL)
433     || (tagPtr->rMarginString != NULL)
434     || (tagPtr->spacing1String != NULL)
435     || (tagPtr->spacing2String != NULL)
436     || (tagPtr->spacing3String != NULL)
437     || (tagPtr->tabString != NULL)
438     || (tagPtr->underlineString != NULL)
439     || (tagPtr->elideString != NULL)
440     || (tagPtr->wrapMode != TEXT_WRAPMODE_NULL)) {
441     tagPtr->affectsDisplay = 1;
442     }
443     TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
444     (TkTextIndex *) NULL, tagPtr, 1);
445     return result;
446     }
447     } else if ((c == 'd') && (strncmp(argv[2], "delete", length) == 0)) {
448     Tcl_HashEntry *hPtr;
449    
450     if (argc < 4) {
451     Tcl_AppendResult(interp, "wrong # args: should be \"",
452     argv[0], " tag delete tagName tagName ...\"",
453     (char *) NULL);
454     return TCL_ERROR;
455     }
456     for (i = 3; i < argc; i++) {
457     hPtr = Tcl_FindHashEntry(&textPtr->tagTable, argv[i]);
458     if (hPtr == NULL) {
459     continue;
460     }
461     tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
462     if (tagPtr == textPtr->selTagPtr) {
463     continue;
464     }
465     if (tagPtr->affectsDisplay) {
466     TkTextRedrawTag(textPtr, (TkTextIndex *) NULL,
467     (TkTextIndex *) NULL, tagPtr, 1);
468     }
469     TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
470     TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
471     0, &last),
472     TkBTreeTag(&first, &last, tagPtr, 0);
473     Tcl_DeleteHashEntry(hPtr);
474     if (textPtr->bindingTable != NULL) {
475     Tk_DeleteAllBindings(textPtr->bindingTable,
476     (ClientData) tagPtr);
477     }
478    
479     /*
480     * Update the tag priorities to reflect the deletion of this tag.
481     */
482    
483     ChangeTagPriority(textPtr, tagPtr, textPtr->numTags-1);
484     textPtr->numTags -= 1;
485     TkTextFreeTag(textPtr, tagPtr);
486     }
487     } else if ((c == 'l') && (strncmp(argv[2], "lower", length) == 0)) {
488     TkTextTag *tagPtr2;
489     int prio;
490    
491     if ((argc != 4) && (argc != 5)) {
492     Tcl_AppendResult(interp, "wrong # args: should be \"",
493     argv[0], " tag lower tagName ?belowThis?\"",
494     (char *) NULL);
495     return TCL_ERROR;
496     }
497     tagPtr = FindTag(interp, textPtr, argv[3]);
498     if (tagPtr == NULL) {
499     return TCL_ERROR;
500     }
501     if (argc == 5) {
502     tagPtr2 = FindTag(interp, textPtr, argv[4]);
503     if (tagPtr2 == NULL) {
504     return TCL_ERROR;
505     }
506     if (tagPtr->priority < tagPtr2->priority) {
507     prio = tagPtr2->priority - 1;
508     } else {
509     prio = tagPtr2->priority;
510     }
511     } else {
512     prio = 0;
513     }
514     ChangeTagPriority(textPtr, tagPtr, prio);
515     TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
516     tagPtr, 1);
517     } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)
518     && (length >= 2)) {
519     TkTextTag **arrayPtr;
520     int arraySize;
521    
522     if ((argc != 3) && (argc != 4)) {
523     Tcl_AppendResult(interp, "wrong # args: should be \"",
524     argv[0], " tag names ?index?\"",
525     (char *) NULL);
526     return TCL_ERROR;
527     }
528     if (argc == 3) {
529     Tcl_HashSearch search;
530     Tcl_HashEntry *hPtr;
531    
532     arrayPtr = (TkTextTag **) ckalloc((unsigned)
533     (textPtr->numTags * sizeof(TkTextTag *)));
534     for (i = 0, hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
535     hPtr != NULL; i++, hPtr = Tcl_NextHashEntry(&search)) {
536     arrayPtr[i] = (TkTextTag *) Tcl_GetHashValue(hPtr);
537     }
538     arraySize = textPtr->numTags;
539     } else {
540     if (TkTextGetIndex(interp, textPtr, argv[3], &index1)
541     != TCL_OK) {
542     return TCL_ERROR;
543     }
544     arrayPtr = TkBTreeGetTags(&index1, &arraySize);
545     if (arrayPtr == NULL) {
546     return TCL_OK;
547     }
548     }
549     SortTags(arraySize, arrayPtr);
550     for (i = 0; i < arraySize; i++) {
551     tagPtr = arrayPtr[i];
552     Tcl_AppendElement(interp, tagPtr->name);
553     }
554     ckfree((char *) arrayPtr);
555     } else if ((c == 'n') && (strncmp(argv[2], "nextrange", length) == 0)
556     && (length >= 2)) {
557     TkTextSearch tSearch;
558     char position[TK_POS_CHARS];
559    
560     if ((argc != 5) && (argc != 6)) {
561     Tcl_AppendResult(interp, "wrong # args: should be \"",
562     argv[0], " tag nextrange tagName index1 ?index2?\"",
563     (char *) NULL);
564     return TCL_ERROR;
565     }
566     tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
567     if (tagPtr == NULL) {
568     return TCL_OK;
569     }
570     if (TkTextGetIndex(interp, textPtr, argv[4], &index1) != TCL_OK) {
571     return TCL_ERROR;
572     }
573     TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
574     0, &last);
575     if (argc == 5) {
576     index2 = last;
577     } else if (TkTextGetIndex(interp, textPtr, argv[5], &index2)
578     != TCL_OK) {
579     return TCL_ERROR;
580     }
581    
582     /*
583     * The search below is a bit tricky. Rather than use the B-tree
584     * facilities to stop the search at index2, let it search up
585     * until the end of the file but check for a position past index2
586     * ourselves. The reason for doing it this way is that we only
587     * care whether the *start* of the range is before index2; once
588     * we find the start, we don't want TkBTreeNextTag to abort the
589     * search because the end of the range is after index2.
590     */
591    
592     TkBTreeStartSearch(&index1, &last, tagPtr, &tSearch);
593     if (TkBTreeCharTagged(&index1, tagPtr)) {
594     TkTextSegment *segPtr;
595     int offset;
596    
597     /*
598     * The first character is tagged. See if there is an
599     * on-toggle just before the character. If not, then
600     * skip to the end of this tagged range.
601     */
602    
603     for (segPtr = index1.linePtr->segPtr, offset = index1.byteIndex;
604     offset >= 0;
605     offset -= segPtr->size, segPtr = segPtr->nextPtr) {
606     if ((offset == 0) && (segPtr->typePtr == &tkTextToggleOnType)
607     && (segPtr->body.toggle.tagPtr == tagPtr)) {
608     goto gotStart;
609     }
610     }
611     if (!TkBTreeNextTag(&tSearch)) {
612     return TCL_OK;
613     }
614     }
615    
616     /*
617     * Find the start of the tagged range.
618     */
619    
620     if (!TkBTreeNextTag(&tSearch)) {
621     return TCL_OK;
622     }
623     gotStart:
624     if (TkTextIndexCmp(&tSearch.curIndex, &index2) >= 0) {
625     return TCL_OK;
626     }
627     TkTextPrintIndex(&tSearch.curIndex, position);
628     Tcl_AppendElement(interp, position);
629     TkBTreeNextTag(&tSearch);
630     TkTextPrintIndex(&tSearch.curIndex, position);
631     Tcl_AppendElement(interp, position);
632     } else if ((c == 'p') && (strncmp(argv[2], "prevrange", length) == 0)
633     && (length >= 2)) {
634     TkTextSearch tSearch;
635     char position1[TK_POS_CHARS];
636     char position2[TK_POS_CHARS];
637    
638     if ((argc != 5) && (argc != 6)) {
639     Tcl_AppendResult(interp, "wrong # args: should be \"",
640     argv[0], " tag prevrange tagName index1 ?index2?\"",
641     (char *) NULL);
642     return TCL_ERROR;
643     }
644     tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
645     if (tagPtr == NULL) {
646     return TCL_OK;
647     }
648     if (TkTextGetIndex(interp, textPtr, argv[4], &index1) != TCL_OK) {
649     return TCL_ERROR;
650     }
651     if (argc == 5) {
652     TkTextMakeByteIndex(textPtr->tree, 0, 0, &index2);
653     } else if (TkTextGetIndex(interp, textPtr, argv[5], &index2)
654     != TCL_OK) {
655     return TCL_ERROR;
656     }
657    
658     /*
659     * The search below is a bit weird. The previous toggle can be
660     * either an on or off toggle. If it is an on toggle, then we
661     * need to turn around and search forward for the end toggle.
662     * Otherwise we keep searching backwards.
663     */
664    
665     TkBTreeStartSearchBack(&index1, &index2, tagPtr, &tSearch);
666    
667     if (!TkBTreePrevTag(&tSearch)) {
668     return TCL_OK;
669     }
670     if (tSearch.segPtr->typePtr == &tkTextToggleOnType) {
671     TkTextPrintIndex(&tSearch.curIndex, position1);
672     TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
673     0, &last);
674     TkBTreeStartSearch(&tSearch.curIndex, &last, tagPtr, &tSearch);
675     TkBTreeNextTag(&tSearch);
676     TkTextPrintIndex(&tSearch.curIndex, position2);
677     } else {
678     TkTextPrintIndex(&tSearch.curIndex, position2);
679     TkBTreePrevTag(&tSearch);
680     if (TkTextIndexCmp(&tSearch.curIndex, &index2) < 0) {
681     return TCL_OK;
682     }
683     TkTextPrintIndex(&tSearch.curIndex, position1);
684     }
685     Tcl_AppendElement(interp, position1);
686     Tcl_AppendElement(interp, position2);
687     } else if ((c == 'r') && (strncmp(argv[2], "raise", length) == 0)
688     && (length >= 3)) {
689     TkTextTag *tagPtr2;
690     int prio;
691    
692     if ((argc != 4) && (argc != 5)) {
693     Tcl_AppendResult(interp, "wrong # args: should be \"",
694     argv[0], " tag raise tagName ?aboveThis?\"",
695     (char *) NULL);
696     return TCL_ERROR;
697     }
698     tagPtr = FindTag(interp, textPtr, argv[3]);
699     if (tagPtr == NULL) {
700     return TCL_ERROR;
701     }
702     if (argc == 5) {
703     tagPtr2 = FindTag(interp, textPtr, argv[4]);
704     if (tagPtr2 == NULL) {
705     return TCL_ERROR;
706     }
707     if (tagPtr->priority <= tagPtr2->priority) {
708     prio = tagPtr2->priority;
709     } else {
710     prio = tagPtr2->priority + 1;
711     }
712     } else {
713     prio = textPtr->numTags-1;
714     }
715     ChangeTagPriority(textPtr, tagPtr, prio);
716     TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
717     tagPtr, 1);
718     } else if ((c == 'r') && (strncmp(argv[2], "ranges", length) == 0)
719     && (length >= 3)) {
720     TkTextSearch tSearch;
721     char position[TK_POS_CHARS];
722    
723     if (argc != 4) {
724     Tcl_AppendResult(interp, "wrong # args: should be \"",
725     argv[0], " tag ranges tagName\"", (char *) NULL);
726     return TCL_ERROR;
727     }
728     tagPtr = FindTag((Tcl_Interp *) NULL, textPtr, argv[3]);
729     if (tagPtr == NULL) {
730     return TCL_OK;
731     }
732     TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
733     TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
734     0, &last);
735     TkBTreeStartSearch(&first, &last, tagPtr, &tSearch);
736     if (TkBTreeCharTagged(&first, tagPtr)) {
737     TkTextPrintIndex(&first, position);
738     Tcl_AppendElement(interp, position);
739     }
740     while (TkBTreeNextTag(&tSearch)) {
741     TkTextPrintIndex(&tSearch.curIndex, position);
742     Tcl_AppendElement(interp, position);
743     }
744     } else if ((c == 'r') && (strncmp(argv[2], "remove", length) == 0)
745     && (length >= 2)) {
746     fullOption = "remove";
747     addTag = 0;
748     goto addAndRemove;
749     } else {
750     Tcl_AppendResult(interp, "bad tag option \"", argv[2],
751     "\": must be add, bind, cget, configure, delete, lower, ",
752     "names, nextrange, raise, ranges, or remove",
753     (char *) NULL);
754     return TCL_ERROR;
755     }
756     return TCL_OK;
757     }
758    
759     /*
760     *----------------------------------------------------------------------
761     *
762     * TkTextCreateTag --
763     *
764     * Find the record describing a tag within a given text widget,
765     * creating a new record if one doesn't already exist.
766     *
767     * Results:
768     * The return value is a pointer to the TkTextTag record for tagName.
769     *
770     * Side effects:
771     * A new tag record is created if there isn't one already defined
772     * for tagName.
773     *
774     *----------------------------------------------------------------------
775     */
776    
777     TkTextTag *
778     TkTextCreateTag(textPtr, tagName)
779     TkText *textPtr; /* Widget in which tag is being used. */
780     char *tagName; /* Name of desired tag. */
781     {
782     register TkTextTag *tagPtr;
783     Tcl_HashEntry *hPtr;
784     int new;
785    
786     hPtr = Tcl_CreateHashEntry(&textPtr->tagTable, tagName, &new);
787     if (!new) {
788     return (TkTextTag *) Tcl_GetHashValue(hPtr);
789     }
790    
791     /*
792     * No existing entry. Create a new one, initialize it, and add a
793     * pointer to it to the hash table entry.
794     */
795    
796     tagPtr = (TkTextTag *) ckalloc(sizeof(TkTextTag));
797     tagPtr->name = Tcl_GetHashKey(&textPtr->tagTable, hPtr);
798     tagPtr->toggleCount = 0;
799     tagPtr->tagRootPtr = NULL;
800     tagPtr->priority = textPtr->numTags;
801     tagPtr->border = NULL;
802     tagPtr->bdString = NULL;
803     tagPtr->borderWidth = 0;
804     tagPtr->reliefString = NULL;
805     tagPtr->relief = TK_RELIEF_FLAT;
806     tagPtr->bgStipple = None;
807     tagPtr->fgColor = NULL;
808     tagPtr->tkfont = NULL;
809     tagPtr->fgStipple = None;
810     tagPtr->justifyString = NULL;
811     tagPtr->justify = TK_JUSTIFY_LEFT;
812     tagPtr->lMargin1String = NULL;
813     tagPtr->lMargin1 = 0;
814     tagPtr->lMargin2String = NULL;
815     tagPtr->lMargin2 = 0;
816     tagPtr->offsetString = NULL;
817     tagPtr->offset = 0;
818     tagPtr->overstrikeString = NULL;
819     tagPtr->overstrike = 0;
820     tagPtr->rMarginString = NULL;
821     tagPtr->rMargin = 0;
822     tagPtr->spacing1String = NULL;
823     tagPtr->spacing1 = 0;
824     tagPtr->spacing2String = NULL;
825     tagPtr->spacing2 = 0;
826     tagPtr->spacing3String = NULL;
827     tagPtr->spacing3 = 0;
828     tagPtr->tabString = NULL;
829     tagPtr->tabArrayPtr = NULL;
830     tagPtr->underlineString = NULL;
831     tagPtr->underline = 0;
832     tagPtr->elideString = NULL;
833     tagPtr->elide = 0;
834     tagPtr->wrapMode = TEXT_WRAPMODE_NULL;
835     tagPtr->affectsDisplay = 0;
836     textPtr->numTags++;
837     Tcl_SetHashValue(hPtr, tagPtr);
838     return tagPtr;
839     }
840    
841     /*
842     *----------------------------------------------------------------------
843     *
844     * FindTag --
845     *
846     * See if tag is defined for a given widget.
847     *
848     * Results:
849     * If tagName is defined in textPtr, a pointer to its TkTextTag
850     * structure is returned. Otherwise NULL is returned and an
851     * error message is recorded in the interp's result unless interp
852     * is NULL.
853     *
854     * Side effects:
855     * None.
856     *
857     *----------------------------------------------------------------------
858     */
859    
860     static TkTextTag *
861     FindTag(interp, textPtr, tagName)
862     Tcl_Interp *interp; /* Interpreter to use for error message;
863     * if NULL, then don't record an error
864     * message. */
865     TkText *textPtr; /* Widget in which tag is being used. */
866     char *tagName; /* Name of desired tag. */
867     {
868     Tcl_HashEntry *hPtr;
869    
870     hPtr = Tcl_FindHashEntry(&textPtr->tagTable, tagName);
871     if (hPtr != NULL) {
872     return (TkTextTag *) Tcl_GetHashValue(hPtr);
873     }
874     if (interp != NULL) {
875     Tcl_AppendResult(interp, "tag \"", tagName,
876     "\" isn't defined in text widget", (char *) NULL);
877     }
878     return NULL;
879     }
880    
881     /*
882     *----------------------------------------------------------------------
883     *
884     * TkTextFreeTag --
885     *
886     * This procedure is called when a tag is deleted to free up the
887     * memory and other resources associated with the tag.
888     *
889     * Results:
890     * None.
891     *
892     * Side effects:
893     * Memory and other resources are freed.
894     *
895     *----------------------------------------------------------------------
896     */
897    
898     void
899     TkTextFreeTag(textPtr, tagPtr)
900     TkText *textPtr; /* Info about overall widget. */
901     register TkTextTag *tagPtr; /* Tag being deleted. */
902     {
903     if (tagPtr->border != None) {
904     Tk_Free3DBorder(tagPtr->border);
905     }
906     if (tagPtr->bdString != NULL) {
907     ckfree(tagPtr->bdString);
908     }
909     if (tagPtr->reliefString != NULL) {
910     ckfree(tagPtr->reliefString);
911     }
912     if (tagPtr->bgStipple != None) {
913     Tk_FreeBitmap(textPtr->display, tagPtr->bgStipple);
914     }
915     if (tagPtr->fgColor != None) {
916     Tk_FreeColor(tagPtr->fgColor);
917     }
918     Tk_FreeFont(tagPtr->tkfont);
919     if (tagPtr->fgStipple != None) {
920     Tk_FreeBitmap(textPtr->display, tagPtr->fgStipple);
921     }
922     if (tagPtr->justifyString != NULL) {
923     ckfree(tagPtr->justifyString);
924     }
925     if (tagPtr->lMargin1String != NULL) {
926     ckfree(tagPtr->lMargin1String);
927     }
928     if (tagPtr->lMargin2String != NULL) {
929     ckfree(tagPtr->lMargin2String);
930     }
931     if (tagPtr->offsetString != NULL) {
932     ckfree(tagPtr->offsetString);
933     }
934     if (tagPtr->overstrikeString != NULL) {
935     ckfree(tagPtr->overstrikeString);
936     }
937     if (tagPtr->rMarginString != NULL) {
938     ckfree(tagPtr->rMarginString);
939     }
940     if (tagPtr->spacing1String != NULL) {
941     ckfree(tagPtr->spacing1String);
942     }
943     if (tagPtr->spacing2String != NULL) {
944     ckfree(tagPtr->spacing2String);
945     }
946     if (tagPtr->spacing3String != NULL) {
947     ckfree(tagPtr->spacing3String);
948     }
949     if (tagPtr->tabString != NULL) {
950     ckfree(tagPtr->tabString);
951     }
952     if (tagPtr->tabArrayPtr != NULL) {
953     ckfree((char *) tagPtr->tabArrayPtr);
954     }
955     if (tagPtr->underlineString != NULL) {
956     ckfree(tagPtr->underlineString);
957     }
958     ckfree((char *) tagPtr);
959     }
960    
961     /*
962     *----------------------------------------------------------------------
963     *
964     * SortTags --
965     *
966     * This procedure sorts an array of tag pointers in increasing
967     * order of priority, optimizing for the common case where the
968     * array is small.
969     *
970     * Results:
971     * None.
972     *
973     * Side effects:
974     * None.
975     *
976     *----------------------------------------------------------------------
977     */
978    
979     static void
980     SortTags(numTags, tagArrayPtr)
981     int numTags; /* Number of tag pointers at *tagArrayPtr. */
982     TkTextTag **tagArrayPtr; /* Pointer to array of pointers. */
983     {
984     int i, j, prio;
985     register TkTextTag **tagPtrPtr;
986     TkTextTag **maxPtrPtr, *tmp;
987    
988     if (numTags < 2) {
989     return;
990     }
991     if (numTags < 20) {
992     for (i = numTags-1; i > 0; i--, tagArrayPtr++) {
993     maxPtrPtr = tagPtrPtr = tagArrayPtr;
994     prio = tagPtrPtr[0]->priority;
995     for (j = i, tagPtrPtr++; j > 0; j--, tagPtrPtr++) {
996     if (tagPtrPtr[0]->priority < prio) {
997     prio = tagPtrPtr[0]->priority;
998     maxPtrPtr = tagPtrPtr;
999     }
1000     }
1001     tmp = *maxPtrPtr;
1002     *maxPtrPtr = *tagArrayPtr;
1003     *tagArrayPtr = tmp;
1004     }
1005     } else {
1006     qsort((VOID *) tagArrayPtr, (unsigned) numTags, sizeof (TkTextTag *),
1007     TagSortProc);
1008     }
1009     }
1010    
1011     /*
1012     *----------------------------------------------------------------------
1013     *
1014     * TagSortProc --
1015     *
1016     * This procedure is called by qsort when sorting an array of
1017     * tags in priority order.
1018     *
1019     * Results:
1020     * The return value is -1 if the first argument should be before
1021     * the second element (i.e. it has lower priority), 0 if it's
1022     * equivalent (this should never happen!), and 1 if it should be
1023     * after the second element.
1024     *
1025     * Side effects:
1026     * None.
1027     *
1028     *----------------------------------------------------------------------
1029     */
1030    
1031     static int
1032     TagSortProc(first, second)
1033     CONST VOID *first, *second; /* Elements to be compared. */
1034     {
1035     TkTextTag *tagPtr1, *tagPtr2;
1036    
1037     tagPtr1 = * (TkTextTag **) first;
1038     tagPtr2 = * (TkTextTag **) second;
1039     return tagPtr1->priority - tagPtr2->priority;
1040     }
1041    
1042     /*
1043     *----------------------------------------------------------------------
1044     *
1045     * ChangeTagPriority --
1046     *
1047     * This procedure changes the priority of a tag by modifying
1048     * its priority and the priorities of other tags that are affected
1049     * by the change.
1050     *
1051     * Results:
1052     * None.
1053     *
1054     * Side effects:
1055     * Priorities may be changed for some or all of the tags in
1056     * textPtr. The tags will be arranged so that there is exactly
1057     * one tag at each priority level between 0 and textPtr->numTags-1,
1058     * with tagPtr at priority "prio".
1059     *
1060     *----------------------------------------------------------------------
1061     */
1062    
1063     static void
1064     ChangeTagPriority(textPtr, tagPtr, prio)
1065     TkText *textPtr; /* Information about text widget. */
1066     TkTextTag *tagPtr; /* Tag whose priority is to be
1067     * changed. */
1068     int prio; /* New priority for tag. */
1069     {
1070     int low, high, delta;
1071     register TkTextTag *tagPtr2;
1072     Tcl_HashEntry *hPtr;
1073     Tcl_HashSearch search;
1074    
1075     if (prio < 0) {
1076     prio = 0;
1077     }
1078     if (prio >= textPtr->numTags) {
1079     prio = textPtr->numTags-1;
1080     }
1081     if (prio == tagPtr->priority) {
1082     return;
1083     } else if (prio < tagPtr->priority) {
1084     low = prio;
1085     high = tagPtr->priority-1;
1086     delta = 1;
1087     } else {
1088     low = tagPtr->priority+1;
1089     high = prio;
1090     delta = -1;
1091     }
1092     for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
1093     hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
1094     tagPtr2 = (TkTextTag *) Tcl_GetHashValue(hPtr);
1095     if ((tagPtr2->priority >= low) && (tagPtr2->priority <= high)) {
1096     tagPtr2->priority += delta;
1097     }
1098     }
1099     tagPtr->priority = prio;
1100     }
1101    
1102     /*
1103     *--------------------------------------------------------------
1104     *
1105     * TkTextBindProc --
1106     *
1107     * This procedure is invoked by the Tk dispatcher to handle
1108     * events associated with bindings on items.
1109     *
1110     * Results:
1111     * None.
1112     *
1113     * Side effects:
1114     * Depends on the command invoked as part of the binding
1115     * (if there was any).
1116     *
1117     *--------------------------------------------------------------
1118     */
1119    
1120     void
1121     TkTextBindProc(clientData, eventPtr)
1122     ClientData clientData; /* Pointer to canvas structure. */
1123     XEvent *eventPtr; /* Pointer to X event that just
1124     * happened. */
1125     {
1126     TkText *textPtr = (TkText *) clientData;
1127     int repick = 0;
1128    
1129     # define AnyButtonMask (Button1Mask|Button2Mask|Button3Mask\
1130     |Button4Mask|Button5Mask)
1131    
1132     Tcl_Preserve((ClientData) textPtr);
1133    
1134     /*
1135     * This code simulates grabs for mouse buttons by keeping track
1136     * of whether a button is pressed and refusing to pick a new current
1137     * character while a button is pressed.
1138     */
1139    
1140     if (eventPtr->type == ButtonPress) {
1141     textPtr->flags |= BUTTON_DOWN;
1142     } else if (eventPtr->type == ButtonRelease) {
1143     int mask;
1144    
1145     switch (eventPtr->xbutton.button) {
1146     case Button1:
1147     mask = Button1Mask;
1148     break;
1149     case Button2:
1150     mask = Button2Mask;
1151     break;
1152     case Button3:
1153     mask = Button3Mask;
1154     break;
1155     case Button4:
1156     mask = Button4Mask;
1157     break;
1158     case Button5:
1159     mask = Button5Mask;
1160     break;
1161     default:
1162     mask = 0;
1163     break;
1164     }
1165     if ((eventPtr->xbutton.state & AnyButtonMask) == (unsigned) mask) {
1166     textPtr->flags &= ~BUTTON_DOWN;
1167     repick = 1;
1168     }
1169     } else if ((eventPtr->type == EnterNotify)
1170     || (eventPtr->type == LeaveNotify)) {
1171     if (eventPtr->xcrossing.state & AnyButtonMask) {
1172     textPtr->flags |= BUTTON_DOWN;
1173     } else {
1174     textPtr->flags &= ~BUTTON_DOWN;
1175     }
1176     TkTextPickCurrent(textPtr, eventPtr);
1177     goto done;
1178     } else if (eventPtr->type == MotionNotify) {
1179     if (eventPtr->xmotion.state & AnyButtonMask) {
1180     textPtr->flags |= BUTTON_DOWN;
1181     } else {
1182     textPtr->flags &= ~BUTTON_DOWN;
1183     }
1184     TkTextPickCurrent(textPtr, eventPtr);
1185     }
1186     if ((textPtr->numCurTags > 0) && (textPtr->bindingTable != NULL)
1187     && (textPtr->tkwin != NULL)) {
1188     Tk_BindEvent(textPtr->bindingTable, eventPtr, textPtr->tkwin,
1189     textPtr->numCurTags, (ClientData *) textPtr->curTagArrayPtr);
1190     }
1191     if (repick) {
1192     unsigned int oldState;
1193    
1194     oldState = eventPtr->xbutton.state;
1195     eventPtr->xbutton.state &= ~(Button1Mask|Button2Mask
1196     |Button3Mask|Button4Mask|Button5Mask);
1197     TkTextPickCurrent(textPtr, eventPtr);
1198     eventPtr->xbutton.state = oldState;
1199     }
1200    
1201     done:
1202     Tcl_Release((ClientData) textPtr);
1203     }
1204    
1205     /*
1206     *--------------------------------------------------------------
1207     *
1208     * TkTextPickCurrent --
1209     *
1210     * Find the character containing the coordinates in an event
1211     * and place the "current" mark on that character. If the
1212     * "current" mark has moved then generate a fake leave event
1213     * on the old current character and a fake enter event on the new
1214     * current character.
1215     *
1216     * Results:
1217     * None.
1218     *
1219     * Side effects:
1220     * The current mark for textPtr may change. If it does,
1221     * then the commands associated with character entry and leave
1222     * could do just about anything. For example, the text widget
1223     * might be deleted. It is up to the caller to protect itself
1224     * with calls to Tcl_Preserve and Tcl_Release.
1225     *
1226     *--------------------------------------------------------------
1227     */
1228    
1229     void
1230     TkTextPickCurrent(textPtr, eventPtr)
1231     register TkText *textPtr; /* Text widget in which to select
1232     * current character. */
1233     XEvent *eventPtr; /* Event describing location of
1234     * mouse cursor. Must be EnterWindow,
1235     * LeaveWindow, ButtonRelease, or
1236     * MotionNotify. */
1237     {
1238     TkTextIndex index;
1239     TkTextTag **oldArrayPtr, **newArrayPtr;
1240     TkTextTag **copyArrayPtr = NULL; /* Initialization needed to prevent
1241     * compiler warning. */
1242    
1243     int numOldTags, numNewTags, i, j, size;
1244     XEvent event;
1245    
1246     /*
1247     * If a button is down, then don't do anything at all; we'll be
1248     * called again when all buttons are up, and we can repick then.
1249     * This implements a form of mouse grabbing.
1250     */
1251    
1252     if (textPtr->flags & BUTTON_DOWN) {
1253     if (((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify))
1254     && ((eventPtr->xcrossing.mode == NotifyGrab)
1255     || (eventPtr->xcrossing.mode == NotifyUngrab))) {
1256     /*
1257     * Special case: the window is being entered or left because
1258     * of a grab or ungrab. In this case, repick after all.
1259     * Furthermore, clear BUTTON_DOWN to release the simulated
1260     * grab.
1261     */
1262    
1263     textPtr->flags &= ~BUTTON_DOWN;
1264     } else {
1265     return;
1266     }
1267     }
1268    
1269     /*
1270     * Save information about this event in the widget in case we have
1271     * to synthesize more enter and leave events later (e.g. because a
1272     * character was deleted, causing a new character to be underneath
1273     * the mouse cursor). Also translate MotionNotify events into
1274     * EnterNotify events, since that's what gets reported to event
1275     * handlers when the current character changes.
1276     */
1277    
1278     if (eventPtr != &textPtr->pickEvent) {
1279     if ((eventPtr->type == MotionNotify)
1280     || (eventPtr->type == ButtonRelease)) {
1281     textPtr->pickEvent.xcrossing.type = EnterNotify;
1282     textPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
1283     textPtr->pickEvent.xcrossing.send_event
1284     = eventPtr->xmotion.send_event;
1285     textPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
1286     textPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
1287     textPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
1288     textPtr->pickEvent.xcrossing.subwindow = None;
1289     textPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
1290     textPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
1291     textPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
1292     textPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
1293     textPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
1294     textPtr->pickEvent.xcrossing.mode = NotifyNormal;
1295     textPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
1296     textPtr->pickEvent.xcrossing.same_screen
1297     = eventPtr->xmotion.same_screen;
1298     textPtr->pickEvent.xcrossing.focus = False;
1299     textPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
1300     } else {
1301     textPtr->pickEvent = *eventPtr;
1302     }
1303     }
1304    
1305     /*
1306     * Find the new current character, then find and sort all of the
1307     * tags associated with it.
1308     */
1309    
1310     if (textPtr->pickEvent.type != LeaveNotify) {
1311     TkTextPixelIndex(textPtr, textPtr->pickEvent.xcrossing.x,
1312     textPtr->pickEvent.xcrossing.y, &index);
1313     newArrayPtr = TkBTreeGetTags(&index, &numNewTags);
1314     SortTags(numNewTags, newArrayPtr);
1315     } else {
1316     newArrayPtr = NULL;
1317     numNewTags = 0;
1318     }
1319    
1320     /*
1321     * Resort the tags associated with the previous marked character
1322     * (the priorities might have changed), then make a copy of the
1323     * new tags, and compare the old tags to the copy, nullifying
1324     * any tags that are present in both groups (i.e. the tags that
1325     * haven't changed).
1326     */
1327    
1328     SortTags(textPtr->numCurTags, textPtr->curTagArrayPtr);
1329     if (numNewTags > 0) {
1330     size = numNewTags * sizeof(TkTextTag *);
1331     copyArrayPtr = (TkTextTag **) ckalloc((unsigned) size);
1332     memcpy((VOID *) copyArrayPtr, (VOID *) newArrayPtr, (size_t) size);
1333     for (i = 0; i < textPtr->numCurTags; i++) {
1334     for (j = 0; j < numNewTags; j++) {
1335     if (textPtr->curTagArrayPtr[i] == copyArrayPtr[j]) {
1336     textPtr->curTagArrayPtr[i] = NULL;
1337     copyArrayPtr[j] = NULL;
1338     break;
1339     }
1340     }
1341     }
1342     }
1343    
1344     /*
1345     * Invoke the binding system with a LeaveNotify event for all of
1346     * the tags that have gone away. We have to be careful here,
1347     * because it's possible that the binding could do something
1348     * (like calling tkwait) that eventually modifies
1349     * textPtr->curTagArrayPtr. To avoid problems in situations like
1350     * this, update curTagArrayPtr to its new value before invoking
1351     * any bindings, and don't use it any more here.
1352     */
1353    
1354     numOldTags = textPtr->numCurTags;
1355     textPtr->numCurTags = numNewTags;
1356     oldArrayPtr = textPtr->curTagArrayPtr;
1357     textPtr->curTagArrayPtr = newArrayPtr;
1358     if (numOldTags != 0) {
1359     if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)) {
1360     event = textPtr->pickEvent;
1361     event.type = LeaveNotify;
1362    
1363     /*
1364     * Always use a detail of NotifyAncestor. Besides being
1365     * consistent, this avoids problems where the binding code
1366     * will discard NotifyInferior events.
1367     */
1368    
1369     event.xcrossing.detail = NotifyAncestor;
1370     Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
1371     numOldTags, (ClientData *) oldArrayPtr);
1372     }
1373     ckfree((char *) oldArrayPtr);
1374     }
1375    
1376     /*
1377     * Reset the "current" mark (be careful to recompute its location,
1378     * since it might have changed during an event binding). Then
1379     * invoke the binding system with an EnterNotify event for all of
1380     * the tags that have just appeared.
1381     */
1382    
1383     TkTextPixelIndex(textPtr, textPtr->pickEvent.xcrossing.x,
1384     textPtr->pickEvent.xcrossing.y, &index);
1385     TkTextSetMark(textPtr, "current", &index);
1386     if (numNewTags != 0) {
1387     if ((textPtr->bindingTable != NULL) && (textPtr->tkwin != NULL)) {
1388     event = textPtr->pickEvent;
1389     event.type = EnterNotify;
1390     event.xcrossing.detail = NotifyAncestor;
1391     Tk_BindEvent(textPtr->bindingTable, &event, textPtr->tkwin,
1392     numNewTags, (ClientData *) copyArrayPtr);
1393     }
1394     ckfree((char *) copyArrayPtr);
1395     }
1396     }
1397    
1398 dashley 69 /* End of tktexttag.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25