1/* 2 * tkTextMark.c -- 3 * 4 * This file contains the functions that implement marks for text 5 * widgets. 6 * 7 * Copyright (c) 1994 The Regents of the University of California. 8 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include "tkInt.h" 17#include "tkText.h" 18 19/* 20 * Macro that determines the size of a mark segment: 21 */ 22 23#define MSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \ 24 + sizeof(TkTextMark))) 25 26/* 27 * Forward references for functions defined in this file: 28 */ 29 30static void InsertUndisplayProc(TkText *textPtr, 31 TkTextDispChunk *chunkPtr); 32static int MarkDeleteProc(TkTextSegment *segPtr, 33 TkTextLine *linePtr, int treeGone); 34static TkTextSegment * MarkCleanupProc(TkTextSegment *segPtr, 35 TkTextLine *linePtr); 36static void MarkCheckProc(TkTextSegment *segPtr, 37 TkTextLine *linePtr); 38static int MarkLayoutProc(TkText *textPtr, TkTextIndex *indexPtr, 39 TkTextSegment *segPtr, int offset, int maxX, 40 int maxChars, int noCharsYet, TkWrapMode wrapMode, 41 TkTextDispChunk *chunkPtr); 42static int MarkFindNext(Tcl_Interp *interp, 43 TkText *textPtr, const char *markName); 44static int MarkFindPrev(Tcl_Interp *interp, 45 TkText *textPtr, const char *markName); 46 47 48/* 49 * The following structures declare the "mark" segment types. There are 50 * actually two types for marks, one with left gravity and one with right 51 * gravity. They are identical except for their gravity property. 52 */ 53 54const Tk_SegType tkTextRightMarkType = { 55 "mark", /* name */ 56 0, /* leftGravity */ 57 NULL, /* splitProc */ 58 MarkDeleteProc, /* deleteProc */ 59 MarkCleanupProc, /* cleanupProc */ 60 NULL, /* lineChangeProc */ 61 MarkLayoutProc, /* layoutProc */ 62 MarkCheckProc /* checkProc */ 63}; 64 65const Tk_SegType tkTextLeftMarkType = { 66 "mark", /* name */ 67 1, /* leftGravity */ 68 NULL, /* splitProc */ 69 MarkDeleteProc, /* deleteProc */ 70 MarkCleanupProc, /* cleanupProc */ 71 NULL, /* lineChangeProc */ 72 MarkLayoutProc, /* layoutProc */ 73 MarkCheckProc /* checkProc */ 74}; 75 76/* 77 *-------------------------------------------------------------- 78 * 79 * TkTextMarkCmd -- 80 * 81 * This function is invoked to process the "mark" options of the widget 82 * command for text widgets. See the user documentation for details on 83 * what it does. 84 * 85 * Results: 86 * A standard Tcl result. 87 * 88 * Side effects: 89 * See the user documentation. 90 * 91 *-------------------------------------------------------------- 92 */ 93 94int 95TkTextMarkCmd( 96 register TkText *textPtr, /* Information about text widget. */ 97 Tcl_Interp *interp, /* Current interpreter. */ 98 int objc, /* Number of arguments. */ 99 Tcl_Obj *const objv[]) /* Argument objects. Someone else has already 100 * parsed this command enough to know that 101 * objv[1] is "mark". */ 102{ 103 Tcl_HashEntry *hPtr; 104 TkTextSegment *markPtr; 105 Tcl_HashSearch search; 106 TkTextIndex index; 107 const Tk_SegType *newTypePtr; 108 int optionIndex; 109 static const char *markOptionStrings[] = { 110 "gravity", "names", "next", "previous", "set", "unset", NULL 111 }; 112 enum markOptions { 113 MARK_GRAVITY, MARK_NAMES, MARK_NEXT, MARK_PREVIOUS, MARK_SET, 114 MARK_UNSET 115 }; 116 117 if (objc < 3) { 118 Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); 119 return TCL_ERROR; 120 } 121 if (Tcl_GetIndexFromObj(interp, objv[2], markOptionStrings, "mark option", 122 0, &optionIndex) != TCL_OK) { 123 return TCL_ERROR; 124 } 125 126 switch ((enum markOptions) optionIndex) { 127 case MARK_GRAVITY: { 128 char c; 129 int length; 130 char *str; 131 132 if (objc < 4 || objc > 5) { 133 Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?"); 134 return TCL_ERROR; 135 } 136 str = Tcl_GetStringFromObj(objv[3],&length); 137 if (length == 6 && !strcmp(str, "insert")) { 138 markPtr = textPtr->insertMarkPtr; 139 } else if (length == 7 && !strcmp(str, "current")) { 140 markPtr = textPtr->currentMarkPtr; 141 } else { 142 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str); 143 if (hPtr == NULL) { 144 Tcl_AppendResult(interp, "there is no mark named \"", 145 Tcl_GetString(objv[3]), "\"", NULL); 146 return TCL_ERROR; 147 } 148 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 149 } 150 if (objc == 4) { 151 if (markPtr->typePtr == &tkTextRightMarkType) { 152 Tcl_SetResult(interp, "right", TCL_STATIC); 153 } else { 154 Tcl_SetResult(interp, "left", TCL_STATIC); 155 } 156 return TCL_OK; 157 } 158 str = Tcl_GetStringFromObj(objv[4],&length); 159 c = str[0]; 160 if ((c == 'l') && (strncmp(str, "left", (unsigned)length) == 0)) { 161 newTypePtr = &tkTextLeftMarkType; 162 } else if ((c == 'r') && 163 (strncmp(str, "right", (unsigned)length) == 0)) { 164 newTypePtr = &tkTextRightMarkType; 165 } else { 166 Tcl_AppendResult(interp, "bad mark gravity \"", str, 167 "\": must be left or right", NULL); 168 return TCL_ERROR; 169 } 170 TkTextMarkSegToIndex(textPtr, markPtr, &index); 171 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); 172 markPtr->typePtr = newTypePtr; 173 TkBTreeLinkSegment(markPtr, &index); 174 break; 175 } 176 case MARK_NAMES: 177 if (objc != 3) { 178 Tcl_WrongNumArgs(interp, 3, objv, NULL); 179 return TCL_ERROR; 180 } 181 Tcl_AppendElement(interp, "insert"); 182 Tcl_AppendElement(interp, "current"); 183 for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable, 184 &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { 185 Tcl_AppendElement(interp, 186 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr)); 187 } 188 break; 189 case MARK_NEXT: 190 if (objc != 4) { 191 Tcl_WrongNumArgs(interp, 3, objv, "index"); 192 return TCL_ERROR; 193 } 194 return MarkFindNext(interp, textPtr, Tcl_GetString(objv[3])); 195 case MARK_PREVIOUS: 196 if (objc != 4) { 197 Tcl_WrongNumArgs(interp, 3, objv, "index"); 198 return TCL_ERROR; 199 } 200 return MarkFindPrev(interp, textPtr, Tcl_GetString(objv[3])); 201 case MARK_SET: 202 if (objc != 5) { 203 Tcl_WrongNumArgs(interp, 3, objv, "markName index"); 204 return TCL_ERROR; 205 } 206 if (TkTextGetObjIndex(interp, textPtr, objv[4], &index) != TCL_OK) { 207 return TCL_ERROR; 208 } 209 TkTextSetMark(textPtr, Tcl_GetString(objv[3]), &index); 210 return TCL_OK; 211 case MARK_UNSET: { 212 int i; 213 214 for (i = 3; i < objc; i++) { 215 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, 216 Tcl_GetString(objv[i])); 217 if (hPtr != NULL) { 218 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 219 220 /* 221 * Special case not needed with peer widgets. 222 */ 223 224 if ((markPtr == textPtr->insertMarkPtr) 225 || (markPtr == textPtr->currentMarkPtr)) { 226 continue; 227 } 228 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); 229 Tcl_DeleteHashEntry(hPtr); 230 ckfree((char *) markPtr); 231 } 232 } 233 break; 234 } 235 } 236 return TCL_OK; 237} 238 239/* 240 *---------------------------------------------------------------------- 241 * 242 * TkTextSetMark -- 243 * 244 * Set a mark to a particular position, creating a new mark if one 245 * doesn't already exist. 246 * 247 * Results: 248 * The return value is a pointer to the mark that was just set. 249 * 250 * Side effects: 251 * A new mark is created, or an existing mark is moved. 252 * 253 *---------------------------------------------------------------------- 254 */ 255 256TkTextSegment * 257TkTextSetMark( 258 TkText *textPtr, /* Text widget in which to create mark. */ 259 const char *name, /* Name of mark to set. */ 260 TkTextIndex *indexPtr) /* Where to set mark. */ 261{ 262 Tcl_HashEntry *hPtr = NULL; 263 TkTextSegment *markPtr; 264 TkTextIndex insertIndex; 265 int isNew, widgetSpecific; 266 267 if (!strcmp(name, "insert")) { 268 widgetSpecific = 1; 269 markPtr = textPtr->insertMarkPtr; 270 isNew = (markPtr == NULL ? 1 : 0); 271 } else if (!strcmp(name, "current")) { 272 widgetSpecific = 2; 273 markPtr = textPtr->currentMarkPtr; 274 isNew = (markPtr == NULL ? 1 : 0); 275 } else { 276 widgetSpecific = 0; 277 hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name, 278 &isNew); 279 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 280 } 281 if (!isNew) { 282 /* 283 * If this is the insertion point that's being moved, be sure to force 284 * a display update at the old position. Also, don't let the insertion 285 * cursor be after the final newline of the file. 286 */ 287 288 if (markPtr == textPtr->insertMarkPtr) { 289 TkTextIndex index, index2; 290 291 TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); 292 TkTextIndexForwChars(NULL,&index, 1, &index2, COUNT_INDICES); 293 294 /* 295 * While we wish to redisplay, no heights have changed, so no need 296 * to call TkTextInvalidateLineMetrics. 297 */ 298 299 TkTextChanged(NULL, textPtr, &index, &index2); 300 if (TkBTreeLinesTo(textPtr, indexPtr->linePtr) == 301 TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr)) { 302 TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex, 303 COUNT_INDICES); 304 indexPtr = &insertIndex; 305 } 306 } 307 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); 308 } else { 309 markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE); 310 markPtr->typePtr = &tkTextRightMarkType; 311 markPtr->size = 0; 312 markPtr->body.mark.textPtr = textPtr; 313 markPtr->body.mark.linePtr = indexPtr->linePtr; 314 markPtr->body.mark.hPtr = hPtr; 315 if (widgetSpecific == 0) { 316 Tcl_SetHashValue(hPtr, markPtr); 317 } else if (widgetSpecific == 1) { 318 textPtr->insertMarkPtr = markPtr; 319 } else { 320 textPtr->currentMarkPtr = markPtr; 321 } 322 } 323 TkBTreeLinkSegment(markPtr, indexPtr); 324 325 /* 326 * If the mark is the insertion cursor, then update the screen at the 327 * mark's new location. 328 */ 329 330 if (markPtr == textPtr->insertMarkPtr) { 331 TkTextIndex index2; 332 333 TkTextIndexForwChars(NULL,indexPtr, 1, &index2, COUNT_INDICES); 334 335 /* 336 * While we wish to redisplay, no heights have changed, so no need to 337 * call TkTextInvalidateLineMetrics 338 */ 339 340 TkTextChanged(NULL, textPtr, indexPtr, &index2); 341 } 342 return markPtr; 343} 344 345/* 346 *-------------------------------------------------------------- 347 * 348 * TkTextMarkSegToIndex -- 349 * 350 * Given a segment that is a mark, create an index that refers to the 351 * next text character (or other text segment with non-zero size) after 352 * the mark. 353 * 354 * Results: 355 * *IndexPtr is filled in with index information. 356 * 357 * Side effects: 358 * None. 359 * 360 *-------------------------------------------------------------- 361 */ 362 363void 364TkTextMarkSegToIndex( 365 TkText *textPtr, /* Text widget containing mark. */ 366 TkTextSegment *markPtr, /* Mark segment. */ 367 TkTextIndex *indexPtr) /* Index information gets stored here. */ 368{ 369 TkTextSegment *segPtr; 370 371 indexPtr->tree = textPtr->sharedTextPtr->tree; 372 indexPtr->linePtr = markPtr->body.mark.linePtr; 373 indexPtr->byteIndex = 0; 374 for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr; 375 segPtr = segPtr->nextPtr) { 376 indexPtr->byteIndex += segPtr->size; 377 } 378} 379 380/* 381 *-------------------------------------------------------------- 382 * 383 * TkTextMarkNameToIndex -- 384 * 385 * Given the name of a mark, return an index corresponding to the mark 386 * name. 387 * 388 * Results: 389 * The return value is TCL_OK if "name" exists as a mark in the text 390 * widget. In this case *indexPtr is filled in with the next segment 391 * whose after the mark whose size is non-zero. TCL_ERROR is returned if 392 * the mark doesn't exist in the text widget. 393 * 394 * Side effects: 395 * None. 396 * 397 *-------------------------------------------------------------- 398 */ 399 400int 401TkTextMarkNameToIndex( 402 TkText *textPtr, /* Text widget containing mark. */ 403 const char *name, /* Name of mark. */ 404 TkTextIndex *indexPtr) /* Index information gets stored here. */ 405{ 406 TkTextSegment *segPtr; 407 408 if (textPtr == NULL) { 409 return TCL_ERROR; 410 } 411 412 if (!strcmp(name, "insert")) { 413 segPtr = textPtr->insertMarkPtr; 414 } else if (!strcmp(name, "current")) { 415 segPtr = textPtr->currentMarkPtr; 416 } else { 417 Tcl_HashEntry *hPtr; 418 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name); 419 if (hPtr == NULL) { 420 return TCL_ERROR; 421 } 422 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 423 } 424 TkTextMarkSegToIndex(textPtr, segPtr, indexPtr); 425 return TCL_OK; 426} 427 428/* 429 *-------------------------------------------------------------- 430 * 431 * MarkDeleteProc -- 432 * 433 * This function is invoked by the text B-tree code whenever a mark lies 434 * in a range of characters being deleted. 435 * 436 * Results: 437 * Returns 1 to indicate that deletion has been rejected. 438 * 439 * Side effects: 440 * None (even if the whole tree is being deleted we don't free up the 441 * mark; it will be done elsewhere). 442 * 443 *-------------------------------------------------------------- 444 */ 445 446 /* ARGSUSED */ 447static int 448MarkDeleteProc( 449 TkTextSegment *segPtr, /* Segment being deleted. */ 450 TkTextLine *linePtr, /* Line containing segment. */ 451 int treeGone) /* Non-zero means the entire tree is being 452 * deleted, so everything must get cleaned 453 * up. */ 454{ 455 return 1; 456} 457 458/* 459 *-------------------------------------------------------------- 460 * 461 * MarkCleanupProc -- 462 * 463 * This function is invoked by the B-tree code whenever a mark segment is 464 * moved from one line to another. 465 * 466 * Results: 467 * None. 468 * 469 * Side effects: 470 * The linePtr field of the segment gets updated. 471 * 472 *-------------------------------------------------------------- 473 */ 474 475static TkTextSegment * 476MarkCleanupProc( 477 TkTextSegment *markPtr, /* Mark segment that's being moved. */ 478 TkTextLine *linePtr) /* Line that now contains segment. */ 479{ 480 markPtr->body.mark.linePtr = linePtr; 481 return markPtr; 482} 483 484/* 485 *-------------------------------------------------------------- 486 * 487 * MarkLayoutProc -- 488 * 489 * This function is the "layoutProc" for mark segments. 490 * 491 * Results: 492 * If the mark isn't the insertion cursor then the return value is -1 to 493 * indicate that this segment shouldn't be displayed. If the mark is the 494 * insertion character then 1 is returned and the chunkPtr structure is 495 * filled in. 496 * 497 * Side effects: 498 * None, except for filling in chunkPtr. 499 * 500 *-------------------------------------------------------------- 501 */ 502 503static int 504MarkLayoutProc( 505 TkText *textPtr, /* Text widget being layed out. */ 506 TkTextIndex *indexPtr, /* Identifies first character in chunk. */ 507 TkTextSegment *segPtr, /* Segment corresponding to indexPtr. */ 508 int offset, /* Offset within segPtr corresponding to 509 * indexPtr (always 0). */ 510 int maxX, /* Chunk must not occupy pixels at this 511 * position or higher. */ 512 int maxChars, /* Chunk must not include more than this many 513 * characters. */ 514 int noCharsYet, /* Non-zero means no characters have been 515 * assigned to this line yet. */ 516 TkWrapMode wrapMode, /* Not used. */ 517 register TkTextDispChunk *chunkPtr) 518 /* Structure to fill in with information about 519 * this chunk. The x field has already been 520 * set by the caller. */ 521{ 522 if (segPtr != textPtr->insertMarkPtr) { 523 return -1; 524 } 525 526 chunkPtr->displayProc = TkTextInsertDisplayProc; 527 chunkPtr->undisplayProc = InsertUndisplayProc; 528 chunkPtr->measureProc = NULL; 529 chunkPtr->bboxProc = NULL; 530 chunkPtr->numBytes = 0; 531 chunkPtr->minAscent = 0; 532 chunkPtr->minDescent = 0; 533 chunkPtr->minHeight = 0; 534 chunkPtr->width = 0; 535 536 /* 537 * Note: can't break a line after the insertion cursor: this prevents the 538 * insertion cursor from being stranded at the end of a line. 539 */ 540 541 chunkPtr->breakIndex = -1; 542 chunkPtr->clientData = (ClientData) textPtr; 543 return 1; 544} 545 546/* 547 *-------------------------------------------------------------- 548 * 549 * TkTextInsertDisplayProc -- 550 * 551 * This function is called to display the insertion cursor. 552 * 553 * Results: 554 * None. 555 * 556 * Side effects: 557 * Graphics are drawn. 558 * 559 *-------------------------------------------------------------- 560 */ 561 562 /* ARGSUSED */ 563void 564TkTextInsertDisplayProc( 565 TkText *textPtr, /* The current text widget. */ 566 TkTextDispChunk *chunkPtr, /* Chunk that is to be drawn. */ 567 int x, /* X-position in dst at which to draw this 568 * chunk (may differ from the x-position in 569 * the chunk because of scrolling). */ 570 int y, /* Y-position at which to draw this chunk in 571 * dst (x-position is in the chunk itself). */ 572 int height, /* Total height of line. */ 573 int baseline, /* Offset of baseline from y. */ 574 Display *display, /* Display to use for drawing. */ 575 Drawable dst, /* Pixmap or window in which to draw chunk. */ 576 int screenY) /* Y-coordinate in text window that 577 * corresponds to y. */ 578{ 579 /* 580 * We have no need for the clientData. 581 */ 582 583 /* TkText *textPtr = (TkText *) chunkPtr->clientData; */ 584 TkTextIndex index; 585 int halfWidth = textPtr->insertWidth/2; 586 int rightSideWidth; 587 int ix = 0, iy = 0, iw = 0, ih = 0, charWidth = 0; 588 589 if(textPtr->insertCursorType) { 590 TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); 591 TkTextIndexBbox(textPtr, &index, &ix, &iy, &iw, &ih, &charWidth); 592 rightSideWidth = charWidth + halfWidth; 593 } else { 594 rightSideWidth = halfWidth; 595 } 596 597 if ((x + rightSideWidth) < 0) { 598 /* 599 * The insertion cursor is off-screen. Indicate caret at 0,0 and 600 * return. 601 */ 602 603 Tk_SetCaretPos(textPtr->tkwin, 0, 0, height); 604 return; 605 } 606 607 Tk_SetCaretPos(textPtr->tkwin, x - halfWidth, screenY, height); 608 609 /* 610 * As a special hack to keep the cursor visible on mono displays (or 611 * anywhere else that the selection and insertion cursors have the same 612 * color) write the default background in the cursor area (instead of 613 * nothing) when the cursor isn't on. Otherwise the selection might hide 614 * the cursor. 615 */ 616 617 if (textPtr->flags & INSERT_ON) { 618 Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder, 619 x - halfWidth, y, charWidth + textPtr->insertWidth, height, 620 textPtr->insertBorderWidth, TK_RELIEF_RAISED); 621 } else if (textPtr->selBorder == textPtr->insertBorder) { 622 Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border, 623 x - halfWidth, y, charWidth + textPtr->insertWidth, height, 624 0, TK_RELIEF_FLAT); 625 } 626} 627 628/* 629 *-------------------------------------------------------------- 630 * 631 * InsertUndisplayProc -- 632 * 633 * This function is called when the insertion cursor is no longer at a 634 * visible point on the display. It does nothing right now. 635 * 636 * Results: 637 * None. 638 * 639 * Side effects: 640 * None. 641 * 642 *-------------------------------------------------------------- 643 */ 644 645 /* ARGSUSED */ 646static void 647InsertUndisplayProc( 648 TkText *textPtr, /* Overall information about text widget. */ 649 TkTextDispChunk *chunkPtr) /* Chunk that is about to be freed. */ 650{ 651 return; 652} 653 654/* 655 *-------------------------------------------------------------- 656 * 657 * MarkCheckProc -- 658 * 659 * This function is invoked by the B-tree code to perform consistency 660 * checks on mark segments. 661 * 662 * Results: 663 * None. 664 * 665 * Side effects: 666 * The function panics if it detects anything wrong with 667 * the mark. 668 * 669 *-------------------------------------------------------------- 670 */ 671 672static void 673MarkCheckProc( 674 TkTextSegment *markPtr, /* Segment to check. */ 675 TkTextLine *linePtr) /* Line containing segment. */ 676{ 677 Tcl_HashSearch search; 678 Tcl_HashEntry *hPtr; 679 680 if (markPtr->body.mark.linePtr != linePtr) { 681 Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus"); 682 } 683 684 /* 685 * These two marks are not in the hash table 686 */ 687 688 if (markPtr->body.mark.textPtr->insertMarkPtr == markPtr) { 689 return; 690 } 691 if (markPtr->body.mark.textPtr->currentMarkPtr == markPtr) { 692 return; 693 } 694 695 /* 696 * Make sure that the mark is still present in the text's mark hash table. 697 */ 698 699 for (hPtr = Tcl_FirstHashEntry( 700 &markPtr->body.mark.textPtr->sharedTextPtr->markTable, 701 &search); hPtr != markPtr->body.mark.hPtr; 702 hPtr = Tcl_NextHashEntry(&search)) { 703 if (hPtr == NULL) { 704 Tcl_Panic("MarkCheckProc couldn't find hash table entry for mark"); 705 } 706 } 707} 708 709/* 710 *-------------------------------------------------------------- 711 * 712 * MarkFindNext -- 713 * 714 * This function searches forward for the next mark. 715 * 716 * Results: 717 * A standard Tcl result, which is a mark name or an empty string. 718 * 719 * Side effects: 720 * None. 721 * 722 *-------------------------------------------------------------- 723 */ 724 725static int 726MarkFindNext( 727 Tcl_Interp *interp, /* For error reporting */ 728 TkText *textPtr, /* The widget */ 729 const char *string) /* The starting index or mark name */ 730{ 731 TkTextIndex index; 732 Tcl_HashEntry *hPtr; 733 register TkTextSegment *segPtr; 734 int offset; 735 736 if (!strcmp(string, "insert")) { 737 segPtr = textPtr->insertMarkPtr; 738 TkTextMarkSegToIndex(textPtr, segPtr, &index); 739 segPtr = segPtr->nextPtr; 740 } else if (!strcmp(string, "current")) { 741 segPtr = textPtr->currentMarkPtr; 742 TkTextMarkSegToIndex(textPtr, segPtr, &index); 743 segPtr = segPtr->nextPtr; 744 } else { 745 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string); 746 if (hPtr != NULL) { 747 /* 748 * If given a mark name, return the next mark in the list of 749 * segments, even if it happens to be at the same character 750 * position. 751 */ 752 753 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 754 TkTextMarkSegToIndex(textPtr, segPtr, &index); 755 segPtr = segPtr->nextPtr; 756 } else { 757 /* 758 * For non-mark name indices we want to return any marks that are 759 * right at the index. 760 */ 761 762 if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) { 763 return TCL_ERROR; 764 } 765 for (offset = 0, segPtr = index.linePtr->segPtr; 766 segPtr != NULL && offset < index.byteIndex; 767 offset += segPtr->size, segPtr = segPtr->nextPtr) { 768 /* Empty loop body */ ; 769 } 770 } 771 } 772 773 while (1) { 774 /* 775 * segPtr points at the first possible candidate, or NULL if we ran 776 * off the end of the line. 777 */ 778 779 for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) { 780 if (segPtr->typePtr == &tkTextRightMarkType || 781 segPtr->typePtr == &tkTextLeftMarkType) { 782 if (segPtr == textPtr->currentMarkPtr) { 783 Tcl_SetResult(interp, "current", TCL_STATIC); 784 } else if (segPtr == textPtr->insertMarkPtr) { 785 Tcl_SetResult(interp, "insert", TCL_STATIC); 786 } else if (segPtr->body.mark.textPtr != textPtr) { 787 /* 788 * Ignore widget-specific marks for the other widgets. 789 */ 790 791 continue; 792 } else { 793 Tcl_SetResult(interp, 794 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, 795 segPtr->body.mark.hPtr), TCL_STATIC); 796 } 797 return TCL_OK; 798 } 799 } 800 index.linePtr = TkBTreeNextLine(textPtr, index.linePtr); 801 if (index.linePtr == NULL) { 802 return TCL_OK; 803 } 804 index.byteIndex = 0; 805 segPtr = index.linePtr->segPtr; 806 } 807} 808 809/* 810 *-------------------------------------------------------------- 811 * 812 * MarkFindPrev -- 813 * 814 * This function searches backwards for the previous mark. 815 * 816 * Results: 817 * A standard Tcl result, which is a mark name or an empty string. 818 * 819 * Side effects: 820 * None. 821 * 822 *-------------------------------------------------------------- 823 */ 824 825static int 826MarkFindPrev( 827 Tcl_Interp *interp, /* For error reporting */ 828 TkText *textPtr, /* The widget */ 829 const char *string) /* The starting index or mark name */ 830{ 831 TkTextIndex index; 832 Tcl_HashEntry *hPtr; 833 register TkTextSegment *segPtr, *seg2Ptr, *prevPtr; 834 int offset; 835 836 if (!strcmp(string, "insert")) { 837 segPtr = textPtr->insertMarkPtr; 838 TkTextMarkSegToIndex(textPtr, segPtr, &index); 839 } else if (!strcmp(string, "current")) { 840 segPtr = textPtr->currentMarkPtr; 841 TkTextMarkSegToIndex(textPtr, segPtr, &index); 842 } else { 843 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string); 844 if (hPtr != NULL) { 845 /* 846 * If given a mark name, return the previous mark in the list of 847 * segments, even if it happens to be at the same character 848 * position. 849 */ 850 851 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); 852 TkTextMarkSegToIndex(textPtr, segPtr, &index); 853 } else { 854 /* 855 * For non-mark name indices we do not return any marks that are 856 * right at the index. 857 */ 858 859 if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) { 860 return TCL_ERROR; 861 } 862 for (offset = 0, segPtr = index.linePtr->segPtr; 863 segPtr != NULL && offset < index.byteIndex; 864 offset += segPtr->size, segPtr = segPtr->nextPtr) { 865 /* Empty loop body */ 866 } 867 } 868 } 869 870 while (1) { 871 /* 872 * segPtr points just past the first possible candidate, or at the 873 * begining of the line. 874 */ 875 876 for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; 877 seg2Ptr != NULL && seg2Ptr != segPtr; 878 seg2Ptr = seg2Ptr->nextPtr) { 879 if (seg2Ptr->typePtr == &tkTextRightMarkType || 880 seg2Ptr->typePtr == &tkTextLeftMarkType) { 881 prevPtr = seg2Ptr; 882 } 883 } 884 if (prevPtr != NULL) { 885 if (prevPtr == textPtr->currentMarkPtr) { 886 Tcl_SetResult(interp, "current", TCL_STATIC); 887 } else if (prevPtr == textPtr->insertMarkPtr) { 888 Tcl_SetResult(interp, "insert", TCL_STATIC); 889 } else if (prevPtr->body.mark.textPtr != textPtr) { 890 /* 891 * Ignore widget-specific marks for the other widgets. 892 */ 893 894 continue; 895 } else { 896 Tcl_SetResult(interp, 897 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, 898 prevPtr->body.mark.hPtr), TCL_STATIC); 899 } 900 return TCL_OK; 901 } 902 index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr); 903 if (index.linePtr == NULL) { 904 return TCL_OK; 905 } 906 segPtr = NULL; 907 } 908} 909 910/* 911 * Local Variables: 912 * mode: c 913 * c-basic-offset: 4 914 * fill-column: 78 915 * End: 916 */ 917