source: branches/1.3/gui/scripts/barchartresult.tcl @ 4557

Last change on this file since 4557 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

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