1# File: canvist.tcl 2 3# Purpose: a 1-dimension list using a canvas 4 5# 6# Copyright (c) 1997-2001 Tim Baker 7# 8# This software may be copied and distributed for educational, research, and 9# not for profit purposes provided that this copyright and statement are 10# included in all such copies. 11# 12 13namespace eval NSCanvist { 14 15 variable Priv 16 17 set Priv(scan,afterId) {} 18 set Priv(canvistPrev) -1 19 20# namespace eval NSCanvist 21} 22 23# NSCanvist::NSCanvist -- 24# 25# Object constructor called by NSObject::New. 26# 27# Arguments: 28# arg1 about arg1 29# 30# Results: 31# What happened. 32 33proc NSCanvist::NSCanvist {oop parent rowHgt wid hgt newRowCmd highlightCmd} { 34 35 global NSCanvist 36 variable Priv 37 38 set c $parent.canvist$oop 39 canvas $c \ 40 -scrollregion [list 0 0 $wid 0] -width $wid -height $hgt \ 41 -relief flat -background white -highlightthickness 0 \ 42 -yscrollincrement $rowHgt -takefocus 1 43 44 # 45 # Do stuff when the canvas is clicked 46 # 47 48 bind $c <ButtonPress-1> "NSCanvist::Button1 $oop %x %y 0" 49 bind $c <Button1-Motion> "NSCanvist::Motion1 $oop %x %y" 50 bind $c <Double-ButtonPress-1> "NSCanvist::Double1 $oop %x %y" 51 bind $c <ButtonRelease-1> "NSCanvist::Release1 $oop %x %y" 52 bind $c <Button1-Leave> "NSCanvist::Leave1 $oop %x %y" 53 bind $c <Button1-Enter> "NSCanvist::CancelRepeat $oop" 54 55 # KeyPress bindings 56 bind $c <KeyPress-Home> "$c yview moveto 0 ; break" 57 bind $c <KeyPress-End> "$c yview moveto 1 ; break" 58 bind $c <KeyPress-Prior> "$c yview scroll -1 pages ; break" 59 bind $c <KeyPress-Next> "$c yview scroll 1 pages ; break" 60 bind $c <KeyPress-Up> "NSCanvist::UpDown $oop -1 ; break" 61 bind $c <KeyPress-Down> "NSCanvist::UpDown $oop +1 ; break" 62 63 # 64 # The Control key toggles selected rows. 65 # 66 67 bind $c <Control-ButtonPress-1> "NSCanvist::Button1 $oop %x %y 1" 68 69 # Destroy the object along with the canvas (later) 70 NSUtils::DestroyObjectWithWidget NSCanvist $oop $c 71 72 # Allows client to draw selection depending on focus 73 bindtags $c [concat [bindtags $c] NSCanvistBindTag$oop] 74 bind NSCanvistBindTag$oop <FocusIn> \ 75 "NSCanvist::Activate $oop 1" 76 bind NSCanvistBindTag$oop <FocusOut> \ 77 "NSCanvist::Activate $oop 0" 78 79 set Priv(stroke) 0 80 81 set NSCanvist($oop,canvas) $c 82 set NSCanvist($oop,rowHgt) $rowHgt 83 set NSCanvist($oop,newRowCmd) $newRowCmd 84 set NSCanvist($oop,highlightCmd) $highlightCmd 85 set NSCanvist($oop,invokeCmd) {} 86 set NSCanvist($oop,selectionCmd) {} 87 set NSCanvist($oop,count) 0 88 set NSCanvist($oop,nextRowTag) 0 89 set NSCanvist($oop,rowTags) {} 90 set NSCanvist($oop,selection) {} 91 set NSCanvist($oop,rowsEnabled) 1 92 set NSCanvist($oop,nearest) 0 93 set NSCanvist($oop,stroke) 0 94 set NSCanvist($oop,trackIgnore) 0 95 set NSCanvist($oop,clickCmd) {} 96 97 # Total hack -- PDjam module uses Drag & Drop 98 set NSCanvist($oop,dragSpecial) 0 99 100 return 101} 102 103# NSCanvist::~NSCanvist -- 104# 105# Object destructor called by NSObject::Delete. 106# 107# Arguments: 108# arg1 about arg1 109# 110# Results: 111# What happened. 112 113proc NSCanvist::~NSCanvist {oop} { 114 115 return 116} 117 118# NSCanvist::Info -- 119# 120# Query and modify info. 121# 122# Arguments: 123# arg1 about arg1 124# 125# Results: 126# What happened. 127 128proc NSCanvist::Info {oop info args} { 129 130 global NSCanvist 131 132 # Verify the object 133 NSObject::CheckObject NSCanvist $oop 134 135 # Set info 136 if {[llength $args]} { 137 switch -- $info { 138 default { 139 set NSCanvist($oop,$info) [lindex $args 0] 140 } 141 } 142 143 # Get info 144 } else { 145 switch -- $info { 146 default { 147 return $NSCanvist($oop,$info) 148 } 149 } 150 } 151 152 return 153} 154 155# NSCanvist::Insert -- 156# 157# Insert a row at the given index. 158# 159# Arguments: 160# arg1 about arg1 161# 162# Results: 163# What happened. 164 165proc NSCanvist::Insert {oop index args} { 166 167 global NSCanvist 168 169 set canvas $NSCanvist($oop,canvas) 170 set count $NSCanvist($oop,count) 171 set rowHgt $NSCanvist($oop,rowHgt) 172 173 if {$index == "end"} {set index $count} 174 if {$index < 0} {set index 0} 175 if {$index > $count} {set index $count} 176 177 set y [expr {$rowHgt * $index}] 178 179 # 180 # Move following rows down by one. 181 # 182 183 if {$index < $count} { 184 foreach rowTag [lrange $NSCanvist($oop,rowTags) $index end] { 185 $canvas move $rowTag 0 $rowHgt 186 } 187 } 188 189 # 190 # The newRowCmd returns a list of all items added that are 191 # on the new row. They get tagged with a common "group tag" 192 # of the form ":N" where N is some integer. 193 # 194 195 set itemIdList [uplevel #0 $NSCanvist($oop,newRowCmd) $oop $y $args] 196 set rowTag ":$NSCanvist($oop,nextRowTag)" 197 foreach itemId $itemIdList { 198 $canvas addtag $rowTag withtag $itemId 199 } 200 201 # Insert 202 if {$index < $count} { 203 204 # Remember the tag applied to all items on this row 205 set NSCanvist($oop,rowTags) \ 206 [linsert $NSCanvist($oop,rowTags) $index $rowTag] 207 208 # This row is not selected 209 set NSCanvist($oop,selection) \ 210 [linsert $NSCanvist($oop,selection) $index 0] 211 212 # Append 213 } else { 214 lappend NSCanvist($oop,rowTags) $rowTag 215 lappend NSCanvist($oop,selection) 0 216 } 217 218 incr NSCanvist($oop,count) 219 incr NSCanvist($oop,nextRowTag) 220 Synch $oop 221 222 return 223} 224 225# NSCanvist::InsertMany -- 226# 227# Insert multiple rows at the given index. 228# 229# Arguments: 230# arg1 about arg1 231# 232# Results: 233# What happened. 234 235proc NSCanvist::InsertMany {oop index itemList} { 236 237 global NSCanvist 238 239 set canvas $NSCanvist($oop,canvas) 240 set count $NSCanvist($oop,count) 241 set rowHgt $NSCanvist($oop,rowHgt) 242 243 set itemListCount [llength $itemList] 244 if {!$itemListCount} return 245 246 if {$index == "end"} {set index $count} 247 if {$index < 0} {set index 0} 248 if {$index > $count} {set index $count} 249 250 set y [expr {$rowHgt * $index}] 251 252 # 253 # Move following rows down. 254 # 255 256 if {$index < $count} { 257 set offset [expr {$itemListCount * $rowHgt}] 258 foreach rowTag [lrange $NSCanvist($oop,rowTags) $index end] { 259 $canvas move $rowTag 0 $offset 260 } 261 } 262 263 set newRowTag {} 264 set newSelected {} 265 266 foreach item $itemList { 267 268 # 269 # The newRowCmd returns a list of all items added that are 270 # on the new row. They get tagged with a common "group tag" 271 # of the form ":N" where N is some integer. 272 # 273 274 set itemIdList [uplevel #0 $NSCanvist($oop,newRowCmd) $oop $y $item] 275 set rowTag ":$NSCanvist($oop,nextRowTag)" 276 foreach itemId $itemIdList { 277 $canvas addtag $rowTag withtag $itemId 278 } 279 280 # Remember the tag applied to all items on this row 281 lappend newRowTag $rowTag 282 283 # This row is not selected 284 lappend newSelected 0 285 286 incr NSCanvist($oop,nextRowTag) 287 288 incr y $rowHgt 289 } 290 291 if {$index < $count} { 292 set NSCanvist($oop,rowTags) \ 293 [eval linsert [list $NSCanvist($oop,rowTags)] $index $newRowTag] 294 set NSCanvist($oop,selection) \ 295 [eval linsert [list $NSCanvist($oop,selection)] $index $newSelected] 296 } else { 297 eval lappend NSCanvist($oop,rowTags) $newRowTag 298 eval lappend NSCanvist($oop,selection) $newSelected 299 } 300 301 incr NSCanvist($oop,count) $itemListCount 302# incr NSCanvist($oop,nextRowTag) $count 303 304 Synch $oop 305 306 return 307} 308 309# NSCanvist::Delete -- 310# 311# Delete one or more rows from the list. 312# 313# Arguments: 314# arg1 about arg1 315# 316# Results: 317# What happened. 318 319proc NSCanvist::Delete {oop index1 index2} { 320 321 global NSCanvist 322 323 set canvas $NSCanvist($oop,canvas) 324 set count $NSCanvist($oop,count) 325 set rowHgt $NSCanvist($oop,rowHgt) 326 327 # Nothing to delete 328 if {$count == 0} return 329 330 if {$index1 >= $count} {set index1 [expr {$count - 1}]} 331 if {$index1 < 0} {set index1 0} 332 if {$index2 == "end"} {set index2 $count} 333 if {$index2 >= $count} {set index2 [expr {$count - 1}]} 334 if {$index2 < 0} {set index2 0} 335 336 set num [expr {$index2 - $index1 + 1}] 337 if {!$num} return 338 339 # Call client's selectionCmd if given 340 set command $NSCanvist($oop,selectionCmd) 341 if {[string length $command]} { 342 set deselect {} 343 for {set row $index1} {$row <= $index2} {incr row} { 344 if {[IsRowSelected $oop $row]} { 345 set NSCanvist($oop,selection) \ 346 [lreplace $NSCanvist($oop,selection) $row $row 0] 347 lappend deselect $row 348 } 349 } 350 if {[llength $deselect]} { 351 uplevel #0 $command $oop [list {} $deselect] 352 } 353 } 354 355 # 356 # Delete all canvas items on each deleted row 357 # 358 359 foreach rowTag [lrange $NSCanvist($oop,rowTags) $index1 $index2] { 360 $canvas delete $rowTag 361 } 362 363 # 364 # Move following rows up. 365 # 366 367 incr index2 368 foreach rowTag [lrange $NSCanvist($oop,rowTags) $index2 end] { 369 $canvas move $rowTag 0 -[expr {$rowHgt * $num}] 370 } 371 372 # Delete row tags from list of row tags for deleted rows. 373 incr index2 -1 374 set NSCanvist($oop,rowTags) [lreplace $NSCanvist($oop,rowTags) $index1 $index2] 375 376 # Delete selection info for deleted rows 377 set NSCanvist($oop,selection) \ 378 [lreplace $NSCanvist($oop,selection) $index1 $index2] 379 380 # Debug 381 if {$num > $count} { 382 NSUtils::ProgError "NSCanvist::Delete: $num > $count" 383 set $num $count 384 } 385 386 incr NSCanvist($oop,count) -$num 387 388 Synch $oop 389 390 return 391} 392 393# NSCanvist::DeleteAll -- 394# 395# Delete all the rows. 396# 397# Arguments: 398# arg1 about arg1 399# 400# Results: 401# What happened. 402 403proc NSCanvist::DeleteAll {oop} { 404 405 global NSCanvist 406 407 set canvas $NSCanvist($oop,canvas) 408 409 # Call client's selectionCmd if given 410 set command $NSCanvist($oop,selectionCmd) 411 if {[string length $command]} { 412 set selection [Selection $oop] 413 if {[llength $selection]} { 414 set NSCanvist($oop,selection) {} 415 uplevel #0 $command $oop [list {} $selection] 416 } 417 } 418 419 # Bye-bye, suckers! 420 $canvas delete all 421 422 set NSCanvist($oop,count) 0 423 set NSCanvist($oop,rowTags) {} 424 set NSCanvist($oop,selection) {} 425 426 Synch $oop 427 428 return 429} 430 431# NSCanvist::_GetRowTag -- 432# 433# Get the tag common to all items on a row containing the given 434# item. 435# 436# Arguments: 437# arg1 about arg1 438# 439# Results: 440# What happened. 441 442proc NSCanvist::_GetRowTag {oop tagOrId} { 443 444 global NSCanvist 445 446 set canvas $NSCanvist($oop,canvas) 447 448 # Get list of tags for item 449 set tagList [$canvas gettags $tagOrId] 450 451 # Items without enabled tag are considered "disabled" 452 if {[lsearch $tagList "enabled"] == -1} {return {}} 453 454 # Search list of tags for grouping tag (eg ":1", ":2" etc) 455 set idx [lsearch $tagList ":*"] 456 457 return [lindex $tagList $idx] 458} 459 460# NSCanvist::RemoveSelection -- 461# 462# Remove the selection from all rows. 463# 464# Arguments: 465# arg1 about arg1 466# 467# Results: 468# What happened. 469 470proc NSCanvist::RemoveSelection {oop} { 471 472if 1 { 473 UpdateSelection $oop {} all 474} else { 475 global NSCanvist 476 477 set row 0 478 foreach state $NSCanvist($oop,selection) { 479 if {[IsRowSelected $oop $row]} { 480 DeselectRow $oop $row 481 } 482 incr row 483 } 484} 485 return 486} 487 488# NSCanvist::SelectRow -- 489# 490# Select the given row. 491# 492# Arguments: 493# arg1 about arg1 494# 495# Results: 496# What happened. 497 498proc NSCanvist::SelectRow {oop row} { 499 500 global NSCanvist 501 502 # Get the widget command 503 set canvas $NSCanvist($oop,canvas) 504 505 set rowTag [lindex $NSCanvist($oop,rowTags) $row] 506 set itemIdList [$canvas find withtag $rowTag] 507 508 # Call user's command to highlight this row 509 uplevel #0 $NSCanvist($oop,highlightCmd) $oop 1 $itemIdList 510 511 # Mark the row as selected 512 set NSCanvist($oop,selection) \ 513 [lreplace $NSCanvist($oop,selection) $row $row 1] 514 515 return 516} 517 518# NSCanvist::DeselectRow -- 519# 520# Deselect the given row. 521# 522# Arguments: 523# arg1 about arg1 524# 525# Results: 526# What happened. 527 528proc NSCanvist::DeselectRow {oop row} { 529 530 global NSCanvist 531 532 # Get the widget command 533 set canvas $NSCanvist($oop,canvas) 534 535 # Get list of items on this row 536 set rowTag [lindex $NSCanvist($oop,rowTags) $row] 537 set itemIdList [$canvas find withtag $rowTag] 538 539 # Mark the row as un-selected 540 set NSCanvist($oop,selection) \ 541 [lreplace $NSCanvist($oop,selection) $row $row 0] 542 543 # Call user's command to un-highlight this row 544 uplevel #0 $NSCanvist($oop,highlightCmd) $oop 0 $itemIdList 545 546 return 547} 548 549# NSCanvist::IsRowSelected -- 550# 551# Is a given row selected? 552# 553# Arguments: 554# arg1 about arg1 555# 556# Results: 557# What happened. 558 559proc NSCanvist::IsRowSelected {oop row} { 560 561 global NSCanvist 562 563 set count [expr {$NSCanvist($oop,count) - 1}] 564 if {($row < 0) || ($row > $count)} { 565 error "bad row \"$row\": must be from 0 to $count" 566 } 567 return [lindex $NSCanvist($oop,selection) $row] 568} 569 570# NSCanvist::UpdateSelection -- 571# 572# Select and deselect some rows. 573# When the selection changes, call client's routine (if any). 574# 575# Arguments: 576# arg1 about arg1 577# 578# Results: 579# What happened. 580 581proc NSCanvist::UpdateSelection {oop selected deselected} { 582 583 global NSCanvist 584 585 # "Selected" takes precedence over "deselected" 586 587 set doneRows {} 588 589 if {([llength $selected] == 1) && ($selected == "all")} { 590 set selected {} 591 set count [Info $oop count] 592 for {set row 0} {$row < $count} {incr row} { 593 lappend selected $row 594 } 595 } 596 if {([llength $deselected] == 1) && ($deselected == "all")} { 597 set deselected [Selection $oop] 598 } 599 600 set newlySelected {} 601 foreach row $selected { 602 if {[lsearch -exact $doneRows $row] >= 0} continue 603 lappend doneRows $row 604 if {[IsRowSelected $oop $row]} continue 605 lappend newlySelected $row 606 } 607 608 set newlyDeselected {} 609 foreach row $deselected { 610 if {[lsearch -exact $doneRows $row] >= 0} continue 611 lappend doneRows $row 612 if {![IsRowSelected $oop $row]} continue 613 lappend newlyDeselected $row 614 } 615 616 if {[llength $newlySelected] || [llength $newlyDeselected]} { 617 618 lsort -integer $newlySelected 619 lsort -integer $newlyDeselected 620 621 # Select rows 622 foreach row $newlySelected { 623 SelectRow $oop $row 624 } 625 626 # Deselect rows 627 foreach row $newlyDeselected { 628 DeselectRow $oop $row 629 } 630 631 # Call client's selectionCmd if given 632 set command $NSCanvist($oop,selectionCmd) 633 if {[string length $command]} { 634 uplevel #0 $command $oop [list $newlySelected $newlyDeselected] 635 } 636 } 637 638 return 639} 640 641# NSCanvist::Selection -- 642# 643# Return a list of row indexes of all currently selected rows. 644# 645# Arguments: 646# arg1 about arg1 647# 648# Results: 649# Returns list of indexes or empty list if no rows are 650# selected. 651 652proc NSCanvist::Selection {oop} { 653 654 global NSCanvist 655 656 set selection {} 657 set row 0 658 foreach state $NSCanvist($oop,selection) { 659 if {$state} { 660 lappend selection $row 661 } 662 incr row 663 } 664 665 return $selection 666} 667 668# NSCanvist::Button1 -- 669# 670# Handle ButtonPress-1 event. 671# 672# Arguments: 673# arg1 about arg1 674# 675# Results: 676# What happened. 677 678proc NSCanvist::Button1 {oop x y extend} { 679 680 global NSCanvist 681 variable Priv 682 683 set c $NSCanvist($oop,canvas) 684 685 # Claim the input focus 686 focus $c 687 688 # Get the hit row. 689 set row [PointToRow $oop $x $y] 690 691 # List rows to select/deselect 692 set select {} 693 set deselect {} 694 695 set callClickCmd 0 696 697 # No item was hit 698 if {$row == -1} { 699 700 # Unselect all rows if not extending selection. 701 if {!$extend} { 702 set deselect all 703 } 704 705 # Prepare for drag 706 if {[Info $oop stroke]} { 707 itemMark $c $x $y 708 } 709 710 # Remember no cell was hit 711 set Priv(canvistPrev) -1 712 713 # An item was hit 714 } else { 715 716 # The row is currently selected 717 if {[IsRowSelected $oop $row]} { 718 719 # Control-click toggles selection 720 if {$extend} { 721 set deselect $row 722 } else { 723 set deselect all 724 set select $row 725 set callClickCmd 1 726 } 727 728 729 # Row was not selected 730 } else { 731 732 # Unselect all rows if not extending selection. 733 if {!$extend} { 734 set deselect all 735 } 736 737 # Select the hit row 738 set select $row 739 } 740 741 # Remember the current row 742 set Priv(canvistPrev) $row 743 } 744 745 # Update the selection 746 UpdateSelection $oop $select $deselect 747 748 if {$callClickCmd} { 749 set command [Info $oop clickCmd] 750 if {[string length $command]} { 751 uplevel #0 $command $oop $row 752 } 753 } 754 755 return 756} 757 758# NSCanvist::Release1 -- 759# 760# . 761# 762# Arguments: 763# arg1 about arg1 764# 765# Results: 766# What happened. 767 768proc NSCanvist::Release1 {oop x y} { 769 770 global NSCanvist 771 variable Priv 772 773 set canvas $NSCanvist($oop,canvas) 774 775 itemSelect $oop 776 set Priv(stroke) 0 777 $canvas delete area 778 779 CancelRepeat $oop 780 781 return 782} 783 784# NSCanvist::Motion1 -- 785# 786# . 787# 788# Arguments: 789# arg1 about arg1 790# 791# Results: 792# What happened. 793 794proc NSCanvist::Motion1 {oop x y} { 795 796 variable Priv 797 798 set canvas [Info $oop canvas] 799 800 # Don't track while UpdateSelection() is in progress 801 if {[Info $oop trackIgnore]} return 802 803 # Don't track if initial click was outside any cell 804 if {$Priv(canvistPrev) == -1} return 805 806 # When mouse tracking (but not the initial click) we find 807 # the cell nearest to the given location, even if the location 808 # is outside any cell, or even the canvas boundary. 809 Info $oop nearest 1 810 811 # Get the hit row. 812 set row [PointToRow $oop $x $y] 813 814 Info $oop nearest 0 815 816 # No item was hit 817 if {($row == -1) || $Priv(stroke)} { 818 819 if {[Info $oop stroke]} { 820 821 # Drag out selection box 822 itemStroke $canvas $x $y 823 } 824 825 # An item was hit 826 } else { 827 828 # Same row as last time 829 if {$row == $Priv(canvistPrev)} return 830 831 if {![Info $oop dragSpecial]} { 832 Info $oop trackIgnore 1 833 UpdateSelection $oop $row all 834 Info $oop trackIgnore 0 835 } 836 837 set Priv(canvistPrev) $row 838 } 839 840 return 841} 842 843# NSCanvist::Leave1 -- 844# 845# . 846# 847# Arguments: 848# arg1 about arg1 849# 850# Results: 851# What happened. 852 853proc NSCanvist::Leave1 {oop x y} { 854 855 AutoScan $oop 856 857 return 858} 859 860# NSCanvist::Double1 -- 861# 862# Call client's command when canvas double-clicked. 863# 864# Arguments: 865# arg1 about arg1 866# 867# Results: 868# What happened. 869 870proc NSCanvist::Double1 {oop x y} { 871 872 global NSCanvist 873 874 set command $NSCanvist($oop,invokeCmd) 875 if {[string length $command]} { 876 uplevel #0 $command $oop $x $y 877 } 878 879 return 880} 881 882# NSCanvist::AutoScan -- 883# 884# Description. 885# 886# Arguments: 887# arg1 about arg1 888# 889# Results: 890# What happened. 891 892proc NSCanvist::AutoScan {oop} { 893 894 global NSCanvist 895 variable Priv 896 897 set canvas $NSCanvist($oop,canvas) 898 if {![winfo exists $canvas]} return 899 900 # Don't track while UpdateSelection() is in progress 901 if {[Info $oop trackIgnore]} return 902 903 set pointerx [winfo pointerx $canvas] 904 set pointery [winfo pointery $canvas] 905 if {[winfo containing $pointerx $pointery] == "$canvas"} return 906 907 set x [expr {$pointerx - [winfo rootx $canvas]}] 908 set y [expr {$pointery - [winfo rooty $canvas]}] 909 910 set scrollRgn [$canvas cget -scrollregion] 911 set scrollWidth [expr {[lindex $scrollRgn 2] - [lindex $scrollRgn 0]}] 912 set scrollHeight [expr {[lindex $scrollRgn 3] - [lindex $scrollRgn 1]}] 913 914 if {[winfo width $canvas] < $scrollWidth} { 915 if {$x >= [winfo width $canvas]} { 916 $canvas xview scroll 1 units 917 } elseif {$x < 0} { 918 $canvas xview scroll -1 units 919 } 920 } 921 922 if {[winfo height $canvas] < $scrollHeight} { 923 if {$y >= [winfo height $canvas]} { 924 $canvas yview scroll 1 units 925 } elseif {$y < 0} { 926 $canvas yview scroll -1 units 927 } 928 } 929 930 Motion1 $oop $x $y 931 932 set Priv(scan,afterId) [after 50 NSCanvist::AutoScan $oop] 933 934 return 935} 936 937# NSCanvist::CancelRepeat -- 938# 939# Cancel auto-scrolling "after" command. 940# 941# Arguments: 942# arg1 about arg1 943# 944# Results: 945# What happened. 946 947proc NSCanvist::CancelRepeat {oop} { 948 949 variable Priv 950 951 after cancel $Priv(scan,afterId) 952 set Priv(scan,afterId) {} 953 954 return 955} 956 957# NSCanvist::Synch -- 958# 959# Sets the scroll region of the canvas to the row height 960# multiplied by the number of items in the list. 961# 962# Arguments: 963# arg1 about arg1 964# 965# Results: 966# What happened. 967 968proc NSCanvist::Synch {oop} { 969 970 global NSCanvist 971 972 set c $NSCanvist($oop,canvas) 973 974 # The canvist height is (num rows) * (row height) 975 set rowHgt $NSCanvist($oop,rowHgt) 976 set height [expr {$rowHgt * $NSCanvist($oop,count)}] 977 978 # Get the scroll region and change the height 979 set scrollRegion [lreplace [$c cget -scrollregion] 3 3 $height] 980 $c configure -scrollregion $scrollRegion 981 982 return 983} 984 985# NSCanvist::ItemRow -- 986# 987# Return the row index the given item is on 988# 989# Arguments: 990# arg1 about arg1 991# 992# Results: 993# What happened. 994 995proc NSCanvist::ItemRow {oop tagOrId} { 996 997 global NSCanvist 998 999 set rowTag [_GetRowTag $oop $tagOrId] 1000 if {$rowTag == {}} {return -1} 1001 return [lsearch -exact $NSCanvist($oop,rowTags) $rowTag] 1002} 1003 1004# NSCanvist::PointToRow -- 1005# 1006# Finds the row containing the given point. If the rowsEnabled option 1007# is set, returns the row containing the point, or -1 of no row 1008# contains the point. If the nearest option is also set, returns the 1009# row closest to the given point, even if the point is outside any 1010# row. 1011# 1012# If the rowsEnabled option is not set, returns the row for which an 1013# enabled canvas item contains the point, otherwise returns -1. 1014# 1015# Arguments: 1016# arg1 about arg1 1017# 1018# Results: 1019# What happened. 1020 1021proc NSCanvist::PointToRow {oop x y} { 1022 1023 global NSCanvist 1024 1025 set canvas $NSCanvist($oop,canvas) 1026 1027 # Option: Don't check for enabled items, just hit the row 1028 if {$NSCanvist($oop,rowsEnabled)} { 1029 set rows [Info $oop count] 1030 set rowHeight [Info $oop rowHgt] 1031 set row [expr {int([$canvas canvasy $y] / $rowHeight)}] 1032 1033 # Option: Find nearest hit row (used for mouse tracking) 1034 if {[Info $oop nearest]} { 1035 if {$row < 0} { 1036 set row 0 1037 } elseif {$row >= $rows} { 1038 set row [expr {$rows - 1}] 1039 } 1040 1041 # Restrict to visible rows only 1042 set rowTop [expr {int([$canvas canvasy 0 $rowHeight] / $rowHeight)}] 1043 set rowBottom [expr {int([$canvas canvasy [winfo height $canvas] $rowHeight] / $rowHeight - 1)}] 1044 if {$row < $rowTop} { 1045 set row $rowTop 1046 } elseif {$row > $rowBottom} { 1047 set row $rowBottom 1048 } 1049 } 1050 1051 if {$row < $rows && $row >= 0} { 1052 return $row 1053 } 1054 return -1 1055 } 1056 1057 set x [$canvas canvasx $x] 1058 set y [$canvas canvasy $y] 1059 1060 # Get the item(s) under the point. 1061 set itemIdList [$canvas find overlapping $x $y [expr {$x + 1}] [expr {$y + 1}]] 1062 1063 # No item is under that point 1064 if {![llength $itemIdList]} {return -1} 1065 1066 # Get the topmost enabled item 1067 foreach itemId $itemIdList { 1068 if {[lsearch -exact [$canvas gettags $itemId] enabled] != -1} { 1069 return [ItemRow $oop $itemId] 1070 } 1071 } 1072 1073 # No enabled item is overlapping the given location 1074 return -1 1075} 1076 1077# NSCanvist::UpDown -- 1078# 1079# Handle KeyPress-Up and KeyPress-Down. 1080# 1081# Arguments: 1082# arg1 about arg1 1083# 1084# Results: 1085# What happened. 1086 1087proc NSCanvist::UpDown {oop delta} { 1088 1089 global NSCanvist 1090 1091 set canvas $NSCanvist($oop,canvas) 1092 1093 set selection [Selection $oop] 1094 set max [expr {$NSCanvist($oop,count) - 1}] 1095 if {$max < 0} return 1096 1097 if {[llength $selection]} { 1098 set row [expr {[lindex $selection 0] + $delta}] 1099 if {$row < 0} { 1100 set row $max 1101 } elseif {$row > $max} { 1102 set row 0 1103 } 1104 } else { 1105 if {$delta > 0} { 1106 set row 0 1107 } else { 1108 set row $max 1109 } 1110 } 1111 1112 UpdateSelection $oop $row $selection 1113 See $oop $row 1114 1115 return 1116} 1117 1118# NSCanvist::See -- 1119# 1120# Scroll the given row into view. If it is the row above the currently- 1121# visible top row, then scroll up one row. If it is the row below the 1122# currently-visible bottom row, then scroll down one row. Otherwise 1123# attempt to center the row. 1124# 1125# Arguments: 1126# arg1 about arg1 1127# 1128# Results: 1129# What happened. 1130 1131proc NSCanvist::See {oop row} { 1132 1133 global NSCanvist 1134 1135 set canvas $NSCanvist($oop,canvas) 1136 set rowHeight $NSCanvist($oop,rowHgt) 1137 set scrollRgn [$canvas cget -scrollregion] 1138 set height [lindex $scrollRgn 3] 1139 1140 set rowTop [expr {int([$canvas canvasy 0 $rowHeight] / $rowHeight)}] 1141 set rowBottom [expr {int($rowTop + [winfo height $canvas] / $rowHeight - 1)}] 1142 1143 if {($row >= $rowTop) && ($row <= $rowBottom)} { 1144 1145 } elseif {$row == $rowTop - 1} { 1146 $canvas yview scroll -1 units 1147 1148 } elseif {$row == $rowBottom + 1} { 1149 $canvas yview scroll +1 units 1150 1151 } else { 1152 set top [expr {($row * $rowHeight - [winfo height $canvas] / 2) \ 1153 / double($height)}] 1154 $canvas yview moveto $top 1155 } 1156 1157 return 1158} 1159 1160 1161 1162# Utility procedures for stroking out a rectangle 1163# Adopted from Tk "Widget Demo" 1164 1165proc NSCanvist::itemMark {c x y} { 1166 1167 variable Priv 1168 1169 set Priv(areaX1) [$c canvasx $x] 1170 set Priv(areaY1) [$c canvasy $y] 1171 set Priv(areaX2) $Priv(areaX1) 1172 set Priv(areaY2) $Priv(areaY1) 1173 $c delete area 1174 set Priv(stroke) 1 1175 1176 return 1177} 1178 1179proc NSCanvist::itemStroke {c x y} { 1180 1181 variable Priv 1182 1183 if {!$Priv(stroke)} return 1184 set x [$c canvasx $x] 1185 set y [$c canvasy $y] 1186 if {($Priv(areaX1) != $x) && ($Priv(areaY1) != $y)} { 1187 $c delete area 1188 $c addtag area withtag [$c create rect $Priv(areaX1) \ 1189 $Priv(areaY1) $x $y -outline Grey] 1190 set Priv(areaX2) $x 1191 set Priv(areaY2) $y 1192 } 1193 1194 return 1195} 1196 1197proc NSCanvist::itemSelect {oop} { 1198 1199 global NSCanvist 1200 variable Priv 1201 1202 if {!$Priv(stroke)} return 1203 1204 # Gotta delete it or its included in the list! 1205 $NSCanvist($oop,canvas) delete area 1206 1207 if {($Priv(areaX1) == $Priv(areaX2)) || \ 1208 ($Priv(areaY1) == $Priv(areaY2))} return 1209 1210 # Find all items overlapping the selection rectangle 1211 set list [$NSCanvist($oop,canvas) find overlapping \ 1212 $Priv(areaX1) $Priv(areaY1) \ 1213 $Priv(areaX2) $Priv(areaY2)] 1214 1215 set doneRows {} 1216 set select {} 1217 set deselect {} 1218 1219 foreach index $list { 1220 1221 # Some items are not "enabled" 1222 if {[_GetRowTag $oop $index] == {}} continue 1223 1224 # Get the row this item is on 1225 set row [ItemRow $oop $index] 1226 1227 # Already processed this row 1228 if {[lsearch -exact $doneRows $row] != -1} continue 1229 1230 # Select this row 1231 lappend select $row 1232 1233 # Remember we did this row 1234 lappend doneRows $row 1235 } 1236 1237 # Update the selection 1238 UpdateSelection $oop $select $deselect 1239 1240 return 1241} 1242 1243# NSCanvist::Activate -- 1244# 1245# Called when the focus enters or leaves the canvas. Calls the 1246# client highlight routine for each selected row. This is so 1247# the client can highlight differently depending on whether the 1248# canvas has the focus or not. 1249# 1250# Arguments: 1251# arg1 about arg1 1252# 1253# Results: 1254# What happened. 1255 1256proc NSCanvist::Activate {oop activate} { 1257 1258 foreach row [Selection $oop] { 1259 SelectRow $oop $row 1260 } 1261 1262 return 1263} 1264 1265# FindItemByTag -- 1266# 1267# Return a list of canvas itemIds from the given list of item ids 1268# which are tagged with the given tag. 1269# 1270# Arguments: 1271# arg1 about arg1 1272# 1273# Results: 1274# What happened. 1275 1276proc FindItemByTag {canvas itemIdList tag} { 1277 1278 set result {} 1279 foreach itemId $itemIdList { 1280 set tagList [$canvas gettags $itemId] 1281 if {[lsearch -exact $tagList $tag] != -1} { 1282 lappend result $itemId 1283 } 1284 } 1285 1286 return $result 1287} 1288