Canvas selection with marching ants

escargo 18 Jan 2005 - I used wish-reaper to download this to my Windows XP Pro system. No ants marched.

DKF: IIRC, Tk on Win has a few deficiencies in the dash handling... :^(

MM See Canvas dash lines. HJG and Dash lines on Windows


 # canvas_marching_ants.tcl --
 # 
 # Part of: Useless Widgets Package
 # Contents: test script for the marching ants selection
 # Date: Mon Jan 17, 2005
 # 
 # Abstract
 # 
 #        Selects items with the marching ants.
 # 
 # Copyright (c) 2005 Marco Maggi
 #
 # Most of [uwp_p_marching_ants_start_auto_scan] comes from the TK source
 # version 8.4, file "listbox.tcl".
 #
 # Copyright (c) 1994 The Regents of the University of California.
 # Copyright (c) 1994-1995 Sun Microsystems, Inc.
 # Copyright (c) 1998 by Scriptics Corporation.
 # 
 # 
 # The author  hereby grant permission to use,  copy, modify, distribute,
 # and  license this  software  and its  documentation  for any  purpose,
 # provided that  existing copyright notices  are retained in  all copies
 # and that  this notice  is included verbatim  in any  distributions. No
 # written agreement, license, or royalty  fee is required for any of the
 # authorized uses.  Modifications to this software may be copyrighted by
 # their authors and need not  follow the licensing terms described here,
 # provided that the new terms are clearly indicated on the first page of
 # each file where they apply.
 # 
 # IN NO  EVENT SHALL THE AUTHOR  OR DISTRIBUTORS BE LIABLE  TO ANY PARTY
 # FOR  DIRECT, INDIRECT, SPECIAL,  INCIDENTAL, OR  CONSEQUENTIAL DAMAGES
 # ARISING OUT  OF THE  USE OF THIS  SOFTWARE, ITS DOCUMENTATION,  OR ANY
 # DERIVATIVES  THEREOF, EVEN  IF THE  AUTHOR  HAVE BEEN  ADVISED OF  THE
 # POSSIBILITY OF SUCH DAMAGE.
 # 
 # THE  AUTHOR  AND DISTRIBUTORS  SPECIFICALLY  DISCLAIM ANY  WARRANTIES,
 # INCLUDING,   BUT   NOT  LIMITED   TO,   THE   IMPLIED  WARRANTIES   OF
 # MERCHANTABILITY,    FITNESS   FOR    A    PARTICULAR   PURPOSE,    AND
 # NON-INFRINGEMENT.  THIS  SOFTWARE IS PROVIDED  ON AN "AS  IS" BASIS,
 # AND  THE  AUTHOR  AND  DISTRIBUTORS  HAVE  NO  OBLIGATION  TO  PROVIDE
 # MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
 #
 
 #page
 ## ------------------------------------------------------------
 ## Setup.
 ## ------------------------------------------------------------
 
 package require Tcl 8.4
 package require Tk  8.4
 
 #page
 ## ------------------------------------------------------------
 ## Widget options.
 ## ------------------------------------------------------------
 
 option add *borderWidth                                1
 
 option add *Canvas.select_move_cursor                fleur
 option add *Canvas.select_point_cursor                hand1
 
 option add *Canvas.marching_ant_pattern                {10 4}
 option add *Canvas.marching_ant_step                4
 option add *Canvas.marching_ant_select_color        green
 option add *Canvas.marching_ant_marching_color        red
 option add *Canvas.marching_ant_delay                300
 
 
 #page
 ## ------------------------------------------------------------
 ## Main.
 ## ------------------------------------------------------------
 
 proc main {} {
     global        exit_trigger
 
     wm title . "Canvas Marching Ants Selection"
     wm geometry . +200+100
 
     frame .f
     canvas [set c .f.c] -width 300 -height 300 -background white \
         -xscrollcommand {.f.hb set} -yscrollcommand {.f.vb set}
     scrollbar .f.vb -orient vertical -command {.f.c yview}
     scrollbar .f.hb -orient horizontal -command {.f.c xview}
     grid .f.c -sticky news -row 0 -column 0
     grid .f.vb -sticky ns -row 0 -column 1
     grid .f.hb -sticky ew -row 1 -column 0
 
     listbox .l -background bisque
     
     grid .f .l -sticky news
     
     $c configure -scrollregion [list 0 0 400 400]
     $c create rectangle 20 20 60 60 -tags Rectangle
     $c create text 350 290 -text {This is some text} -tags Text
     $c create oval 50 50 80 80 -tags {"Little Circle"}
     $c create oval 50 50 120 120 -tags {"Medium Circle"}
     $c create oval 50 50 200 200 -tags {"Big Circle"}
     
     uwp_marching_ants_bind $c update_selection_list
 
     grid [button .quit -text Exit -command main_exit]
     focus .quit
     bind .quit <Return> main_exit
     bind . <Escape> main_exit
     
     interp alias {} main_exit {} set exit_trigger 1
     vwait exit_trigger
     exit
 }
 proc update_selection_list { widget selected_items_identifiers_list } {
     set selected_items_first_tags {}
     foreach item $selected_items_identifiers_list {
         lappend selected_items_first_tags [lindex [$widget gettags $item] 0]
     }
     .l delete 0 end
     eval {.l insert end} $selected_items_first_tags
 }
 #page
 proc uwp_data_set { widget key value } {
     global        uwp_data
     set uwp_data($widget:$key) $value
 }
 proc uwp_data_get { widget key } {
     global        uwp_data
     return $uwp_data($widget:$key)
 }
 proc uwp_p_append_tag { tag widget } {
     bindtags $widget [linsert [bindtags $widget] end $tag]
 }
 proc uwp_p_remove_tag { tag widget } {
     set idx [lsearch [set ell [bindtags $widget]] $tag]
     bindtags $widget [lreplace $ell $idx $idx]
 }
 proc alias { alias args } {
     eval { interp alias {} $alias {} } $args
 }
 #page
 bind MarchingAnts <ButtonPress-1>        { uwp_marching_ants_event_press %W %x %y }
 bind MarchingAnts <ButtonRelease-1>        { uwp_marching_ants_event_release %W %x %y }
 bind MarchingAnts <ButtonRelease-3>        { uwp_marching_ants_event_release3 %W }
 bind MarchingAnts <Enter>                { uwp_marching_ants_event_enter %W }
 bind MarchingAnts <Leave>                { uwp_marching_ants_event_leave %W }
 bind MarchingAnts <B1-Leave>                { uwp_marching_ants_event_pressed_and_leave %W }
 bind MarchingAntsSelect <Motion>        { uwp_marching_ants_event_motion %W %x %y }
 alias uwp_marching_ants_unbind                uwp_p_remove_tag MarchingAnts
 alias uwp_marching_ants_bind_motion        uwp_p_append_tag MarchingAntsSelect
 alias uwp_marching_ants_unbind_motion        uwp_p_remove_tag MarchingAntsSelect
 
 proc uwp_marching_ants_bind { widget {command {}} } {
     uwp_p_append_tag MarchingAnts $widget
     uwp_data_set $widget MarchingAntsSelectCommand $command
 }
 #page
 proc uwp_marching_ants_event_press { widget x y } {
     uwp_p_marching_ants_delete_and_deselect_all $widget
     uwp_p_marching_ants_set_select_cursor $widget
     uwp_p_marching_ants_save_current_pointer_position $widget $x $y
     uwp_marching_ants_bind_motion $widget
     uwp_p_marching_ants_draw_selection_rectangle $widget $x $y
 }
 proc uwp_marching_ants_event_release { widget x y } {
     uwp_marching_ants_unbind_motion $widget    
     uwp_p_marching_ants_set_point_cursor $widget
     uwp_p_marching_ants_delete_selection_rectangle $widget
     uwp_p_marching_ants_draw_marching_ants $widget $x $y
     uwp_p_marching_ants_select_items $widget
     uwp_p_marching_ants_stop_auto_scan $widget
 }
 proc uwp_marching_ants_event_release3 { widget } {
     uwp_p_marching_ants_delete_and_deselect_all $widget
 }
 proc uwp_marching_ants_event_enter { widget } {
     uwp_p_marching_ants_save_current_cursor $widget
     uwp_p_marching_ants_set_point_cursor $widget
     uwp_p_marching_ants_stop_auto_scan $widget
 }
 proc uwp_marching_ants_event_leave { widget } {
     uwp_p_marching_ants_restore_current_cursor $widget
 }
 proc uwp_marching_ants_event_motion { widget x y } {
     uwp_p_marching_ants_update_selection_rectangle_coords $widget $x $y
 }
 proc uwp_marching_ants_event_pressed_and_leave { widget args } {
     uwp_p_marching_ants_start_auto_scan $widget
 }
 #page
 proc uwp_p_marching_ants_save_current_pointer_position { widget spotX spotY } {
     uwp_data_set $widget spot \
         [list [$widget canvasx $spotX] [$widget canvasy $spotY]]
 }
 proc uwp_p_marching_ants_save_current_cursor { widget } {
     uwp_data_set $widget oldCursor [. cget -cursor]
 }
 proc uwp_p_marching_ants_restore_current_cursor { widget } {
     . configure -cursor [uwp_data_get $widget oldCursor]
 }
 proc uwp_p_marching_ants_set_point_cursor { widget } {
     . configure -cursor [option get $widget select_point_cursor {}]
 }
 proc uwp_p_marching_ants_set_select_cursor { widget } {
     . configure -cursor [option get $widget select_move_cursor {}]
 }
 #page
 proc uwp_p_marching_ants_draw_selection_rectangle { widget x y } {
     foreach {spotX spotY} [uwp_data_get $widget spot] {}
     $widget create rectangle \
         $spotX $spotY [$widget canvasx $x] [$widget canvasy $y] \
         -tags SelectionRectangle \
         -outline [option get $widget marching_ant_select_color {}] \
         -dash [option get $widget marching_ant_pattern {}]
 }
 proc uwp_p_marching_ants_delete_selection_rectangle { widget } {
     $widget delete SelectionRectangle
 }
 proc uwp_p_marching_ants_update_selection_rectangle_coords { widget x y } {
     foreach {spotX spotY} [uwp_data_get $widget spot] {}
     $widget coords SelectionRectangle \
         $spotX $spotY [$widget canvasx $x] [$widget canvasy $y]
 }
 #page
 proc uwp_p_marching_ants_draw_marching_ants { widget x y } {
     foreach {spotX spotY} [uwp_data_get $widget spot] {}
     $widget create rectangle \
         $spotX $spotY [$widget canvasx $x] [$widget canvasy $y] \
         -tags MarchingAnts \
         -dash [option get $widget marching_ant_pattern {}] \
         -outline [option get $widget marching_ant_marching_color {}]
     uwp_p_marching_ants_schedule_step $widget
 }
 proc uwp_p_marching_ants_delete_marching_ants { widget } {
     uwp_p_marching_ants_unschedule_step $widget
     $widget delete MarchingAnts
 }
 proc uwp_p_marching_ants_schedule_step { widget {step 0} } {
     uwp_data_set $widget MarchingAntsMarchAfterId \
         [after [option get $widget marching_ant_delay {}] \
              [list uwp_p_marching_ants_step $widget \
                   [incr step [option get $widget marching_ant_step {}]]]]
 }
 proc uwp_p_marching_ants_unschedule_step { widget } {
     catch {after cancel [uwp_data_get $widget MarchingAntsMarchAfterId]}
 }
 proc uwp_p_marching_ants_step { widget step } {
     if { [winfo exists $widget]} {
         $widget itemconfigure MarchingAnts -dashoffset $step
         uwp_p_marching_ants_schedule_step $widget $step
     }
 }
 proc uwp_p_marching_ants_delete_and_deselect_all { widget } {
     uwp_p_marching_ants_delete_selection_rectangle $widget
     uwp_p_marching_ants_delete_marching_ants $widget
     uwp_p_marching_ants_deselect_items $widget
 }
 #page
 proc uwp_p_marching_ants_select_items { widget } {
     eval {$widget addtag MarchingAntSelection enclosed} [$widget coords MarchingAnts]
     utp_p_marching_ants_invoke_selection_command $widget \
         [$widget find withtag MarchingAntSelection]
 }
 proc uwp_p_marching_ants_deselect_items { widget } {
     $widget dtag MarchingAntSelection MarchingAntSelection
     utp_p_marching_ants_invoke_selection_command $widget {}
 }
 proc utp_p_marching_ants_invoke_selection_command { widget selected_items_tags } {
     set command [uwp_data_get $widget MarchingAntsSelectCommand]
     if { [string length $command] } {
         uplevel \#0 $command [list $widget $selected_items_tags]
     }
 }
 #page
 # Most of this comes from the TK source version 8.4, file "listbox.tcl".
 proc uwp_p_marching_ants_start_auto_scan { widget } {
     if { [winfo exists $widget] } {
         set x [expr [winfo pointerx $widget]-[winfo rootx $widget]]
         set y [expr [winfo pointery $widget]-[winfo rooty $widget]]
         if {$y >= [winfo height $widget]} {
             $widget yview scroll 1 units
         } elseif {$y < 0} {
             $widget yview scroll -1 units
         } elseif {$x >= [winfo width $widget]} {
             $widget xview scroll 1 units
         } elseif {$x < 0} {
             $widget xview scroll -1 units
         } else {
             return
         }
         uwp_data_set $widget MarchingAntsAutoScanAfterId \
             [after 50 [list uwp_marching_ants_event_pressed_and_leave $widget]]
     }
 }
 proc uwp_p_marching_ants_stop_auto_scan { widget } {
     catch {after cancel [uwp_data_get $widget MarchingAntsAutoScanAfterId]}
 }
 #page
 ## ------------------------------------------------------------
 ## Let's go.
 ## ------------------------------------------------------------
 
 main
 
 ### end of file
 # Local Variables:
 # mode: tcl
 # End: