source: trunk/gui/scripts/barchartresult.tcl @ 4642

Last change on this file since 4642 was 3813, checked in by ldelgass, 11 years ago

Fix bug in 'add' method of viewer widgets: list search for existing dataobj
entry was wrong (list and pattern transposed), causing potential duplicate
entries in dataobj list.

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.