Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch glyph_indexing Excluding Merge-Ins
This is equivalent to a diff from 905e6388 to 94473623
2020-05-26
| ||
16:28 | Add a range command to the entry in order to support glyph-based selection. Closed-Leaf check-in: 94473623 user: marc_culler tags: glyph_indexing | |
12:26 | Adjust the TextManager interface slightly. check-in: 4ce9e66f user: marc_culler tags: glyph_indexing | |
2020-05-23
| ||
22:13 | Merge 8.6 check-in: 60835526 user: jan.nijtmans tags: trunk | |
21:40 | Merge trunk. Fix C++ builds, also for TCL_UTF_MAX=4 and TCL_UTF_MAX=6 check-in: 0cb6fdeb user: jan.nijtmans tags: size-for-sel | |
16:51 | Proof of concept for glyph-based indexing using a platform-specific TextManager - only implemented for macOS tk entry widgets so far. check-in: 0c9ad396 user: marc_culler tags: glyph_indexing | |
2020-05-22
| ||
13:40 | Merge 8.6 check-in: 905e6388 user: jan.nijtmans tags: trunk | |
13:36 | Merge 8.5 check-in: ba9430d0 user: jan.nijtmans tags: core-8-6-branch | |
12:43 | Don't use TCL_AUTO_LENGTH in Tk any more, just use TCL_INDEX_NONE consistantly everywhere: It's actually the same. check-in: 1c6ef075 user: jan.nijtmans tags: trunk | |
Changes to doc/entry.n.
︙ | |||
88 89 90 91 92 93 94 | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | - - + + + + + + + + + + + + | command line or in the option database to configure aspects of the entry such as its colors, font, and relief. The \fBentry\fR command returns its \fIpathName\fR argument. At the time this command is invoked, there must not exist a window named \fIpathName\fR, but \fIpathName\fR's parent must exist. .PP |
︙ | |||
292 293 294 295 296 297 298 299 300 301 302 303 304 305 | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | + + + + + + + + | \fIpathName \fBindex\fI index\fR Returns the numerical index corresponding to \fIindex\fR. .TP \fIpathName \fBinsert \fIindex string\fR Insert the characters of \fIstring\fR just before the character indicated by \fIindex\fR. Returns an empty string. .TP \fIpathName \fBrange \fIfirst last\fR Returns a string consisting of the characters in the entry beginning with the character represented by the index \fIfirst\fR and ending with the character represented by the index \fIlast\fR. The numerical values of \fIfirst\fR and \fIlast\fR can be arbitrary, but negative values are replaced by 0 while values larger than the length of the entry string is the length. An empyt string is returned if \fIfirst\fR > \fIlast\fR. .TP \fIpathName \fBscan\fR \fIoption args\fR This command is used to implement scanning on entries. It has two forms, depending on \fIoption\fR: .RS .TP \fIpathName \fBscan mark \fIx\fR Records \fIx\fR and the current view in the entry window; used in |
︙ |
Changes to generic/tkEntry.c.
︙ | |||
327 328 329 330 331 332 333 | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | - + - + | * The following tables define the entry widget commands (and sub-commands) * and map the indexes into the string tables into enumerated types used to * dispatch the entry widget command. */ static const char *const entryCmdNames[] = { "bbox", "cget", "configure", "delete", "get", "icursor", "index", |
︙ | |||
480 481 482 483 484 485 486 | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | - + - | * See the user documentation. * *-------------------------------------------------------------- */ int Tk_EntryObjCmd( |
︙ | |||
527 528 529 530 531 532 533 | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | + + - - - + + + + + + + | entryPtr->display = Tk_Display(tkwin); entryPtr->interp = interp; entryPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd, entryPtr, EntryCmdDeletedProc); entryPtr->optionTable = optionTable; entryPtr->type = TK_ENTRY; #ifndef USE_GLYPH_INDEXES { |
︙ | |||
731 732 733 734 735 736 737 738 739 740 741 742 743 744 | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 | + + + + + | Tcl_WrongNumArgs(interp, 2, objv, "string"); goto error; } if (GetEntryIndex(interp, entryPtr, objv[2], &index) != TCL_OK) { goto error; } #ifdef USE_GLYPH_INDEXES index = TkpTextManagerContainingCluster(entryPtr->manager, index, NULL); #endif Tcl_SetObjResult(interp, Tcl_NewWideIntObj(index)); break; } case COMMAND_INSERT: { int index, code; |
︙ | |||
754 755 756 757 758 759 760 761 762 763 764 765 766 767 | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | code = InsertChars(entryPtr, index, Tcl_GetString(objv[3])); if (code != TCL_OK) { goto error; } } break; } case COMMAND_RANGE: { const char *substring; int first, last; if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "first last"); goto error; } if (Tcl_GetIntFromObj(interp, objv[2], &first) != TCL_OK) { goto error; } if (Tcl_GetIntFromObj(interp, objv[3], &last) != TCL_OK) { goto error; } #ifndef USE_GLYPH_INDEXES if (first < 0) { first = 0; } if (last < first) { Tcl_SetObjResult(interp, Tcl_NewObj()); } else { const char *end; int length; Tcl_UniChar ch = 0; substring = Tcl_UtfAtIndex(entryPtr->displayString, first); length = Tcl_NumUtfChars(entryPtr->displayString, TCL_INDEX_NONE); if (last >= length) { Tcl_SetObjResult(interp, Tcl_NewStringObj(substring, TCL_INDEX_NONE)); } else { end = Tcl_UtfAtIndex(entryPtr->displayString, last); end += Tcl_UtfToUniChar(end, &ch); Tcl_SetObjResult(interp, Tcl_NewStringObj(substring, end - substring)); } } #else substring = TkpTextManagerUTF8StringForClusterRange(entryPtr->manager, first, last); Tcl_SetObjResult(interp, Tcl_NewStringObj(substring, TCL_INDEX_NONE)); #endif break; } case COMMAND_SCAN: { int x; const char *minorCmd; if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x"); |
︙ | |||
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 | + + + + + | Entry *entryPtr = (Entry *)memPtr; /* * Free up all the stuff that requires special handling, then let * Tk_FreeOptions handle all the standard option-related stuff. */ #ifndef USE_GLYPH_INDEXES ckfree((char *)entryPtr->string); #else TkpTextManagerDestroy(entryPtr->manager); #endif if (entryPtr->textVarName != NULL) { Tcl_UntraceVar2(entryPtr->interp, entryPtr->textVarName, NULL, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, EntryTextVarProc, entryPtr); entryPtr->flags &= ~ENTRY_VAR_TRACED; } if (entryPtr->textGC != NULL) { |
︙ | |||
1763 1764 1765 1766 1767 1768 1769 | 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 | - | } } /* * Draw the text in two pieces: first the unselected portion, then the * selected portion on top of it. */ |
︙ | |||
2111 2112 2113 2114 2115 2116 2117 | 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 | - + - + - + - - - + + + + + + - - - + + + - + + + + + + + + + + + + - - + + + + + + + + + + | } /* *---------------------------------------------------------------------- * * InsertChars -- * |
︙ | |||
2210 2211 2212 2213 2214 2215 2216 | 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 | - + - - + + - - - + + + + + + | } /* *---------------------------------------------------------------------- * * DeleteChars -- * |
︙ | |||
2261 2262 2263 2264 2265 2266 2267 | 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 | - - + + + + + + + + + + + + + + + + + + + | entryPtr->validate == VALIDATE_ALL) && EntryValidateChange(entryPtr, toDelete, newStr, index, VALIDATE_DELETE) != TCL_OK) { ckfree(newStr); ckfree(toDelete); return TCL_OK; } |
︙ | |||
2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 | 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 | + + + + + + - + | entryPtr->flags &= ~VALIDATE_ABORT; ckfree((char *)value); return; } } oldSource = entryPtr->string; #ifndef USE_GLYPH_INDEXES ckfree((char *)entryPtr->string); if (malloced) { entryPtr->string = value; } else { char *tmp = (char *)ckalloc(valueLen + 1); strcpy(tmp, value); entryPtr->string = tmp; } entryPtr->numBytes = valueLen; entryPtr->numChars = Tcl_NumUtfChars(value, valueLen); #else entryPtr->string = TkpTextManagerSet(entryPtr->manager, value, &entryPtr->numChars, &entryPtr->numBytes); #endif if (entryPtr->displayString == oldSource) { entryPtr->displayString = entryPtr->string; |
︙ | |||
2643 2644 2645 2646 2647 2648 2649 | 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 | - - + + + + + + + + + + + + + + + + + + + + + + | *--------------------------------------------------------------------------- */ static int GetEntryIndex( Tcl_Interp *interp, /* For error messages. */ Entry *entryPtr, /* Entry for which the index is being |
︙ |
Changes to generic/tkEntry.h.
︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | + - + - - + - | * other things, so that resources can be * freed even after tkwin has gone away. */ Tcl_Interp *interp; /* Interpreter associated with entry. */ Tcl_Command widgetCmd; /* Token for entry's widget command. */ Tk_OptionTable optionTable; /* Table that defines configuration options * available for this widget. */ enum EntryType type; /* Specialized type of Entry widget */ ClientData manager; /* Platform-specific TextManager. */ /* * Fields that are set by widget commands other than "configure". */ |
︙ |
Changes to generic/tkInt.h.
︙ | |||
1345 1346 1347 1348 1349 1350 1351 | 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | MODULE_SCOPE void TkpCancelWarp(TkDisplay *dispPtr); MODULE_SCOPE int TkListCreateFrame(ClientData clientData, Tcl_Interp *interp, Tcl_Obj *listObj, int toplevel, Tcl_Obj *nameObj); MODULE_SCOPE void TkRotatePoint(double originX, double originY, double sine, double cosine, double *xPtr, double *yPtr); |
︙ |
Changes to library/entry.tcl.
︙ | |||
672 673 674 675 676 677 678 | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | - + | # # Returns the selected text of the entry with respect to the -show option. # # Arguments: # w - The entry window from which the text to get proc ::tk::EntryGetSelection {w} { |
︙ |
Changes to macosx/tkMacOSXFont.c.
︙ | |||
180 181 182 183 184 185 186 | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + | } #ifndef __clang__ @synthesize UTF8String = _UTF8String; #endif @end /* * Implementation of the TextManager for macOS. * * The TextManager is really little more than an NSMutableString, except * that Apple says this about the UTF8String property: * * "This C string is a pointer to a structure inside the string object, * which may have a lifetime shorter than the string object and will * certainly not have a longer lifetime. Therefore, you should copy the C * string if it needs to be stored outside of the memory context in which * you use this property." * */ typedef struct TextManager { NSMutableString *string; NSMutableString *backup; char *utf8string; /* We need to store a copy of the UTF8String */ int numClusters; } TextManager; /* * Static functions used to access a TextManager. */ /* * Called after the NSMutableString has been changed. It resets the counts of * bytes and chars and saves a copy of the UTF8String of the NSMutableString. */ static char * TextManagerUpdate( TextManager *managerPtr, int *numChars, int *numBytes) { *numChars = [managerPtr->string length]; *numBytes = [managerPtr->string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; if (managerPtr->utf8string) { ckfree(managerPtr->utf8string); } managerPtr->utf8string = ckalloc(*numBytes + 1); strcpy(managerPtr->utf8string, [managerPtr->string UTF8String]); managerPtr->numClusters = -1; /* recomputed by TkpTextManagerNumClusters */ return managerPtr->utf8string; } /* * Destroy the cached NSString, if there is one. */ static void TextManagerClearCache( TextManager *managerPtr) { if (managerPtr->backup) { [managerPtr->backup release]; managerPtr->backup = nil; } } /* * Cache a copy of the current string, for use when reverting changes after * validation fails. */ static void TextManagerCache( TextManager *managerPtr) { TextManagerClearCache(managerPtr); managerPtr->backup = [[NSMutableString stringWithString:managerPtr->string] retain]; } /* * Return the character range of of the cluster with given index, or a range * with length 0 and location equal to the string length. * * NOTE: A future optimization could cache the character index of the last base * character which was looked up inside the TextManager, as a hint. Then the * next time a base character index were needed, the search could begin at the * hint location instead of the beginning of the string. Since changes to the * insert cursor are usually small, this would be quite a bit faster. */ static NSRange ClusterRange( NSString *string, NSUInteger clusterIndex) { NSRange clusterRange = NSMakeRange(0, 0); NSUInteger i, charIndex = 0, end = [string length]; if (end == 0) { return NSMakeRange(0,0); } for (i = 0; i < clusterIndex; i++) { clusterRange = [string rangeOfComposedCharacterSequenceAtIndex:charIndex]; charIndex = clusterRange.location + clusterRange.length; if (charIndex >= end) { return NSMakeRange(charIndex, 0); break; } } return [string rangeOfComposedCharacterSequenceAtIndex:charIndex]; } /* * Returns the index of the cluster which constains the the character with * given index, or the total number of clusters. * * NOTE: This could also benefit from a cached hint. */ static NSUInteger IndexOfContainingCluster( NSString *string, NSUInteger charIndex, int *clusterLength) { NSRange clusterRange; NSUInteger idx, clusterIndex; if (charIndex > string.length) { charIndex = string.length; } for (idx = 0, clusterIndex = 0; idx < charIndex; clusterIndex++) { clusterRange = [string rangeOfComposedCharacterSequenceAtIndex:idx]; idx += clusterRange.length; if (idx > charIndex) { if (clusterLength) { clusterLength = 0; } return clusterIndex; } } if (clusterLength) { *clusterLength = clusterRange.length; } return clusterIndex; } /* * Computes the range of characters filled by a range of clusters of given * length, such that a given character is contained in the first cluster. * Used by TkpTextManagerDelete to determine which chars to delete from * the NSMutableString. */ static NSRange CharRangeFromClusterRange( NSString *string, NSUInteger charIndex, NSUInteger clusterCount) { NSRange clusterRange; NSUInteger max = string.length, charLocation, charLength = 0; if (max == 0 || charIndex >= max) { return NSMakeRange(max, 0); } clusterRange = [string rangeOfComposedCharacterSequenceAtIndex:charIndex]; charLocation = clusterRange.location; charIndex = charLocation; while (clusterCount--) { clusterRange = [string rangeOfComposedCharacterSequenceAtIndex:charIndex]; charLength += clusterRange.length; charIndex += clusterRange.length; if (charIndex >= max) { return NSMakeRange(charLocation, max - charLocation); } } return NSMakeRange(charLocation, charLength); } /* *--------------------------------------------------------------------------- * * TkpTextManagerCreate -- * * Allocate and initialize a TextManager to handle glyph-based indexing. * * Results: * A pointer to a TextManager, cast as an opaque ClientData type. * * Side effects: * Allocates a TextManager, an NSString and a UTF-8 char buffer. * Also stores a pointer to the initial (empty) UTF-8 string in * the variable referenced by the initialString parameter. * *--------------------------------------------------------------------------- */ ClientData TkpTextManagerCreate( const char **initialString) { TextManager *managerPtr = (TextManager *) ckalloc(sizeof(TextManager)); int dummy; managerPtr->string = [[NSMutableString string] retain]; managerPtr->backup = nil; managerPtr->utf8string = NULL; *initialString = TextManagerUpdate(managerPtr, &dummy, &dummy); return (ClientData) managerPtr; } /* *--------------------------------------------------------------------------- * * TkpTextManagerDestroy -- * * Free the resources associated to a TextManager * * Results: * None * * Side effects: * Release the NSString, frees the UTF8 string and the TextManager. * *--------------------------------------------------------------------------- */ void TkpTextManagerDestroy( ClientData clientData) { TextManager *managerPtr = (TextManager *) clientData; [managerPtr->string release]; if (managerPtr->backup) { [managerPtr->backup release]; } if (managerPtr->utf8string) { ckfree(managerPtr->utf8string); } ckfree(managerPtr); } /* *--------------------------------------------------------------------------- * * TkpTextManagerClusterPosition -- * * Computes the character index of the base character of the cluster * with the given cluster index. If the clusterLength parameter is * not NULL, the integer variable that it references is set to the * length of the cluster. * * Results: * A cluster index. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int TkpTextManagerClusterPosition( ClientData clientData, int clusterIndex, int *clusterLength) { TextManager *managerPtr = (TextManager *) clientData; NSRange range; if (clusterIndex < 0) { clusterIndex = 0; } range = ClusterRange(managerPtr->string, clusterIndex); if (clusterLength) { *clusterLength = range.length; } return range.location; } /* *--------------------------------------------------------------------------- * * TkpTextManagerContainingCluster -- * * Given a character index, find the index of the cluster which contains * the indexed character. If the parameter clusterLength is not NULL the * integer variable that it references is set to the length of the cluster. * * Results: * A cluster index. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int TkpTextManagerContainingCluster( ClientData clientData, int charIndex, int *clusterLength) { TextManager *managerPtr = (TextManager *) clientData; if (charIndex < 0) { return 0; } return (int) IndexOfContainingCluster(managerPtr->string, charIndex, clusterLength); } /* *--------------------------------------------------------------------------- * * TkpTextManagerNumClusters -- * * Return the (cached) number of clusters in the entire NSMutableString. * * Results: * The number of clusters. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int TkpTextManagerNumClusters( ClientData clientData) { TextManager *managerPtr = (TextManager *) clientData; NSString *string = managerPtr->string; if (managerPtr->numClusters < 0) { managerPtr->numClusters = IndexOfContainingCluster(string, string.length, NULL); } return managerPtr->numClusters; } /* *--------------------------------------------------------------------------- * * TkpTextManagerCharRangeOfClusterRange -- * * Return a pointer to a UTF-8 encoded C string which represents a * substring of the text manager's string. The result includes all * clusters with index >= first and <= last. It is allowed for first * to be negative and last to be greater than or equal to numClusters. * The resulting pointer has an indeterminate lifespan since it is * equal to the UTF8String property of an NSString. So it should be * copied or used before the current iteration of the event loop * terminates. * * Results: * The number of clusters. * * Side effects: * None. * *--------------------------------------------------------------------------- */ const char * TkpTextManagerUTF8StringForClusterRange( ClientData clientData, int firstCluster, int lastCluster) { TextManager *managerPtr = (TextManager *) clientData; NSRange clusterRange, fullRange; NSUInteger location, charIndex, clusterIndex; int length; NSString *str = managerPtr->string, *temp; static char empty = '\0'; if (firstCluster < 0) { firstCluster = 0; } if (lastCluster < firstCluster) { return ∅ } clusterIndex = firstCluster; location = TkpTextManagerClusterPosition(managerPtr, firstCluster, &length); charIndex = location; while (clusterIndex <= (unsigned int) lastCluster && charIndex < [managerPtr->string length]) { clusterRange = [str rangeOfComposedCharacterSequenceAtIndex:charIndex]; charIndex += clusterRange.length; clusterIndex++; } fullRange = NSMakeRange(location, charIndex - location); temp = [str substringWithRange:fullRange]; return [temp UTF8String]; } /* *--------------------------------------------------------------------------- * * TkpTextManagerSet -- * * Set the contents of the NSMutableString from a UTF-8 encoded string. * * Results: * A pointer to the TextManager's cached UTF-8 string. * * Side effects: * The number of unichars and bytes are recorded in the integer variables * referenced by the numChars and numBytes parameters. * *--------------------------------------------------------------------------- */ const char * TkpTextManagerSet( ClientData clientData, const char *value, int *numChars, int *numBytes) { TextManager *managerPtr = (TextManager *) clientData; NSString *valueString = [[NSString alloc] initWithUTF8String: value]; [managerPtr->string setString:valueString]; return TextManagerUpdate(managerPtr, numChars, numBytes); } /* *--------------------------------------------------------------------------- * * TkpTextManagerRevert -- * * Restore the previous state of the text from the cache. This assumes * that the cache was created before making the last change to the text. * Otherwise it has no effect. * * Results: * A pointer to the UTF-8 string for the restored text. * * Side effects: * The TextManager's string is set to the cached string and the cache is * cleared. * * *--------------------------------------------------------------------------- */ const char * TkpTextManagerRevert( ClientData clientData, int *numChars, int *numBytes) { TextManager *managerPtr = (TextManager *) clientData; if (managerPtr->backup) { [managerPtr->string release]; managerPtr->string = managerPtr->backup; managerPtr->backup = nil; } return TextManagerUpdate(managerPtr, numChars, numBytes); } /* *--------------------------------------------------------------------------- * * TkpTextManagerInsert -- * * Insert the string, as described by the UTF-8 encoded byte array * referenced by the value parameter, at the provided character index. * Inserting a cluster may be done with sequential calls that each provide * a single unicode code point. The macOS port always provides both * surrogate pairs in a single XEvent, so there should not be misplaced * surrogates, but the modifiers following the base char may be incomplete * after calling this. Also, the provided character index may not refer * to a base character in some calls. If the oldString parameter is not * NULL the pointer which it references will be set to the address of a * UTF-8 encoding of the text prior to the insertion. This is intended for * use in validating the change to the string, and may not persist after * the next iteration of the event loop. * * Results: * A pointer to the TextManager's UTF-8 string. * * Side effects: * The number of unichars and bytes are recorded in the integer variables * referenced by the numChars and numBytes parameters. The pointer * referenced by the parameter oldString may be set to the address of an * encoded byte sequence representing the cached prior state of the * string. * *--------------------------------------------------------------------------- */ const char* TkpTextManagerInsert( ClientData clientData, int charIndex, const char *value, int *numChars, int *numBytes, const char **oldString) { TextManager *managerPtr = (TextManager *) clientData; NSString *valueString = [[NSString alloc] initWithUTF8String: value]; if (oldString) { TextManagerCache(managerPtr); [managerPtr->string insertString:valueString atIndex:charIndex]; *oldString = (char *) [managerPtr->backup UTF8String]; } else { TextManagerClearCache(managerPtr); [managerPtr->string insertString:valueString atIndex:charIndex]; } return TextManagerUpdate(managerPtr, numChars, numBytes); } /* *--------------------------------------------------------------------------- * * TkpTextManagerDelete -- * * Delete the number of clusters specified by the count parameter, * starting at the cluster containing the character referenced by * the charIndex parameter. If that character is not a base character * the entire cluster which contains it will be deleted, so the * resulting string is a sequence of well-formed clusters. * * Results: * A pointer to the TextManager's cached UTF-8 string. * * Side effects: * The number of unichars and bytes in the modified NSMutableString are * recorded in the integer variables referenced by the numChars and * numBytes parameters. Also the number of characters which were removed * is recorded in the integer variable charsDeleted. The index of each * remaining base character will change by addition or subtraction of this * number. If the parameter charsDeleted is a non-null pointer then it * will be set to the address of a UTF-8 encoded C string containing the * deleted characters. If the parameter oldString is non-NULL then the * value of the string will be cached before the characters are deleted * and the pointer referenced by oldString be set to a UTF8-encoded * C-string representing the cached string. These strings are meant for * immediate use in validating the change, and may not persist after the * next iteration of the event loop. * *--------------------------------------------------------------------------- */ const char* TkpTextManagerDelete( ClientData clientData, int charIndex, int count, int *numChars, int *numBytes, int *numDeleted, char **charsDeleted, const char **oldString) { TextManager *managerPtr = (TextManager *) clientData; NSRange deleteRange = CharRangeFromClusterRange(managerPtr->string, charIndex, count); if (charsDeleted || oldString) { NSString *diffString = [managerPtr->string substringWithRange:deleteRange]; TextManagerCache(managerPtr); if (charsDeleted) { *charsDeleted = (char *) [diffString UTF8String]; } if (oldString) { *oldString = [managerPtr->backup UTF8String]; } } else { TextManagerClearCache(managerPtr); } [managerPtr->string deleteCharactersInRange: deleteRange]; *numDeleted = deleteRange.length; return TextManagerUpdate(managerPtr, numChars, numBytes); } |
︙ |
Changes to tests/entry.test.
︙ | |||
980 981 982 983 984 985 986 | 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 | - + | } -result {4} test entry-3.31 {EntryWidgetCmd procedure, "index" widget command} -setup { entry .e } -body { .e in } -cleanup { destroy .e |
︙ | |||
1576 1577 1578 1579 1580 1581 1582 | 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 | - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | entry .e -font {Courier -12} -borderwidth 2 -highlightthickness 2 pack .e update } -body { .e gorp } -cleanup { destroy .e |
︙ |