source: branches/r9/gui/scripts/filelistentry.tcl @ 5106

Last change on this file since 5106 was 3964, checked in by gah, 11 years ago

fix to dicomtovtk routine to stop retrieving field values.

File size: 14.8 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: FileListEntry - widget for entering a choice of strings
4#
5#  This widget represents a <choice> entry on a control panel.
6#  It is used to choose one of several mutually-exclusive strings.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005  Purdue Research Foundation
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15
16itk::usual BltTreeView {
17    keep -foreground -cursor
18}
19itk::usual BltScrollset {
20    #empty
21}
22
23itcl::class Rappture::FileListEntry {
24    inherit itk::Widget
25
26    itk_option define -state state State "normal"
27   
28    private variable _rebuildPending 0
29    private variable _tree ""
30    private variable _owner ""    ;# thing managing this control
31    private variable _path ""     ;# path in XML to this number
32    private variable _icon ""
33
34    constructor {owner path args} {
35        # defined below
36    }
37    destructor {
38        # defined below
39    }
40    public method value {args}
41
42    public method label {}
43    public method tooltip {}
44   
45    protected method Rebuild {}
46    protected method NewValue { args }
47    protected method Tooltip {}
48    protected method WhenIdle {}
49    private method DoGlob { cwd patterns }
50    private method Glob { pattern }
51}
52
53itk::usual FileListEntry {
54    keep -cursor -font
55    keep -foreground -background
56    keep -textforeground -textbackground
57    keep -selectbackground -selectforeground -selectborderwidth
58}
59
60# ----------------------------------------------------------------------
61# CONSTRUCTOR
62# ----------------------------------------------------------------------
63itcl::body Rappture::FileListEntry::constructor {owner path args} {
64    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
65        error "bad object \"$owner\": should be Rappture::ControlOwner"
66    }
67    set _owner $owner
68    set _path $path
69    set _tree [blt::tree create]
70
71    #
72    # Create the widget and configure it properly based on other
73    # hints in the XML.
74    #
75    set label [$_owner xml get $_path.about.label]
76    set desc [$_owner xml get $_path.about.description]
77    itk_component add scrollset {
78        blt::scrollset $itk_interior.ss \
79            -xscrollbar $itk_interior.ss.xs \
80            -yscrollbar $itk_interior.ss.ys \
81            -window $itk_interior.ss.tree \
82            -height 100
83    }
84    blt::tk::scrollbar $itk_interior.ss.xs
85    blt::tk::scrollbar $itk_interior.ss.ys
86    itk_component add tree {
87        blt::treeview $itk_component(scrollset).tree -linewidth 0 \
88            -alternatebackground "" \
89            -bg white -selectmode multiple \
90            -highlightthickness 0 \
91            -tree $_tree \
92            -flat yes -separator /  \
93            -selectcommand [itcl::code $this NewValue]
94    }
95    $itk_component(tree) column configure "treeView" -justify left \
96        -weight 1.0 -text "" -pad 0 -borderwidth 0 -edit no
97    pack $itk_component(scrollset) -fill both -expand yes
98
99    blt::table $itk_interior \
100        0,0 $itk_component(scrollset) -fill both 
101    bind $itk_component(tree) <<Value>> [itcl::code $this NewValue]
102
103    # Standard ButtonPress-1
104    $itk_component(tree) bind Entry <ButtonPress-1> {   
105        Rappture::FileListEntry::SetSelectionAnchor %W current yes set
106        set blt::TreeView::_private(scroll) 1
107    }
108    # Standard B1-Motion
109    $itk_component(tree) bind Entry <B1-Motion> {
110        set blt::TreeView::_private(x) %x
111        set blt::TreeView::_private(y) %y
112        set index [%W nearest %x %y]
113        Rappture::FileListEntry::SetSelectionAnchor %W $index yes set
114    }
115    # Standard ButtonRelease-1
116    $itk_component(tree) button bind all <ButtonRelease-1> {
117        set index [%W nearest %x %y blt::TreeView::_private(who)]
118        if { [%W index current] == $index &&
119             $blt::TreeView::_private(who) == "button" } {
120            %W see -anchor nw current
121            %W toggle current
122        }
123    }
124    # Shift-ButtonPress-1
125    $itk_component(tree) bind Entry <Shift-ButtonPress-1> {     
126        Rappture::FileListEntry::SetSelectionAnchor %W current yes set
127        set blt::TreeView::_private(scroll) 1
128    }
129    # Shift-B1-Motion
130    $itk_component(tree) bind Entry <Shift-B1-Motion> {
131        set blt::TreeView::_private(x) %x
132        set blt::TreeView::_private(y) %y
133        set index [%W nearest %x %y]
134        if { [%W cget -selectmode] == "multiple" } {
135            %W selection mark $index
136        } else {
137            Rappture::FileListEntry::SetSelectionAnchor %W $index yes set
138        }
139    }
140    # Shift-ButtonRelease-1
141    $itk_component(tree) bind Entry <Shift-ButtonRelease-1> {
142        if { [%W cget -selectmode] == "multiple" } {
143            %W selection anchor current
144        }
145        after cancel $blt::TreeView::_private(afterId)
146        set blt::TreeView::_private(afterId) -1
147        set blt::TreeView::_private(scroll) 0
148    }
149    $itk_component(tree) bind Entry <Control-ButtonPress-1> {   
150        Rappture::FileListEntry::SetSelectionAnchor %W current no toggle
151        set blt::TreeView::_private(scroll) 1
152    }
153    $itk_component(tree) bind Entry <Control-B1-Motion> {
154        set blt::TreeView::_private(x) %x
155        set blt::TreeView::_private(y) %y
156        set index [%W nearest %x %y]
157        if { [%W cget -selectmode] == "multiple" } {
158            %W selection mark $index
159        } else {
160            Rappture::FileListEntry::SetSelectionAnchor %W $index no toggle
161        }
162    }
163    $itk_component(tree) bind Entry <Control-ButtonRelease-1> {
164        if { [%W cget -selectmode] == "multiple" } {
165            %W selection anchor current
166        }
167        after cancel $blt::TreeView::_private(afterId)
168        set blt::TreeView::_private(afterId) -1
169        set blt::TreeView::_private(scroll) 0
170    }
171    # First time, parse the <pattern> elements to generate notify callbacks
172    # for each template found.
173    foreach cname [$_owner xml children -type pattern $_path] {
174        set glob [string trim [$_owner xml get $_path.$cname]]
175        # Successively replace each template with its value.
176        while { [regexp -indices {@@[^@]*@@} $glob range] } {
177            foreach {first last} $range break
178            set i1 [expr $first + 2]
179            set i2 [expr $last  - 2]
180            set cpath [string range $glob $i1 $i2]
181            set value [$_owner xml get $cpath.$cname]
182            set glob [string replace $glob $first $last $value]
183            $_owner notify add $this $cpath [itcl::code $this WhenIdle]
184        }
185    }
186    $_owner notify sync
187    eval itk_initialize $args
188
189    # if the control has an icon, plug it in
190    set str [$_owner xml get $path.about.icon]
191    if {$str != ""} {
192        set _icon [image create picture -data $str]
193    }
194    Rebuild
195}
196
197# ----------------------------------------------------------------------
198# DESTRUCTOR
199# ----------------------------------------------------------------------
200itcl::body Rappture::FileListEntry::destructor {} {
201    blt::tree destroy $_tree
202    $_owner notify remove $this
203}
204
205# ----------------------------------------------------------------------
206# USAGE: value ?-check? ?<newval>?
207#
208# Clients use this to query/set the value for this widget.  With
209# no args, it returns the current value for the widget.  If the
210# <newval> is specified, it sets the value of the widget and
211# sends a <<Value>> event.  If the -check flag is included, the
212# new value is not actually applied, but just checked for correctness.
213# ----------------------------------------------------------------------
214itcl::body Rappture::FileListEntry::value {args} {
215    set onlycheck 0
216    set i [lsearch -exact $args -check]
217    if {$i >= 0} {
218        set onlycheck 1
219        set args [lreplace $args $i $i]
220    }
221    if {[llength $args] == 1} {
222        if {$onlycheck} {
223            # someday we may add validation...
224            return
225        }
226        foreach id [$itk_component(tree) curselection] {
227            set path [$_tree get $id "path" ""]
228            set path2id($path) $id
229        }
230        set paths [split $newval ,]
231    } elseif {[llength $args] != 0} {
232        error "wrong # args: should be \"value ?-check? ?newval?\""
233    }
234
235    #
236    # Query the value and return.
237    #
238    set list {}
239    foreach id [$itk_component(tree) curselection] {
240        set path [$_tree get $id "path" ""]
241        if { $path != "" } {
242            lappend list $path
243        }
244    }
245    return [join $list ,]
246}
247
248# ----------------------------------------------------------------------
249# USAGE: label
250#
251# Clients use this to query the label associated with this widget.
252# Reaches into the XML and pulls out the appropriate label string.
253# ----------------------------------------------------------------------
254itcl::body Rappture::FileListEntry::label {} {
255    set label [$_owner xml get $_path.about.label]
256    if {"" == $label} {
257        set label "Choice"
258    }
259    return $label
260}
261
262# ----------------------------------------------------------------------
263# USAGE: tooltip
264#
265# Clients use this to query the tooltip associated with this widget.
266# Reaches into the XML and pulls out the appropriate description
267# string.  Returns the string that should be used with the
268# Rappture::Tooltip facility.
269# ----------------------------------------------------------------------
270itcl::body Rappture::FileListEntry::tooltip {} {
271    # query tooltip on-demand based on current choice
272    return "@[itcl::code $this Tooltip]"
273}
274
275# ----------------------------------------------------------------------
276# USAGE: Rebuild
277#
278# Used internally to rebuild the contents of this choice widget
279# whenever something that it depends on changes.  Scans through the
280# information in the XML spec and builds a list of choices for the
281# widget.
282# ----------------------------------------------------------------------
283itcl::body Rappture::FileListEntry::Rebuild {} {
284    set _rebuildPending 0
285
286    #
287    # Plug in the various options for the choice.
288    #
289    set max 10
290    $_owner notify sync
291    set allfiles {}
292    foreach cname [$_owner xml children -type pattern $_path] {
293        set glob [string trim [$_owner xml get $_path.$cname]]
294        # Successively replace each template with its value.
295        while { [regexp -indices {@@[^@]*@@} $glob range] } {
296            foreach {first last} $range break
297            set i1 [expr $first + 2]
298            set i2 [expr $last  - 2]
299            set cpath [string range $glob $i1 $i2]
300            set value [$_owner xml get $cpath.current]
301            if { $value == "" } {
302                set value [$_owner xml get $cpath.default]
303            }
304            set glob [string replace $glob $first $last $value]
305        }
306        # Replace the template with the substituted value.
307        set files [Glob $glob]
308        set allfiles [concat $allfiles $files]
309    }
310    set first ""
311    eval $_tree tag add unused [$_tree children root]
312    foreach file $allfiles {
313        set tail [file tail $file]
314        if { $first == "" } {
315            set first $tail
316        }
317        set tail [file root $tail]
318        set id [$_tree index root->"$tail"]
319        if { $id < 0 } {
320            set data [list path $file show 0]
321            set id [$_tree insert root -label $tail -data $data -tag ""]
322        } else {
323            $_tree tag delete unused $id
324        }
325        set len [string length $tail]
326        if {$len > $max} { set max $len }
327    }
328    $itk_component(tree) configure -icons ""
329    $itk_component(tree) entry configure all -icons ""
330    eval $_tree delete [$_tree tag nodes unused]
331    $itk_component(tree) configure -width $max
332    catch {
333        if { ![$itk_component(tree) selection present] } {
334            $itk_component(tree) selection set [$_tree firstchild root]
335        }
336    }
337}
338
339# ----------------------------------------------------------------------
340# USAGE: NewValue
341#
342# Invoked automatically whenever the value in the choice changes.
343# Sends a <<Value>> event to notify clients of the change.
344# ----------------------------------------------------------------------
345itcl::body Rappture::FileListEntry::NewValue { args } {
346    event generate $itk_component(hull) <<Value>>
347}
348
349# ----------------------------------------------------------------------
350# USAGE: Tooltip
351#
352# Returns the tooltip for this widget, given the current choice in
353# the selector.  This is normally called by the Rappture::Tooltip
354# facility whenever it is about to pop up a tooltip for this widget.
355# ----------------------------------------------------------------------
356itcl::body Rappture::FileListEntry::Tooltip {} {
357    set tip [string trim [$_owner xml get $_path.about.description]]
358    # get the description for the current choice, if there is one
359    set path ""
360    set desc ""
361    if {$path == ""} {
362        set desc [$_owner xml get $_path.about.description]
363    }
364    set str ""
365    if {[string length $str] > 0 && [string length $desc] > 0} {
366        append tip "\n\n$str:\n$desc"
367    }
368    return $tip
369}
370
371# ----------------------------------------------------------------------
372# CONFIGURATION OPTION: -state
373# ----------------------------------------------------------------------
374itcl::configbody Rappture::FileListEntry::state {
375    set valid {normal disabled}
376    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
377        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
378    }
379    #$itk_component(tree) configure -state $itk_option(-state)
380}
381
382itcl::body Rappture::FileListEntry::WhenIdle {} {
383    if { !$_rebuildPending } {
384        after 10 [itcl::code $this Rebuild]
385        set _rebuildPending 1
386    }
387}
388
389proc Rappture::FileListEntry::SetSelectionAnchor { w tagOrId clear how } {
390    set index [$w index $tagOrId]
391    if { $clear } {
392        $w selection clearall
393    }
394    $w see $index
395    $w focus $index
396    $w selection $how $index
397    $w selection anchor $index
398}
399
400itcl::body Rappture::FileListEntry::DoGlob { cwd patterns } {
401    set rest [lrange $patterns 1 end]
402    set pattern [file join $cwd [lindex $patterns 0]]
403    set files ""
404    if { [llength $rest] > 0 } {
405        if { [catch {
406            glob -nocomplain -type d $pattern
407        } dirs] != 0 } {
408            puts stderr "can't glob \"$pattern\": $dirs"
409            return
410        }
411        foreach d $dirs {
412            set files [concat $files [DoGlob $d $rest]]
413        }
414    } else {
415        if { [catch {
416            glob -nocomplain $pattern
417        } files] != 0 } {
418            puts stderr "can't glob \"$pattern\": $files"
419            return
420        }
421    }
422    return $files
423}
424
425#
426# Glob --
427#
428#       Matches a single pattern for files. This differs from the
429#       Tcl glob by
430#
431#       1. Only matches files, not directories.
432#       2. Doesn't stop on errors (e.g. unreadable directories).
433#
434itcl::body Rappture::FileListEntry::Glob { pattern } {
435    return [DoGlob "" [file split $pattern]]
436}
437
Note: See TracBrowser for help on using the repository browser.