source: trunk/gui/scripts/barresult.tcl @ 2417

Last change on this file since 2417 was 2241, checked in by gah, 13 years ago
File size: 57.3 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: barresult - X/Y plot in a ResultSet
4#
5#  This widget is an X/Y plot, meant to view line graphs produced
6#  as output from the run of a Rappture tool.  Use the "add" and
7#  "delete" methods to control the dataobjs showing on the plot.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17
18option add *BarResult.width 3i widgetDefault
19option add *BarResult.height 3i widgetDefault
20option add *BarResult.gridColor #d9d9d9 widgetDefault
21option add *BarResult.activeColor blue widgetDefault
22option add *BarResult.dimColor gray widgetDefault
23option add *BarResult.controlBackground gray widgetDefault
24option add *BarResult.font \
25    -*-helvetica-medium-r-normal-*-12-* widgetDefault
26
27set autocolors {
28    #0000ff #ff0000 #00cc00
29    #cc00cc #ff9900 #cccc00
30    #000080 #800000 #006600
31    #660066 #996600 #666600
32}
33set autocolors {
34#0000cd
35#cd0000
36#00cd00
37#3a5fcd
38#cdcd00
39#cd1076
40#009acd
41#00c5cd
42#a2b5cd
43#7ac5cd
44#66cdaa
45#a2cd5a
46#cd9b9b
47#cdba96
48#cd3333
49#cd6600
50#cd8c95
51#cd00cd
52#9a32cd
53#6ca6cd
54#9ac0cd
55#9bcd9b
56#00cd66
57#cdc673
58#cdad00
59#cd5555
60#cd853f
61#cd7054
62#cd5b45
63#cd6889
64#cd69c9
65#551a8b
66}
67
68option add *BarResult.autoColors $autocolors widgetDefault
69option add *BarResult*Balloon*Entry.background white widgetDefault
70
71itcl::class Rappture::BarResult {
72    inherit itk::Widget
73
74    itk_option define -gridcolor gridColor GridColor ""
75    itk_option define -activecolor activeColor ActiveColor ""
76    itk_option define -dimcolor dimColor DimColor ""
77    itk_option define -autocolors autoColors AutoColors ""
78
79    constructor {args} {
80        # defined below
81    }
82    destructor {
83        # defined below
84    }
85    public method add {dataobj {settings ""}}
86    public method get {}
87    public method delete {args}
88    public method scale {args}
89    public method parameters {title args} {
90        # do nothing
91    }
92    public method download {option args}
93
94    protected method _rebuild {}
95    protected method _resetLimits {}
96    protected method _zoom {option args}
97    protected method _hilite {state x y}
98    protected method _axis {option args}
99    protected method _getAxes {dataobj}
100    protected method _getLineMarkerOptions { style }
101    protected method _getTextMarkerOptions { style }
102    protected method _enterMarker { g name x y text }
103    protected method _leaveMarker { g name }
104    private method _formatTickLabel { w value }
105
106    private variable _dispatcher "" ;# dispatcher for !events
107    private variable _dlist ""     ;# list of dataobjs
108    private variable _dataobj2color  ;# maps dataobj => plotting color
109    private variable _dataobj2width  ;# maps dataobj => line width
110    private variable _dataobj2dashes ;# maps dataobj => BLT -dashes list
111    private variable _dataobj2raise  ;# maps dataobj => raise flag 0/1
112    private variable _dataobj2desc   ;# maps dataobj => description of data
113    private variable _elem2dataobj   ;# maps graph element => dataobj
114    private variable _label2axis   ;# maps axis label => axis ID
115    private variable _limits       ;# axis limits:  x-min, x-max, etc.
116    private variable _autoColorI 0 ;# index for next "-color auto"
117
118    private variable _hilite       ;# info for element currently highlighted
119    private variable _axis         ;# info for axis manipulations
120    private variable _axisPopup    ;# info for axis being edited in popup
121    common _downloadPopup          ;# download options from popup
122    private variable _markers
123    private variable _tickLabels
124}
125                                                                               
126itk::usual BarResult {
127    keep -background -foreground -cursor -font
128}
129
130itk::usual Panedwindow {
131    keep -background -cursor
132}
133
134# ----------------------------------------------------------------------
135# CONSTRUCTOR
136# ----------------------------------------------------------------------
137itcl::body Rappture::BarResult::constructor {args} {
138    Rappture::dispatcher _dispatcher
139    $_dispatcher register !rebuild
140    $_dispatcher dispatch $this !rebuild "[itcl::code $this _rebuild]; list"
141
142    array set _downloadPopup {
143        format csv
144    }
145
146    option add hull.width hull.height
147    pack propagate $itk_component(hull) no
148
149    itk_component add main {
150        Rappture::SidebarFrame $itk_interior.main
151    }
152    pack $itk_component(main) -expand yes -fill both
153    set f [$itk_component(main) component controls]
154
155    itk_component add reset {
156        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
157            -highlightthickness 0 \
158            -image [Rappture::icon reset-view] \
159            -command [itcl::code $this _zoom reset]
160    } {
161        usual
162        ignore -borderwidth -highlightthickness
163    }
164    pack $itk_component(reset) -padx 4 -pady 2 -anchor e
165    Rappture::Tooltip::for $itk_component(reset) \
166        "Reset the view to the default zoom level"
167
168    set f [$itk_component(main) component frame]
169    itk_component add plot {
170        blt::barchart $f.plot \
171            -highlightthickness 0 -plotpadx 0 -plotpady 0 \
172            -barmode aligned -rightmargin 1
173    } {
174        keep -background -foreground -cursor -font
175    }
176    pack $itk_component(plot) -expand yes -fill both
177
178    $itk_component(plot) pen configure activeBar \
179        -borderwidth 2 -foreground black -background red
180
181    # Add bindings so you can mouse over points to see values:
182    bind $itk_component(plot) <Motion> \
183        [itcl::code $this _hilite at %x %y]
184    bind $itk_component(plot) <Leave> \
185        [itcl::code $this _hilite off %x %y]
186
187    # Add support for editing axes:
188    Rappture::Balloon $itk_component(hull).axes -title "Axis Options"
189    set inner [$itk_component(hull).axes component inner]
190
191    label $inner.labell -text "Label:"
192    entry $inner.label -width 15 -highlightbackground $itk_option(-background)
193    grid $inner.labell -row 1 -column 0 -sticky e
194    grid $inner.label -row 1 -column 1 -sticky ew -pady 4
195
196    label $inner.minl -text "Minimum:"
197    entry $inner.min -width 15 -highlightbackground $itk_option(-background)
198    grid $inner.minl -row 2 -column 0 -sticky e
199    grid $inner.min -row 2 -column 1 -sticky ew -pady 4
200
201    label $inner.maxl -text "Maximum:"
202    entry $inner.max -width 15 -highlightbackground $itk_option(-background)
203    grid $inner.maxl -row 3 -column 0 -sticky e
204    grid $inner.max -row 3 -column 1 -sticky ew -pady 4
205
206    label $inner.formatl -text "Format:"
207    Rappture::Combobox $inner.format -width 15 -editable no
208    $inner.format choices insert end \
209        "%.6g"  "Auto"         \
210        "%.0f"  "X"          \
211        "%.1f"  "X.X"          \
212        "%.2f"  "X.XX"         \
213        "%.3f"  "X.XXX"        \
214        "%.6f"  "X.XXXXXX"     \
215        "%.1e"  "X.Xe+XX"      \
216        "%.2e"  "X.XXe+XX"     \
217        "%.3e"  "X.XXXe+XX"    \
218        "%.6e"  "X.XXXXXXe+XX"
219    grid $inner.formatl -row 4 -column 0 -sticky e
220    grid $inner.format -row 4 -column 1 -sticky ew -pady 4
221
222    label $inner.scalel -text "Scale:"
223    frame $inner.scales
224    radiobutton $inner.scales.linear -text "Linear" \
225        -variable [itcl::scope _axisPopup(scale)] -value "linear"
226    pack $inner.scales.linear -side left
227    radiobutton $inner.scales.log -text "Logarithmic" \
228        -variable [itcl::scope _axisPopup(scale)] -value "log"
229    pack $inner.scales.log -side left
230    grid $inner.scalel -row 5 -column 0 -sticky e
231    grid $inner.scales -row 5 -column 1 -sticky ew -pady 4
232
233    foreach axis {x y} {
234        set _axisPopup(format-$axis) "%.6g"
235    }
236    _axis scale x linear
237    _axis scale y linear
238
239    $itk_component(plot) legend configure -hide yes
240
241    #
242    # Add legend for editing hidden/elements:
243    #
244    set inner [$itk_component(main) insert end \
245        -title "Legend" \
246        -icon [Rappture::icon wrench]]
247    $inner configure -borderwidth 4
248
249    itk_component add legend {
250        Rappture::XyLegend $inner.legend $itk_component(plot)
251    }
252    pack $itk_component(legend) -expand yes -fill both
253    after idle [subst {
254        update idletasks
255        $itk_component(legend) reset
256    }]
257
258    # quick-and-dirty zoom functionality, for now...
259    Blt_ZoomStack $itk_component(plot)
260    eval itk_initialize $args
261
262    set _hilite(elem) ""
263}
264
265# ----------------------------------------------------------------------
266# DESTRUCTOR
267# ----------------------------------------------------------------------
268itcl::body Rappture::BarResult::destructor {} {
269}
270
271# ----------------------------------------------------------------------
272# USAGE: add <dataobj> ?<settings>?
273#
274# Clients use this to add a dataobj to the plot.  The optional <settings>
275# are used to configure the plot.  Allowed settings are -color,
276# -brightness, -width, -linestyle and -raise.
277# ----------------------------------------------------------------------
278itcl::body Rappture::BarResult::add {dataobj {settings ""}} {
279    array set params {
280        -color auto
281        -brightness 0
282        -width 1
283        -type "bar"
284        -raise 0
285        -linestyle solid
286        -description ""
287        -param ""
288    }
289    foreach {opt val} $settings {
290        if {![info exists params($opt)]} {
291            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
292        }
293        set params($opt) $val
294    }
295
296    # if the color is "auto", then select a color from -autocolors
297    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
298        if {$params(-color) == "autoreset"} {
299            set _autoColorI 0
300        }
301        set color [lindex $itk_option(-autocolors) $_autoColorI]
302        if {"" == $color} { set color black }
303        set params(-color) $color
304
305        # set up for next auto color
306        if {[incr _autoColorI] >= [llength $itk_option(-autocolors)]} {
307            set _autoColorI 0
308        }
309    }
310
311    # convert -linestyle to BLT -dashes
312    switch -- $params(-linestyle) {
313        dashed { set params(-linestyle) {4 4} }
314        dotted { set params(-linestyle) {2 4} }
315        default { set params(-linestyle) {} }
316    }
317
318    # if -brightness is set, then update the color
319    if {$params(-brightness) != 0} {
320        set params(-color) [Rappture::color::brightness \
321            $params(-color) $params(-brightness)]
322
323        set bg [$itk_component(plot) cget -plotbackground]
324        foreach {h s v} [Rappture::color::RGBtoHSV $bg] break
325        if {$v > 0.5} {
326            set params(-color) [Rappture::color::brightness_max \
327                $params(-color) 0.8]
328        } else {
329            set params(-color) [Rappture::color::brightness_min \
330                $params(-color) 0.2]
331        }
332    }
333
334    set pos [lsearch -exact $dataobj $_dlist]
335    if {$pos < 0} {
336        lappend _dlist $dataobj
337        set _dataobj2color($dataobj) $params(-color)
338        set _dataobj2width($dataobj) $params(-width)
339        set _dataobj2dashes($dataobj) $params(-linestyle)
340        set _dataobj2raise($dataobj) $params(-raise)
341        set _dataobj2desc($dataobj) $params(-description)
342
343        $_dispatcher event -idle !rebuild
344    }
345}
346
347# ----------------------------------------------------------------------
348# USAGE: get
349#
350# Clients use this to query the list of objects being plotted, in
351# order from bottom to top of this result.
352# ----------------------------------------------------------------------
353itcl::body Rappture::BarResult::get {} {
354    # put the dataobj list in order according to -raise options
355    set dlist $_dlist
356    foreach obj $dlist {
357        if {[info exists _dataobj2raise($obj)] && $_dataobj2raise($obj)} {
358            set i [lsearch -exact $dlist $obj]
359            if {$i >= 0} {
360                set dlist [lreplace $clist $i $i]
361                lappend dlist $obj
362            }
363        }
364    }
365    return $dlist
366}
367
368# ----------------------------------------------------------------------
369# USAGE: delete ?<dataobj1> <dataobj2> ...?
370#
371# Clients use this to delete a dataobj from the plot.  If no dataobjs
372# are specified, then all dataobjs are deleted.
373# ----------------------------------------------------------------------
374itcl::body Rappture::BarResult::delete {args} {
375    if {[llength $args] == 0} {
376        set args $_dlist
377    }
378
379    # delete all specified dataobjs
380    set changed 0
381    foreach dataobj $args {
382        set pos [lsearch -exact $_dlist $dataobj]
383        if {$pos >= 0} {
384            set _dlist [lreplace $_dlist $pos $pos]
385            catch {unset _dataobj2color($dataobj)}
386            catch {unset _dataobj2width($dataobj)}
387            catch {unset _dataobj2dashes($dataobj)}
388            catch {unset _dataobj2raise($dataobj)}
389            foreach elem [array names _elem2dataobj] {
390                if {$_elem2dataobj($elem) == $dataobj} {
391                    unset _elem2dataobj($elem)
392                }
393            }
394            set changed 1
395        }
396    }
397
398    # If anything changed, then rebuild the plot
399    if {$changed} {
400        $_dispatcher event -idle !rebuild
401    }
402
403    # Nothing left? then start over with auto colors
404    if {[llength $_dlist] == 0} {
405        set _autoColorI 0
406    }
407}
408
409# ----------------------------------------------------------------------
410# USAGE: scale ?<dataobj1> <dataobj2> ...?
411#
412# Sets the default limits for the overall plot according to the
413# limits of the data for all of the given <dataobj> objects.  This
414# accounts for all dataobjs--even those not showing on the screen.
415# Because of this, the limits are appropriate for all dataobjs as
416# the user scans through data in the ResultSet viewer.
417# ----------------------------------------------------------------------
418itcl::body Rappture::BarResult::scale {args} {
419    set allx [$itk_component(plot) x2axis use]
420    lappend allx x  ;# fix main x-axis too
421    foreach axis $allx {
422        _axis scale $axis linear
423    }
424
425    set ally [$itk_component(plot) y2axis use]
426    lappend ally y  ;# fix main y-axis too
427    foreach axis $ally {
428        _axis scale $axis linear
429    }
430
431    catch {unset _limits}
432    foreach dataobj $args {
433        # find the axes for this dataobj (e.g., {x y2})
434        foreach {map(x) map(y)} [_getAxes $dataobj] break
435
436        foreach axis {x y} {
437            # get defaults for both linear and log scales
438            foreach type {lin log} {
439                # store results -- ex: _limits(x2log-min)
440                set id $map($axis)$type
441                foreach {min max} [$dataobj limits $axis$type] break
442                if {"" != $min && "" != $max} {
443                    if {![info exists _limits($id-min)]} {
444                        set _limits($id-min) $min
445                        set _limits($id-max) $max
446                    } else {
447                        if {$min < $_limits($id-min)} {
448                            set _limits($id-min) $min
449                        }
450                        if {$max > $_limits($id-max)} {
451                            set _limits($id-max) $max
452                        }
453                    }
454                }
455            }
456
457            if {[$dataobj hints ${axis}scale] == "log"} {
458                _axis scale $map($axis) log
459            }
460        }
461    }
462    _resetLimits
463}
464
465# ----------------------------------------------------------------------
466# USAGE: download coming
467# USAGE: download controls <downloadCommand>
468# USAGE: download now
469#
470# Clients use this method to create a downloadable representation
471# of the plot.  Returns a list of the form {ext string}, where
472# "ext" is the file extension (indicating the type of data) and
473# "string" is the data itself.
474# ----------------------------------------------------------------------
475itcl::body Rappture::BarResult::download {option args} {
476    switch $option {
477        coming {
478            # nothing to do
479        }
480        controls {
481            set popup .barresultdownload
482            if {![winfo exists .barresultdownload]} {
483                # if we haven't created the popup yet, do it now
484                Rappture::Balloon $popup \
485                    -title "[Rappture::filexfer::label downloadWord] as..."
486                set inner [$popup component inner]
487                label $inner.summary -text "" -anchor w
488                pack $inner.summary -side top
489                radiobutton $inner.csv -text "Data as Comma-Separated Values" \
490                    -variable Rappture::BarResult::_downloadPopup(format) \
491                    -value csv
492                pack $inner.csv -anchor w
493                radiobutton $inner.image -text "Image (PS/PDF/PNG/JPEG)" \
494                    -variable Rappture::BarResult::_downloadPopup(format) \
495                    -value image
496                pack $inner.image -anchor w
497                button $inner.go -text [Rappture::filexfer::label download] \
498                    -command [lindex $args 0]
499                pack $inner.go -pady 4
500            } else {
501                set inner [$popup component inner]
502            }
503            set num [llength [get]]
504            set num [expr {($num == 1) ? "1 result" : "$num results"}]
505            $inner.summary configure -text "[Rappture::filexfer::label downloadWord] $num in the following format:"
506            update idletasks ;# fix initial sizes
507            return $popup
508        }
509        now {
510            set popup .barresultdownload
511            if {[winfo exists .barresultdownload]} {
512                $popup deactivate
513            }
514            switch -- $_downloadPopup(format) {
515                csv {
516                    # reverse the objects so the selected data appears on top
517                    set dlist ""
518                    foreach dataobj [get] {
519                        set dlist [linsert $dlist 0 $dataobj]
520                    }
521
522                    # generate the comma-separated value data for these objects
523                    set csvdata ""
524                    foreach dataobj $dlist {
525                        append csvdata "[string repeat - 60]\n"
526                        append csvdata " [$dataobj hints label]\n"
527                        if {[info exists _dataobj2desc($dataobj)]
528                            && [llength [split $_dataobj2desc($dataobj) \n]] > 1} {
529                            set indent "for:"
530                            foreach line [split $_dataobj2desc($dataobj) \n] {
531                                append csvdata " $indent $line\n"
532                                set indent "    "
533                            }
534                        }
535                        append csvdata "[string repeat - 60]\n"
536
537                        append csvdata "[$dataobj hints xlabel], [$dataobj hints ylabel]\n"
538                        set first 1
539                        foreach comp [$dataobj components] {
540                            if {!$first} {
541                                # blank line between components
542                                append csvdata "\n"
543                            }
544                            set xv [$dataobj mesh $comp]
545                            set yv [$dataobj values $comp]
546                            foreach x [$xv range 0 end] y [$yv range 0 end] {
547                                append csvdata [format "%20.15g, %20.15g\n" $x $y]
548                            }
549                            set first 0
550                        }
551                        append csvdata "\n"
552                    }
553                    return [list .txt $csvdata]
554                }
555                image {
556                    set popup .barprintdownload
557                    if { ![winfo exists $popup] } {
558                        # Create a popup for the print dialog
559                        Rappture::Balloon $popup -title "Save as image..."
560                        set inner [$popup component inner]
561                        # Create the print dialog widget and add it to the
562                        # the balloon popup.
563                        Rappture::XyPrint $inner.print
564                        $popup configure \
565                            -deactivatecommand [list $inner.print reset]
566                        blt::table $inner 0,0 $inner.print -fill both
567                    }
568                    update
569                    # Activate the popup and call for the output.
570                    foreach { widget toolName plotName } $args break
571                    $popup activate $widget left
572                    set inner [$popup component inner]
573                    set output [$inner.print print $itk_component(plot) \
574                                    $toolName $plotName]
575                    $popup deactivate
576                    return $output
577                }
578            }
579        }
580        default {
581            error "bad option \"$option\": should be coming, controls, now"
582        }
583    }
584}
585
586# ----------------------------------------------------------------------
587# USAGE: _rebuild
588#
589# Called automatically whenever something changes that affects the
590# data in the widget.  Clears any existing data and rebuilds the
591# widget to display new data.
592# ----------------------------------------------------------------------
593itcl::body Rappture::BarResult::_rebuild {} {
594    set g $itk_component(plot)
595
596    # First clear out the widget
597    eval $g element delete [$g element names]
598    eval $g marker delete [$g marker names]
599    foreach axis [$g axis names] {
600        $g axis configure $axis -hide yes -checklimits no -loose yes
601    }
602    # Presumably you want at least an X-axis and Y-axis displayed.
603    $g xaxis configure -hide no
604    $g yaxis configure -hide no
605    array unset _label2axis
606
607    #
608    # Scan through all objects and create a list of all axes.
609    # The first x-axis gets mapped to "x".  The second, to "x2".
610    # Beyond that, we must create new axes "x3", "x4", etc.
611    # We do the same for y.
612    #
613    set anum(x) 0
614    set anum(y) 0
615    foreach dataobj [get] {
616        foreach ax {x y} {
617            set label [$dataobj hints ${ax}label]
618            if {"" != $label} {
619                if {![info exists _label2axis($ax-$label)]} {
620                    switch [incr anum($ax)] {
621                        1 { set axis $ax }
622                        2 { set axis ${ax}2 }
623                        default {
624                            set axis $ax$anum($ax)
625                            catch {$g axis create $axis}
626                        }
627                    }
628                    $g axis configure $axis -title $label -hide no \
629                        -checklimits no
630                    set _label2axis($ax-$label) $axis
631
632                    # if this axis has a description, add it as a tooltip
633                    set desc [string trim [$dataobj hints ${ax}desc]]
634                    Rappture::Tooltip::text $g-$axis $desc
635                }
636            }
637        }
638    }
639
640    #
641    # All of the extra axes get mapped to the x2/y2 (top/right)
642    # position.
643    #
644    set all ""
645    foreach ax {x y} {
646        lappend all $ax
647
648        set extra ""
649        for {set i 2} {$i <= $anum($ax)} {incr i} {
650            lappend extra ${ax}$i
651        }
652        eval lappend all $extra
653        $g ${ax}2axis use $extra
654        if {$ax == "y"} {
655            $g configure -rightmargin [expr {($extra == "") ? 10 : 0}]
656        }
657    }
658
659    foreach axis $all {
660        set _axisPopup(format-$axis) "%.6g"
661
662        $g axis bind $axis <Enter> \
663            [itcl::code $this _axis hilite $axis on]
664        $g axis bind $axis <Leave> \
665            [itcl::code $this _axis hilite $axis off]
666        $g axis bind $axis <ButtonPress-1> \
667            [itcl::code $this _axis click $axis %x %y]
668        $g axis bind $axis <B1-Motion> \
669            [itcl::code $this _axis drag $axis %x %y]
670        $g axis bind $axis <ButtonRelease-1> \
671            [itcl::code $this _axis release $axis %x %y]
672        $g axis bind $axis <KeyPress> \
673            [list ::Rappture::Tooltip::tooltip cancel]
674    }
675
676    #
677    # Plot all of the dataobjs.
678    #
679    set count 0
680    foreach dataobj $_dlist {
681        set label [$dataobj hints label]
682        foreach {mapx mapy} [_getAxes $dataobj] break
683
684        foreach comp [$dataobj components] {
685            set xv [$dataobj mesh $comp]
686            set yv [$dataobj values $comp]
687
688            if {[info exists _dataobj2color($dataobj)]} {
689                set color $_dataobj2color($dataobj)
690            } else {
691                set color [$dataobj hints color]
692                if {"" == $color} {
693                    set color black
694                }
695            }
696
697            if {[info exists _dataobj2width($dataobj)]} {
698                set lwidth $_dataobj2width($dataobj)
699            } else {
700                set lwidth 2
701            }
702
703            if {[info exists _dataobj2dashes($dataobj)]} {
704                set dashes $_dataobj2dashes($dataobj)
705            } else {
706                set dashes ""
707            }
708
709            if {([$xv length] <= 1) || ($lwidth == 0)} {
710                set sym square
711                set pixels 2
712            } else {
713                set sym ""
714                set pixels 6
715            }
716
717            set elem "elem[incr count]"
718            set _elem2dataobj($elem) $dataobj
719            set labels [$dataobj hints xticks]
720            if { $labels != "" } {
721                $g axis configure $mapx \
722                    -command [itcl::code $this _formatTickLabel] \
723                    -minorticks 0
724                set _tickLabels $labels
725            }
726            $g element create $elem -x $xv -y $yv \
727                -borderwidth $lwidth -label $label \
728                -background $color -foreground $color -mapx $mapx -mapy $mapy
729        }
730    }
731
732    # Fix duplicate labels by appending the simulation number
733    foreach label [array names label2elem] {
734        if { [llength $label2elem($label)] == 1 } {
735            continue
736        }
737        foreach elem $label2elem($label) {
738            set dataobj $_elem2dataobj($elem)
739            scan [$dataobj hints xmlobj] "::libraryObj%d" suffix
740            incr suffix
741            set elabel [format "%s \#%d" $label $suffix]
742            $g element configure $elem -label $elabel
743        }
744    }       
745
746    foreach dataobj $_dlist {
747        set xmin -Inf
748        set ymin -Inf
749        set xmax Inf
750        set ymax Inf
751        #
752        # Create text/line markers for each *axis.marker specified.
753        #
754        foreach m [$dataobj xmarkers] {
755            foreach {at label style} $m break
756            set id [$g marker create line -coords [list $at $ymin $at $ymax]]
757            $g marker bind $id <Enter> \
758                [itcl::code $this _enterMarker $g x-$label $at $ymin $at]
759            $g marker bind $id <Leave> \
760                [itcl::code $this _leaveMarker $g x-$label]
761            set options [_getLineMarkerOptions $style]
762            if { $options != "" } {
763                eval $g marker configure $id $options
764            }
765            if { $label != "" } {
766                set id [$g marker create text -anchor nw \
767                            -text $label -coords [list $at $ymax]]
768                set options [_getTextMarkerOptions $style]
769                if { $options != "" } {
770                    eval $g marker configure $id $options
771                }
772            }
773        }
774        foreach m [$dataobj ymarkers] {
775            foreach {at label style} $m break
776            set id [$g marker create line -coords [list $xmin $at $xmax $at]]
777            $g marker bind $id <Enter> \
778                [itcl::code $this _enterMarker $g y-$label $at $xmin $at]
779            $g marker bind $id <Leave> \
780                [itcl::code $this _leaveMarker $g y-$label]
781            set options [_getLineMarkerOptions $style]
782            if { $options != "" } {
783                eval $g marker configure $id $options
784            }
785            if { $label != "" } {
786                set id [$g marker create text -anchor se \
787                        -text $label -coords [list $xmax $at]]
788                set options [_getTextMarkerOptions $style]
789                if { $options != "" } {
790                    eval $g marker configure $id $options
791                }
792            }
793        }
794    }
795    $itk_component(legend) reset
796}
797
798# ----------------------------------------------------------------------
799# USAGE: _resetLimits
800#
801# Used internally to apply automatic limits to the axes for the
802# current plot.
803# ----------------------------------------------------------------------
804itcl::body Rappture::BarResult::_resetLimits {} {
805    set g $itk_component(plot)
806    foreach axis [$g axis names] {
807        $g axis configure $axis -min "" -max ""
808    }
809    return
810
811    #
812    # HACK ALERT!
813    # Use this code to fix up the y-axis limits for the BLT graph.
814    # The auto-limits don't always work well.  We want them to be
815    # set to a "nice" number slightly above or below the min/max
816    # limits.
817    #
818    foreach axis [$g axis names] {
819        if {[info exists _limits(${axis}lin-min)]} {
820            set log [$g axis cget $axis -logscale]
821            if {$log} {
822                set min $_limits(${axis}log-min)
823                if {$min == 0} { set min 1 }
824                set max $_limits(${axis}log-max)
825                if {$max == 0} { set max 1 }
826
827                if {$min == $max} {
828                    set logmin [expr {floor(log10(abs(0.9*$min)))}]
829                    set logmax [expr {ceil(log10(abs(1.1*$max)))}]
830                } else {
831                    set logmin [expr {floor(log10(abs($min)))}]
832                    set logmax [expr {ceil(log10(abs($max)))}]
833                    if {[string match y* $axis]} {
834                        # add a little padding
835                        set delta [expr {$logmax-$logmin}]
836                        if {$delta == 0} { set delta 1 }
837                        set logmin [expr {$logmin-0.05*$delta}]
838                        set logmax [expr {$logmax+0.05*$delta}]
839                    }
840                }
841                if {$logmin < -300} {
842                    set min 1e-300
843                } elseif {$logmin > 300} {
844                    set min 1e+300
845                } else {
846                    set min [expr {pow(10.0,$logmin)}]
847                }
848
849                if {$logmax < -300} {
850                    set max 1e-300
851                } elseif {$logmax > 300} {
852                    set max 1e+300
853                } else {
854                    set max [expr {pow(10.0,$logmax)}]
855                }
856            } else {
857                set min $_limits(${axis}lin-min)
858                set max $_limits(${axis}lin-max)
859
860                if {[string match y* $axis]} {
861                    # add a little padding
862                    set delta [expr {$max-$min}]
863                    set min [expr {$min-0.05*$delta}]
864                    set max [expr {$max+0.05*$delta}]
865                }
866            }
867            if {$min < $max} {
868                $g axis configure $axis -min $min -max $max
869            } else {
870                $g axis configure $axis -min "" -max ""
871            }
872        } else {
873            $g axis configure $axis -min "" -max ""
874        }
875    }
876}
877
878# ----------------------------------------------------------------------
879# USAGE: _zoom reset
880#
881# Called automatically when the user clicks on one of the zoom
882# controls for this widget.  Changes the zoom for the current view.
883# ----------------------------------------------------------------------
884itcl::body Rappture::BarResult::_zoom {option args} {
885    switch -- $option {
886        reset {
887            _resetLimits
888        }
889    }
890}
891
892# ----------------------------------------------------------------------
893# USAGE: _hilite <state> <x> <y>
894#
895# Called automatically when the user brushes one of the elements
896# on the plot.  Causes the element to highlight and a tooltip to
897# pop up with element info.
898# ----------------------------------------------------------------------
899itcl::body Rappture::BarResult::_hilite {state x y} {
900    set g $itk_component(plot)
901    set elem ""
902   
903    # Peek inside of Blt_ZoomStack package to see if we're currently in the
904    # middle of a zoom selection.
905    if {[info exists ::zoomInfo($g,corner)] && $::zoomInfo($g,corner) == "B" } {
906        return;
907    }
908    set tip ""
909    if {$state == "at"} {
910        if {[$g element closest $x $y info -interpolate yes]} {
911            # for dealing with xy line plots
912            set elem $info(name)
913
914            # Some elements are generated dynamically and therefore will
915            # not have a dataobj associated with them.
916            set mapx [$g element cget $elem -mapx]
917            set mapy [$g element cget $elem -mapy]
918            if {[info exists _elem2dataobj($elem)]} {
919                foreach {mapx mapy} [_getAxes $_elem2dataobj($elem)] break
920            }
921
922            # search again for an exact point -- this time don't interpolate
923            set tip ""
924            array unset info
925            if {[$g element closest $x $y info -interpolate no]
926                  && $info(name) == $elem} {
927
928                set x [$g axis transform $mapx $info(x)]
929                set y [$g axis transform $mapy $info(y)]
930               
931                if {[info exists _elem2dataobj($elem)]} {
932                    set dataobj $_elem2dataobj($elem)
933                    set yunits [$dataobj hints yunits]
934                    set xunits [$dataobj hints xunits]
935                } else {
936                    set xunits ""
937                    set yunits ""
938                }
939                set tip [$g element cget $elem -label]
940                set yval [_axis format y dummy $info(y)]
941                append tip "\n$yval$yunits"
942                set xval [_axis format x dummy $info(x)]
943                append tip " @ $xval$xunits"
944                set tip [string trim $tip]
945            }
946            set state 1
947        } elseif {[$g element closest $x $y info -interpolate no]} {
948            # for dealing with xy scatter plot
949            set elem $info(name)
950
951            # Some elements are generated dynamically and therefore will
952            # not have a dataobj associated with them.
953            set mapx [$g element cget $elem -mapx]
954            set mapy [$g element cget $elem -mapy]
955            if {[info exists _elem2dataobj($elem)]} {
956                foreach {mapx mapy} [_getAxes $_elem2dataobj($elem)] break
957            }
958
959            set tip ""
960            set x [$g axis transform $mapx $info(x)]
961            set y [$g axis transform $mapy $info(y)]
962               
963                if {[info exists _elem2dataobj($elem)]} {
964                    set dataobj $_elem2dataobj($elem)
965                    set yunits [$dataobj hints yunits]
966                    set xunits [$dataobj hints xunits]
967                } else {
968                    set xunits ""
969                    set yunits ""
970                }
971                set tip [$g element cget $elem -label]
972                set yval [_axis format y dummy $info(y)]
973                append tip "\n$yval$yunits"
974                set xval [_axis format x dummy $info(x)]
975                append tip " @ $xval$xunits"
976                set tip [string trim $tip]
977                set state 1
978            } else {
979                set state 0
980            }
981        }
982    }
983
984    if {$state} {
985        #
986        # Highlight ON:
987        # - activate trace
988        # - multiple axes? dim other axes
989        # - pop up tooltip about data
990        #
991        if { [$g element exists $_hilite(elem)] && $_hilite(elem) != $elem } {
992            $g element deactivate $_hilite(elem)
993            $g crosshairs configure -hide yes
994            Rappture::Tooltip::tooltip cancel
995        }
996        set bg [$g element cget $elem -background]
997        set fg [$g element cget $elem -background]
998        foreach {h s v} [Rappture::color::RGBtoHSV $fg] break
999        if { $v > 0.2 } {
1000            set v [expr $v - 0.2]
1001        }
1002        set fg [Rappture::color::HSVtoRGB $h $s $v]
1003        $g pen configure activeBar -background $bg -foreground $fg
1004        $g element activate $elem
1005        set _hilite(elem) $elem
1006
1007        set mapx [$g element cget $elem -mapx]
1008        set mapy [$g element cget $elem -mapy]
1009        if {[info exists _elem2dataobj($elem)]} {
1010            foreach {mapx mapy} [_getAxes $_elem2dataobj($elem)] break
1011        }
1012        set allx [$g x2axis use]
1013        if {[llength $allx] > 0} {
1014            lappend allx x  ;# fix main x-axis too
1015            foreach axis $allx {
1016                if {$axis == $mapx} {
1017                    $g axis configure $axis -color $itk_option(-foreground) \
1018                        -titlecolor $itk_option(-foreground)
1019                } else {
1020                    $g axis configure $axis -color $itk_option(-dimcolor) \
1021                        -titlecolor $itk_option(-dimcolor)
1022                }
1023            }
1024        }
1025        set ally [$g y2axis use]
1026        if {[llength $ally] > 0} {
1027            lappend ally y  ;# fix main y-axis too
1028            foreach axis $ally {
1029                if {$axis == $mapy} {
1030                    $g axis configure $axis -color $itk_option(-foreground) \
1031                        -titlecolor $itk_option(-foreground)
1032                } else {
1033                    $g axis configure $axis -color $itk_option(-dimcolor) \
1034                        -titlecolor $itk_option(-dimcolor)
1035                }
1036            }
1037        }
1038
1039        if {"" != $tip} {
1040            $g crosshairs configure -hide no -position @$x,$y
1041
1042            if {$x > 0.5*[winfo width $g]} {
1043                if {$x < 4} {
1044                    set tipx "-0"
1045                } else {
1046                    set tipx "-[expr {$x-4}]"  ;# move tooltip to the left
1047                }
1048            } else {
1049                if {$x < -4} {
1050                    set tipx "+0"
1051                } else {
1052                    set tipx "+[expr {$x+4}]"  ;# move tooltip to the right
1053                }
1054            }
1055            if {$y > 0.5*[winfo height $g]} {
1056                if {$y < 4} {
1057                    set tipy "-0"
1058                } else {
1059                    set tipy "-[expr {$y-4}]"  ;# move tooltip to the top
1060                }
1061            } else {
1062                if {$y < -4} {
1063                    set tipy "+0"
1064                } else {
1065                    set tipy "+[expr {$y+4}]"  ;# move tooltip to the bottom
1066                }
1067            }
1068            Rappture::Tooltip::text $g $tip
1069            Rappture::Tooltip::tooltip show $g $tipx,$tipy
1070        }
1071    } else {
1072        #
1073        # Highlight OFF:
1074        # - deactivate (color back to normal)
1075        # - put all axes back to normal color
1076        # - take down tooltip
1077        #
1078        if { [$g element exists $_hilite(elem)] } {
1079            $g element deactivate $_hilite(elem)
1080        }
1081        set allx [$g x2axis use]
1082        if {[llength $allx] > 0} {
1083            lappend allx x  ;# fix main x-axis too
1084            foreach axis $allx {
1085                $g axis configure $axis -color $itk_option(-foreground) \
1086                    -titlecolor $itk_option(-foreground)
1087            }
1088        }
1089       
1090        set ally [$g y2axis use]
1091        if {[llength $ally] > 0} {
1092            lappend ally y  ;# fix main y-axis too
1093            foreach axis $ally {
1094                $g axis configure $axis -color $itk_option(-foreground) \
1095                    -titlecolor $itk_option(-foreground)
1096            }
1097        }
1098
1099        $g crosshairs configure -hide yes
1100
1101        # only cancel in plotting area or we'll mess up axes
1102        if {[$g inside $x $y]} {
1103            Rappture::Tooltip::tooltip cancel
1104        }
1105
1106        # There is no currently highlighted element
1107        set _hilite(elem) ""
1108    }
1109}
1110
1111# ----------------------------------------------------------------------
1112# USAGE: _axis hilite <axis> <state>
1113#
1114# USAGE: _axis click <axis> <x> <y>
1115# USAGE: _axis drag <axis> <x> <y>
1116# USAGE: _axis release <axis> <x> <y>
1117#
1118# USAGE: _axis edit <axis>
1119# USAGE: _axis changed <axis> <what>
1120# USAGE: _axis format <axis> <widget> <value>
1121# USAGE: _axis scale <axis> linear|log
1122#
1123# Used internally to handle editing of the x/y axes.  The hilite
1124# operation causes the axis to light up.  The edit operation pops
1125# up a panel with editing options.  The changed operation applies
1126# changes from the panel.
1127# ----------------------------------------------------------------------
1128itcl::body Rappture::BarResult::_axis {option args} {
1129    set inner [$itk_component(hull).axes component inner]
1130    switch -- $option {
1131        hilite {
1132            if {[llength $args] != 2} {
1133                error "wrong # args: should be \"_axis hilite axis state\""
1134            }
1135            set g $itk_component(plot)
1136            set axis [lindex $args 0]
1137            set state [lindex $args 1]
1138
1139            if {$state} {
1140                $g axis configure $axis \
1141                    -color $itk_option(-activecolor) \
1142                    -titlecolor $itk_option(-activecolor)
1143
1144                set x [expr {[winfo pointerx $g]+4}]
1145                set y [expr {[winfo pointery $g]+4}]
1146                Rappture::Tooltip::tooltip pending $g-$axis @$x,$y
1147            } else {
1148                $g axis configure $axis \
1149                    -color $itk_option(-foreground) \
1150                    -titlecolor $itk_option(-foreground)
1151                Rappture::Tooltip::tooltip cancel
1152            }
1153        }
1154        click {
1155            if {[llength $args] != 3} {
1156                error "wrong # args: should be \"_axis click axis x y\""
1157            }
1158            set axis [lindex $args 0]
1159            set x [lindex $args 1]
1160            set y [lindex $args 2]
1161            set g $itk_component(plot)
1162
1163            set _axis(moved) 0
1164            set _axis(click-x) $x
1165            set _axis(click-y) $y
1166            foreach {min max} [$g axis limits $axis] break
1167            set _axis(min0) $min
1168            set _axis(max0) $max
1169            Rappture::Tooltip::tooltip cancel
1170        }
1171        drag {
1172            if {[llength $args] != 3} {
1173                error "wrong # args: should be \"_axis drag axis x y\""
1174            }
1175            if {![info exists _axis(moved)]} {
1176                return  ;# must have skipped click event -- ignore
1177            }
1178            set axis [lindex $args 0]
1179            set x [lindex $args 1]
1180            set y [lindex $args 2]
1181            set g $itk_component(plot)
1182
1183            if {[info exists _axis(click-x)] && [info exists _axis(click-y)]} {
1184                foreach {x0 y0 pw ph} [$g extents plotarea] break
1185                switch -glob $axis {
1186                  x* {
1187                    set pix $x
1188                    set pix0 $_axis(click-x)
1189                    set pixmin $x0
1190                    set pixmax [expr {$x0+$pw}]
1191                  }
1192                  y* {
1193                    set pix $y
1194                    set pix0 $_axis(click-y)
1195                    set pixmin [expr {$y0+$ph}]
1196                    set pixmax $y0
1197                  }
1198                }
1199                set log [$g axis cget $axis -logscale]
1200                set min $_axis(min0)
1201                set max $_axis(max0)
1202                set dpix [expr {abs($pix-$pix0)}]
1203                set v0 [$g axis invtransform $axis $pixmin]
1204                set v1 [$g axis invtransform $axis [expr {$pixmin+$dpix}]]
1205                if {$log} {
1206                    set v0 [expr {log10($v0)}]
1207                    set v1 [expr {log10($v1)}]
1208                    set min [expr {log10($min)}]
1209                    set max [expr {log10($max)}]
1210                }
1211
1212                if {$pix > $pix0} {
1213                    set delta [expr {$v1-$v0}]
1214                } else {
1215                    set delta [expr {$v0-$v1}]
1216                }
1217                set min [expr {$min-$delta}]
1218                set max [expr {$max-$delta}]
1219                if {$log} {
1220                    set min [expr {pow(10.0,$min)}]
1221                    set max [expr {pow(10.0,$max)}]
1222                }
1223                $g axis configure $axis -min $min -max $max
1224
1225                # move axis, don't edit on release
1226                set _axis(move) 1
1227            }
1228        }
1229        release {
1230            if {[llength $args] != 3} {
1231                error "wrong # args: should be \"_axis release axis x y\""
1232            }
1233            if {![info exists _axis(moved)]} {
1234                return  ;# must have skipped click event -- ignore
1235            }
1236            set axis [lindex $args 0]
1237            set x [lindex $args 1]
1238            set y [lindex $args 2]
1239
1240            if {!$_axis(moved)} {
1241                # small movement? then treat as click -- pop up axis editor
1242                set dx [expr {abs($x-$_axis(click-x))}]
1243                set dy [expr {abs($y-$_axis(click-y))}]
1244                if {$dx < 2 && $dy < 2} {
1245                    _axis edit $axis
1246                }
1247            } else {
1248                # one last movement
1249                _axis drag $axis $x $y
1250            }
1251            catch {unset _axis}
1252        }
1253        edit {
1254            if {[llength $args] != 1} {
1255                error "wrong # args: should be \"_axis edit axis\""
1256            }
1257            set axis [lindex $args 0]
1258            set _axisPopup(current) $axis
1259
1260            # apply last value when deactivating
1261            $itk_component(hull).axes configure -deactivatecommand \
1262                [itcl::code $this _axis changed $axis focus]
1263
1264            # fix axis label controls...
1265            set label [$itk_component(plot) axis cget $axis -title]
1266            $inner.label delete 0 end
1267            $inner.label insert end $label
1268            bind $inner.label <KeyPress-Return> \
1269                [itcl::code $this _axis changed $axis label]
1270            bind $inner.label <FocusOut> \
1271                [itcl::code $this _axis changed $axis label]
1272
1273            # fix min/max controls...
1274            foreach {min max} [$itk_component(plot) axis limits $axis] break
1275            $inner.min delete 0 end
1276            $inner.min insert end $min
1277            bind $inner.min <KeyPress-Return> \
1278                [itcl::code $this _axis changed $axis min]
1279            bind $inner.min <FocusOut> \
1280                [itcl::code $this _axis changed $axis min]
1281
1282            $inner.max delete 0 end
1283            $inner.max insert end $max
1284            bind $inner.max <KeyPress-Return> \
1285                [itcl::code $this _axis changed $axis max]
1286            bind $inner.max <FocusOut> \
1287                [itcl::code $this _axis changed $axis max]
1288
1289            # fix format control...
1290            set fmts [$inner.format choices get -value]
1291            set i [lsearch -exact $fmts $_axisPopup(format-$axis)]
1292            if {$i < 0} { set i 0 }  ;# use Auto choice
1293            $inner.format value [$inner.format choices get -label $i]
1294
1295            bind $inner.format <<Value>> \
1296                [itcl::code $this _axis changed $axis format]
1297
1298            # fix scale control...
1299            if {[$itk_component(plot) axis cget $axis -logscale]} {
1300                set _axisPopup(scale) "log"
1301                $inner.format configure -state disabled
1302            } else {
1303                set _axisPopup(scale) "linear"
1304                $inner.format configure -state normal
1305            }
1306            $inner.scales.linear configure \
1307                -command [itcl::code $this _axis changed $axis scale]
1308            $inner.scales.log configure \
1309                -command [itcl::code $this _axis changed $axis scale]
1310
1311            #
1312            # Figure out where the window should pop up.
1313            #
1314            set x [winfo rootx $itk_component(plot)]
1315            set y [winfo rooty $itk_component(plot)]
1316            set w [winfo width $itk_component(plot)]
1317            set h [winfo height $itk_component(plot)]
1318            foreach {x0 y0 pw ph} [$itk_component(plot) extents plotarea] break
1319            switch -glob -- $axis {
1320                x {
1321                    set x [expr {round($x + $x0+0.5*$pw)}]
1322                    set y [expr {round($y + $y0+$ph + 0.5*($h-$y0-$ph))}]
1323                    set dir "above"
1324                }
1325                x* {
1326                    set x [expr {round($x + $x0+0.5*$pw)}]
1327                    set dir "below"
1328                    set allx [$itk_component(plot) x2axis use]
1329                    set max [llength $allx]
1330                    set i [lsearch -exact $allx $axis]
1331                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1332                }
1333                y {
1334                    set x [expr {round($x + 0.5*$x0)}]
1335                    set y [expr {round($y + $y0+0.5*$ph)}]
1336                    set dir "right"
1337                }
1338                y* {
1339                    set y [expr {round($y + $y0+0.5*$ph)}]
1340                    set dir "left"
1341                    set ally [$itk_component(plot) y2axis use]
1342                    set max [llength $ally]
1343                    set i [lsearch -exact $ally $axis]
1344                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1345                    set x [expr {round($x+$x0+$pw + ($i+0.5)*($w-$x0-$pw)/double($max))}]
1346                }
1347            }
1348            $itk_component(hull).axes activate @$x,$y $dir
1349        }
1350        changed {
1351            if {[llength $args] != 2} {
1352                error "wrong # args: should be \"_axis changed axis what\""
1353            }
1354            set axis [lindex $args 0]
1355            set what [lindex $args 1]
1356            if {$what == "focus"} {
1357                set what [focus]
1358                if {[winfo exists $what]} {
1359                    set what [winfo name $what]
1360                }
1361            }
1362
1363            switch -- $what {
1364                label {
1365                    set val [$inner.label get]
1366                    $itk_component(plot) axis configure $axis -title $val
1367                }
1368                min {
1369                    set val [$inner.min get]
1370                    if {![string is double -strict $val]} {
1371                        Rappture::Tooltip::cue $inner.min "Must be a number"
1372                        bell
1373                        return
1374                    }
1375
1376                    set max [lindex [$itk_component(plot) axis limits $axis] 1]
1377                    if {$val >= $max} {
1378                        Rappture::Tooltip::cue $inner.min "Must be <= max ($max)"
1379                        bell
1380                        return
1381                    }
1382                    catch {
1383                        # can fail in log mode
1384                        $itk_component(plot) axis configure $axis -min $val
1385                    }
1386                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1387                    $inner.min delete 0 end
1388                    $inner.min insert end $min
1389                }
1390                max {
1391                    set val [$inner.max get]
1392                    if {![string is double -strict $val]} {
1393                        Rappture::Tooltip::cue $inner.max "Should be a number"
1394                        bell
1395                        return
1396                    }
1397
1398                    set min [lindex [$itk_component(plot) axis limits $axis] 0]
1399                    if {$val <= $min} {
1400                        Rappture::Tooltip::cue $inner.max "Must be >= min ($min)"
1401                        bell
1402                        return
1403                    }
1404                    catch {
1405                        # can fail in log mode
1406                        $itk_component(plot) axis configure $axis -max $val
1407                    }
1408                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1409                    $inner.max delete 0 end
1410                    $inner.max insert end $max
1411                }
1412                format {
1413                    set fmt [$inner.format translate [$inner.format value]]
1414                    set _axisPopup(format-$axis) $fmt
1415
1416                    # force a refresh
1417                    $itk_component(plot) axis configure $axis -min \
1418                        [$itk_component(plot) axis cget $axis -min]
1419                }
1420                scale {
1421                    _axis scale $axis $_axisPopup(scale)
1422
1423                    if {$_axisPopup(scale) == "log"} {
1424                        $inner.format configure -state disabled
1425                    } else {
1426                        $inner.format configure -state normal
1427                    }
1428
1429                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1430                    $inner.min delete 0 end
1431                    $inner.min insert end $min
1432                    $inner.max delete 0 end
1433                    $inner.max insert end $max
1434                }
1435                default {
1436                    # be lenient so we can handle the "focus" case
1437                }
1438            }
1439        }
1440        format {
1441            if {[llength $args] != 3} {
1442                error "wrong # args: should be \"_axis format axis widget value\""
1443            }
1444            set axis [lindex $args 0]
1445            set value [lindex $args 2]
1446
1447            if {[$itk_component(plot) axis cget $axis -logscale]} {
1448                set fmt "%.6g"
1449            } else {
1450                set fmt $_axisPopup(format-$axis)
1451            }
1452            return [format $fmt $value]
1453        }
1454        scale {
1455            if {[llength $args] != 2} {
1456                error "wrong # args: should be \"_axis scale axis type\""
1457            }
1458            set axis [lindex $args 0]
1459            set type [lindex $args 1]
1460
1461            if {$type == "log"} {
1462                catch {$itk_component(plot) axis configure $axis -logscale 1}
1463                # leave format alone in log mode
1464                $itk_component(plot) axis configure $axis -command ""
1465            } else {
1466                catch {$itk_component(plot) axis configure $axis -logscale 0}
1467                # use special formatting for linear mode
1468                $itk_component(plot) axis configure $axis -command \
1469                    [itcl::code $this _axis format $axis]
1470            }
1471        }
1472        default {
1473            error "bad option \"$option\": should be changed, edit, hilite, or format"
1474        }
1475    }
1476}
1477
1478
1479# ----------------------------------------------------------------------
1480# USAGE: _getLineMarkerOptions <style>
1481#
1482# Used internally to create a list of configuration options specific to the
1483# axis line marker.  The input is a list of name value pairs.  Options that
1484# are not recognized are ignored.
1485# ----------------------------------------------------------------------
1486itcl::body Rappture::BarResult::_getLineMarkerOptions {style} {
1487    array set lineOptions {
1488        "-color"  "-color"
1489        "-dashes" "-dashes"
1490        "-linecolor" "-foreground"
1491        "-linewidth" "-borderwidth"
1492    }
1493    set options {}
1494    foreach {name value} $style {
1495        if { [info exists lineOptions($name)] } {
1496            lappend options $lineOptions($name) $value
1497        }
1498    }
1499    return $options
1500}
1501
1502# ----------------------------------------------------------------------
1503# USAGE: _getTextMarkerOptions <style>
1504#
1505# Used internally to create a list of configuration options specific to the
1506# axis text marker.  The input is a list of name value pairs.  Options that
1507# are not recognized are ignored.
1508# ----------------------------------------------------------------------
1509itcl::body Rappture::BarResult::_getTextMarkerOptions {style} {
1510    array set textOptions {
1511        "-color"  "-outline"
1512        "-textcolor"  "-outline"
1513        "-font"   "-font"
1514        "-xoffset" "-xoffset"
1515        "-yoffset" "-yoffset"
1516        "-anchor" "-anchor"
1517        "-rotate" "-rotate"
1518    }
1519    set options {}
1520    foreach {name value} $style {
1521        if { [info exists textOptions($name)] } {
1522            lappend options $textOptions($name) $value
1523        }
1524    }
1525    return $options
1526}
1527
1528# ----------------------------------------------------------------------
1529# USAGE: _getAxes <dataobj>
1530#
1531# Used internally to figure out the axes used to plot the given
1532# <dataobj>.  Returns a list of the form {x y}, where x is the
1533# x-axis name (x, x2, x3, etc.), and y is the y-axis name.
1534# ----------------------------------------------------------------------
1535itcl::body Rappture::BarResult::_getAxes {dataobj} {
1536    # rebuild if needed, so we know about the axes
1537    if {[$_dispatcher ispending !rebuild]} {
1538        $_dispatcher cancel !rebuild
1539        $_dispatcher event -now !rebuild
1540    }
1541
1542    # what is the x axis?  x? x2? x3? ...
1543    set xlabel [$dataobj hints xlabel]
1544    if {[info exists _label2axis(x-$xlabel)]} {
1545        set mapx $_label2axis(x-$xlabel)
1546    } else {
1547        set mapx "x"
1548    }
1549
1550    # what is the y axis?  y? y2? y3? ...
1551    set ylabel [$dataobj hints ylabel]
1552    if {[info exists _label2axis(y-$ylabel)]} {
1553        set mapy $_label2axis(y-$ylabel)
1554    } else {
1555        set mapy "y"
1556    }
1557
1558    return [list $mapx $mapy]
1559}
1560
1561# ----------------------------------------------------------------------
1562# CONFIGURATION OPTION: -gridcolor
1563# ----------------------------------------------------------------------
1564itcl::configbody Rappture::BarResult::gridcolor {
1565    if {"" == $itk_option(-gridcolor)} {
1566        $itk_component(plot) grid off
1567    } else {
1568        $itk_component(plot) grid configure -color $itk_option(-gridcolor)
1569        $itk_component(plot) grid on
1570    }
1571}
1572
1573# ----------------------------------------------------------------------
1574# CONFIGURATION OPTION: -autocolors
1575# ----------------------------------------------------------------------
1576itcl::configbody Rappture::BarResult::autocolors {
1577    foreach c $itk_option(-autocolors) {
1578        if {[catch {winfo rgb $itk_component(hull) $c}]} {
1579            error "bad color \"$c\""
1580        }
1581    }
1582    if {$_autoColorI >= [llength $itk_option(-autocolors)]} {
1583        set _autoColorI 0
1584    }
1585}
1586
1587itcl::body Rappture::BarResult::_enterMarker { g name x y text } {
1588    _leaveMarker $g $name
1589    set id [$g marker create text \
1590                -coords [list $x $y] \
1591                -yoffset -1 \
1592                -anchor s \
1593                -text $text]
1594    set _markers($name) $id
1595}
1596
1597itcl::body Rappture::BarResult::_leaveMarker { g name } {
1598    if { [info exists _markers($name)] } {
1599        set id $_markers($name)
1600        $g marker delete $id
1601        unset _markers($name)
1602    }
1603}
1604
1605itcl::body Rappture::BarResult::_formatTickLabel { w value } {
1606    # Determine the element name from the value
1607
1608    set index [expr round($value)]
1609    if { $index != $value } {
1610        return $value
1611    }
1612    return [lindex  $_tickLabels [expr $index - 1]]
1613    if { $label == "" } {
1614        return $value
1615    }
1616    return $label
1617}
Note: See TracBrowser for help on using the repository browser.