1# ----------------------------------------------------------------------------
2#  panedw.tcl
3#  This file is part of Unifix BWidget Toolkit
4#  $Id: panedw.tcl,v 1.16 2009/10/25 20:55:36 oberdorfer Exp $
5# ----------------------------------------------------------------------------
6#  Index of commands:
7#     - PanedWindow::create
8#     - PanedWindow::configure
9#     - PanedWindow::cget
10#     - PanedWindow::add
11#     - PanedWindow::getframe
12#     - PanedWindow::_apply_weights
13#     - PanedWindow::_destroy
14#     - PanedWindow::_beg_move_sash
15#     - PanedWindow::_move_sash
16#     - PanedWindow::_end_move_sash
17#     - PanedWindow::_realize
18# ----------------------------------------------------------------------------
19
20# JDC: added option to choose behavior of weights
21#    -weights extra : only apply weights to extra space (as current (>= 1.3.1) with grid command)
22#    -weights available : apply weights to total available space (as before (<1.3.1) with place command)
23
24namespace eval PanedWindow {
25    Widget::define PanedWindow panedw
26
27    namespace eval Pane {
28        Widget::declare PanedWindow::Pane {
29            {-minsize Int 0 0 "%d >= 0"}
30            {-weight  Int 1 0 "%d >= 0"}
31        }
32    }
33
34    Widget::declare PanedWindow {
35        {-side       Enum       top   1 {top left bottom right}}
36        {-width      Int        10    1 "%d >=3"}
37        {-pad        Int        4     1 "%d >= 0"}
38        {-background Color      "SystemWindowFrame" 0}
39        {-bg         Synonym    -background}
40        {-activator  Enum       ""    1 {line button}}
41	{-weights    Enum       extra 1 {extra available}}
42    }
43
44    variable _panedw
45
46    if { [BWidget::using ttk] } {
47        if {[lsearch [bindtags .] PanedWThemeChanged] < 0} {
48            bindtags . [linsert [bindtags .] 1 PanedWThemeChanged]
49        }
50    }
51
52}
53
54
55
56# ----------------------------------------------------------------------------
57#  Command PanedWindow::create
58# ----------------------------------------------------------------------------
59proc PanedWindow::create { path args } {
60    variable _panedw
61
62    Widget::init PanedWindow $path $args
63    frame $path -background [Widget::cget $path -background] \
64                -class PanedWindow \
65		-highlightthickness 0
66
67    set _panedw($path,nbpanes) 0
68    set _panedw($path,weights) ""
69    set _panedw($path,configuredone) 0
70
71    set activator [Widget::getoption $path -activator]
72    if {[string equal $activator ""]} {
73        if { $::tcl_platform(platform) != "windows" } {
74            Widget::setMegawidgetOption $path -activator button
75        } else {
76            Widget::setMegawidgetOption $path -activator line
77        }
78    }
79    if {[string equal [Widget::getoption $path -activator] "line"]} {
80        Widget::setMegawidgetOption $path -width 3
81    }
82
83    bind $path <Configure> [list PanedWindow::_realize $path %w %h]
84    bind $path <Destroy>   [list PanedWindow::_destroy $path]
85
86    if { [BWidget::using ttk] } {
87        bind PanedWThemeChanged <<ThemeChanged>> \
88	       "+[namespace current]::_themechanged $path"
89    }
90
91
92    return [Widget::create PanedWindow $path]
93}
94
95
96# ----------------------------------------------------------------------------
97#  Command PanedWindow::configure
98# ----------------------------------------------------------------------------
99proc PanedWindow::configure { path args } {
100    variable _panedw
101
102    set res [Widget::configure $path $args]
103
104    if { [Widget::hasChanged $path -background bg] && $_panedw($path,nbpanes) > 0 } {
105        $path:cmd configure -background $bg
106        $path.f0 configure -background $bg
107        for {set i 1} {$i < $_panedw($path,nbpanes)} {incr i} {
108            set frame $path.sash$i
109            $frame configure -background $bg
110            $frame.sep configure -background $bg
111            $frame.but configure -background $bg
112            $path.f$i configure -background $bg
113            $path.f$i.frame configure -background $bg
114        }
115    }
116    return $res
117}
118
119
120# ----------------------------------------------------------------------------
121#  Command PanedWindow::cget
122# ----------------------------------------------------------------------------
123proc PanedWindow::cget { path option } {
124    return [Widget::cget $path $option]
125}
126
127
128# ----------------------------------------------------------------------------
129#  Command PanedWindow::add
130# ----------------------------------------------------------------------------
131proc PanedWindow::add { path args } {
132    variable _panedw
133
134    set num $_panedw($path,nbpanes)
135    Widget::init PanedWindow::Pane $path.f$num $args
136    set bg [Widget::getoption $path -background]
137
138    set wbut   [Widget::getoption $path -width]
139    set pad    [Widget::getoption $path -pad]
140    set width  [expr {$wbut+2*$pad}]
141    set side   [Widget::getoption $path -side]
142    set weight [Widget::getoption $path.f$num -weight]
143    lappend _panedw($path,weights) $weight
144
145    if { $num > 0 } {
146        set frame [frame $path.sash$num -relief flat -bd 0 \
147                       -highlightthickness 0 -width $width -height $width -bg $bg]
148        set sep [frame $frame.sep -bd 5 -relief raised \
149                     -highlightthickness 0 -bg $bg]
150        set but [frame $frame.but -bd 1 -relief raised \
151                     -highlightthickness 0 -bg $bg -width $wbut -height $wbut]
152	set sepsize 2
153
154        set activator [Widget::getoption $path -activator]
155	if {$activator == "button"} {
156	    set activator $but
157	    set placeButton 1
158	} else {
159	    set activator $sep
160	    $sep configure -bd 1
161	    set placeButton 0
162	}
163        if {[string equal $side "top"] || [string equal $side "bottom"]} {
164            place $sep -relx 0.5 -y 0 -width $sepsize -relheight 1.0 -anchor n
165	    if { $placeButton } {
166		if {[string equal $side "top"]} {
167		    place $but -relx 0.5 -y [expr {6+$wbut/2}] -anchor c
168		} else {
169		    place $but -relx 0.5 -rely 1.0 -y [expr {-6-$wbut/2}] \
170			    -anchor c
171		}
172	    }
173            $activator configure -cursor sb_h_double_arrow
174            grid $frame -column [expr {2*$num-1}] -row 0 -sticky ns
175            grid columnconfigure $path [expr {2*$num-1}] -weight 0
176        } else {
177            place $sep -x 0 -rely 0.5 -height $sepsize -relwidth 1.0 -anchor w
178	    if { $placeButton } {
179		if {[string equal $side "left"]} {
180		    place $but -rely 0.5 -x [expr {6+$wbut/2}] -anchor c
181		} else {
182		    place $but -rely 0.5 -relx 1.0 -x [expr {-6-$wbut/2}] \
183			    -anchor c
184		}
185	    }
186            $activator configure -cursor sb_v_double_arrow
187            grid $frame -row [expr {2*$num-1}] -column 0 -sticky ew
188            grid rowconfigure $path [expr {2*$num-1}] -weight 0
189        }
190        bind $activator <ButtonPress-1> \
191	    [list PanedWindow::_beg_move_sash $path $num %X %Y]
192    } else {
193        if { [string equal $side "top"] || \
194		[string equal $side "bottom"] } {
195            grid rowconfigure $path 0 -weight 1
196        } else {
197            grid columnconfigure $path 0 -weight 1
198        }
199    }
200
201    set pane [frame $path.f$num -bd 0 -relief flat \
202	    -highlightthickness 0 -bg $bg]
203    set user [frame $path.f$num.frame  -bd 0 -relief flat \
204	    -highlightthickness 0 -bg $bg]
205    if { [string equal $side "top"] || [string equal $side "bottom"] } {
206        grid $pane -column [expr {2*$num}] -row 0 -sticky nsew
207        grid columnconfigure $path [expr {2*$num}] -weight $weight
208    } else {
209        grid $pane -row [expr {2*$num}] -column 0 -sticky nsew
210        grid rowconfigure $path [expr {2*$num}] -weight $weight
211    }
212    pack $user -fill both -expand yes
213    incr _panedw($path,nbpanes)
214    if {$_panedw($path,configuredone)} {
215	_realize $path [winfo width $path] [winfo height $path]
216    }
217
218    return $user
219}
220
221
222# ----------------------------------------------------------------------------
223#  Command PanedWindow::getframe
224# ----------------------------------------------------------------------------
225proc PanedWindow::getframe { path index } {
226    if { [winfo exists $path.f$index.frame] } {
227        return $path.f$index.frame
228    }
229}
230
231
232# ----------------------------------------------------------------------------
233#  Command PanedWindow::_beg_move_sash
234# ----------------------------------------------------------------------------
235proc PanedWindow::_beg_move_sash { path num x y } {
236    variable _panedw
237
238    set fprev $path.f[expr {$num-1}]
239    set fnext $path.f$num
240    set wsash [expr {[Widget::getoption $path -width] + 2*[Widget::getoption $path -pad]}]
241
242    $path.sash$num.but configure -relief sunken
243    set top  [toplevel $path.sash -borderwidth 1 -relief raised]
244
245    set minszg [Widget::getoption $fprev -minsize]
246    set minszd [Widget::getoption $fnext -minsize]
247    set side   [Widget::getoption $path -side]
248
249    if { [string equal $side "top"] || [string equal $side "bottom"] } {
250        $top configure -cursor sb_h_double_arrow
251        set h    [winfo height $path]
252        set yr   [winfo rooty $path.sash$num]
253        set xmin [expr {$wsash/2+[winfo rootx $fprev]+$minszg}]
254        set xmax [expr {-$wsash/2-1+[winfo rootx $fnext]+[winfo width $fnext]-$minszd}]
255        wm overrideredirect $top 1
256        wm geom $top "2x${h}+$x+$yr"
257
258        update idletasks
259        grab set $top
260        bind $top <ButtonRelease-1> [list PanedWindow::_end_move_sash $path $top $num $xmin $xmax %X rootx width]
261        bind $top <Motion>          [list PanedWindow::_move_sash $top $xmin $xmax %X +%%d+$yr]
262        _move_sash $top $xmin $xmax $x "+%d+$yr"
263    } else {
264        $top configure -cursor sb_v_double_arrow
265        set w    [winfo width $path]
266        set xr   [winfo rootx $path.sash$num]
267        set ymin [expr {$wsash/2+[winfo rooty $fprev]+$minszg}]
268        set ymax [expr {-$wsash/2-1+[winfo rooty $fnext]+[winfo height $fnext]-$minszd}]
269        wm overrideredirect $top 1
270        wm geom $top "${w}x2+$xr+$y"
271
272        update idletasks
273        grab set $top
274        bind $top <ButtonRelease-1> [list PanedWindow::_end_move_sash \
275		$path $top $num $ymin $ymax %Y rooty height]
276        bind $top <Motion>          [list PanedWindow::_move_sash \
277		$top $ymin $ymax %Y +$xr+%%d]
278        _move_sash $top $ymin $ymax $y "+$xr+%d"
279    }
280}
281
282
283# ----------------------------------------------------------------------------
284#  Command PanedWindow::_move_sash
285# ----------------------------------------------------------------------------
286proc PanedWindow::_move_sash { top min max v form } {
287
288    if { $v < $min } {
289	set v $min
290    } elseif { $v > $max } {
291	set v $max
292    }
293    wm geom $top [format $form $v]
294}
295
296
297# ----------------------------------------------------------------------------
298#  Command PanedWindow::_end_move_sash
299# ----------------------------------------------------------------------------
300proc PanedWindow::_end_move_sash { path top num min max v rootv size } {
301    variable _panedw
302
303    destroy $top
304    if { $v < $min } {
305	set v $min
306    } elseif { $v > $max } {
307	set v $max
308    }
309    set fprev $path.f[expr {$num-1}]
310    set fnext $path.f$num
311
312    $path.sash$num.but configure -relief raised
313
314    set wsash [expr {[Widget::getoption $path -width] + 2*[Widget::getoption $path -pad]}]
315    set dv    [expr {$v-[winfo $rootv $path.sash$num]-$wsash/2}]
316    set w1    [winfo $size $fprev]
317    set w2    [winfo $size $fnext]
318
319    for {set i 0} {$i < $_panedw($path,nbpanes)} {incr i} {
320        if { $i == $num-1} {
321            $fprev configure -$size [expr {[winfo $size $fprev]+$dv}]
322        } elseif { $i == $num } {
323            $fnext configure -$size [expr {[winfo $size $fnext]-$dv}]
324        } else {
325            $path.f$i configure -$size [winfo $size $path.f$i]
326        }
327    }
328}
329
330
331# ----------------------------------------------------------------------------
332#  Command PanedWindow::_realize
333# ----------------------------------------------------------------------------
334proc PanedWindow::_realize { path width height } {
335    variable _panedw
336
337    set x    0
338    set y    0
339    set hc   [winfo reqheight $path]
340    set hmax 0
341    for {set i 0} {$i < $_panedw($path,nbpanes)} {incr i} {
342        $path.f$i configure \
343            -width  [winfo reqwidth  $path.f$i.frame] \
344            -height [winfo reqheight $path.f$i.frame]
345        place $path.f$i.frame -x 0 -y 0 -relwidth 1 -relheight 1
346    }
347
348    bind $path <Configure> {}
349
350    _apply_weights $path
351    set _panedw($path,configuredone) 1
352    return
353}
354
355# ----------------------------------------------------------------------------
356#  Command PanedWindow::_apply_weights
357# ----------------------------------------------------------------------------
358proc PanedWindow::_apply_weights { path } {
359    variable _panedw
360
361    set weights [Widget::getoption $path -weights]
362    if {[string equal $weights "extra"]} {
363	return
364    }
365
366    set side   [Widget::getoption $path -side]
367    if {[string equal $side "top"] || [string equal $side "bottom"] } {
368	set size width
369    } else {
370	set size height
371    }
372    set wsash [expr {[Widget::getoption $path -width] + 2*[Widget::getoption $path -pad]}]
373    set rs [winfo $size $path]
374    set s [expr {$rs - ($_panedw($path,nbpanes) - 1) * $wsash}]
375
376    set tw 0.0
377    foreach w $_panedw($path,weights) {
378	set tw [expr {$tw + $w}]
379    }
380
381    for {set i 0} {$i < $_panedw($path,nbpanes)} {incr i} {
382	set rw [lindex $_panedw($path,weights) $i]
383	set ps [expr {int($rw / $tw * $s)}]
384	$path.f$i configure -$size $ps
385    }
386    return
387}
388
389
390# ----------------------------------------------------------------------------
391#  Command PanedWindow::_destroy
392# ----------------------------------------------------------------------------
393proc PanedWindow::_destroy { path } {
394    variable _panedw
395
396    for {set i 0} {$i < $_panedw($path,nbpanes)} {incr i} {
397        Widget::destroy $path.f$i
398    }
399    unset _panedw($path,nbpanes)
400    Widget::destroy $path
401}
402
403
404# ----------------------------------------------------------------------------
405#  Command PanedWindow::_themechanged
406# ----------------------------------------------------------------------------
407proc PanedWindow::_themechanged { path } {
408
409    if { ![winfo exists $path] } { return }
410    BWidget::set_themedefaults
411
412    $path configure \
413           -background $BWidget::colors(SystemWindowFrame)
414}
415