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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 42611 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 /* $Header$ */
2
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 /* End of tktexttag.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25