1# $Id$
2#
3# ttk::treeview widget bindings and utilities.
4#
5
6namespace eval ttk::treeview {
7    variable State
8
9    # Enter/Leave/Motion
10    #
11    set State(activeWidget) 	{}
12    set State(activeHeading) 	{}
13
14    # Press/drag/release:
15    #
16    set State(pressMode) 	none
17    set State(pressX)		0
18
19    # For pressMode == "resize"
20    set State(resizeColumn)	#0
21
22    # For pressmode == "heading"
23    set State(heading)  	{}
24}
25
26### Widget bindings.
27#
28
29bind Treeview	<Motion> 		{ ttk::treeview::Motion %W %x %y }
30bind Treeview	<B1-Leave>		{ #nothing }
31bind Treeview	<Leave>			{ ttk::treeview::ActivateHeading {} {}}
32bind Treeview	<ButtonPress-1> 	{ ttk::treeview::Press %W %x %y }
33bind Treeview	<Double-ButtonPress-1> 	{ ttk::treeview::DoubleClick %W %x %y }
34bind Treeview	<ButtonRelease-1> 	{ ttk::treeview::Release %W %x %y }
35bind Treeview	<B1-Motion> 		{ ttk::treeview::Drag %W %x %y }
36bind Treeview 	<KeyPress-Up>    	{ ttk::treeview::Keynav %W up }
37bind Treeview 	<KeyPress-Down>  	{ ttk::treeview::Keynav %W down }
38bind Treeview 	<KeyPress-Right> 	{ ttk::treeview::Keynav %W right }
39bind Treeview 	<KeyPress-Left>  	{ ttk::treeview::Keynav %W left }
40bind Treeview	<KeyPress-Prior>	{ %W yview scroll -1 pages }
41bind Treeview	<KeyPress-Next> 	{ %W yview scroll  1 pages }
42bind Treeview	<KeyPress-Return>	{ ttk::treeview::ToggleFocus %W }
43bind Treeview	<KeyPress-space>	{ ttk::treeview::ToggleFocus %W }
44
45bind Treeview	<Shift-ButtonPress-1> \
46		{ ttk::treeview::Select %W %x %y extend }
47bind Treeview	<Control-ButtonPress-1> \
48		{ ttk::treeview::Select %W %x %y toggle }
49
50ttk::copyBindings TtkScrollable Treeview
51
52### Binding procedures.
53#
54
55## Keynav -- Keyboard navigation
56#
57# @@@ TODO: verify/rewrite up and down code.
58#
59proc ttk::treeview::Keynav {w dir} {
60    set focus [$w focus]
61    if {$focus eq ""} { return }
62
63    switch -- $dir {
64	up {
65	    if {[set up [$w prev $focus]] eq ""} {
66	        set focus [$w parent $focus]
67	    } else {
68		while {[$w item $up -open] && [llength [$w children $up]]} {
69		    set up [lindex [$w children $up] end]
70		}
71		set focus $up
72	    }
73	}
74	down {
75	    if {[$w item $focus -open] && [llength [$w children $focus]]} {
76	        set focus [lindex [$w children $focus] 0]
77	    } else {
78		set up $focus
79		while {$up ne "" && [set down [$w next $up]] eq ""} {
80		    set up [$w parent $up]
81		}
82		set focus $down
83	    }
84	}
85	left {
86	    if {[$w item $focus -open] && [llength [$w children $focus]]} {
87	    	CloseItem $w $focus
88	    } else {
89	    	set focus [$w parent $focus]
90	    }
91	}
92	right {
93	    OpenItem $w $focus
94	}
95    }
96
97    if {$focus != {}} {
98	SelectOp $w $focus choose
99    }
100}
101
102## Motion -- pointer motion binding.
103#	Sets cursor, active element ...
104#
105proc ttk::treeview::Motion {w x y} {
106    set cursor {}
107    set activeHeading {}
108
109    switch -- [$w identify region $x $y] {
110	separator { set cursor hresize }
111	heading { set activeHeading [$w identify column $x $y] }
112    }
113
114    ttk::setCursor $w $cursor
115    ActivateHeading $w $activeHeading
116}
117
118## ActivateHeading -- track active heading element
119#
120proc ttk::treeview::ActivateHeading {w heading} {
121    variable State
122
123    if {$w != $State(activeWidget) || $heading != $State(activeHeading)} {
124	if {$State(activeHeading) != {}} {
125	    $State(activeWidget) heading $State(activeHeading) state !active
126	}
127	if {$heading != {}} {
128	    $w heading $heading state active
129	}
130	set State(activeHeading) $heading
131	set State(activeWidget) $w
132    }
133}
134
135## Select $w $x $y $selectop
136#	Binding procedure for selection operations.
137#	See "Selection modes", below.
138#
139proc ttk::treeview::Select {w x y op} {
140    if {[set item [$w identify row $x $y]] ne "" } {
141	SelectOp $w $item $op
142    }
143}
144
145## DoubleClick -- Double-ButtonPress-1 binding.
146#
147proc ttk::treeview::DoubleClick {w x y} {
148    if {[set row [$w identify row $x $y]] ne ""} {
149	Toggle $w $row
150    } else {
151	Press $w $x $y ;# perform single-click action
152    }
153}
154
155## Press -- ButtonPress binding.
156#
157proc ttk::treeview::Press {w x y} {
158    focus $w
159    switch -- [$w identify region $x $y] {
160	nothing { }
161	heading { heading.press $w $x $y }
162	separator { resize.press $w $x $y }
163	tree -
164	cell {
165	    set item [$w identify item $x $y]
166	    SelectOp $w $item choose
167	    switch -glob -- [$w identify element $x $y] {
168		*indicator -
169		*disclosure { Toggle $w $item }
170	    }
171	}
172    }
173}
174
175## Drag -- B1-Motion binding
176#
177proc ttk::treeview::Drag {w x y} {
178    variable State
179    switch $State(pressMode) {
180	resize	{ resize.drag $w $x }
181	heading	{ heading.drag $w $x $y }
182    }
183}
184
185proc ttk::treeview::Release {w x y} {
186    variable State
187    switch $State(pressMode) {
188	resize	{ resize.release $w $x }
189	heading	{ heading.release $w }
190    }
191    set State(pressMode) none
192    Motion $w $x $y
193}
194
195### Interactive column resizing.
196#
197proc ttk::treeview::resize.press {w x y} {
198    variable State
199    set State(pressMode) "resize"
200    set State(resizeColumn) [$w identify column $x $y]
201}
202
203proc ttk::treeview::resize.drag {w x} {
204    variable State
205    $w drag $State(resizeColumn) $x
206}
207
208proc ttk::treeview::resize.release {w x} {
209    # no-op
210}
211
212### Heading activation.
213#
214
215proc ttk::treeview::heading.press {w x y} {
216    variable State
217    set column [$w identify column $x $y]
218    set State(pressMode) "heading"
219    set State(heading) $column
220    $w heading $column state pressed
221}
222
223proc ttk::treeview::heading.drag {w x y} {
224    variable State
225    if {   [$w identify region $x $y] eq "heading"
226        && [$w identify column $x $y] eq $State(heading)
227    } {
228    	$w heading $State(heading) state pressed
229    } else {
230    	$w heading $State(heading) state !pressed
231    }
232}
233
234proc ttk::treeview::heading.release {w} {
235    variable State
236    if {[lsearch -exact [$w heading $State(heading) state] pressed] >= 0} {
237	after 0 [$w heading $State(heading) -command]
238    }
239    $w heading $State(heading) state !pressed
240}
241
242### Selection modes.
243#
244
245## SelectOp $w $item [ choose | extend | toggle ] --
246#	Dispatch to appropriate selection operation
247#	depending on current value of -selectmode.
248#
249proc ttk::treeview::SelectOp {w item op} {
250    select.$op.[$w cget -selectmode] $w $item
251}
252
253## -selectmode none:
254#
255proc ttk::treeview::select.choose.none {w item} { $w focus $item }
256proc ttk::treeview::select.toggle.none {w item} { $w focus $item }
257proc ttk::treeview::select.extend.none {w item} { $w focus $item }
258
259## -selectmode browse:
260#
261proc ttk::treeview::select.choose.browse {w item} { BrowseTo $w $item }
262proc ttk::treeview::select.toggle.browse {w item} { BrowseTo $w $item }
263proc ttk::treeview::select.extend.browse {w item} { BrowseTo $w $item }
264
265## -selectmode multiple:
266#
267proc ttk::treeview::select.choose.extended {w item} {
268    BrowseTo $w $item
269}
270proc ttk::treeview::select.toggle.extended {w item} {
271    $w selection toggle [list $item]
272}
273proc ttk::treeview::select.extend.extended {w item} {
274    if {[set anchor [$w focus]] ne ""} {
275	$w selection set [between $w $anchor $item]
276    } else {
277    	BrowseTo $w $item
278    }
279}
280
281### Tree structure utilities.
282#
283
284## between $tv $item1 $item2 --
285#	Returns a list of all items between $item1 and $item2,
286#	in preorder traversal order.  $item1 and $item2 may be
287#	in either order.
288#
289# NOTES:
290#	This routine is O(N) in the size of the tree.
291#	There's probably a way to do this that's O(N) in the number
292#	of items returned, but I'm not clever enough to figure it out.
293#
294proc ttk::treeview::between {tv item1 item2} {
295    variable between [list]
296    variable selectingBetween 0
297    ScanBetween $tv $item1 $item2 {}
298    return $between
299}
300
301## ScanBetween --
302#	Recursive worker routine for ttk::treeview::between
303#
304proc ttk::treeview::ScanBetween {tv item1 item2 item} {
305    variable between
306    variable selectingBetween
307
308    if {$item eq $item1 || $item eq $item2} {
309    	lappend between $item
310	set selectingBetween [expr {!$selectingBetween}]
311    } elseif {$selectingBetween} {
312    	lappend between $item
313    }
314    foreach child [$tv children $item] {
315	ScanBetween $tv $item1 $item2 $child
316    }
317}
318
319### User interaction utilities.
320#
321
322## OpenItem, CloseItem -- Set the open state of an item, generate event
323#
324
325proc ttk::treeview::OpenItem {w item} {
326    $w focus $item
327    event generate $w <<TreeviewOpen>>
328    $w item $item -open true
329}
330
331proc ttk::treeview::CloseItem {w item} {
332    $w item $item -open false
333    $w focus $item
334    event generate $w <<TreeviewClose>>
335}
336
337## Toggle -- toggle opened/closed state of item
338#
339proc ttk::treeview::Toggle {w item} {
340    if {[$w item $item -open]} {
341	CloseItem $w $item
342    } else {
343	OpenItem $w $item
344    }
345}
346
347## ToggleFocus -- toggle opened/closed state of focus item
348#
349proc ttk::treeview::ToggleFocus {w} {
350    set item [$w focus]
351    if {$item ne ""} {
352    	Toggle $w $item
353    }
354}
355
356## BrowseTo -- navigate to specified item; set focus and selection
357#
358proc ttk::treeview::BrowseTo {w item} {
359    $w see $item
360    $w focus $item
361    $w selection set [list $item]
362}
363
364#*EOF*
365