PageRenderTime 68ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/SN-NG4.1/tk/generic/tkTextTag.c

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