summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--library/graph.tcl842
-rw-r--r--src/bltChain.C518
-rw-r--r--src/bltChain.h90
-rw-r--r--src/bltConfig.C2493
-rw-r--r--src/bltConfig.h406
-rw-r--r--src/bltGrAxis.C5765
-rw-r--r--src/bltGrAxis.h332
-rw-r--r--src/bltGrBind.C1825
-rw-r--r--src/bltGrBind.h120
-rw-r--r--src/bltGrElemBar.C2543
-rw-r--r--src/bltGrElemLine.C5347
-rw-r--r--src/bltGrElemLineSpline.C1399
-rw-r--r--src/bltGrElemOp.C2456
-rw-r--r--src/bltGrElemOp.h271
-rw-r--r--src/bltGrHairs.C534
-rw-r--r--src/bltGrLegd.C3047
-rw-r--r--src/bltGrLegd.h60
-rw-r--r--src/bltGrMarker.C5196
-rw-r--r--src/bltGrMisc.C2024
-rw-r--r--src/bltGrPSOutput.C1697
-rw-r--r--src/bltGrPSOutput.h215
-rw-r--r--src/bltGrPenOp.C765
-rw-r--r--src/bltGrText.C2018
-rw-r--r--src/bltGrText.h265
-rw-r--r--src/bltGraph.C2381
-rw-r--r--src/bltGraph.h699
-rw-r--r--src/bltInt.C375
-rw-r--r--src/bltNsUtil.C174
-rw-r--r--src/bltNsUtil.h101
-rw-r--r--src/bltOp.h37
-rw-r--r--src/bltParse.C546
-rw-r--r--src/bltParse.h43
-rw-r--r--src/bltSwitch.C554
-rw-r--r--src/bltSwitch.h136
-rw-r--r--src/bltVecCmd.C2414
-rw-r--r--src/bltVecInt.h251
-rw-r--r--src/bltVecMath.C1895
-rw-r--r--src/bltVector.C2808
-rw-r--r--src/bltVector.h137
39 files changed, 52779 insertions, 0 deletions
diff --git a/library/graph.tcl b/library/graph.tcl
new file mode 100644
index 0000000..8c4fd8d
--- /dev/null
+++ b/library/graph.tcl
@@ -0,0 +1,842 @@
+
+namespace eval ::blt::legend {
+ variable _private
+ array set _private {
+ afterId ""
+ scroll 0
+ space off
+ drag 0
+ x 0
+ y 0
+ }
+}
+
+namespace eval ::blt::ZoomStack {
+ variable _private
+ array set _private {
+ afterId ""
+ scroll 0
+ space off
+ drag 0
+ x 0
+ y 0
+ }
+}
+
+option add *zoomOutline.dashes 4
+option add *zoomOutline.lineWidth 2
+option add *zoomOutline.xor yes
+option add *zoomTitle.anchor nw
+option add *zoomTitle.coords "-Inf Inf"
+option add *zoomTitle.font "Arial 14"
+option add *zoomTitle.foreground yellow3
+option add *zoomTitle.shadow yellow4
+
+# ----------------------------------------------------------------------
+#
+# Initialize --
+#
+# Invoked by internally by Treeview_Init routine. Initializes
+# the default bindings for the treeview widget entries. These
+# are local to the widget, so they can't be set through the
+# widget's class bind tags.
+#
+# ----------------------------------------------------------------------
+proc blt::LegendSelections { w } {
+ if 0 {
+ #
+ # Active entry bindings
+ #
+ $w legend bind all <Enter> {
+ %W entry highlight current
+ }
+ $w legend bind all <Leave> {
+ %W entry highlight ""
+ }
+ }
+
+ #
+ # ButtonPress-1
+ #
+ # Performs the following operations:
+ #
+ # 1. Clears the previous selection.
+ # 2. Selects the current entry.
+ # 3. Sets the focus to this entry.
+ # 4. Scrolls the entry into view.
+ # 5. Sets the selection anchor to this entry, just in case
+ # this is "multiple" mode.
+ #
+
+ $w legend bind all <ButtonPress-1> {
+ blt::legend::SetSelectionAnchor %W current
+ set blt::legend::_private(scroll) 1
+ }
+
+ #
+ # B1-Motion
+ #
+ # For "multiple" mode only. Saves the current location of the
+ # pointer for auto-scrolling. Resets the selection mark.
+ #
+ $w legend bind all <B1-Motion> {
+ set blt::legend::_private(x) %x
+ set blt::legend::_private(y) %y
+ set elem [%W legend get @%x,%y]
+ if { $elem != "" } {
+ if { [%W legend cget -selectmode] == "multiple" } {
+ %W legend selection mark $elem
+ } else {
+ blt::legend::SetSelectionAnchor %W $elem
+ }
+ }
+ }
+
+ #
+ # ButtonRelease-1
+ #
+ # For "multiple" mode only.
+ #
+ $w legend bind all <ButtonRelease-1> {
+ if { [%W legend cget -selectmode] == "multiple" } {
+ %W legend selection anchor current
+ }
+ after cancel $blt::legend::_private(afterId)
+ set blt::legend::_private(scroll) 0
+ }
+
+ #
+ # Shift-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+
+ $w legend bind all <Shift-ButtonPress-1> {
+ if { [%W legend cget -selectmode] == "multiple" &&
+ [%W legend selection present] } {
+ if { [%W legend get anchor] == "" } {
+ %W legend selection anchor current
+ }
+ set elem [%W legend get anchor]
+ %W legend selection clearall
+ %W legend selection set $elem current
+ } else {
+ blt::legend::SetSelectionAnchor %W current
+ }
+ }
+ $w legend bind all <Shift-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w legend bind all <Shift-B1-Motion> {
+ # do nothing
+ }
+ $w legend bind all <Shift-ButtonRelease-1> {
+ after cancel $blt::legend::_private(afterId)
+ set blt::legend::_private(scroll) 0
+ }
+
+ #
+ # Control-ButtonPress-1
+ #
+ # For "multiple" mode only.
+ #
+ $w legend bind all <Control-ButtonPress-1> {
+ if { [%W legend cget -selectmode] == "multiple" } {
+ set elem [%W legend get current]
+ %W legend selection toggle $elem
+ %W legend selection anchor $elem
+ } else {
+ blt::legend::SetSelectionAnchor %W current
+ }
+ }
+ $w legend bind all <Control-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w legend bind all <Control-B1-Motion> {
+ # do nothing
+ }
+ $w legend bind all <Control-ButtonRelease-1> {
+ after cancel $blt::legend::_private(afterId)
+ set blt::legend::_private(scroll) 0
+ }
+
+ $w legend bind all <Control-Shift-ButtonPress-1> {
+ if { [%W legend cget -selectmode] == "multiple" &&
+ [%W legend selection present] } {
+ if { [%W legend get anchor] == "" } {
+ %W selection anchor current
+ }
+ if { [%W legend selection includes anchor] } {
+ %W legend selection set anchor current
+ } else {
+ %W legend selection clear anchor current
+ %W legend selection set current
+ }
+ } else {
+ blt::legend::SetSelectionAnchor %W current
+ }
+ }
+ $w legend bind all <Control-Shift-Double-ButtonPress-1> {
+ # do nothing
+ }
+ $w legend bind all <Control-Shift-B1-Motion> {
+ # do nothing
+ }
+ $w legend bind all <KeyPress-Up> {
+ blt::legend::MoveFocus %W previous.row
+ if { $blt::legend::_private(space) } {
+ %W legend selection toggle focus
+ }
+ }
+ $w legend bind all <KeyPress-Down> {
+ blt::legend::MoveFocus %W next.row
+ if { $blt::legend::_private(space) } {
+ %W legend selection toggle focus
+ }
+ }
+ $w legend bind all <KeyPress-Left> {
+ blt::legend::MoveFocus %W previous.column
+ if { $blt::legend::_private(space) } {
+ %W legend selection toggle focus
+ }
+ }
+ $w legend bind all <KeyPress-Right> {
+ blt::legend::MoveFocus %W next.column
+ if { $blt::legend::_private(space) } {
+ %W legend selection toggle focus
+ }
+ }
+ $w legend bind all <KeyPress-space> {
+ if { [%W legend cget -selectmode] == "single" } {
+ if { [%W legend selection includes focus] } {
+ %W legend selection clearall
+ } else {
+ %W legend selection clearall
+ %W legend selection set focus
+ }
+ } else {
+ %W legend selection toggle focus
+ }
+ set blt::legend::_private(space) on
+ }
+
+ $w legend bind all <KeyRelease-space> {
+ set blt::legend::_private(space) off
+ }
+ $w legend bind all <KeyPress-Return> {
+ blt::legend::MoveFocus %W focus
+ set blt::legend::_private(space) on
+ }
+ $w legend bind all <KeyRelease-Return> {
+ set blt::legend::_private(space) off
+ }
+ $w legend bind all <KeyPress-Home> {
+ blt::legend::MoveFocus %W first
+ }
+ $w legend bind all <KeyPress-End> {
+ blt::tv::MoveFocus %W last
+ }
+}
+
+proc blt::legend::SetSelectionAnchor { w tagOrId } {
+ set elem [$w legend get $tagOrId]
+ # If the anchor hasn't changed, don't do anything
+ if { $elem != [$w legend get anchor] } {
+ $w legend selection clearall
+ $w legend focus $elem
+ $w legend selection set $elem
+ $w legend selection anchor $elem
+ }
+}
+
+# ----------------------------------------------------------------------
+#
+# MoveFocus --
+#
+# Invoked by KeyPress bindings. Moves the active selection to
+# the entry <where>, which is an index such as "up", "down",
+# "prevsibling", "nextsibling", etc.
+#
+# ----------------------------------------------------------------------
+proc blt::legend::MoveFocus { w elem } {
+ catch {$w legend focus $elem} result
+ puts stderr "result=$result elem=$elem"
+ if { [$w legend cget -selectmode] == "single" } {
+ $w legend selection clearall
+ $w legend selection set focus
+ $w legend selection anchor focus
+ }
+}
+
+
+proc Blt_ActiveLegend { g } {
+ $g legend bind all <Enter> [list blt::ActivateLegend $g ]
+ $g legend bind all <Leave> [list blt::DeactivateLegend $g]
+ $g legend bind all <ButtonPress-1> [list blt::HighlightLegend $g]
+}
+
+proc Blt_Crosshairs { g } {
+ blt::Crosshairs $g
+}
+
+proc Blt_ResetCrosshairs { g state } {
+ blt::Crosshairs $g "Any-Motion" $state
+}
+
+proc Blt_ZoomStack { g args } {
+ array set params {
+ -mode click
+ }
+ array set params $args
+ if { $params(-mode) == "click" } {
+ blt::ZoomStack::ClickClick $g
+ } else {
+ blt::ZoomStack::ClickRelease $g
+ }
+}
+
+proc Blt_PrintKey { g } {
+ blt::PrintKey $g
+}
+
+proc Blt_ClosestPoint { g } {
+ blt::ClosestPoint $g
+}
+
+#
+# The following procedures that reside in the "blt" namespace are
+# supposed to be private.
+#
+
+proc blt::ActivateLegend { g } {
+ set elem [$g legend get current]
+ $g legend activate $elem
+}
+proc blt::DeactivateLegend { g } {
+ set elem [$g legend get current]
+ $g legend deactivate $elem
+}
+
+proc blt::HighlightLegend { g } {
+ set elem [$g legend get current]
+ if { $elem != "" } {
+ set relief [$g element cget $elem -legendrelief]
+ if { $relief == "flat" } {
+ $g element configure $elem -legendrelief raised
+ $g element activate $elem
+ } else {
+ $g element configure $elem -legendrelief flat
+ $g element deactivate $elem
+ }
+ }
+}
+
+proc blt::Crosshairs { g {event "Any-Motion"} {state "on"}} {
+ $g crosshairs $state
+ bind crosshairs-$g <$event> {
+ %W crosshairs configure -position @%x,%y
+ }
+ bind crosshairs-$g <Leave> {
+ %W crosshairs off
+ }
+ bind crosshairs-$g <Enter> {
+ %W crosshairs on
+ }
+ $g crosshairs configure -color red
+ if { $state == "on" } {
+ blt::AddBindTag $g crosshairs-$g
+ } elseif { $state == "off" } {
+ blt::RemoveBindTag $g crosshairs-$g
+ }
+}
+
+proc blt::PrintKey { g {event "Shift-ButtonRelease-3"} } {
+ bind print-$g <$event> { Blt_PostScriptDialog %W }
+ blt::AddBindTag $g print-$g
+}
+
+proc blt::ClosestPoint { g {event "Control-ButtonPress-2"} } {
+ bind closest-point-$g <$event> {
+ blt::FindElement %W %x %y
+ }
+ blt::AddBindTag $g closest-point-$g
+}
+
+proc blt::AddBindTag { widget tag } {
+ set oldTagList [bindtags $widget]
+ if { [lsearch $oldTagList $tag] < 0 } {
+ bindtags $widget [linsert $oldTagList 0 $tag]
+ }
+}
+
+proc blt::RemoveBindTag { widget tag } {
+ set oldTagList [bindtags $widget]
+ set index [lsearch $oldTagList $tag]
+ if { $index >= 0 } {
+ bindtags $widget [lreplace $oldTagList $index $index]
+ }
+}
+
+proc blt::FindElement { g x y } {
+ array set info [$g element closest $x $y -interpolate yes]
+ if { ![info exists info(name)] } {
+ beep
+ return
+ }
+ # --------------------------------------------------------------
+ # find(name) - element Id
+ # find(index) - index of closest point
+ # find(x) find(y) - coordinates of closest point
+ # or closest point on line segment.
+ # find(dist) - distance from sample coordinate
+ # --------------------------------------------------------------
+ set markerName "bltClosest_$info(name)"
+ catch { $g marker delete $markerName }
+ $g marker create text -coords { $info(x) $info(y) } \
+ -name $markerName \
+ -text "$info(name): $info(dist)\nindex $info(index)" \
+ -font "Arial 6" \
+ -anchor center -justify left \
+ -yoffset 0 -bg {}
+
+ set coords [$g invtransform $x $y]
+ set nx [lindex $coords 0]
+ set ny [lindex $coords 1]
+
+ $g marker create line -coords "$nx $ny $info(x) $info(y)" \
+ -name line.$markerName
+
+ blt::FlashPoint $g $info(name) $info(index) 10
+ blt::FlashPoint $g $info(name) [expr $info(index) + 1] 10
+}
+
+proc blt::FlashPoint { g name index count } {
+ if { $count & 1 } {
+ $g element deactivate $name
+ } else {
+ $g element activate $name $index
+ }
+ incr count -1
+ if { $count > 0 } {
+ after 200 blt::FlashPoint $g $name $index $count
+ update
+ } else {
+ eval $g marker delete [$g marker names "bltClosest_*"]
+ }
+}
+
+
+proc blt::ZoomStack::Init { g } {
+ variable _private
+ set _private($g,interval) 100
+ set _private($g,afterId) 0
+ set _private($g,A,x) {}
+ set _private($g,A,y) {}
+ set _private($g,B,x) {}
+ set _private($g,B,y) {}
+ set _private($g,stack) {}
+ set _private($g,corner) A
+}
+
+proc blt::ZoomStack::ClickClick { g {start "ButtonPress-1"} {reset "ButtonPress-3"} } {
+ variable _private
+
+ Init $g
+
+ bind zoom-$g <Enter> "focus %W"
+ bind zoom-$g <KeyPress-Escape> { blt::ZoomStack::Reset %W }
+ bind zoom-$g <${start}> { blt::ZoomStack::SetPoint %W %x %y }
+ bind zoom-$g <${reset}> {
+ if { [%W inside %x %y] } {
+ blt::ZoomStack::Reset %W
+ }
+ }
+ blt::AddBindTag $g zoom-$g
+}
+
+proc blt::ZoomStack::ClickRelease { g } {
+ variable _private
+
+ Init $g
+ bind zoom-$g <Enter> "focus %W"
+ bind zoom-$g <KeyPress-Escape> { blt::ZoomStack::Reset %W }
+ bind zoom-$g <ButtonPress-1> { blt::ZoomStack::DragStart %W %x %y }
+ bind zoom-$g <B1-Motion> { blt::ZoomStack::DragMotion %W %x %y }
+ bind zoom-$g <ButtonRelease-1> { blt::ZoomStack::DragFinish %W %x %y }
+ bind zoom-$g <ButtonPress-3> {
+ if { [%W inside %x %y] } {
+ blt::ZoomStack::Reset %W
+ }
+ }
+ blt::AddBindTag $g zoom-$g
+}
+
+proc blt::ZoomStack::GetCoords { g x y index } {
+ variable _private
+ if { [$g cget -invertxy] } {
+ set _private($g,$index,x) $y
+ set _private($g,$index,y) $x
+ } else {
+ set _private($g,$index,x) $x
+ set _private($g,$index,y) $y
+ }
+}
+
+proc blt::ZoomStack::MarkPoint { g index } {
+ variable _private
+
+ if { [llength [$g xaxis use]] > 0 } {
+ set x [$g xaxis invtransform $_private($g,$index,x)]
+ } else if { [llength [$g x2axis use]] > 0 } {
+ set x [$g x2axis invtransform $_private($g,$index,x)]
+ }
+ if { [llength [$g yaxis use]] > 0 } {
+ set y [$g yaxis invtransform $_private($g,$index,y)]
+ } else if { [llength [$g y2axis use]] > 0 } {
+ set y [$g y2axis invtransform $_private($g,$index,y)]
+ }
+ set marker "zoomText_$index"
+ set text [format "x=%.4g\ny=%.4g" $x $y]
+
+ if [$g marker exists $marker] {
+ $g marker configure $marker -coords { $x $y } -text $text
+ } else {
+ $g marker create text -coords { $x $y } -name $marker \
+ -font "mathmatica1 10" \
+ -text $text -anchor center -bg {} -justify left
+ }
+}
+
+proc blt::ZoomStack::DestroyTitle { g } {
+ variable _private
+
+ if { $_private($g,corner) == "A" } {
+ catch { $g marker delete "zoomTitle" }
+ }
+}
+
+proc blt::ZoomStack::Pop { g } {
+ variable _private
+
+ set zoomStack $_private($g,stack)
+ if { [llength $zoomStack] > 0 } {
+ set cmd [lindex $zoomStack 0]
+ set _private($g,stack) [lrange $zoomStack 1 end]
+ eval $cmd
+ TitleLast $g
+ blt::busy hold $g
+ update
+ blt::busy release $g
+ after 2000 [list blt::ZoomStack::DestroyTitle $g]
+ } else {
+ catch { $g marker delete "zoomTitle" }
+ }
+}
+
+# Push the old axis limits on the stack and set the new ones
+
+proc blt::ZoomStack::Push { g } {
+ variable _private
+
+ eval $g marker delete [$g marker names "zoom*"]
+ if { [info exists _private($g,afterId)] } {
+ after cancel $_private($g,afterId)
+ }
+ set x1 $_private($g,A,x)
+ set y1 $_private($g,A,y)
+ set x2 $_private($g,B,x)
+ set y2 $_private($g,B,y)
+
+ if { ($x1 == $x2) || ($y1 == $y2) } {
+ # No delta, revert to start
+ return
+ }
+ set cmd {}
+ foreach axis [$g axis names] {
+ if { [$g axis cget $axis -hide] } {
+ continue
+ }
+ set min [$g axis cget $axis -min]
+ set max [$g axis cget $axis -max]
+ set logscale [$g axis cget $axis -logscale]
+ # Save the current scale (log or linear) so that we can restore it.
+ # This is for the case where the user changes to logscale while
+ # zooming. A previously pushed axis limit could be negative. It
+ # seems better for popping the zoom stack to restore a previous view
+ # (not convert the ranges).
+ set c [list $g axis configure $axis]
+ lappend c -min $min -max $max -logscale $logscale
+ append cmd "$c\n"
+ }
+
+ # This effectively pushes the command to reset the graph to the current
+ # zoom level onto the stack. This is useful if the new axis ranges are
+ # bad and we need to reset the zoom stack.
+ set _private($g,stack) [linsert $_private($g,stack) 0 $cmd]
+ foreach axis [$g axis names] {
+ if { [$g axis cget $axis -hide] } {
+ continue; # Don't set zoom on axes not displayed.
+ }
+ set type [$g axis type $axis]
+ if { $type == "x" } {
+ set min [$g axis invtransform $axis $x1]
+ set max [$g axis invtransform $axis $x2]
+ } elseif { $type == "y" } {
+ set min [$g axis invtransform $axis $y1]
+ set max [$g axis invtransform $axis $y2]
+ } else {
+ continue; # Axis is not bound to any margin.
+ }
+ if { ![SetAxisRanges $g $axis $min $max] } {
+ Pop $g
+ bell
+ return
+ }
+ }
+ blt::busy hold $g
+ update; # This "update" redraws the graph
+ blt::busy release $g
+}
+
+proc blt::ZoomStack::SetAxisRanges { g axis min max } {
+ if { $min > $max } {
+ set tmp $max; set max $min; set min $tmp
+ }
+ if { [catch { $g axis configure $axis -min $min -max $max }] != 0 } {
+ return 0
+ }
+ return 1
+}
+
+#
+# This routine terminates either an existing zoom, or pops back to
+# the previous zoom level (if no zoom is in progress).
+#
+proc blt::ZoomStack::Reset { g } {
+ variable _private
+
+ if { ![info exists _private($g,corner)] } {
+ Init $g
+ }
+ eval $g marker delete [$g marker names "zoom*"]
+
+ if { $_private($g,corner) == "A" } {
+ # Reset the whole axis
+ Pop $g
+ } else {
+ set _private($g,corner) A
+ blt::RemoveBindTag $g select-region-$g
+ }
+}
+
+proc blt::ZoomStack::TitleNext { g } {
+ variable _private
+
+ set level [expr [llength $_private($g,stack)] + 1]
+ if { [$g cget -invertxy] } {
+ set coords "Inf -Inf"
+ } else {
+ set coords "-Inf Inf"
+ }
+ $g marker create text -name "zoomTitle" -text "Zoom #$level" \
+ -coords $coords -bindtags "" -anchor nw
+}
+
+proc blt::ZoomStack::TitleLast { g } {
+ variable _private
+
+ set level [llength $_private($g,stack)]
+ if { $level > 0 } {
+ $g marker create text -name "zoomTitle" -anchor nw \
+ -text "Zoom #$level"
+ }
+}
+
+
+proc blt::ZoomStack::SetPoint { g x y } {
+ variable _private
+ if { ![info exists _private($g,corner)] } {
+ Init $g
+ }
+ GetCoords $g $x $y $_private($g,corner)
+ bind select-region-$g <Motion> {
+ blt::ZoomStack::GetCoords %W %x %y B
+ #blt::ZoomStack::MarkPoint $g B
+ blt::ZoomStack::Box %W
+ }
+ if { $_private($g,corner) == "A" } {
+ if { ![$g inside $x $y] } {
+ return
+ }
+ # First corner selected, start watching motion events
+
+ #MarkPoint $g A
+ TitleNext $g
+
+ blt::AddBindTag $g select-region-$g
+ set _private($g,corner) B
+ } else {
+ # Delete the modal binding
+ blt::RemoveBindTag $g select-region-$g
+ Push $g
+ set _private($g,corner) A
+ }
+}
+
+proc blt::ZoomStack::DragStart { g x y } {
+ variable _private
+ if { ![info exists _private($g,corner)] } {
+ Init $g
+ }
+ GetCoords $g $x $y A
+ if { ![$g inside $x $y] } {
+ return
+ }
+ set _private(drag) 1
+ TitleNext $g
+}
+
+proc blt::ZoomStack::DragMotion { g x y } {
+ variable _private
+
+ if { $_private(drag) } {
+ GetCoords $g $x $y B
+ set dx [expr abs($_private($g,B,x) - $_private($g,A,x))]
+ set dy [expr abs($_private($g,B,y) - $_private($g,A,y))]
+ Box $g
+ if { $dy > 10 && $dx > 10 } {
+ return 1
+ }
+ }
+ return 0
+}
+
+proc blt::ZoomStack::DragFinish { g x y } {
+ variable _private
+ if { [DragMotion $g $x $y] } {
+ Push $g
+ } else {
+ eval $g marker delete [$g marker names "zoom*"]
+ if { [info exists _private($g,afterId)] } {
+ after cancel $_private($g,afterId)
+ }
+ }
+ set _private(drag) 0
+}
+
+
+proc blt::ZoomStack::MarchingAnts { g offset } {
+ variable _private
+
+ incr offset
+ # wrap the counter after 2^16
+ set offset [expr $offset & 0xFFFF]
+ if { [$g marker exists zoomOutline] } {
+ $g marker configure zoomOutline -dashoffset $offset
+ set interval $_private($g,interval)
+ set id [after $interval [list blt::ZoomStack::MarchingAnts $g $offset]]
+ set _private($g,afterId) $id
+ }
+}
+
+proc blt::ZoomStack::Box { g } {
+ variable _private
+
+ if { $_private($g,A,x) > $_private($g,B,x) } {
+ set x1 [$g xaxis invtransform $_private($g,B,x)]
+ set y1 [$g yaxis invtransform $_private($g,B,y)]
+ set x2 [$g xaxis invtransform $_private($g,A,x)]
+ set y2 [$g yaxis invtransform $_private($g,A,y)]
+ } else {
+ set x1 [$g xaxis invtransform $_private($g,A,x)]
+ set y1 [$g yaxis invtransform $_private($g,A,y)]
+ set x2 [$g xaxis invtransform $_private($g,B,x)]
+ set y2 [$g yaxis invtransform $_private($g,B,y)]
+ }
+ set coords { $x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1 }
+ if { [$g marker exists "zoomOutline"] } {
+ $g marker configure "zoomOutline" -coords $coords
+ } else {
+ set X [lindex [$g xaxis use] 0]
+ set Y [lindex [$g yaxis use] 0]
+ $g marker create line -coords $coords -name "zoomOutline" \
+ -mapx $X -mapy $Y
+ set interval $_private($g,interval)
+ set id [after $interval [list blt::ZoomStack::MarchingAnts $g 0]]
+ set _private($g,afterId) $id
+ }
+}
+
+
+proc Blt_PostScriptDialog { g } {
+ set top $g.top
+ toplevel $top
+
+ foreach var { center landscape maxpect preview decorations padx
+ pady paperwidth paperheight width height colormode } {
+ global $g.$var
+ set $g.$var [$g postscript cget -$var]
+ }
+ set row 1
+ set col 0
+ label $top.title -text "PostScript Options"
+ blt::table $top $top.title -cspan 7
+ foreach bool { center landscape maxpect preview decorations } {
+ set w $top.$bool-label
+ label $w -text "-$bool" -font "courier 12"
+ blt::table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 }
+ set w $top.$bool-yes
+ global $g.$bool
+ radiobutton $w -text "yes" -variable $g.$bool -value 1
+ blt::table $top $row,$col+1 $w -anchor w
+ set w $top.$bool-no
+ radiobutton $w -text "no" -variable $g.$bool -value 0
+ blt::table $top $row,$col+2 $w -anchor w
+ incr row
+ }
+ label $top.modes -text "-colormode" -font "courier 12"
+ blt::table $top $row,0 $top.modes -anchor e -pady { 2 0 } -padx { 0 4 }
+ set col 1
+ foreach m { color greyscale } {
+ set w $top.$m
+ radiobutton $w -text $m -variable $g.colormode -value $m
+ blt::table $top $row,$col $w -anchor w
+ incr col
+ }
+ set row 1
+ frame $top.sep -width 2 -bd 1 -relief sunken
+ blt::table $top $row,3 $top.sep -fill y -rspan 6
+ set col 4
+ foreach value { padx pady paperwidth paperheight width height } {
+ set w $top.$value-label
+ label $w -text "-$value" -font "courier 12"
+ blt::table $top $row,$col $w -anchor e -pady { 2 0 } -padx { 0 4 }
+ set w $top.$value-entry
+ global $g.$value
+ entry $w -textvariable $g.$value -width 8
+ blt::table $top $row,$col+1 $w -cspan 2 -anchor w -padx 8
+ incr row
+ }
+ blt::table configure $top c3 -width .125i
+ button $top.cancel -text "Cancel" -command "destroy $top"
+ blt::table $top $row,0 $top.cancel -width 1i -pady 2 -cspan 3
+ button $top.reset -text "Reset" -command "destroy $top"
+ #blt::table $top $row,1 $top.reset -width 1i
+ button $top.print -text "Print" -command "blt::ResetPostScript $g"
+ blt::table $top $row,4 $top.print -width 1i -pady 2 -cspan 2
+}
+
+proc blt::ResetPostScript { g } {
+ foreach var { center landscape maxpect preview decorations padx
+ pady paperwidth paperheight width height colormode } {
+ global $g.$var
+ set old [$g postscript cget -$var]
+ if { [catch {$g postscript configure -$var [set $g.$var]}] != 0 } {
+ $g postscript configure -$var $old
+ set $g.$var $old
+ }
+ }
+ $g postscript output "out.ps"
+ puts stdout "wrote file \"out.ps\"."
+ flush stdout
+}
diff --git a/src/bltChain.C b/src/bltChain.C
new file mode 100644
index 0000000..b6051b8
--- /dev/null
+++ b/src/bltChain.C
@@ -0,0 +1,518 @@
+
+/*
+ * bltChain.c --
+ *
+ * The module implements a generic linked list package.
+ *
+ * Copyright 1991-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include "bltChain.h"
+
+#ifndef ALIGN
+#define ALIGN(a) \
+ (((size_t)a + (sizeof(double) - 1)) & (~(sizeof(double) - 1)))
+#endif /* ALIGN */
+
+typedef struct _Blt_ChainLink ChainLink;
+typedef struct _Blt_Chain Chain;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Create --
+ *
+ * Creates a new linked list (chain) structure and initializes its
+ * pointers;
+ *
+ * Results:
+ * Returns a pointer to the newly created chain structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_Chain
+Blt_Chain_Create(void)
+{
+ Chain *chainPtr;
+
+ chainPtr = Blt_Malloc(sizeof(Chain));
+ if (chainPtr != NULL) {
+ Blt_Chain_Init(chainPtr);
+ }
+ return chainPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_AllocLink --
+ *
+ * Creates a new chain link. Unlink Blt_Chain_NewLink, this routine also
+ * allocates extra memory in the node for data.
+ *
+ * Results:
+ * The return value is the pointer to the newly created entry.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_ChainLink
+Blt_Chain_AllocLink(size_t extraSize)
+{
+ ChainLink *linkPtr;
+ size_t linkSize;
+
+ linkSize = ALIGN(sizeof(ChainLink));
+ linkPtr = Blt_AssertCalloc(1, linkSize + extraSize);
+ if (extraSize > 0) {
+ /* Point clientData at the memory beyond the normal structure. */
+ linkPtr->clientData = (ClientData)((char *)linkPtr + linkSize);
+ }
+ return linkPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_InitLink --
+ *
+ * Initializes the new link. This routine is for applications that use
+ * their own memory allocation procedures to allocate links.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_InitLink(ChainLink *linkPtr)
+{
+ linkPtr->clientData = NULL;
+ linkPtr->next = linkPtr->prev = NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_NewLink --
+ *
+ * Creates a new link.
+ *
+ * Results:
+ * The return value is the pointer to the newly created link.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_ChainLink
+Blt_Chain_NewLink(void)
+{
+ ChainLink *linkPtr;
+
+ linkPtr = Blt_AssertMalloc(sizeof(ChainLink));
+ linkPtr->clientData = NULL;
+ linkPtr->next = linkPtr->prev = NULL;
+ return linkPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Reset --
+ *
+ * Removes all the links in the chain, freeing the memory used for each
+ * link. Memory pointed to by the link (clientData) is not freed. It's
+ * the caller's responsibility to deallocate it.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_Reset(Chain *chainPtr) /* Chain to clear */
+{
+ if (chainPtr != NULL) {
+ ChainLink *oldPtr;
+ ChainLink *linkPtr = chainPtr->head;
+
+ while (linkPtr != NULL) {
+ oldPtr = linkPtr;
+ linkPtr = linkPtr->next;
+ Blt_Free(oldPtr);
+ }
+ Blt_Chain_Init(chainPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Destroy
+ *
+ * Frees all the nodes in the chain and deallocates the memory used for
+ * the chain structure itself. It's assumed that the chain was previously
+ * allocated by Blt_Chain_Create.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_Destroy(Chain *chainPtr)
+{
+ if (chainPtr != NULL) {
+ Blt_Chain_Reset(chainPtr);
+ Blt_Free(chainPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Init --
+ *
+ * Initializes a linked list.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_Init(Chain *chainPtr)
+{
+ chainPtr->nLinks = 0;
+ chainPtr->head = chainPtr->tail = NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_LinkAfter --
+ *
+ * Inserts a link after another link. If afterPtr is NULL, then the new
+ * link is prepended to the beginning of the chain.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_LinkAfter(Chain *chainPtr, ChainLink *linkPtr, ChainLink *afterPtr)
+{
+ if (chainPtr->head == NULL) {
+ chainPtr->tail = chainPtr->head = linkPtr;
+ } else {
+ if (afterPtr == NULL) {
+ /* Append to the end of the chain. */
+ linkPtr->next = NULL;
+ linkPtr->prev = chainPtr->tail;
+ chainPtr->tail->next = linkPtr;
+ chainPtr->tail = linkPtr;
+ } else {
+ linkPtr->next = afterPtr->next;
+ linkPtr->prev = afterPtr;
+ if (afterPtr == chainPtr->tail) {
+ chainPtr->tail = linkPtr;
+ } else {
+ afterPtr->next->prev = linkPtr;
+ }
+ afterPtr->next = linkPtr;
+ }
+ }
+ chainPtr->nLinks++;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_LinkBefore --
+ *
+ * Inserts a new link preceding a given link in a chain. If beforePtr is
+ * NULL, then the new link is placed at the beginning of the list.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_LinkBefore(Chain *chainPtr, ChainLink *linkPtr, ChainLink *beforePtr)
+{
+ if (chainPtr->head == NULL) {
+ chainPtr->tail = chainPtr->head = linkPtr;
+ } else {
+ if (beforePtr == NULL) {
+ /* Prepend to the front of the chain */
+ linkPtr->next = chainPtr->head;
+ linkPtr->prev = NULL;
+ chainPtr->head->prev = linkPtr;
+ chainPtr->head = linkPtr;
+ } else {
+ linkPtr->prev = beforePtr->prev;
+ linkPtr->next = beforePtr;
+ if (beforePtr == chainPtr->head) {
+ chainPtr->head = linkPtr;
+ } else {
+ beforePtr->prev->next = linkPtr;
+ }
+ beforePtr->prev = linkPtr;
+ }
+ }
+ chainPtr->nLinks++;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_UnlinkLink --
+ *
+ * Unlinks a link from the chain. The link is not deallocated, but only
+ * removed from the chain.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_UnlinkLink(Chain *chainPtr, ChainLink *linkPtr)
+{
+ int unlinked; /* Indicates if the link is actually removed
+ * from the chain. */
+
+ unlinked = FALSE;
+ if (chainPtr->head == linkPtr) {
+ chainPtr->head = linkPtr->next;
+ unlinked = TRUE;
+ }
+ if (chainPtr->tail == linkPtr) {
+ chainPtr->tail = linkPtr->prev;
+ unlinked = TRUE;
+ }
+ if (linkPtr->next != NULL) {
+ linkPtr->next->prev = linkPtr->prev;
+ unlinked = TRUE;
+ }
+ if (linkPtr->prev != NULL) {
+ linkPtr->prev->next = linkPtr->next;
+ unlinked = TRUE;
+ }
+ if (unlinked) {
+ chainPtr->nLinks--;
+ }
+ linkPtr->prev = linkPtr->next = NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_DeleteLink --
+ *
+ * Unlinks and frees the given link from the chain. It's assumed that
+ * the link belong to the chain. No error checking is performed to verify
+ * this.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_DeleteLink(Blt_Chain chain, Blt_ChainLink link)
+{
+ Blt_Chain_UnlinkLink(chain, link);
+ Blt_Free(link);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Append
+ *
+ * Creates and new link with the given data and appends it to the end of
+ * the chain.
+ *
+ * Results:
+ * Returns a pointer to the link created.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_ChainLink
+Blt_Chain_Append(Blt_Chain chain, ClientData clientData)
+{
+ Blt_ChainLink link;
+
+ link = Blt_Chain_NewLink();
+ Blt_Chain_LinkAfter(chain, link, (Blt_ChainLink)NULL);
+ Blt_Chain_SetValue(link, clientData);
+ return link;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Prepend
+ *
+ * Creates and new link with the given data and prepends it to beginning
+ * of the chain.
+ *
+ * Results:
+ * Returns a pointer to the link created.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_ChainLink
+Blt_Chain_Prepend(Blt_Chain chain, ClientData clientData)
+{
+ Blt_ChainLink link;
+
+ link = Blt_Chain_NewLink();
+ Blt_Chain_LinkBefore(chain, link, (Blt_ChainLink)NULL);
+ Blt_Chain_SetValue(link, clientData);
+ return link;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_GetNthLink --
+ *
+ * Find the link at the given position in the chain. The position
+ * is number from 0. If position is negative is returns the nth
+ * link from the back of the chain.
+ *
+ * Results:
+ * Returns the pointer to the link, if that numbered link
+ * exists. Otherwise NULL.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_ChainLink
+Blt_Chain_GetNthLink(Chain *chainPtr, long position)
+{
+ if (chainPtr != NULL) {
+ if (position < 0) {
+ ChainLink *linkPtr;
+ int i;
+
+ position = -position;
+ for (i = 0, linkPtr = chainPtr->tail; linkPtr != NULL;
+ linkPtr = linkPtr->prev, i++) {
+ if (i == position) {
+ return linkPtr;
+ }
+ }
+ } else {
+ ChainLink *linkPtr;
+ int i;
+
+ linkPtr = chainPtr->head;
+ for (i = 0, linkPtr = chainPtr->head; linkPtr != NULL;
+ linkPtr = linkPtr->next, i++) {
+ if (i == position) {
+ return linkPtr;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_Sort --
+ *
+ * Sorts the chain according to the given comparison routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The chain is reordered.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Chain_Sort(Chain *chainPtr, Blt_ChainCompareProc *proc)
+{
+ ChainLink **linkArr;
+ ChainLink *linkPtr;
+ long i;
+
+ if (chainPtr->nLinks < 2) {
+ return;
+ }
+ linkArr = Blt_Malloc(sizeof(Blt_ChainLink) * (chainPtr->nLinks + 1));
+ if (linkArr == NULL) {
+ return; /* Out of memory. */
+ }
+ i = 0;
+ for (linkPtr = chainPtr->head; linkPtr != NULL;
+ linkPtr = linkPtr->next) {
+ linkArr[i++] = linkPtr;
+ }
+ qsort((char *)linkArr, chainPtr->nLinks, sizeof(Blt_ChainLink),
+ (QSortCompareProc *)proc);
+
+ /* Rethread the chain. */
+ linkPtr = linkArr[0];
+ chainPtr->head = linkPtr;
+ linkPtr->prev = NULL;
+ for (i = 1; i < chainPtr->nLinks; i++) {
+ linkPtr->next = linkArr[i];
+ linkPtr->next->prev = linkPtr;
+ linkPtr = linkPtr->next;
+ }
+ chainPtr->tail = linkPtr;
+ linkPtr->next = NULL;
+ Blt_Free(linkArr);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Chain_IsBefore --
+ *
+ *
+ * Results:
+ * Return boolean value if the first link comes before the second.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Chain_IsBefore(ChainLink *firstPtr, ChainLink *lastPtr)
+{
+ ChainLink *linkPtr;
+
+ for (linkPtr = firstPtr; linkPtr != NULL; linkPtr = linkPtr->next) {
+ if (linkPtr == lastPtr) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
diff --git a/src/bltChain.h b/src/bltChain.h
new file mode 100644
index 0000000..e0b0259
--- /dev/null
+++ b/src/bltChain.h
@@ -0,0 +1,90 @@
+
+/*
+ * bltChain.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _BLT_CHAIN_H
+#define _BLT_CHAIN_H
+
+typedef struct _Blt_Chain *Blt_Chain;
+typedef struct _Blt_ChainLink *Blt_ChainLink;
+
+/*
+ * A Blt_ChainLink is the container structure for the Blt_Chain.
+ */
+
+struct _Blt_ChainLink {
+ Blt_ChainLink prev; /* Link to the previous link */
+ Blt_ChainLink next; /* Link to the next link */
+ ClientData clientData; /* Pointer to the data object */
+};
+
+typedef int (Blt_ChainCompareProc)(Blt_ChainLink *l1Ptr, Blt_ChainLink *l2Ptr);
+
+/*
+ * A Blt_Chain is a doubly chained list structure.
+ */
+struct _Blt_Chain {
+ Blt_ChainLink head; /* Pointer to first element in chain */
+ Blt_ChainLink tail; /* Pointer to last element in chain */
+ long nLinks; /* Number of elements in chain */
+};
+
+BLT_EXTERN void Blt_Chain_Init(Blt_Chain chain);
+BLT_EXTERN Blt_Chain Blt_Chain_Create(void);
+BLT_EXTERN void Blt_Chain_Destroy(Blt_Chain chain);
+BLT_EXTERN Blt_ChainLink Blt_Chain_NewLink(void);
+BLT_EXTERN Blt_ChainLink Blt_Chain_AllocLink(size_t size);
+BLT_EXTERN Blt_ChainLink Blt_Chain_Append(Blt_Chain chain,
+ ClientData clientData);
+BLT_EXTERN Blt_ChainLink Blt_Chain_Prepend(Blt_Chain chain,
+ ClientData clientData);
+BLT_EXTERN void Blt_Chain_Reset(Blt_Chain chain);
+BLT_EXTERN void Blt_Chain_InitLink(Blt_ChainLink link);
+BLT_EXTERN void Blt_Chain_LinkAfter(Blt_Chain chain, Blt_ChainLink link,
+ Blt_ChainLink after);
+BLT_EXTERN void Blt_Chain_LinkBefore(Blt_Chain chain, Blt_ChainLink link,
+ Blt_ChainLink before);
+BLT_EXTERN void Blt_Chain_UnlinkLink(Blt_Chain chain, Blt_ChainLink link);
+BLT_EXTERN void Blt_Chain_DeleteLink(Blt_Chain chain, Blt_ChainLink link);
+BLT_EXTERN Blt_ChainLink Blt_Chain_GetNthLink(Blt_Chain chain, long position);
+BLT_EXTERN void Blt_Chain_Sort(Blt_Chain chain, Blt_ChainCompareProc *proc);
+BLT_EXTERN int Blt_Chain_IsBefore(Blt_ChainLink first, Blt_ChainLink last);
+
+#define Blt_Chain_GetLength(c) (((c) == NULL) ? 0 : (c)->nLinks)
+#define Blt_Chain_FirstLink(c) (((c) == NULL) ? NULL : (c)->head)
+#define Blt_Chain_LastLink(c) (((c) == NULL) ? NULL : (c)->tail)
+#define Blt_Chain_PrevLink(l) ((l)->prev)
+#define Blt_Chain_NextLink(l) ((l)->next)
+#define Blt_Chain_GetValue(l) ((l)->clientData)
+#define Blt_Chain_FirstValue(c) (((c)->head == NULL) ? NULL : (c)->head->clientData)
+#define Blt_Chain_SetValue(l, value) ((l)->clientData = (ClientData)(value))
+#define Blt_Chain_AppendLink(c, l) \
+ (Blt_Chain_LinkAfter((c), (l), (Blt_ChainLink)NULL))
+#define Blt_Chain_PrependLink(c, l) \
+ (Blt_Chain_LinkBefore((c), (l), (Blt_ChainLink)NULL))
+
+#endif /* _BLT_CHAIN_H */
diff --git a/src/bltConfig.C b/src/bltConfig.C
new file mode 100644
index 0000000..cf67a23
--- /dev/null
+++ b/src/bltConfig.C
@@ -0,0 +1,2493 @@
+
+/*
+ * bltConfig.c --
+ *
+ * This file contains a Tcl_Obj based replacement for the widget
+ * configuration functions in Tk.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * Copyright 2003-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This is a Tcl_Obj based replacement for the widget configuration
+ * functions in Tk.
+ *
+ * What not use the new Tk_Option interface?
+ *
+ * There were design changes in the new Tk_Option interface that
+ * make it unwieldy.
+ *
+ * o You have to dynamically allocate, store, and deallocate
+ * your option table.
+ * o The Tk_FreeConfigOptions routine requires a tkwin argument.
+ * Unfortunately, most widgets save the display pointer and
+ * de-reference their tkwin when the window is destroyed.
+ * o There's no TK_CONFIG_CUSTOM functionality. This means that
+ * save special options must be saved as strings by
+ * Tk_ConfigureWidget and processed later, thus losing the
+ * benefits of Tcl_Objs. It also make error handling
+ * problematic, since you don't pick up certain errors like
+ *
+ * .widget configure -myoption bad -myoption good
+ *
+ * You will never see the first "bad" value.
+ * o Especially compared to the former Tk_ConfigureWidget calls,
+ * the new interface is overly complex. If there was a big
+ * performance win, it might be worth the effort. But let's
+ * face it, this biggest wins are in processing custom options
+ * values with thousands of elements. Most common resources
+ * (font, color, etc) have string tokens anyways.
+ *
+ * On the other hand, the replacement functions in this file fell
+ * into place quite easily both from the aspect of API writer and
+ * user. The biggest benefit is that you don't need to change lots
+ * of working code just to get the benefits of Tcl_Objs.
+ *
+ */
+
+#include "bltInt.h"
+
+#include <stdarg.h>
+#include "bltFont.h"
+#include "bltPicture.h"
+#include "bltBgStyle.h"
+
+#if (_TK_VERSION < _VERSION(8,1,0))
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_GetAnchorFromObj --
+ *
+ * Return a Tk_Anchor value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard TCL result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Tk_GetAnchorFromObj(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Obj *objPtr, /* The object we are trying to get the
+ * value from. */
+ Tk_Anchor *anchorPtr) /* Where to place the Tk_Anchor that
+ * corresponds to the string value of
+ * objPtr. */
+{
+ return Tk_GetAnchor(interp, Tcl_GetString(objPtr), anchorPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_GetJustifyFromObj --
+ *
+ * Return a Tk_Justify value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard TCL result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Tk_GetJustifyFromObj(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Obj *objPtr, /* The object we are trying to get the
+ * value from. */
+ Tk_Justify *justifyPtr) /* Where to place the Tk_Justify that
+ * corresponds to the string value of
+ * objPtr. */
+{
+ return Tk_GetJustify(interp, Tcl_GetString(objPtr), justifyPtr);
+}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_GetReliefFromObj --
+ *
+ * Return an integer value based on the value of the objPtr.
+ *
+ * Results:
+ * The return value is a standard TCL result. If an error occurs during
+ * conversion, an error message is left in the interpreter's result
+ * unless "interp" is NULL.
+ *
+ * Side effects:
+ * The object gets converted by Tcl_GetIndexFromObj.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Tk_GetReliefFromObj(
+ Tcl_Interp *interp, /* Used for error reporting. */
+ Tcl_Obj *objPtr, /* The object we are trying to get the
+ * value from. */
+ int *reliefPtr) /* Where to place the answer. */
+{
+ return Tk_GetRelief(interp, Tcl_GetString(objPtr), reliefPtr);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_Alloc3DBorderFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Tk_3DBorder structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is a token for a data structure describing a
+ * 3-D border. This token may be passed to procedures such as
+ * Blt_Draw3DRectangle and Tk_Free3DBorder. If an error prevented
+ * the border from being created then NULL is returned and an error
+ * message will be left in the interp's result.
+ *
+ * Side effects:
+ * The border is added to an internal database with a reference
+ * count. For each call to this procedure, there should eventually
+ * be a call to FreeBorderObjProc so that the database is
+ * cleaned up when borders aren't in use anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_3DBorder
+Tk_Alloc3DBorderFromObj(
+ Tcl_Interp *interp, /* Interp for error results. */
+ Tk_Window tkwin, /* Need the screen the border is used on.*/
+ Tcl_Obj *objPtr) /* Object giving name of color for window
+ * background. */
+{
+ return Tk_Get3DBorder(interp, tkwin, Tcl_GetString(objPtr));
+}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocBitmapFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Pixmap structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is the X identifer for the desired bitmap
+ * (i.e. a Pixmap with a single plane), unless string couldn't be
+ * parsed correctly. In this case, None is returned and an error
+ * message is left in the interp's result. The caller should never
+ * modify the bitmap that is returned, and should eventually call
+ * Tk_FreeBitmapFromObj when the bitmap is no longer needed.
+ *
+ * Side effects:
+ * The bitmap is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeBitmapFromObj, so that the database can be cleaned up
+ * when bitmaps aren't needed anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Pixmap
+Tk_AllocBitmapFromObj(
+ Tcl_Interp *interp, /* Interp for error results. This may
+ * be NULL. */
+ Tk_Window tkwin, /* Need the screen the bitmap is used on.*/
+ Tcl_Obj *objPtr) /* Object describing bitmap; see manual
+ * entry for legal syntax of string value. */
+{
+ return Tk_GetBitmap(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocFontFromObj --
+ *
+ * Given a string description of a font, map the description to a
+ * corresponding Blt_Font that represents the font.
+ *
+ * Results:
+ * The return value is token for the font, or NULL if an error
+ * prevented the font from being created. If NULL is returned, an
+ * error message will be left in interp's result object.
+ *
+ * Side effects:
+ * The font is added to an internal database with a reference
+ * count. For each call to this procedure, there should eventually
+ * be a call to Blt_FreeFont() or Blt_FreeFontFromObj() so that the
+ * database is cleaned up when fonts aren't in use anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_Font
+Tk_AllocFontFromObj(
+ Tcl_Interp *interp, /* Interp for database and error return. */
+ Tk_Window tkwin, /* For screen on which font will be used. */
+ Tcl_Obj *objPtr) /* Object describing font, as: named font,
+ * native format, or parseable string. */
+{
+ return Tk_GetFont(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocCursorFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * Tk_Cursor structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is the X identifer for the desired cursor,
+ * unless objPtr couldn't be parsed correctly. In this case,
+ * None is returned and an error message is left in the interp's result.
+ * The caller should never modify the cursor that is returned, and
+ * should eventually call Tk_FreeCursorFromObj when the cursor is no
+ * longer needed.
+ *
+ * Side effects:
+ * The cursor is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeCursorFromObj, so that the database can be cleaned up
+ * when cursors aren't needed anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+Tk_Cursor
+Tk_AllocCursorFromObj(
+ Tcl_Interp *interp, /* Interp for error results. */
+ Tk_Window tkwin, /* Window in which the cursor will be used.*/
+ Tcl_Obj *objPtr) /* Object describing cursor; see manual
+ * entry for description of legal
+ * syntax of this obj's string rep. */
+{
+ return Tk_GetCursor(interp, tkwin, Tcl_GetString(objPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_AllocColorFromObj --
+ *
+ * Given a Tcl_Obj *, map the value to a corresponding
+ * XColor structure based on the tkwin given.
+ *
+ * Results:
+ * The return value is a pointer to an XColor structure that
+ * indicates the red, blue, and green intensities for the color
+ * given by the string in objPtr, and also specifies a pixel value
+ * to use to draw in that color. If an error occurs, NULL is
+ * returned and an error message will be left in interp's result
+ * (unless interp is NULL).
+ *
+ * Side effects:
+ * The color is added to an internal database with a reference count.
+ * For each call to this procedure, there should eventually be a call
+ * to Tk_FreeColorFromObj so that the database is cleaned up when colors
+ * aren't in use anymore.
+ *
+ *---------------------------------------------------------------------------
+ */
+XColor *
+Tk_AllocColorFromObj(
+ Tcl_Interp *interp, /* Used only for error reporting. If NULL,
+ * then no messages are provided. */
+ Tk_Window tkwin, /* Window in which the color will be used.*/
+ Tcl_Obj *objPtr) /* Object that describes the color; string
+ * value is a color name such as "red" or
+ * "#ff0000".*/
+{
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ return Tk_GetColor(interp, tkwin, Tk_GetUid(string));
+}
+#endif
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetPositionFromObj --
+ *
+ * Convert a string representing a numeric position.
+ * A position can be in one of the following forms.
+ *
+ * number - number of the item in the hierarchy, indexed
+ * from zero.
+ * "end" - last position in the hierarchy.
+ *
+ * Results:
+ * A standard TCL result. If "string" is a valid index, then
+ * *indexPtr is filled with the corresponding numeric index.
+ * If "end" was selected then *indexPtr is set to -1.
+ * Otherwise an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetPositionFromObj(
+ Tcl_Interp *interp, /* Interpreter to report results back
+ * to. */
+ Tcl_Obj *objPtr, /* Tcl_Obj representation of the index.
+ * Can be an integer or "end" to refer
+ * to the last index. */
+ long *indexPtr) /* Holds the converted index. */
+{
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
+ *indexPtr = -1; /* Indicates last position in hierarchy. */
+ } else {
+ long position;
+
+ if (Tcl_GetLongFromObj(interp, objPtr, &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (position < 0) {
+ Tcl_AppendResult(interp, "bad position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = position;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetPixelsFromObj --
+ *
+ * Like Tk_GetPixelsFromObj, but checks for negative, zero.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetPixelsFromObj(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ Tcl_Obj *objPtr,
+ int check, /* Can be PIXELS_POS, PIXELS_NNEG,
+ * or PIXELS_ANY, */
+ int *valuePtr)
+{
+ int length;
+
+ if (Tk_GetPixelsFromObj(interp, tkwin, objPtr, &length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (length >= SHRT_MAX) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": too big to represent", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (check) {
+ case PIXELS_NNEG:
+ if (length < 0) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": can't be negative", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+
+ case PIXELS_POS:
+ if (length <= 0) {
+ Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(objPtr),
+ "\": must be positive", (char *)NULL);
+ return TCL_ERROR;
+ }
+ break;
+
+ case PIXELS_ANY:
+ break;
+ }
+ *valuePtr = length;
+ return TCL_OK;
+}
+
+int
+Blt_GetPadFromObj(
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Window */
+ Tcl_Obj *objPtr, /* Pixel value string */
+ Blt_Pad *padPtr)
+{
+ int side1, side2;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc < 1) || (objc > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in padding list",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_GetPixelsFromObj(interp, tkwin, objv[0], PIXELS_NNEG,
+ &side1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ side2 = side1;
+ if ((objc > 1) &&
+ (Blt_GetPixelsFromObj(interp, tkwin, objv[1], PIXELS_NNEG,
+ &side2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ /* Don't update the pad structure until we know both values are okay. */
+ padPtr->side1 = side1;
+ padPtr->side2 = side2;
+ return TCL_OK;
+}
+
+int
+Blt_GetStateFromObj(
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tcl_Obj *objPtr, /* Pixel value string */
+ int *statePtr)
+{
+ char c;
+ const char *string;
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'n') && (strncmp(string, "normal", length) == 0)) {
+ *statePtr = STATE_NORMAL;
+ } else if ((c == 'd') && (strncmp(string, "disabled", length) == 0)) {
+ *statePtr = STATE_DISABLED;
+ } else if ((c == 'a') && (strncmp(string, "active", length) == 0)) {
+ *statePtr = STATE_ACTIVE;
+ } else {
+ Tcl_AppendResult(interp, "bad state \"", string,
+ "\": should be normal, active, or disabled", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+const char *
+Blt_NameOfState(int state)
+{
+ switch (state) {
+ case STATE_ACTIVE:
+ return "active";
+ case STATE_DISABLED:
+ return "disabled";
+ case STATE_NORMAL:
+ return "normal";
+ default:
+ return "???";
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_NameOfFill --
+ *
+ * Converts the integer representing the fill style into a string.
+ *
+ *---------------------------------------------------------------------------
+ */
+const char *
+Blt_NameOfFill(int fill)
+{
+ switch (fill) {
+ case FILL_X:
+ return "x";
+ case FILL_Y:
+ return "y";
+ case FILL_NONE:
+ return "none";
+ case FILL_BOTH:
+ return "both";
+ default:
+ return "unknown value";
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetFillFromObj --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none" Use neither plane.
+ * "x" X-coordinate plane.
+ * "y" Y-coordinate plane.
+ * "both" Use both coordinate planes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_GetFillFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *fillPtr)
+{
+ char c;
+ const char *string;
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *fillPtr = FILL_NONE;
+ } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) {
+ *fillPtr = FILL_X;
+ } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) {
+ *fillPtr = FILL_Y;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *fillPtr = FILL_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", string,
+ "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_NameOfResize --
+ *
+ * Converts the resize value into its string representation.
+ *
+ * Results:
+ * Returns a pointer to the static name string.
+ *
+ *---------------------------------------------------------------------------
+ */
+const char *
+Blt_NameOfResize(int resize)
+{
+ switch (resize & RESIZE_BOTH) {
+ case RESIZE_NONE:
+ return "none";
+ case RESIZE_EXPAND:
+ return "expand";
+ case RESIZE_SHRINK:
+ return "shrink";
+ case RESIZE_BOTH:
+ return "both";
+ default:
+ return "unknown resize value";
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetResizeFromObj --
+ *
+ * Converts the resize string into its numeric representation.
+ *
+ * Valid style strings are:
+ *
+ * "none"
+ * "expand"
+ * "shrink"
+ * "both"
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_GetResizeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *resizePtr)
+{
+ char c;
+ const char *string;
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ *resizePtr = RESIZE_NONE;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *resizePtr = RESIZE_BOTH;
+ } else if ((c == 'e') && (strncmp(string, "expand", length) == 0)) {
+ *resizePtr = RESIZE_EXPAND;
+ } else if ((c == 's') && (strncmp(string, "shrink", length) == 0)) {
+ *resizePtr = RESIZE_SHRINK;
+ } else {
+ Tcl_AppendResult(interp, "bad resize argument \"", string,
+ "\": should be \"none\", \"expand\", \"shrink\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetDashesFromObj --
+ *
+ * Converts a TCL list of dash values into a dash list ready for
+ * use with XSetDashes.
+ *
+ * A valid list dash values can have zero through 11 elements
+ * (PostScript limit). Values must be between 1 and 255. Although
+ * a list of 0 (like the empty string) means no dashes.
+ *
+ * Results:
+ * A standard TCL result. If the list represented a valid dash
+ * list TCL_OK is returned and *dashesPtr* will contain the
+ * valid dash list. Otherwise, TCL_ERROR is returned and
+ * interp->result will contain an error message.
+ *
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetDashesFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ Blt_Dashes *dashesPtr)
+{
+ const char *string;
+ char c;
+
+ string = Tcl_GetString(objPtr);
+ if (string == NULL) {
+ dashesPtr->values[0] = 0;
+ return TCL_OK;
+ }
+ c = string[0];
+ if (c == '\0') {
+ dashesPtr->values[0] = 0;
+ } else if ((c == 'd') && (strcmp(string, "dot") == 0)) {
+ /* 1 */
+ dashesPtr->values[0] = 1;
+ dashesPtr->values[1] = 0;
+ } else if ((c == 'd') && (strcmp(string, "dash") == 0)) {
+ /* 5 2 */
+ dashesPtr->values[0] = 5;
+ dashesPtr->values[1] = 2;
+ dashesPtr->values[2] = 0;
+ } else if ((c == 'd') && (strcmp(string, "dashdot") == 0)) {
+ /* 2 4 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 0;
+ } else if ((c == 'd') && (strcmp(string, "dashdotdot") == 0)) {
+ /* 2 4 2 2 */
+ dashesPtr->values[0] = 2;
+ dashesPtr->values[1] = 4;
+ dashesPtr->values[2] = 2;
+ dashesPtr->values[3] = 2;
+ dashesPtr->values[4] = 0;
+ } else {
+ int objc;
+ Tcl_Obj **objv;
+ int i;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 11) { /* This is the postscript limit */
+ Tcl_AppendResult(interp, "too many values in dash list \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ int value;
+
+ if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Backward compatibility:
+ * Allow list of 0 to turn off dashes
+ */
+ if ((value == 0) && (objc == 1)) {
+ break;
+ }
+ if ((value < 1) || (value > 255)) {
+ Tcl_AppendResult(interp, "dash value \"",
+ Tcl_GetString(objv[i]), "\" is out of range",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ dashesPtr->values[i] = (unsigned char)value;
+ }
+ /* Make sure the array ends with a NUL byte */
+ dashesPtr->values[i] = 0;
+ }
+ return TCL_OK;
+}
+
+const char *
+Blt_NameOfSide(int side)
+{
+ switch (side) {
+ case SIDE_LEFT:
+ return "left";
+ case SIDE_RIGHT:
+ return "right";
+ case SIDE_BOTTOM:
+ return "bottom";
+ case SIDE_TOP:
+ return "top";
+ }
+ return "unknown side value";
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetSideFromObj --
+ *
+ * Converts the fill style string into its numeric representation.
+ *
+ * Valid style strings are "left", "right", "top", or "bottom".
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED */
+int
+Blt_GetSideFromObj(
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tcl_Obj *objPtr, /* Value string */
+ int *sidePtr) /* (out) Token representing side:
+ * either SIDE_LEFT, SIDE_RIGHT,
+ * SIDE_TOP, or SIDE_BOTTOM. */
+{
+ char c;
+ const char *string;
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'l') && (strncmp(string, "left", length) == 0)) {
+ *sidePtr = SIDE_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "right", length) == 0)) {
+ *sidePtr = SIDE_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
+ *sidePtr = SIDE_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottom", length) == 0)) {
+ *sidePtr = SIDE_BOTTOM;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", string,
+ "\": should be left, right, top, or bottom", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ResetLimits --
+ *
+ * Resets the limits to their default values.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ResetLimits(Blt_Limits *limitsPtr) /* Limits to be imposed on the value */
+{
+ limitsPtr->flags = 0;
+ limitsPtr->min = LIMITS_MIN;
+ limitsPtr->max = LIMITS_MAX;
+ limitsPtr->nom = LIMITS_NOM;
+}
+
+int
+Blt_GetLimitsFromObj(Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr,
+ Blt_Limits *limitsPtr)
+{
+ int values[3];
+ int nValues;
+ int limitsFlags;
+
+ /* Initialize limits to default values */
+ values[2] = LIMITS_NOM;
+ values[1] = LIMITS_MAX;
+ values[0] = LIMITS_MIN;
+ limitsFlags = 0;
+ nValues = 0;
+ if (objPtr != NULL) {
+ Tcl_Obj **objv;
+ int objc;
+ int i;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 3) {
+ Tcl_AppendResult(interp, "wrong # limits \"", Tcl_GetString(objPtr),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ const char *string;
+ int size;
+
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '\0') {
+ continue; /* Empty string: use default value */
+ }
+ limitsFlags |= (1 << i);
+ if (Tk_GetPixelsFromObj(interp, tkwin, objv[i], &size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((size < LIMITS_MIN) || (size > LIMITS_MAX)) {
+ Tcl_AppendResult(interp, "bad limit \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ values[i] = size;
+ }
+ nValues = objc;
+ }
+ /*
+ * Check the limits specified. We can't check the requested size of
+ * widgets.
+ */
+ switch (nValues) {
+ case 1:
+ limitsFlags |= (LIMITS_MIN_SET | LIMITS_MAX_SET);
+ values[1] = values[0]; /* Set minimum and maximum to value */
+ break;
+
+ case 2:
+ if (values[1] < values[0]) {
+ Tcl_AppendResult(interp, "bad range \"", Tcl_GetString(objPtr),
+ "\": min > max", (char *)NULL);
+ return TCL_ERROR; /* Minimum is greater than maximum */
+ }
+ break;
+
+ case 3:
+ if (values[1] < values[0]) {
+ Tcl_AppendResult(interp, "bad range \"", Tcl_GetString(objPtr),
+ "\": min > max", (char *)NULL);
+ return TCL_ERROR; /* Minimum is greater than maximum */
+ }
+ if ((values[2] < values[0]) || (values[2] > values[1])) {
+ Tcl_AppendResult(interp, "nominal value \"", Tcl_GetString(objPtr),
+ "\" out of range", (char *)NULL);
+ return TCL_ERROR; /* Nominal is outside of range defined
+ * by minimum and maximum */
+ }
+ break;
+ }
+ limitsPtr->min = values[0];
+ limitsPtr->max = values[1];
+ limitsPtr->nom = values[2];
+ limitsPtr->flags = limitsFlags;
+ return TCL_OK;
+}
+
+/* Configuration option helper routines */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DoConfig --
+ *
+ * This procedure applies a single configuration option
+ * to a widget record.
+ *
+ * Results:
+ * A standard TCL return value.
+ *
+ * Side effects:
+ * WidgRec is modified as indicated by specPtr and value.
+ * The old value is recycled, if that is appropriate for
+ * the value type.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+DoConfig(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Tk_Window tkwin, /* Window containing widget (needed to
+ * set up X resources). */
+ Blt_ConfigSpec *sp, /* Specifier to apply. */
+ Tcl_Obj *objPtr, /* Value to use to fill in widgRec. */
+ char *widgRec) /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+{
+ char *ptr;
+ int objIsEmpty;
+
+ objIsEmpty = FALSE;
+ if (objPtr == NULL) {
+ objIsEmpty = TRUE;
+ } else if (sp->specFlags & BLT_CONFIG_NULL_OK) {
+ int length;
+
+ if (objPtr->bytes != NULL) {
+ length = objPtr->length;
+ } else {
+ Tcl_GetStringFromObj(objPtr, &length);
+ }
+ objIsEmpty = (length == 0);
+ }
+ do {
+ ptr = widgRec + sp->offset;
+ switch (sp->type) {
+ case BLT_CONFIG_ANCHOR:
+ {
+ Tk_Anchor anchor;
+
+ if (Tk_GetAnchorFromObj(interp, objPtr, &anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(Tk_Anchor *)ptr = anchor;
+ }
+ break;
+
+ case BLT_CONFIG_BITMAP:
+ {
+ Pixmap bitmap;
+
+ if (objIsEmpty) {
+ bitmap = None;
+ } else {
+ bitmap = Tk_AllocBitmapFromObj(interp, tkwin, objPtr);
+ if (bitmap == None) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Pixmap *)ptr != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), *(Pixmap *)ptr);
+ }
+ *(Pixmap *)ptr = bitmap;
+ }
+ break;
+
+ case BLT_CONFIG_BOOLEAN:
+ {
+ int bool;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = bool;
+ }
+ break;
+
+ case BLT_CONFIG_BORDER:
+ {
+ Tk_3DBorder border;
+
+ if (objIsEmpty) {
+ border = NULL;
+ } else {
+ border = Tk_Alloc3DBorderFromObj(interp, tkwin, objPtr);
+ if (border == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Tk_3DBorder *)ptr != NULL) {
+ Tk_Free3DBorder(*(Tk_3DBorder *)ptr);
+ }
+ *(Tk_3DBorder *)ptr = border;
+ }
+ break;
+
+ case BLT_CONFIG_CAP_STYLE:
+ {
+ int cap;
+ Tk_Uid uid;
+
+ uid = Tk_GetUid(Tcl_GetString(objPtr));
+ if (Tk_GetCapStyle(interp, uid, &cap) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = cap;
+ }
+ break;
+
+ case BLT_CONFIG_COLOR:
+ {
+ XColor *color;
+
+ if (objIsEmpty) {
+ color = NULL;
+ } else {
+ color = Tk_GetColor(interp, tkwin,
+ Tk_GetUid(Tcl_GetString(objPtr)));
+ if (color == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(XColor **)ptr != NULL) {
+ Tk_FreeColor(*(XColor **)ptr);
+ }
+ *(XColor **)ptr = color;
+ }
+ break;
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ {
+ Tk_Cursor cursor;
+
+ if (objIsEmpty) {
+ cursor = None;
+ } else {
+ cursor = Tk_AllocCursorFromObj(interp, tkwin, objPtr);
+ if (cursor == None) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Tk_Cursor *)ptr != None) {
+ Tk_FreeCursor(Tk_Display(tkwin), *(Tk_Cursor *)ptr);
+ }
+ *(Tk_Cursor *)ptr = cursor;
+ if (sp->type == BLT_CONFIG_ACTIVE_CURSOR) {
+ Tk_DefineCursor(tkwin, cursor);
+ }
+ }
+ break;
+
+ case BLT_CONFIG_CUSTOM:
+ if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp,
+ tkwin, objPtr, widgRec, sp->offset, sp->specFlags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_DOUBLE:
+ {
+ double value;
+
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(double *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_FONT:
+ {
+ Blt_Font font;
+
+ if (objIsEmpty) {
+ font = NULL;
+ } else {
+ font = Blt_AllocFontFromObj(interp, tkwin, objPtr);
+ if (font == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Blt_Font *)ptr != NULL) {
+ Blt_FreeFont(*(Blt_Font *)ptr);
+ }
+ *(Blt_Font *)ptr = font;
+ }
+ break;
+
+ case BLT_CONFIG_TK_FONT:
+ {
+ Tk_Font font;
+
+ if (objIsEmpty) {
+ font = NULL;
+ } else {
+ font = Tk_AllocFontFromObj(interp, tkwin, objPtr);
+ if (font == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Tk_Font *)ptr != NULL) {
+ Tk_FreeFont(*(Tk_Font *)ptr);
+ }
+ *(Tk_Font *)ptr = font;
+ }
+ break;
+
+ case BLT_CONFIG_INT:
+ {
+ int value;
+
+ if (Tcl_GetIntFromObj(interp, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_JOIN_STYLE:
+ {
+ int join;
+ Tk_Uid uid;
+
+ uid = Tk_GetUid(Tcl_GetString(objPtr));
+ if (Tk_GetJoinStyle(interp, uid, &join) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = join;
+ }
+ break;
+
+ case BLT_CONFIG_JUSTIFY:
+ {
+ Tk_Justify justify;
+
+ if (Tk_GetJustifyFromObj(interp, objPtr, &justify) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(Tk_Justify *)ptr = justify;
+ }
+ break;
+
+ case BLT_CONFIG_MM:
+ {
+ double value;
+
+ if (Tk_GetMMFromObj(interp, tkwin, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(double *)ptr = value;
+ }
+ break;
+
+
+ case BLT_CONFIG_RELIEF:
+ {
+ int relief;
+
+ if (Tk_GetReliefFromObj(interp, objPtr, &relief) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = relief;
+ }
+ break;
+
+ case BLT_CONFIG_STRING:
+ {
+ char *value;
+
+ value = (objIsEmpty) ? NULL :
+ Blt_AssertStrdup(Tcl_GetString(objPtr));
+ if (*(char **)ptr != NULL) {
+ Blt_Free(*(char **)ptr);
+ }
+ *(char **)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_UID:
+ if (*(Blt_Uid *)ptr != NULL) {
+ Blt_FreeUid(*(Blt_Uid *)ptr);
+ }
+ if (objIsEmpty) {
+ *(Blt_Uid *)ptr = NULL;
+ } else {
+ *(Blt_Uid *)ptr = Blt_GetUid(Tcl_GetString(objPtr));
+ }
+ break;
+
+ case BLT_CONFIG_WINDOW:
+ {
+ Tk_Window tkwin2;
+
+ if (objIsEmpty) {
+ tkwin2 = None;
+ } else {
+ const char *path;
+
+ path = Tcl_GetString(objPtr);
+ tkwin2 = Tk_NameToWindow(interp, path, tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ *(Tk_Window *)ptr = tkwin2;
+ }
+ break;
+
+ case BLT_CONFIG_BITMASK:
+ {
+ int bool;
+ unsigned long mask, flags;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ mask = (unsigned long)sp->customPtr;
+ flags = *(int *)ptr;
+ flags &= ~mask;
+ if (bool) {
+ flags |= mask;
+ }
+ *(int *)ptr = flags;
+ }
+ break;
+
+ case BLT_CONFIG_BITMASK_INVERT:
+ {
+ int bool;
+ unsigned long mask, flags;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ mask = (unsigned long)sp->customPtr;
+ flags = *(int *)ptr;
+ flags &= ~mask;
+ if (!bool) {
+ flags |= mask;
+ }
+ *(int *)ptr = flags;
+ }
+ break;
+
+ case BLT_CONFIG_DASHES:
+ if (Blt_GetDashesFromObj(interp, objPtr, (Blt_Dashes *)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+
+ case BLT_CONFIG_FILL:
+ if (Blt_GetFillFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_RESIZE:
+ if (Blt_GetResizeFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_FLOAT:
+ {
+ double value;
+
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(float *)ptr = (float)value;
+ }
+ break;
+
+ case BLT_CONFIG_INT_NNEG:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = (int)value;
+ }
+ break;
+
+
+ case BLT_CONFIG_INT_POS:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, &value)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = (int)value;
+ }
+ break;
+
+
+ case BLT_CONFIG_LIST:
+ {
+ const char **argv;
+ int argc;
+
+ if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (*(char ***)ptr != NULL) {
+ Blt_Free(*(char ***)ptr);
+ }
+ *(const char ***)ptr = argv;
+ }
+ break;
+
+ case BLT_CONFIG_LONG:
+ {
+ long value;
+
+ if (Tcl_GetLongFromObj(interp, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(long *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_LONG_NNEG:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(long *)ptr = value;
+ }
+ break;
+
+
+ case BLT_CONFIG_LONG_POS:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, &value)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(long *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_OBJ:
+ {
+ Tcl_IncrRefCount(objPtr);
+ if (*(Tcl_Obj **)ptr != NULL) {
+ Tcl_DecrRefCount(*(Tcl_Obj **)ptr);
+ }
+ *(Tcl_Obj **)ptr = objPtr;
+ }
+ break;
+
+ case BLT_CONFIG_PAD:
+ if (Blt_GetPadFromObj(interp, tkwin, objPtr, (Blt_Pad *)ptr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_PIXELS_NNEG:
+ {
+ int value;
+
+ if (Blt_GetPixelsFromObj(interp, tkwin, objPtr,
+ PIXELS_NNEG, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_PIXELS:
+ {
+ int value;
+
+ if (Blt_GetPixelsFromObj(interp, tkwin, objPtr, PIXELS_ANY,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_PIXELS_POS:
+ {
+ int value;
+
+ if (Blt_GetPixelsFromObj(interp, tkwin, objPtr, PIXELS_POS,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = value;
+ }
+ break;
+
+ case BLT_CONFIG_STATE:
+ if (Blt_GetStateFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_SIDE:
+ if (Blt_GetSideFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_CONFIG_BACKGROUND:
+ {
+ Blt_Background style;
+
+ if (objIsEmpty) {
+ style = NULL;
+ } else {
+ style = Blt_GetBackgroundFromObj(interp, tkwin, objPtr);
+ if (style == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ if (*(Blt_Background *)ptr != NULL) {
+ Blt_FreeBackground(*(Blt_Background *)ptr);
+ }
+ *(Blt_Background *)ptr = style;
+ }
+ break;
+
+ case BLT_CONFIG_PIX32:
+ if (Blt_GetPixelFromObj(interp, objPtr, (Blt_Pixel *)ptr)!=TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "bad config table: unknown type ",
+ Blt_Itoa(sp->type), (char *)NULL);
+ return TCL_ERROR;
+ }
+ sp++;
+ } while ((sp->switchName == NULL) && (sp->type != BLT_CONFIG_END));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FormatConfigValue --
+ *
+ * This procedure formats the current value of a configuration
+ * option.
+ *
+ * Results:
+ * The return value is the formatted value of the option given
+ * by specPtr and widgRec. If the value is static, so that it
+ * need not be freed, *freeProcPtr will be set to NULL; otherwise
+ * *freeProcPtr will be set to the address of a procedure to
+ * free the result, and the caller must invoke this procedure
+ * when it is finished with the result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigValue(
+ Tcl_Interp *interp, /* Interpreter for use in real conversions. */
+ Tk_Window tkwin, /* Window corresponding to widget. */
+ Blt_ConfigSpec *sp, /* Pointer to information describing option.
+ * Must not point to a synonym option. */
+ char *widgRec) /* Pointer to record holding current
+ * values of info for widget. */
+{
+ char *ptr;
+ const char *string;
+
+ ptr = widgRec + sp->offset;
+ string = "";
+ switch (sp->type) {
+ case BLT_CONFIG_ANCHOR:
+ string = Tk_NameOfAnchor(*(Tk_Anchor *)ptr);
+ break;
+
+ case BLT_CONFIG_BITMAP:
+ if (*(Pixmap *)ptr != None) {
+ string = Tk_NameOfBitmap(Tk_Display(tkwin), *(Pixmap *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_BOOLEAN:
+ return Tcl_NewBooleanObj(*(int *)ptr);
+
+ case BLT_CONFIG_BORDER:
+ if (*(Tk_3DBorder *)ptr != NULL) {
+ string = Tk_NameOf3DBorder(*(Tk_3DBorder *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CAP_STYLE:
+ string = Tk_NameOfCapStyle(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_COLOR:
+ if (*(XColor **)ptr != NULL) {
+ string = Tk_NameOfColor(*(XColor **)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ if (*(Tk_Cursor *)ptr != None) {
+ string = Tk_NameOfCursor(Tk_Display(tkwin), *(Tk_Cursor *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_CUSTOM:
+ return (*sp->customPtr->printProc)
+ (sp->customPtr->clientData, interp, tkwin, widgRec,
+ sp->offset, sp->specFlags);
+
+ case BLT_CONFIG_DOUBLE:
+ return Tcl_NewDoubleObj(*(double *)ptr);
+
+ case BLT_CONFIG_FONT:
+ if (*(Blt_Font *)ptr != NULL) {
+ string = Blt_NameOfFont(*(Blt_Font *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_TK_FONT:
+ if (*(Tk_Font *)ptr != NULL) {
+ string = Tk_NameOfFont(*(Tk_Font *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_INT:
+ return Tcl_NewIntObj(*(int *)ptr);
+
+ case BLT_CONFIG_JOIN_STYLE:
+ string = Tk_NameOfJoinStyle(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_JUSTIFY:
+ string = Tk_NameOfJustify(*(Tk_Justify *)ptr);
+ break;
+
+ case BLT_CONFIG_MM:
+ return Tcl_NewDoubleObj(*(double *)ptr);
+
+ case BLT_CONFIG_PIXELS:
+ case BLT_CONFIG_PIXELS_POS:
+ case BLT_CONFIG_PIXELS_NNEG:
+ return Tcl_NewIntObj(*(int *)ptr);
+
+ case BLT_CONFIG_RELIEF:
+ string = Tk_NameOfRelief(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_STRING:
+ case BLT_CONFIG_UID:
+ if (*(char **)ptr != NULL) {
+ string = *(char **)ptr;
+ }
+ break;
+
+ case BLT_CONFIG_BITMASK:
+ {
+ unsigned long flag;
+
+ flag = (*(unsigned long *)ptr) & (unsigned long)sp->customPtr;
+ return Tcl_NewBooleanObj((flag != 0));
+ }
+
+ case BLT_CONFIG_BITMASK_INVERT:
+ {
+ unsigned long flag;
+
+ flag = (*(unsigned long *)ptr) & (unsigned long)sp->customPtr;
+ return Tcl_NewBooleanObj((flag == 0));
+ }
+
+ case BLT_CONFIG_DASHES:
+ {
+ unsigned char *p;
+ Tcl_Obj *listObjPtr;
+ Blt_Dashes *dashesPtr = (Blt_Dashes *)ptr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for(p = dashesPtr->values; *p != 0; p++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(*p));
+ }
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_INT_NNEG:
+ case BLT_CONFIG_INT_POS:
+ return Tcl_NewIntObj(*(int *)ptr);
+
+ case BLT_CONFIG_FILL:
+ string = Blt_NameOfFill(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_RESIZE:
+ string = Blt_NameOfResize(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_FLOAT:
+ {
+ double x = *(float *)ptr;
+ return Tcl_NewDoubleObj(x);
+ }
+
+ case BLT_CONFIG_LIST:
+ {
+ Tcl_Obj *objPtr, *listObjPtr;
+ char *const *p;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (p = *(char ***)ptr; *p != NULL; p++) {
+ objPtr = Tcl_NewStringObj(*p, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_LONG:
+ return Tcl_NewLongObj(*(long *)ptr);
+
+ case BLT_CONFIG_LONG_NNEG:
+ case BLT_CONFIG_LONG_POS:
+ return Tcl_NewLongObj(*(long *)ptr);
+
+ case BLT_CONFIG_OBJ:
+ if (*(Tcl_Obj **)ptr != NULL) {
+ return *(Tcl_Obj **)ptr;
+ }
+ break;
+
+ case BLT_CONFIG_PAD:
+ {
+ Blt_Pad *padPtr = (Blt_Pad *)ptr;
+ Tcl_Obj *objPtr, *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ objPtr = Tcl_NewIntObj(padPtr->side1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ objPtr = Tcl_NewIntObj(padPtr->side2);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ return listObjPtr;
+ }
+
+ case BLT_CONFIG_STATE:
+ string = Blt_NameOfState(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_SIDE:
+ string = Blt_NameOfSide(*(int *)ptr);
+ break;
+
+ case BLT_CONFIG_BACKGROUND:
+ if (*(Blt_Background *)ptr != NULL) {
+ string = Blt_NameOfBackground(*(Blt_Background *)ptr);
+ }
+ break;
+
+ case BLT_CONFIG_PIX32:
+ string = Blt_NameOfPixel((Blt_Pixel *)ptr);
+ break;
+
+ default:
+ string = "?? unknown type ??";
+ }
+ return Tcl_NewStringObj(string, -1);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FormatConfigInfo --
+ *
+ * Create a valid TCL list holding the configuration information
+ * for a single configuration option.
+ *
+ * Results:
+ * A TCL list, dynamically allocated. The caller is expected to
+ * arrange for this list to be freed eventually.
+ *
+ * Side effects:
+ * Memory is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Tcl_Obj *
+FormatConfigInfo(
+ Tcl_Interp *interp, /* Interpreter to use for things
+ * like floating-point precision. */
+ Tk_Window tkwin, /* Window corresponding to widget. */
+ Blt_ConfigSpec *sp, /* Pointer to information describing
+ * option. */
+ char *widgRec) /* Pointer to record holding current
+ * values of info for widget. */
+{
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (sp->switchName != NULL) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(sp->switchName, -1));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ if (sp->dbName != NULL) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(sp->dbName, -1));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ if (sp->type == BLT_CONFIG_SYNONYM) {
+ return listObjPtr;
+ }
+ if (sp->dbClass != NULL) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(sp->dbClass, -1));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ if (sp->defValue != NULL) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(sp->defValue, -1));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ FormatConfigValue(interp, tkwin, sp, widgRec));
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FindConfigSpec --
+ *
+ * Search through a table of configuration specs, looking for
+ * one that matches a given switchName.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry, or NULL
+ * if nothing matched. In that case an error message is left
+ * in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Blt_ConfigSpec *
+FindConfigSpec(
+ Tcl_Interp *interp, /* Used for reporting errors. */
+ Blt_ConfigSpec *specs, /* Pointer to table of configuration
+ * specifications for a widget. */
+ Tcl_Obj *objPtr, /* Name (suitable for use in a "config"
+ * command) identifying particular option. */
+ int needFlags, /* Flags that must be present in matching
+ * entry. */
+ int hateFlags) /* Flags that must NOT be present in
+ * matching entry. */
+{
+ Blt_ConfigSpec *matchPtr; /* Matching spec, or NULL. */
+ Blt_ConfigSpec *sp;
+ const char *string;
+ char c; /* First character of current argument. */
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[1];
+ matchPtr = NULL;
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ if (sp->switchName == NULL) {
+ continue;
+ }
+ if ((sp->switchName[1] != c) ||
+ (strncmp(sp->switchName, string, length) != 0)) {
+ continue;
+ }
+ if (((sp->specFlags & needFlags) != needFlags) ||
+ (sp->specFlags & hateFlags)) {
+ continue;
+ }
+ if (sp->switchName[length] == 0) {
+ matchPtr = sp;
+ goto gotMatch;
+ }
+ if (matchPtr != NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "ambiguous option \"", string, "\"",
+ (char *)NULL);
+ }
+ return (Blt_ConfigSpec *)NULL;
+ }
+ matchPtr = sp;
+ }
+
+ if (matchPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "unknown option \"", string, "\"",
+ (char *)NULL);
+ }
+ return (Blt_ConfigSpec *)NULL;
+ }
+
+ /*
+ * Found a matching entry. If it's a synonym, then find the
+ * entry that it's a synonym for.
+ */
+
+ gotMatch:
+ sp = matchPtr;
+ if (sp->type == BLT_CONFIG_SYNONYM) {
+ for (sp = specs; /*empty*/; sp++) {
+ if (sp->type == BLT_CONFIG_END) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp,
+ "couldn't find synonym for option \"", string, "\"",
+ (char *)NULL);
+ }
+ return (Blt_ConfigSpec *) NULL;
+ }
+ if ((sp->dbName == matchPtr->dbName) &&
+ (sp->type != BLT_CONFIG_SYNONYM) &&
+ ((sp->specFlags & needFlags) == needFlags) &&
+ !(sp->specFlags & hateFlags)) {
+ break;
+ }
+ }
+ }
+ return sp;
+}
+
+/* Public routines */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureWidgetFromObj --
+ *
+ * Process command-line options and database options to
+ * fill in fields of a widget record with resources and
+ * other parameters.
+ *
+ * Results:
+ * A standard TCL return value. In case of an error,
+ * the interp's result will hold an error message.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information
+ * from argc/argv and the option database. Old information
+ * in widgRec's fields gets recycled.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ConfigureWidgetFromObj(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Tk_Window tkwin, /* Window containing widget (needed to
+ * set up X resources). */
+ Blt_ConfigSpec *specs, /* Describes legal options. */
+ int objc, /* Number of elements in argv. */
+ Tcl_Obj *const *objv, /* Command-line options. */
+ char *widgRec, /* Record whose fields are to be
+ * modified. Values must be properly
+ * initialized. */
+ int flags) /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. Also,
+ * may have BLT_CONFIG_OBJV_ONLY set. */
+{
+ Blt_ConfigSpec *sp;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's
+ * not considered. */
+ int result;
+
+ if (tkwin == NULL) {
+ /*
+ * Either we're not really in Tk, or the main window was destroyed and
+ * we're on our way out of the application
+ */
+ Tcl_AppendResult(interp, "NULL main window", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * Pass one: scan through all the option specs, replacing strings
+ * with Tk_Uid structs (if this hasn't been done already) and
+ * clearing the BLT_CONFIG_OPTION_SPECIFIED flags.
+ */
+
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ if (!(sp->specFlags & INIT) && (sp->switchName != NULL)) {
+ if (sp->dbName != NULL) {
+ sp->dbName = Tk_GetUid(sp->dbName);
+ }
+ if (sp->dbClass != NULL) {
+ sp->dbClass = Tk_GetUid(sp->dbClass);
+ }
+ if (sp->defValue != NULL) {
+ sp->defValue = Tk_GetUid(sp->defValue);
+ }
+ }
+ sp->specFlags = (sp->specFlags & ~BLT_CONFIG_OPTION_SPECIFIED) | INIT;
+ }
+
+ /*
+ * Pass two: scan through all of the arguments, processing those
+ * that match entries in the specs.
+ */
+ while (objc > 0) {
+ sp = FindConfigSpec(interp, specs, objv[0], needFlags, hateFlags);
+ if (sp == NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Process the entry. */
+ if (objc < 2) {
+ Tcl_AppendResult(interp, "value for \"", Tcl_GetString(objv[0]),
+ "\" missing", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (DoConfig(interp, tkwin, sp, objv[1], widgRec) != TCL_OK) {
+ char msg[100];
+
+ sprintf_s(msg, 100, "\n (processing \"%.40s\" option)",
+ sp->switchName);
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ sp->specFlags |= BLT_CONFIG_OPTION_SPECIFIED;
+ objc -= 2, objv += 2;
+ }
+
+ /*
+ * Pass three: scan through all of the specs again; if no
+ * command-line argument matched a spec, then check for info
+ * in the option database. If there was nothing in the
+ * database, then use the default.
+ */
+
+ if ((flags & BLT_CONFIG_OBJV_ONLY) == 0) {
+ Tcl_Obj *objPtr;
+
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ if ((sp->specFlags & BLT_CONFIG_OPTION_SPECIFIED) ||
+ (sp->switchName == NULL) || (sp->type == BLT_CONFIG_SYNONYM)) {
+ continue;
+ }
+ if (((sp->specFlags & needFlags) != needFlags) ||
+ (sp->specFlags & hateFlags)) {
+ continue;
+ }
+ objPtr = NULL;
+ if (sp->dbName != NULL) {
+ Tk_Uid value;
+
+ /* If a resource name was specified, check if there's
+ * also a value was associated with it. This
+ * overrides the default value. */
+ value = Tk_GetOption(tkwin, sp->dbName, sp->dbClass);
+ if (value != NULL) {
+ objPtr = Tcl_NewStringObj(value, -1);
+ }
+ }
+
+ if (objPtr != NULL) {
+ Tcl_IncrRefCount(objPtr);
+ result = DoConfig(interp, tkwin, sp, objPtr, widgRec);
+ Tcl_DecrRefCount(objPtr);
+ if (result != TCL_OK) {
+ char msg[200];
+
+ sprintf_s(msg, 200,
+ "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "database entry for", sp->dbName, Tk_PathName(tkwin));
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ } else if ((sp->defValue != NULL) &&
+ ((sp->specFlags & BLT_CONFIG_DONT_SET_DEFAULT) == 0)) {
+
+ /* No resource value is found, use the default value. */
+ objPtr = Tcl_NewStringObj(sp->defValue, -1);
+ Tcl_IncrRefCount(objPtr);
+ result = DoConfig(interp, tkwin, sp, objPtr, widgRec);
+ Tcl_DecrRefCount(objPtr);
+ if (result != TCL_OK) {
+ char msg[200];
+
+ sprintf_s(msg, 200,
+ "\n (%s \"%.50s\" in widget \"%.50s\")",
+ "default value for", sp->dbName, Tk_PathName(tkwin));
+ Tcl_AddErrorInfo(interp, msg);
+ return TCL_ERROR;
+ }
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureInfoFromObj --
+ *
+ * Return information about the configuration options
+ * for a window, and their current values.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp's result will be modified
+ * hold a description of either a single configuration option
+ * available for "widgRec" via "specs", or all the configuration
+ * options available. In the "all" case, the result will
+ * available for "widgRec" via "specs". The result will
+ * be a list, each of whose entries describes one option.
+ * Each entry will itself be a list containing the option's
+ * name for use on command lines, database name, database
+ * class, default value, and current value (empty string
+ * if none). For options that are synonyms, the list will
+ * contain only two values: name and synonym name. If the
+ * "name" argument is non-NULL, then the only information
+ * returned is that for the named argument (i.e. the corresponding
+ * entry in the overall list is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Blt_ConfigureInfoFromObj(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Tk_Window tkwin, /* Window corresponding to widgRec. */
+ Blt_ConfigSpec *specs, /* Describes legal options. */
+ char *widgRec, /* Record whose fields contain current
+ * values for options. */
+ Tcl_Obj *objPtr, /* If non-NULL, indicates a single option
+ * whose info is to be returned. Otherwise
+ * info is returned for all options. */
+ int flags) /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Blt_ConfigSpec *sp;
+ Tcl_Obj *listObjPtr, *valueObjPtr;
+ const char *string;
+ int needFlags, hateFlags;
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+
+ /*
+ * If information is only wanted for a single configuration
+ * spec, then handle that one spec specially.
+ */
+
+ Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
+ if (objPtr != NULL) {
+ sp = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+ if (sp == NULL) {
+ return TCL_ERROR;
+ }
+ valueObjPtr = FormatConfigInfo(interp, tkwin, sp, widgRec);
+ Tcl_SetObjResult(interp, valueObjPtr);
+ return TCL_OK;
+ }
+
+ /*
+ * Loop through all the specs, creating a big list with all
+ * their information.
+ */
+ string = NULL; /* Suppress compiler warning. */
+ if (objPtr != NULL) {
+ string = Tcl_GetString(objPtr);
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ if ((objPtr != NULL) && (sp->switchName != string)) {
+ continue;
+ }
+ if (((sp->specFlags & needFlags) != needFlags) ||
+ (sp->specFlags & hateFlags)) {
+ continue;
+ }
+ if (sp->switchName == NULL) {
+ continue;
+ }
+ valueObjPtr = FormatConfigInfo(interp, tkwin, sp, widgRec);
+ Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureValueFromObj --
+ *
+ * This procedure returns the current value of a configuration
+ * option for a widget.
+ *
+ * Results:
+ * The return value is a standard TCL completion code (TCL_OK or
+ * TCL_ERROR). The interp's result will be set to hold either the value
+ * of the option given by objPtr (if TCL_OK is returned) or
+ * an error message (if TCL_ERROR is returned).
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ConfigureValueFromObj(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Tk_Window tkwin, /* Window corresponding to widgRec. */
+ Blt_ConfigSpec *specs, /* Describes legal options. */
+ char *widgRec, /* Record whose fields contain current
+ * values for options. */
+ Tcl_Obj *objPtr, /* Gives the command-line name for the
+ * option whose value is to be returned. */
+ int flags) /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Blt_ConfigSpec *sp;
+ int needFlags, hateFlags;
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ if (Tk_Depth(tkwin) <= 1) {
+ hateFlags = BLT_CONFIG_COLOR_ONLY;
+ } else {
+ hateFlags = BLT_CONFIG_MONO_ONLY;
+ }
+ sp = FindConfigSpec(interp, specs, objPtr, needFlags, hateFlags);
+ if (sp == NULL) {
+ return TCL_ERROR;
+ }
+ objPtr = FormatConfigValue(interp, tkwin, sp, widgRec);
+ Tcl_SetObjResult(interp, objPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_FreeOptions --
+ *
+ * Free up all resources associated with configuration options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Any resource in widgRec that is controlled by a configuration
+ * option (e.g. a Tk_3DBorder or XColor) is freed in the appropriate
+ * fashion.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_FreeOptions(
+ Blt_ConfigSpec *specs, /* Describes legal options. */
+ char *widgRec, /* Record whose fields contain current
+ * values for options. */
+ Display *display, /* X display; needed for freeing some
+ * resources. */
+ int needFlags) /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. */
+{
+ Blt_ConfigSpec *sp;
+
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ char *ptr;
+
+ if ((sp->specFlags & needFlags) != needFlags) {
+ continue;
+ }
+ ptr = widgRec + sp->offset;
+ switch (sp->type) {
+ case BLT_CONFIG_STRING:
+ if (*((char **) ptr) != NULL) {
+ Blt_Free(*((char **) ptr));
+ *((char **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_COLOR:
+ if (*((XColor **) ptr) != NULL) {
+ Tk_FreeColor(*((XColor **) ptr));
+ *((XColor **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_FONT:
+ if (*((Blt_Font *) ptr) != None) {
+ Blt_FreeFont(*((Blt_Font *) ptr));
+ *((Blt_Font *) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_TK_FONT:
+ if (*((Tk_Font *) ptr) != None) {
+ Tk_FreeFont(*((Tk_Font *) ptr));
+ *((Tk_Font *) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_BITMAP:
+ if (*((Pixmap *) ptr) != None) {
+ Tk_FreeBitmap(display, *((Pixmap *) ptr));
+ *((Pixmap *) ptr) = None;
+ }
+ break;
+
+ case BLT_CONFIG_BORDER:
+ if (*((Tk_3DBorder *) ptr) != NULL) {
+ Tk_Free3DBorder(*((Tk_3DBorder *) ptr));
+ *((Tk_3DBorder *) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_CURSOR:
+ case BLT_CONFIG_ACTIVE_CURSOR:
+ if (*((Tk_Cursor *) ptr) != None) {
+ Tk_FreeCursor(display, *((Tk_Cursor *) ptr));
+ *((Tk_Cursor *) ptr) = None;
+ }
+ break;
+
+ case BLT_CONFIG_OBJ:
+ if (*(Tcl_Obj **)ptr != NULL) {
+ Tcl_DecrRefCount(*(Tcl_Obj **)ptr);
+ *(Tcl_Obj **)ptr = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_LIST:
+ if (*((char ***) ptr) != NULL) {
+ Blt_Free(*((char ***) ptr));
+ *((char ***) ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_UID:
+ if (*(Blt_Uid *)ptr != NULL) {
+ Blt_FreeUid(*(Blt_Uid *)ptr);
+ *(Blt_Uid *)ptr = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_BACKGROUND:
+ if (*((Blt_Background *)ptr) != NULL) {
+ Blt_FreeBackground(*((Blt_Background *)ptr));
+ *((Blt_Background *)ptr) = NULL;
+ }
+ break;
+
+ case BLT_CONFIG_CUSTOM:
+ if ((sp->customPtr->freeProc != NULL) && (*(char **)ptr != NULL)) {
+ (*sp->customPtr->freeProc)(sp->customPtr->clientData,
+ display, widgRec, sp->offset);
+ }
+ break;
+
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigModified --
+ *
+ * Given the configuration specifications and one or more option
+ * patterns (terminated by a NULL), indicate if any of the matching
+ * configuration options has been reset.
+ *
+ * Results:
+ * Returns 1 if one of the options has changed, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ConfigModified TCL_VARARGS_DEF(Blt_ConfigSpec *, arg1)
+{
+ va_list argList;
+ Blt_ConfigSpec *specs;
+ Blt_ConfigSpec *sp;
+ const char *option;
+
+ specs = TCL_VARARGS_START(Blt_ConfigSpec *, arg1, argList);
+ while ((option = va_arg(argList, const char *)) != NULL) {
+ for (sp = specs; sp->type != BLT_CONFIG_END; sp++) {
+ if ((Tcl_StringMatch(sp->switchName, option)) &&
+ (sp->specFlags & BLT_CONFIG_OPTION_SPECIFIED)) {
+ va_end(argList);
+ return 1;
+ }
+ }
+ }
+ va_end(argList);
+ return 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureComponentFromObj --
+ *
+ * Configures a component of a widget. This is useful for
+ * widgets that have multiple components which aren't uniquely
+ * identified by a Tk_Window. It allows us, for example, set
+ * resources for axes of the graph widget. The graph really has
+ * only one window, but its convenient to specify components in a
+ * hierarchy of options.
+ *
+ * *graph.x.logScale yes
+ * *graph.Axis.logScale yes
+ * *graph.temperature.scaleSymbols yes
+ * *graph.Element.scaleSymbols yes
+ *
+ * This is really a hack to work around the limitations of the Tk
+ * resource database. It creates a temporary window, needed to
+ * call Tk_ConfigureWidget, using the name of the component.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * A temporary window is created merely to pass to Tk_ConfigureWidget.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ConfigureComponentFromObj(
+ Tcl_Interp *interp,
+ Tk_Window parent, /* Window to associate with component */
+ const char *name, /* Name of component */
+ const char *className,
+ Blt_ConfigSpec *sp,
+ int objc,
+ Tcl_Obj *const *objv,
+ char *widgRec,
+ int flags)
+{
+ Tk_Window tkwin;
+ int result;
+ char *tmpName;
+ int isTemporary = FALSE;
+
+ tmpName = Blt_AssertStrdup(name);
+
+ /* Window name can't start with an upper case letter */
+ tmpName[0] = tolower(name[0]);
+
+ /*
+ * Create component if a child window by the component's name
+ * doesn't already exist.
+ */
+ tkwin = Blt_FindChild(parent, tmpName);
+ if (tkwin == NULL) {
+ tkwin = Tk_CreateWindow(interp, parent, tmpName, (char *)NULL);
+ isTemporary = TRUE;
+ }
+ if (tkwin == NULL) {
+ Tcl_AppendResult(interp, "can't find window in \"",
+ Tk_PathName(parent), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ assert(Tk_Depth(tkwin) == Tk_Depth(parent));
+ Blt_Free(tmpName);
+
+ Tk_SetClass(tkwin, className);
+ result = Blt_ConfigureWidgetFromObj(interp, tkwin, sp, objc, objv, widgRec,
+ flags);
+ if (isTemporary) {
+ Tk_DestroyWindow(tkwin);
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ObjIsOption --
+ *
+ * Indicates whether objPtr is a valid configuration option
+ * such as -background.
+ *
+ * Results:
+ * Returns 1 is a matching option is found and 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ObjIsOption(
+ Blt_ConfigSpec *specs, /* Describes legal options. */
+ Tcl_Obj *objPtr, /* Command-line option name. */
+ int flags) /* Used to specify additional flags
+ * that must be present in config specs
+ * for them to be considered. Also,
+ * may have BLT_CONFIG_OBJV_ONLY set. */
+{
+ Blt_ConfigSpec *sp;
+ int needFlags; /* Specs must contain this set of flags
+ * or else they are not considered. */
+
+ needFlags = flags & ~(BLT_CONFIG_USER_BIT - 1);
+ sp = FindConfigSpec((Tcl_Interp *)NULL, specs, objPtr, needFlags, 0);
+ return (sp != NULL);
+}
diff --git a/src/bltConfig.h b/src/bltConfig.h
new file mode 100644
index 0000000..7a19e9d
--- /dev/null
+++ b/src/bltConfig.h
@@ -0,0 +1,406 @@
+
+/*
+ * bltConfig.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef BLT_CONFIG_H
+#define BLT_CONFIG_H
+
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+
+#ifndef Blt_Offset
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+#endif /* Blt_Offset */
+
+
+typedef int (Blt_OptionParseProc)(ClientData clientData, Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr, char *widgRec, int offset, int flags);
+typedef Tcl_Obj *(Blt_OptionPrintProc)(ClientData clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *widgRec, int offset,
+ int flags);
+typedef void (Blt_OptionFreeProc)(ClientData clientData, Display *display,
+ char *widgRec, int offset);
+
+typedef struct Blt_CustomOption {
+ Blt_OptionParseProc *parseProc; /* Procedure to call to parse
+ * an option and store it in
+ * converted form. */
+
+ Blt_OptionPrintProc *printProc; /* Procedure to return a
+ * Tcl_Obj representing an
+ * existing option value. */
+
+ Blt_OptionFreeProc *freeProc; /* Procedure used to free the
+ * value. */
+
+ ClientData clientData; /* Arbitrary one-word value
+ * used by option parser:
+ * passed to parseProc and
+ * printProc. */
+} Blt_CustomOption;
+
+/*
+ * Structure used to specify information for Tk_ConfigureWidget. Each
+ * structure gives complete information for one option, including
+ * how the option is specified on the command line, where it appears
+ * in the option database, etc.
+ */
+
+typedef struct {
+ int type; /* Type of option, such as
+ * BLT_CONFIG_COLOR; see definitions
+ * below. Last option in table must
+ * have type BLT_CONFIG_END. */
+
+ const char *switchName; /* Switch used to specify option in
+ * argv. NULL means this spec is part
+ * of a group. */
+
+ Tk_Uid dbName; /* Name for option in option
+ * database. */
+
+ Tk_Uid dbClass; /* Class for option in database. */
+
+ Tk_Uid defValue; /* Default value for option if not
+ * specified in command line or
+ * database. */
+
+ int offset; /* Where in widget record to store
+ * value; use Blt_Offset macro to
+ * generate values for this. */
+
+ int specFlags; /* Any combination of the values
+ * defined below; other bits are used
+ * internally by tkConfig.c. */
+
+ Blt_CustomOption *customPtr; /* If type is BLT_CONFIG_CUSTOM then
+ * this is a pointer to info about how
+ * to parse and print the option.
+ * Otherwise it is irrelevant. */
+} Blt_ConfigSpec;
+
+/*
+ * Type values for Blt_ConfigSpec structures. See the user
+ * documentation for details.
+ */
+typedef enum {
+ BLT_CONFIG_ACTIVE_CURSOR,
+ BLT_CONFIG_ANCHOR,
+ BLT_CONFIG_BITMAP,
+ BLT_CONFIG_BOOLEAN,
+ BLT_CONFIG_BORDER,
+ BLT_CONFIG_CAP_STYLE,
+ BLT_CONFIG_COLOR,
+ BLT_CONFIG_CURSOR,
+ BLT_CONFIG_CUSTOM,
+ BLT_CONFIG_DOUBLE,
+ BLT_CONFIG_FONT,
+ BLT_CONFIG_INT,
+ BLT_CONFIG_JOIN_STYLE,
+ BLT_CONFIG_JUSTIFY,
+ BLT_CONFIG_MM,
+ BLT_CONFIG_RELIEF,
+ BLT_CONFIG_STRING,
+ BLT_CONFIG_SYNONYM,
+ BLT_CONFIG_UID,
+ BLT_CONFIG_WINDOW,
+
+ BLT_CONFIG_BITMASK,
+ BLT_CONFIG_BITMASK_INVERT,
+ BLT_CONFIG_DASHES,
+ BLT_CONFIG_FILL,
+ BLT_CONFIG_FLOAT,
+ BLT_CONFIG_INT_NNEG, /* 0..N */
+ BLT_CONFIG_INT_POS, /* 1..N */
+ BLT_CONFIG_LIST,
+ BLT_CONFIG_LONG,
+ BLT_CONFIG_LONG_NNEG, /* 0..N */
+ BLT_CONFIG_LONG_POS, /* 1..N */
+ BLT_CONFIG_OBJ,
+ BLT_CONFIG_PAD,
+ BLT_CONFIG_PIXELS_NNEG, /* 1.1c 2m 3.2i excluding negative
+ values. */
+ BLT_CONFIG_PIXELS_POS, /* 1.1c 2m 3.2i excluding negative
+ * values and zero. */
+ BLT_CONFIG_PIXELS, /* 1.1c 2m 3.2i. */
+ BLT_CONFIG_RESIZE,
+ BLT_CONFIG_SIDE,
+ BLT_CONFIG_STATE,
+ BLT_CONFIG_BACKGROUND,
+ BLT_CONFIG_PIX32,
+
+ BLT_CONFIG_TK_FONT,
+ BLT_CONFIG_END
+} Blt_ConfigTypes;
+
+/*
+ * Possible values for flags argument to Tk_ConfigureWidget:
+ */
+#define BLT_CONFIG_OBJV_ONLY 1
+
+/*
+ * Possible flag values for Blt_ConfigSpec structures. Any bits at or
+ * above BLT_CONFIG_USER_BIT may be used by clients for selecting
+ * certain entries. Before changing any values here, coordinate with
+ * tkOldConfig.c (internal-use-only flags are defined there).
+ */
+/*
+ * Values for "flags" field of Blt_ConfigSpec structures. Be sure to
+ * coordinate these values with those defined in tk.h
+ * (BLT_CONFIG_COLOR_ONLY, etc.). There must not be overlap!
+ *
+ * INIT - Non-zero means (char *) things have been
+ * converted to Tk_Uid's.
+ */
+#define INIT (1<<0)
+#define BLT_CONFIG_NULL_OK (1<<1)
+#define BLT_CONFIG_COLOR_ONLY (1<<2)
+#define BLT_CONFIG_MONO_ONLY (1<<3)
+#define BLT_CONFIG_DONT_SET_DEFAULT (1<<4)
+#define BLT_CONFIG_OPTION_SPECIFIED (1<<5)
+#define BLT_CONFIG_USER_BIT (1<<8)
+
+
+#define SIDE_LEFT (1<<0)
+#define SIDE_TOP (1<<1)
+#define SIDE_RIGHT (1<<2)
+#define SIDE_BOTTOM (1<<3)
+
+#define STATE_NORMAL (0)
+#define STATE_ACTIVE (1<<0)
+#define STATE_DISABLED (1<<1)
+#define STATE_EMPHASIS (1<<2)
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Pad --
+ *
+ * Specifies vertical and horizontal padding.
+ *
+ * Padding can be specified on a per side basis. The fields
+ * side1 and side2 refer to the opposite sides, either
+ * horizontally or vertically.
+ *
+ * side1 side2
+ * ----- -----
+ * x | left right
+ * y | top bottom
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ unsigned short int side1, side2;
+} Blt_Pad;
+
+#define padLeft xPad.side1
+#define padRight xPad.side2
+#define padTop yPad.side1
+#define padBottom yPad.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * The following enumerated values are used as bit flags.
+ * FILL_NONE Neither coordinate plane is specified
+ * FILL_X Horizontal plane.
+ * FILL_Y Vertical plane.
+ * FILL_BOTH Both vertical and horizontal planes.
+ *
+ *---------------------------------------------------------------------------
+ */
+#define FILL_NONE 0
+#define FILL_X 1
+#define FILL_Y 2
+#define FILL_BOTH 3
+
+/*
+ * Resize --
+ *
+ * These flags indicate in what ways each partition in a table can be
+ * resized from its default dimensions. The normal size of a row/column
+ * is the minimum amount of space needed to hold the widgets that span
+ * it. The table may then be stretched or shrunk depending if the
+ * container is larger or smaller than the table. This can occur if 1)
+ * the user resizes the toplevel widget, or 2) the container is in turn
+ * packed into a larger widget and the "fill" option is set.
+ *
+ * RESIZE_NONE - No resizing from normal size.
+ * RESIZE_EXPAND - Do not allow the size to decrease.
+ * The size may increase however.
+ * RESIZE_SHRINK - Do not allow the size to increase.
+ * The size may decrease however.
+ * RESIZE_BOTH - Allow the size to increase or
+ * decrease from the normal size.
+ * RESIZE_VIRGIN - Special case of the resize flag. Used to
+ * indicate the initial state of the flag.
+ * Empty rows/columns are treated differently
+ * if this row/column is set.
+ */
+
+#define RESIZE_NONE 0
+#define RESIZE_EXPAND (1<<0)
+#define RESIZE_SHRINK (1<<1)
+#define RESIZE_BOTH (RESIZE_EXPAND | RESIZE_SHRINK)
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Dashes --
+ *
+ * List of dash values (maximum 11 based upon PostScript limit).
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ unsigned char values[12];
+ int offset;
+} Blt_Dashes;
+
+#define LineIsDashed(d) ((d).values[0] != 0)
+
+/*
+ * Blt_Limits --
+ *
+ * Defines the bounding of a size (width or height) in the paneset. It may
+ * be related to the widget, pane or paneset size.
+ */
+typedef struct {
+ int flags; /* Flags indicate whether using default
+ * values for limits or not. See flags
+ * below. */
+ int max, min; /* Values for respective limits. */
+ int nom; /* Nominal starting value. */
+} Blt_Limits;
+
+#define LIMITS_MIN_SET (1<<0)
+#define LIMITS_MAX_SET (1<<1)
+#define LIMITS_NOM_SET (1<<2)
+
+#define LIMITS_MIN 0 /* Default minimum limit */
+#define LIMITS_MAX SHRT_MAX /* Default maximum limit */
+#define LIMITS_NOM -1000 /* Default nomimal value. Indicates
+ * if a pane has received any space
+ * yet */
+
+BLT_EXTERN void Blt_SetDashes (Display *display, GC gc, Blt_Dashes *dashesPtr);
+BLT_EXTERN Blt_Dashes *Blt_GetDashes (GC gc);
+
+BLT_EXTERN void Blt_ResetLimits(Blt_Limits *limitsPtr);
+BLT_EXTERN int Blt_GetLimitsFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, Blt_Limits *limitsPtr);
+
+BLT_EXTERN int Blt_ConfigureInfoFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Blt_ConfigSpec *specs, char *widgRec, Tcl_Obj *objPtr, int flags);
+
+BLT_EXTERN int Blt_ConfigureValueFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Blt_ConfigSpec *specs, char *widgRec, Tcl_Obj *objPtr, int flags);
+
+BLT_EXTERN int Blt_ConfigureWidgetFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Blt_ConfigSpec *specs, int objc, Tcl_Obj *const *objv, char *widgRec,
+ int flags);
+
+BLT_EXTERN int Blt_ConfigureComponentFromObj(Tcl_Interp *interp,
+ Tk_Window tkwin, const char *name, const char *className,
+ Blt_ConfigSpec *specs, int objc, Tcl_Obj *const *objv, char *widgRec,
+ int flags);
+
+BLT_EXTERN int Blt_ConfigModified TCL_VARARGS(Blt_ConfigSpec *, specs);
+
+BLT_EXTERN const char *Blt_NameOfState(int state);
+BLT_EXTERN const char *Blt_NameOfSide(int side);
+
+BLT_EXTERN void Blt_FreeOptions(Blt_ConfigSpec *specs, char *widgRec,
+ Display *display, int needFlags);
+
+BLT_EXTERN int Blt_ObjIsOption(Blt_ConfigSpec *specs, Tcl_Obj *objPtr,
+ int flags);
+
+BLT_EXTERN int Blt_GetSideFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *sidePtr);
+
+BLT_EXTERN int Blt_GetPositionFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ long *indexPtr);
+
+BLT_EXTERN int Blt_GetPixelsFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, int flags, int *valuePtr);
+
+BLT_EXTERN int Blt_GetPadFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, Blt_Pad *padPtr);
+
+BLT_EXTERN int Blt_GetStateFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *statePtr);
+
+BLT_EXTERN int Blt_GetFillFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *fillPtr);
+
+BLT_EXTERN int Blt_GetResizeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *fillPtr);
+
+BLT_EXTERN int Blt_GetDashesFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Blt_Dashes *dashesPtr);
+
+#if (_TK_VERSION < _VERSION(8,1,0))
+BLT_EXTERN int Tk_GetAnchorFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Tk_Anchor *anchorPtr);
+
+BLT_EXTERN int Tk_GetJustifyFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Tk_Justify *justifyPtr);
+
+BLT_EXTERN int Tk_GetReliefFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *reliefPtr);
+
+BLT_EXTERN int Tk_GetMMFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, double *doublePtr);
+
+BLT_EXTERN int Tk_GetPixelsFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr, int *intPtr);
+
+BLT_EXTERN Tk_3DBorder Tk_Alloc3DBorderFromObj(Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr);
+
+BLT_EXTERN Pixmap Tk_AllocBitmapFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr);
+
+BLT_EXTERN Tk_Font Tk_AllocFontFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr);
+
+BLT_EXTERN Tk_Cursor Tk_AllocCursorFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr);
+
+BLT_EXTERN XColor *Tk_AllocColorFromObj(Tcl_Interp *interp, Tk_Window tkwin,
+ Tcl_Obj *objPtr);
+#endif /* < 8.1 */
+
+#endif /* BLT_CONFIG_H */
diff --git a/src/bltGrAxis.C b/src/bltGrAxis.C
new file mode 100644
index 0000000..f6f54e9
--- /dev/null
+++ b/src/bltGrAxis.C
@@ -0,0 +1,5765 @@
+
+/*
+ * bltGrAxis.c --
+ *
+ * This module implements coordinate axes for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltGrElem.h"
+#include <X11/Xutil.h>
+
+#define MAXTICKS 10001
+
+#define FCLAMP(x) ((((x) < 0.0) ? 0.0 : ((x) > 1.0) ? 1.0 : (x)))
+
+/*
+ * Round x in terms of units
+ */
+#define UROUND(x,u) (Round((x)/(u))*(u))
+#define UCEIL(x,u) (ceil((x)/(u))*(u))
+#define UFLOOR(x,u) (floor((x)/(u))*(u))
+
+#define NUMDIGITS 15 /* Specifies the number of digits of
+ * accuracy used when outputting axis
+ * tick labels. */
+enum TickRange {
+ AXIS_TIGHT, AXIS_LOOSE, AXIS_ALWAYS_LOOSE
+};
+
+#define AXIS_PAD_TITLE 2 /* Padding for axis title. */
+
+/* Axis flags: */
+
+#define AXIS_AUTO_MAJOR (1<<16) /* Auto-generate major ticks. */
+#define AXIS_AUTO_MINOR (1<<17) /* Auto-generate minor ticks. */
+#define AXIS_USE (1<<18) /* Axis is displayed on the screen via
+ * the "use" operation */
+#define AXIS_GRID (1<<19) /* Display grid lines. */
+#define AXIS_GRIDMINOR (1<<20) /* Display grid lines for minor
+ * ticks. */
+#define AXIS_SHOWTICKS (1<<21) /* Display axis ticks. */
+#define AXIS_EXTERIOR (1<<22) /* Axis is exterior to the plot. */
+#define AXIS_CHECK_LIMITS (1<<23) /* Validate user-defined axis limits. */
+
+#define HORIZMARGIN(m) (!((m)->site & 0x1)) /* Even sites are horizontal */
+
+typedef struct {
+ int axis; /* Length of the axis. */
+ int t1; /* Length of a major tick (in
+ * pixels). */
+ int t2; /* Length of a minor tick (in
+ * pixels). */
+ int label; /* Distance from axis to tick label. */
+} AxisInfo;
+
+typedef struct {
+ const char *name;
+ ClassId classId;
+ int margin, invertMargin;
+} AxisName;
+
+static AxisName axisNames[] = {
+ { "x", CID_AXIS_X, MARGIN_BOTTOM, MARGIN_LEFT },
+ { "y", CID_AXIS_Y, MARGIN_LEFT, MARGIN_BOTTOM },
+ { "x2", CID_AXIS_X, MARGIN_TOP, MARGIN_RIGHT },
+ { "y2", CID_AXIS_Y, MARGIN_RIGHT, MARGIN_TOP }
+} ;
+static int nAxisNames = sizeof(axisNames) / sizeof(AxisName);
+
+static Blt_OptionParseProc ObjToLimitProc;
+static Blt_OptionPrintProc LimitToObjProc;
+static Blt_CustomOption limitOption = {
+ ObjToLimitProc, LimitToObjProc, NULL, (ClientData)0
+};
+
+static Blt_OptionFreeProc FreeTicksProc;
+static Blt_OptionParseProc ObjToTicksProc;
+static Blt_OptionPrintProc TicksToObjProc;
+static Blt_CustomOption majorTicksOption = {
+ ObjToTicksProc, TicksToObjProc, FreeTicksProc, (ClientData)AXIS_AUTO_MAJOR,
+};
+static Blt_CustomOption minorTicksOption = {
+ ObjToTicksProc, TicksToObjProc, FreeTicksProc, (ClientData)AXIS_AUTO_MINOR,
+};
+static Blt_OptionFreeProc FreeAxisProc;
+static Blt_OptionPrintProc AxisToObjProc;
+static Blt_OptionParseProc ObjToAxisProc;
+Blt_CustomOption bltXAxisOption = {
+ ObjToAxisProc, AxisToObjProc, FreeAxisProc, (ClientData)CID_AXIS_X
+};
+Blt_CustomOption bltYAxisOption = {
+ ObjToAxisProc, AxisToObjProc, FreeAxisProc, (ClientData)CID_AXIS_Y
+};
+
+static Blt_OptionFreeProc FreeFormatProc;
+static Blt_OptionParseProc ObjToFormatProc;
+static Blt_OptionPrintProc FormatToObjProc;
+static Blt_CustomOption formatOption = {
+ ObjToFormatProc, FormatToObjProc, FreeFormatProc, (ClientData)0,
+};
+static Blt_OptionParseProc ObjToLooseProc;
+static Blt_OptionPrintProc LooseToObjProc;
+static Blt_CustomOption looseOption = {
+ ObjToLooseProc, LooseToObjProc, NULL, (ClientData)0,
+};
+
+static Blt_OptionParseProc ObjToUseProc;
+static Blt_OptionPrintProc UseToObjProc;
+static Blt_CustomOption useOption = {
+ ObjToUseProc, UseToObjProc, NULL, (ClientData)0
+};
+
+#define DEF_AXIS_ACTIVEBACKGROUND STD_ACTIVE_BACKGROUND
+#define DEF_AXIS_ACTIVEFOREGROUND STD_ACTIVE_FOREGROUND
+#define DEF_AXIS_ACTIVERELIEF "flat"
+#define DEF_AXIS_ANGLE "0.0"
+#define DEF_AXIS_BACKGROUND (char *)NULL
+#define DEF_AXIS_BORDERWIDTH "0"
+#define DEF_AXIS_CHECKLIMITS "0"
+#define DEF_AXIS_COMMAND (char *)NULL
+#define DEF_AXIS_DESCENDING "0"
+#define DEF_AXIS_FOREGROUND RGB_BLACK
+#define DEF_AXIS_GRID_BARCHART "1"
+#define DEF_AXIS_GRIDCOLOR RGB_GREY64
+#define DEF_AXIS_GRIDDASHES "dot"
+#define DEF_AXIS_GRID_GRAPH "0"
+#define DEF_AXIS_GRIDLINEWIDTH "0"
+#define DEF_AXIS_GRIDMINOR "1"
+#define DEF_AXIS_GRIDMINOR_COLOR RGB_GREY64
+#define DEF_AXIS_HIDE "0"
+#define DEF_AXIS_JUSTIFY "c"
+#define DEF_AXIS_LIMITS_FORMAT (char *)NULL
+#define DEF_AXIS_LINEWIDTH "1"
+#define DEF_AXIS_LOGSCALE "0"
+#define DEF_AXIS_LOOSE "0"
+#define DEF_AXIS_RANGE "0.0"
+#define DEF_AXIS_RELIEF "flat"
+#define DEF_AXIS_SCROLL_INCREMENT "10"
+#define DEF_AXIS_SHIFTBY "0.0"
+#define DEF_AXIS_SHOWTICKS "1"
+#define DEF_AXIS_STEP "0.0"
+#define DEF_AXIS_STEP "0.0"
+#define DEF_AXIS_SUBDIVISIONS "2"
+#define DEF_AXIS_TAGS "all"
+#define DEF_AXIS_EXTERIOR "1"
+#define DEF_AXIS_TICK_ANCHOR "c"
+#define DEF_AXIS_LIMITS_FONT STD_FONT_NUMBERS
+#define DEF_AXIS_TICKFONT_GRAPH STD_FONT_NUMBERS
+#define DEF_AXIS_TICKFONT_BARCHART STD_FONT_SMALL
+#define DEF_AXIS_TICKLENGTH "4"
+#define DEF_AXIS_DIVISIONS "10"
+#define DEF_AXIS_TITLE_ALTERNATE "0"
+#define DEF_AXIS_TITLE_FG RGB_BLACK
+#define DEF_AXIS_TITLE_FONT "{Sans Serif} 10"
+#define DEF_AXIS_X_STEP_BARCHART "1.0"
+#define DEF_AXIS_X_SUBDIVISIONS_BARCHART "0"
+
+static Blt_ConfigSpec configSpecs[] =
+{
+ {BLT_CONFIG_BACKGROUND, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_AXIS_ACTIVEBACKGROUND,
+ Blt_Offset(Axis, activeBg), ALL_GRAPHS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_AXIS_ACTIVEFOREGROUND,
+ Blt_Offset(Axis, activeFgColor), ALL_GRAPHS},
+ {BLT_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_AXIS_ACTIVERELIEF, Blt_Offset(Axis, activeRelief),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_DOUBLE, "-autorange", "autoRange", "AutoRange",
+ DEF_AXIS_RANGE, Blt_Offset(Axis, windowSize),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_AXIS_BACKGROUND, Blt_Offset(Axis, normalBg),
+ ALL_GRAPHS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_AXIS_TAGS,
+ Blt_Offset(Axis, obj.tags), ALL_GRAPHS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,
+ 0, ALL_GRAPHS},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_AXIS_BORDERWIDTH, Blt_Offset(Axis, borderWidth),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-checklimits", "checkLimits", "CheckLimits",
+ DEF_AXIS_CHECKLIMITS, Blt_Offset(Axis, flags),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)AXIS_CHECK_LIMITS},
+ {BLT_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_AXIS_FOREGROUND, Blt_Offset(Axis, tickColor), ALL_GRAPHS},
+ {BLT_CONFIG_STRING, "-command", "command", "Command",
+ DEF_AXIS_COMMAND, Blt_Offset(Axis, formatCmd),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS},
+ {BLT_CONFIG_BOOLEAN, "-descending", "descending", "Descending",
+ DEF_AXIS_DESCENDING, Blt_Offset(Axis, descending),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-exterior", "exterior", "exterior", DEF_AXIS_EXTERIOR,
+ Blt_Offset(Axis, flags), ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)AXIS_EXTERIOR},
+ {BLT_CONFIG_SYNONYM, "-fg", "color", (char *)NULL,
+ (char *)NULL, 0, ALL_GRAPHS},
+ {BLT_CONFIG_SYNONYM, "-foreground", "color", (char *)NULL,
+ (char *)NULL, 0, ALL_GRAPHS},
+ {BLT_CONFIG_BITMASK, "-grid", "grid", "Grid", DEF_AXIS_GRID_BARCHART,
+ Blt_Offset(Axis, flags), BARCHART, (Blt_CustomOption *)AXIS_GRID},
+ {BLT_CONFIG_BITMASK, "-grid", "grid", "Grid", DEF_AXIS_GRID_GRAPH,
+ Blt_Offset(Axis, flags), GRAPH | STRIPCHART,
+ (Blt_CustomOption *)AXIS_GRID},
+ {BLT_CONFIG_COLOR, "-gridcolor", "gridColor", "GridColor",
+ DEF_AXIS_GRIDCOLOR, Blt_Offset(Axis, major.color), ALL_GRAPHS},
+ {BLT_CONFIG_DASHES, "-griddashes", "gridDashes", "GridDashes",
+ DEF_AXIS_GRIDDASHES, Blt_Offset(Axis, major.dashes),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS},
+ {BLT_CONFIG_PIXELS_NNEG, "-gridlinewidth", "gridLineWidth",
+ "GridLineWidth", DEF_AXIS_GRIDLINEWIDTH,
+ Blt_Offset(Axis, major.lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+ {BLT_CONFIG_BITMASK, "-gridminor", "gridMinor", "GridMinor",
+ DEF_AXIS_GRIDMINOR, Blt_Offset(Axis, flags),
+ BLT_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS,
+ (Blt_CustomOption *)AXIS_GRIDMINOR},
+ {BLT_CONFIG_COLOR, "-gridminorcolor", "gridMinorColor", "GridColor",
+ DEF_AXIS_GRIDMINOR_COLOR, Blt_Offset(Axis, minor.color), ALL_GRAPHS},
+ {BLT_CONFIG_DASHES, "-gridminordashes", "gridMinorDashes", "GridDashes",
+ DEF_AXIS_GRIDDASHES, Blt_Offset(Axis, minor.dashes),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS},
+ {BLT_CONFIG_PIXELS_NNEG, "-gridminorlinewidth", "gridMinorLineWidth",
+ "GridLineWidth", DEF_AXIS_GRIDLINEWIDTH,
+ Blt_Offset(Axis, minor.lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_AXIS_HIDE,
+ Blt_Offset(Axis, flags), ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_AXIS_JUSTIFY, Blt_Offset(Axis, titleJustify),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-labeloffset", "labelOffset", "LabelOffset",
+ (char *)NULL, Blt_Offset(Axis, labelOffset), ALL_GRAPHS},
+ {BLT_CONFIG_COLOR, "-limitscolor", "limitsColor", "Color",
+ DEF_AXIS_FOREGROUND, Blt_Offset(Axis, limitsTextStyle.color),
+ ALL_GRAPHS},
+ {BLT_CONFIG_FONT, "-limitsfont", "limitsFont", "Font", DEF_AXIS_LIMITS_FONT,
+ Blt_Offset(Axis, limitsTextStyle.font), ALL_GRAPHS},
+ {BLT_CONFIG_CUSTOM, "-limitsformat", "limitsFormat", "LimitsFormat",
+ (char *)NULL, Blt_Offset(Axis, limitsFormats),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS, &formatOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ DEF_AXIS_LINEWIDTH, Blt_Offset(Axis, lineWidth),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-logscale", "logScale", "LogScale",
+ DEF_AXIS_LOGSCALE, Blt_Offset(Axis, logScale),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-loose", "loose", "Loose", DEF_AXIS_LOOSE, 0,
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT, &looseOption},
+ {BLT_CONFIG_CUSTOM, "-majorticks", "majorTicks", "MajorTicks",
+ (char *)NULL, Blt_Offset(Axis, t1Ptr),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS, &majorTicksOption},
+ {BLT_CONFIG_CUSTOM, "-max", "max", "Max", (char *)NULL,
+ Blt_Offset(Axis, reqMax), ALL_GRAPHS, &limitOption},
+ {BLT_CONFIG_CUSTOM, "-min", "min", "Min", (char *)NULL,
+ Blt_Offset(Axis, reqMin), ALL_GRAPHS, &limitOption},
+ {BLT_CONFIG_CUSTOM, "-minorticks", "minorTicks", "MinorTicks",
+ (char *)NULL, Blt_Offset(Axis, t2Ptr),
+ BLT_CONFIG_NULL_OK | ALL_GRAPHS, &minorTicksOption},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_AXIS_RELIEF, Blt_Offset(Axis, relief),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FLOAT, "-rotate", "rotate", "Rotate", DEF_AXIS_ANGLE,
+ Blt_Offset(Axis, tickAngle), ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_OBJ, "-scrollcommand", "scrollCommand", "ScrollCommand",
+ (char *)NULL, Blt_Offset(Axis, scrollCmdObjPtr),
+ ALL_GRAPHS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_POS, "-scrollincrement", "scrollIncrement",
+ "ScrollIncrement", DEF_AXIS_SCROLL_INCREMENT,
+ Blt_Offset(Axis, scrollUnits), ALL_GRAPHS|BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-scrollmax", "scrollMax", "ScrollMax", (char *)NULL,
+ Blt_Offset(Axis, reqScrollMax), ALL_GRAPHS, &limitOption},
+ {BLT_CONFIG_CUSTOM, "-scrollmin", "scrollMin", "ScrollMin", (char *)NULL,
+ Blt_Offset(Axis, reqScrollMin), ALL_GRAPHS, &limitOption},
+ {BLT_CONFIG_DOUBLE, "-shiftby", "shiftBy", "ShiftBy",
+ DEF_AXIS_SHIFTBY, Blt_Offset(Axis, shiftBy),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-showticks", "showTicks", "ShowTicks",
+ DEF_AXIS_SHOWTICKS, Blt_Offset(Axis, flags),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)AXIS_SHOWTICKS},
+ {BLT_CONFIG_DOUBLE, "-stepsize", "stepSize", "StepSize",
+ DEF_AXIS_STEP, Blt_Offset(Axis, reqStep),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_INT, "-subdivisions", "subdivisions", "Subdivisions",
+ DEF_AXIS_SUBDIVISIONS, Blt_Offset(Axis, reqNumMinorTicks), ALL_GRAPHS},
+ {BLT_CONFIG_ANCHOR, "-tickanchor", "tickAnchor", "Anchor",
+ DEF_AXIS_TICK_ANCHOR, Blt_Offset(Axis, reqTickAnchor), ALL_GRAPHS},
+ {BLT_CONFIG_FONT, "-tickfont", "tickFont", "Font",
+ DEF_AXIS_TICKFONT_GRAPH, Blt_Offset(Axis, tickFont),
+ GRAPH | STRIPCHART},
+ {BLT_CONFIG_FONT, "-tickfont", "tickFont", "Font",
+ DEF_AXIS_TICKFONT_BARCHART, Blt_Offset(Axis, tickFont), BARCHART},
+ {BLT_CONFIG_PIXELS_NNEG, "-ticklength", "tickLength", "TickLength",
+ DEF_AXIS_TICKLENGTH, Blt_Offset(Axis, tickLength),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_INT, "-tickdefault", "tickDefault", "TickDefault",
+ DEF_AXIS_DIVISIONS, Blt_Offset(Axis, reqNumMajorTicks),
+ ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-title", "title", "Title",
+ (char *)NULL, Blt_Offset(Axis, title),
+ BLT_CONFIG_DONT_SET_DEFAULT | BLT_CONFIG_NULL_OK | ALL_GRAPHS},
+ {BLT_CONFIG_BOOLEAN, "-titlealternate", "titleAlternate", "TitleAlternate",
+ DEF_AXIS_TITLE_ALTERNATE, Blt_Offset(Axis, titleAlternate),
+ BLT_CONFIG_DONT_SET_DEFAULT | ALL_GRAPHS},
+ {BLT_CONFIG_COLOR, "-titlecolor", "titleColor", "Color",
+ DEF_AXIS_FOREGROUND, Blt_Offset(Axis, titleColor),
+ ALL_GRAPHS},
+ {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font", DEF_AXIS_TITLE_FONT,
+ Blt_Offset(Axis, titleFont), ALL_GRAPHS},
+ {BLT_CONFIG_CUSTOM, "-use", "use", "Use", (char *)NULL, 0, ALL_GRAPHS,
+ &useOption},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+static void DestroyAxis(Axis *axisPtr);
+static Tcl_FreeProc FreeAxis;
+static int GetAxisByClass(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ ClassId classId, Axis **axisPtrPtr);
+static void TimeScaleAxis(Axis *axisPtr, double min, double max);
+
+static int lastMargin;
+typedef int (GraphAxisProc)(Tcl_Interp *interp, Axis *axisPtr, int objc,
+ Tcl_Obj *const *objv);
+typedef int (GraphVirtualAxisProc)(Tcl_Interp *interp, Graph *graphPtr,
+ int objc, Tcl_Obj *const *objv);
+
+INLINE static double
+Clamp(double x)
+{
+ return (x < 0.0) ? 0.0 : (x > 1.0) ? 1.0 : x;
+}
+
+INLINE static int
+Round(double x)
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+static void
+SetAxisRange(AxisRange *rangePtr, double min, double max)
+{
+ rangePtr->min = min;
+ rangePtr->max = max;
+ rangePtr->range = max - min;
+ if (FABS(rangePtr->range) < DBL_EPSILON) {
+ rangePtr->range = 1.0;
+ }
+ rangePtr->scale = 1.0 / rangePtr->range;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ * Determines if a value lies within a given range.
+ *
+ * The value is normalized and compared against the interval [0..1],
+ * where 0.0 is the minimum and 1.0 is the maximum. DBL_EPSILON is the
+ * smallest number that can be represented on the host machine, such that
+ * (1.0 + epsilon) != 1.0.
+ *
+ * Please note, *max* can't equal *min*.
+ *
+ * Results:
+ * If the value is within the interval [min..max], 1 is returned; 0
+ * otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+INLINE static int
+InRange(double x, AxisRange *rangePtr)
+{
+ if (rangePtr->range < DBL_EPSILON) {
+ return (FABS(rangePtr->max - x) >= DBL_EPSILON);
+ } else {
+ double norm;
+
+ norm = (x - rangePtr->min) * rangePtr->scale;
+ return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+ }
+}
+
+INLINE static int
+AxisIsHorizontal(Axis *axisPtr)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ return ((axisPtr->obj.classId == CID_AXIS_Y) == graphPtr->inverted);
+}
+
+
+static void
+ReleaseAxis(Axis *axisPtr)
+{
+ if (axisPtr != NULL) {
+ axisPtr->refCount--;
+ assert(axisPtr->refCount >= 0);
+ if (axisPtr->refCount == 0) {
+ axisPtr->flags |= DELETE_PENDING;
+ Tcl_EventuallyFree(axisPtr, FreeAxis);
+ }
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Custom option parse and print procedures
+ *-----------------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static void
+FreeAxisProc(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+
+ if (*axisPtrPtr != NULL) {
+ ReleaseAxis(*axisPtrPtr);
+ *axisPtrPtr = NULL;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToAxisProc --
+ *
+ * Converts the name of an axis to a pointer to its axis structure.
+ *
+ * Results:
+ * The return value is a standard TCL result. The axis flags are written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToAxisProc(
+ ClientData clientData, /* Class identifier of the type of
+ * axis we are looking for. */
+ Tcl_Interp *interp, /* Interpreter to report results. */
+ Tk_Window tkwin, /* Used to look up pointer to graph. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ ClassId classId = (ClassId)clientData;
+ Axis **axisPtrPtr = (Axis **)(widgRec + offset);
+ Axis *axisPtr;
+ Graph *graphPtr;
+
+ if (flags & BLT_CONFIG_NULL_OK) {
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (string[0] == '\0') {
+ ReleaseAxis(*axisPtrPtr);
+ *axisPtrPtr = NULL;
+ return TCL_OK;
+ }
+ }
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ assert(graphPtr);
+ if (GetAxisByClass(interp, graphPtr, objPtr, classId, &axisPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ReleaseAxis(*axisPtrPtr);
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisToObjProc --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+AxisToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Pointer to structure record .*/
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = *(Axis **)(widgRec + offset);
+ const char *name;
+
+ name = (axisPtr == NULL) ? "" : axisPtr->obj.name;
+ return Tcl_NewStringObj(name, -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeFormatProc(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+
+ if (axisPtr->limitsFormats != NULL) {
+ Blt_Free(axisPtr->limitsFormats);
+ axisPtr->limitsFormats = NULL;
+ }
+ axisPtr->nFormats = 0;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToFormatProc --
+ *
+ * Convert the name of virtual axis to an pointer.
+ *
+ * Results:
+ * The return value is a standard TCL result. The axis flags are written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToFormatProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to report results. */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ const char **argv;
+ int argc;
+
+ if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, &argv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (argc > 2) {
+ Tcl_AppendResult(interp, "too many elements in limits format list \"",
+ Tcl_GetString(objPtr), "\"", (char *)NULL);
+ Blt_Free(argv);
+ return TCL_ERROR;
+ }
+ if (axisPtr->limitsFormats != NULL) {
+ Blt_Free(axisPtr->limitsFormats);
+ }
+ axisPtr->limitsFormats = argv;
+ axisPtr->nFormats = argc;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FormatToObjProc --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+FormatToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ Tcl_Obj *objPtr;
+
+ if (axisPtr->nFormats == 0) {
+ objPtr = Tcl_NewStringObj("", -1);
+ } else {
+ const char *string;
+
+ string = Tcl_Merge(axisPtr->nFormats, axisPtr->limitsFormats);
+ objPtr = Tcl_NewStringObj(string, -1);
+ Blt_Free(string);
+ }
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToLimitProc --
+ *
+ * Convert the string representation of an axis limit into its numeric
+ * form.
+ *
+ * Results:
+ * The return value is a standard TCL result. The symbol type is written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToLimitProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ double *limitPtr = (double *)(widgRec + offset);
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ if (string[0] == '\0') {
+ *limitPtr = Blt_NaN();
+ } else if (Blt_ExprDoubleFromObj(interp, objPtr, limitPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LimitToObjProc --
+ *
+ * Convert the floating point axis limits into a string.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+LimitToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ double limit = *(double *)(widgRec + offset);
+ Tcl_Obj *objPtr;
+
+ if (DEFINED(limit)) {
+ objPtr = Tcl_NewDoubleObj(limit);
+ } else {
+ objPtr = Tcl_NewStringObj("", -1);
+ }
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToUseProc --
+ *
+ * Convert the string representation of the margin to use into its
+ * numeric form.
+ *
+ * Results:
+ * The return value is a standard TCL result. The use type is written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToUseProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results. */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ AxisName *p, *pend;
+ Blt_Chain chain;
+ Graph *graphPtr;
+ const char *string;
+ int margin;
+
+ graphPtr = axisPtr->obj.graphPtr;
+ if (axisPtr->refCount == 0) {
+ /* Clear the axis class if it's not currently used by an element.*/
+ Blt_GraphSetObjectClass(&axisPtr->obj, CID_NONE);
+ }
+ /* Remove the axis from the margin's use list and clear its use flag. */
+ if (axisPtr->link != NULL) {
+ Blt_Chain_UnlinkLink(axisPtr->chain, axisPtr->link);
+ }
+ axisPtr->flags &= ~AXIS_USE;
+ string = Tcl_GetString(objPtr);
+ if ((string == NULL) || (string[0] == '\0')) {
+ goto done;
+ }
+ for (p = axisNames, pend = axisNames + nAxisNames; p < pend; p++) {
+ if (strcmp(p->name, string) == 0) {
+ break; /* Found the axis name. */
+ }
+ }
+ if (p == pend) {
+ Tcl_AppendResult(interp, "unknown axis type \"", string, "\": "
+ "should be x, y, x1, y2, or \"\".", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Check the axis class. Can't use the axis if it's already being used as
+ * another type. */
+ if (axisPtr->obj.classId == CID_NONE) {
+ Blt_GraphSetObjectClass(&axisPtr->obj, p->classId);
+ } else if (axisPtr->obj.classId != p->classId) {
+ Tcl_AppendResult(interp, "wrong type for axis \"",
+ axisPtr->obj.name, "\": can't use ",
+ axisPtr->obj.className, " type axis.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ margin = (graphPtr->inverted) ? p->invertMargin : p->margin;
+ chain = graphPtr->margins[margin].axes;
+ if (axisPtr->link != NULL) {
+ /* Move the axis from the old margin's "use" list to the new. */
+ Blt_Chain_AppendLink(chain, axisPtr->link);
+ } else {
+ axisPtr->link = Blt_Chain_Append(chain, axisPtr);
+ }
+ axisPtr->chain = chain;
+ axisPtr->flags |= AXIS_USE;
+ axisPtr->margin = margin;
+ done:
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+ /* When any axis changes, we need to layout the entire graph. */
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UseToObjProc --
+ *
+ * Convert the floating point axis limits into a string.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+UseToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+
+ if (axisPtr->margin == MARGIN_NONE) {
+ return Tcl_NewStringObj("", -1);
+ }
+ return Tcl_NewStringObj(axisNames[axisPtr->margin].name, -1);
+}
+
+/*ARGSUSED*/
+static void
+FreeTicksProc(
+ ClientData clientData, /* Either AXIS_AUTO_MAJOR or
+ * AXIS_AUTO_MINOR. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Axis *axisPtr = (Axis *)widgRec;
+ Ticks **ticksPtrPtr = (Ticks **) (widgRec + offset);
+ unsigned long mask = (unsigned long)clientData;
+
+ axisPtr->flags |= mask;
+ if (*ticksPtrPtr != NULL) {
+ Blt_Free(*ticksPtrPtr);
+ }
+ *ticksPtrPtr = NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToTicksProc --
+ *
+ *
+ * Results:
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToTicksProc(
+ ClientData clientData, /* Either AXIS_AUTO_MAJOR or
+ * AXIS_AUTO_MINOR. */
+ Tcl_Interp *interp, /* Interpreter to send results. */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)widgRec;
+ Tcl_Obj **objv;
+ Ticks **ticksPtrPtr = (Ticks **) (widgRec + offset);
+ Ticks *ticksPtr;
+ int objc;
+ unsigned long mask = (unsigned long)clientData;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ axisPtr->flags |= mask;
+ ticksPtr = NULL;
+ if (objc > 0) {
+ int i;
+
+ ticksPtr = Blt_AssertMalloc(sizeof(Ticks) + (objc*sizeof(double)));
+ for (i = 0; i < objc; i++) {
+ double value;
+
+ if (Blt_ExprDoubleFromObj(interp, objv[i], &value) != TCL_OK) {
+ Blt_Free(ticksPtr);
+ return TCL_ERROR;
+ }
+ ticksPtr->values[i] = value;
+ }
+ ticksPtr->nTicks = objc;
+ axisPtr->flags &= ~mask;
+ }
+ FreeTicksProc(clientData, Tk_Display(tkwin), widgRec, offset);
+ *ticksPtrPtr = ticksPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TicksToObjProc --
+ *
+ * Convert array of tick coordinates to a list.
+ *
+ * Results:
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+TicksToObjProc(
+ ClientData clientData, /* Either AXIS_AUTO_MAJOR or
+ * AXIS_AUTO_MINOR. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr;
+ Tcl_Obj *listObjPtr;
+ Ticks *ticksPtr;
+ unsigned long mask;
+
+ axisPtr = (Axis *)widgRec;
+ ticksPtr = *(Ticks **) (widgRec + offset);
+ mask = (unsigned long)clientData;
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if ((ticksPtr != NULL) && ((axisPtr->flags & mask) == 0)) {
+ unsigned int i;
+
+ for (i = 0; i < ticksPtr->nTicks; i++) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewDoubleObj(ticksPtr->values[i]);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToLooseProc --
+ *
+ * Convert a string to one of three values.
+ * 0 - false, no, off
+ * 1 - true, yes, on
+ * 2 - always
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left in
+ * interpreter's result field.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToLooseProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results. */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing new value. */
+ char *widgRec, /* Pointer to structure record. */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)(widgRec);
+ Tcl_Obj **objv;
+ int i;
+ int objc;
+ int values[2];
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc < 1) || (objc > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in loose value \"",
+ Tcl_GetString(objPtr), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ const char *string;
+
+ string = Tcl_GetString(objv[i]);
+ if ((string[0] == 'a') && (strcmp(string, "always") == 0)) {
+ values[i] = AXIS_ALWAYS_LOOSE;
+ } else {
+ int bool;
+
+ if (Tcl_GetBooleanFromObj(interp, objv[i], &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ values[i] = bool;
+ }
+ }
+ axisPtr->looseMin = axisPtr->looseMax = values[0];
+ if (objc > 1) {
+ axisPtr->looseMax = values[1];
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LooseToObjProc --
+ *
+ * Results:
+ * The string representation of the auto boolean is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+LooseToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Axis *axisPtr = (Axis *)widgRec;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (axisPtr->looseMin == AXIS_TIGHT) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewBooleanObj(FALSE));
+ } else if (axisPtr->looseMin == AXIS_LOOSE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewBooleanObj(TRUE));
+ } else if (axisPtr->looseMin == AXIS_ALWAYS_LOOSE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("always", 6));
+ }
+ if (axisPtr->looseMin != axisPtr->looseMax) {
+ if (axisPtr->looseMax == AXIS_TIGHT) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewBooleanObj(FALSE));
+ } else if (axisPtr->looseMax == AXIS_LOOSE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewBooleanObj(TRUE));
+ } else if (axisPtr->looseMax == AXIS_ALWAYS_LOOSE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("always", 6));
+ }
+ }
+ return listObjPtr;
+}
+
+static void
+FreeTickLabels(Blt_Chain chain)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ TickLabel *labelPtr;
+
+ labelPtr = Blt_Chain_GetValue(link);
+ Blt_Free(labelPtr);
+ }
+ Blt_Chain_Reset(chain);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MakeLabel --
+ *
+ * Converts a floating point tick value to a string to be used as its
+ * label.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Returns a new label in the string character buffer. The formatted
+ * tick label will be displayed on the graph.
+ *
+ * --------------------------------------------------------------------------
+ */
+static TickLabel *
+MakeLabel(Axis *axisPtr, double value)
+{
+#define TICK_LABEL_SIZE 200
+ char string[TICK_LABEL_SIZE + 1];
+ TickLabel *labelPtr;
+
+ /* Generate a default tick label based upon the tick value. */
+ if (axisPtr->logScale) {
+ sprintf_s(string, TICK_LABEL_SIZE, "1E%d", ROUND(value));
+ } else {
+ sprintf_s(string, TICK_LABEL_SIZE, "%.*G", NUMDIGITS, value);
+ }
+
+ if (axisPtr->formatCmd != NULL) {
+ Graph *graphPtr;
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+
+ graphPtr = axisPtr->obj.graphPtr;
+ interp = graphPtr->interp;
+ tkwin = graphPtr->tkwin;
+ /*
+ * A TCL proc was designated to format tick labels. Append the path
+ * name of the widget and the default tick label as arguments when
+ * invoking it. Copy and save the new label from interp->result.
+ */
+ Tcl_ResetResult(interp);
+ if (Tcl_VarEval(interp, axisPtr->formatCmd, " ", Tk_PathName(tkwin),
+ " ", string, (char *)NULL) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ } else {
+ /*
+ * The proc could return a string of any length, so arbitrarily
+ * limit it to what will fit in the return string.
+ */
+ strncpy(string, Tcl_GetStringResult(interp), TICK_LABEL_SIZE);
+ string[TICK_LABEL_SIZE] = '\0';
+
+ Tcl_ResetResult(interp); /* Clear the interpreter's result. */
+ }
+ }
+ labelPtr = Blt_AssertMalloc(sizeof(TickLabel) + strlen(string));
+ strcpy(labelPtr->string, string);
+ labelPtr->anchorPos.x = labelPtr->anchorPos.y = DBL_MAX;
+ return labelPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_InvHMap --
+ *
+ * Maps the given screen coordinate back to a graph coordinate. Called
+ * by the graph locater routine.
+ *
+ * Results:
+ * Returns the graph coordinate value at the given window
+ * y-coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+double
+Blt_InvHMap(Axis *axisPtr, double x)
+{
+ double value;
+
+ x = (double)(x - axisPtr->screenMin) * axisPtr->screenScale;
+ if (axisPtr->descending) {
+ x = 1.0 - x;
+ }
+ value = (x * axisPtr->axisRange.range) + axisPtr->axisRange.min;
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ return value;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_InvVMap --
+ *
+ * Maps the given screen y-coordinate back to the graph coordinate
+ * value. Called by the graph locater routine.
+ *
+ * Results:
+ * Returns the graph coordinate value for the given screen
+ * coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+double
+Blt_InvVMap(Axis *axisPtr, double y) /* Screen coordinate */
+{
+ double value;
+
+ y = (double)(y - axisPtr->screenMin) * axisPtr->screenScale;
+ if (axisPtr->descending) {
+ y = 1.0 - y;
+ }
+ value = ((1.0 - y) * axisPtr->axisRange.range) + axisPtr->axisRange.min;
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ return value;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_HMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a window
+ * position.
+ *
+ * Results:
+ * Returns a double precision number representing the window coordinate
+ * position on the given axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+double
+Blt_HMap(Axis *axisPtr, double x)
+{
+ if ((axisPtr->logScale) && (x != 0.0)) {
+ x = log10(FABS(x));
+ }
+ /* Map graph coordinate to normalized coordinates [0..1] */
+ x = (x - axisPtr->axisRange.min) * axisPtr->axisRange.scale;
+ if (axisPtr->descending) {
+ x = 1.0 - x;
+ }
+ return (x * axisPtr->screenRange + axisPtr->screenMin);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_VMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a window
+ * position.
+ *
+ * Results:
+ * Returns a double precision number representing the window coordinate
+ * position on the given axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+double
+Blt_VMap(Axis *axisPtr, double y)
+{
+ if ((axisPtr->logScale) && (y != 0.0)) {
+ y = log10(FABS(y));
+ }
+ /* Map graph coordinate to normalized coordinates [0..1] */
+ y = (y - axisPtr->axisRange.min) * axisPtr->axisRange.scale;
+ if (axisPtr->descending) {
+ y = 1.0 - y;
+ }
+ return ((1.0 - y) * axisPtr->screenRange + axisPtr->screenMin);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Map2D --
+ *
+ * Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ * Returns a XPoint structure containing the window coordinates of
+ * the given graph x,y coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+Point2d
+Blt_Map2D(
+ Graph *graphPtr,
+ double x, double y, /* Graph x and y coordinates */
+ Axis2d *axesPtr) /* Specifies which axes to use */
+{
+ Point2d point;
+
+ if (graphPtr->inverted) {
+ point.x = Blt_HMap(axesPtr->y, y);
+ point.y = Blt_VMap(axesPtr->x, x);
+ } else {
+ point.x = Blt_HMap(axesPtr->x, x);
+ point.y = Blt_VMap(axesPtr->y, y);
+ }
+ return point;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_InvMap2D --
+ *
+ * Maps the given window x,y coordinates to graph values.
+ *
+ * Results:
+ * Returns a structure containing the graph coordinates of the given
+ * window x,y coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+Point2d
+Blt_InvMap2D(
+ Graph *graphPtr,
+ double x, double y, /* Window x and y coordinates */
+ Axis2d *axesPtr) /* Specifies which axes to use */
+{
+ Point2d point;
+
+ if (graphPtr->inverted) {
+ point.x = Blt_InvVMap(axesPtr->x, y);
+ point.y = Blt_InvHMap(axesPtr->y, x);
+ } else {
+ point.x = Blt_InvHMap(axesPtr->x, x);
+ point.y = Blt_InvVMap(axesPtr->y, y);
+ }
+ return point;
+}
+
+
+static void
+GetDataLimits(Axis *axisPtr, double min, double max)
+{
+ if (axisPtr->valueRange.min > min) {
+ axisPtr->valueRange.min = min;
+ }
+ if (axisPtr->valueRange.max < max) {
+ axisPtr->valueRange.max = max;
+ }
+}
+
+static void
+FixAxisRange(Axis *axisPtr)
+{
+ double min, max;
+
+ /*
+ * When auto-scaling, the axis limits are the bounds of the element data.
+ * If no data exists, set arbitrary limits (wrt to log/linear scale).
+ */
+ min = axisPtr->valueRange.min;
+ max = axisPtr->valueRange.max;
+
+ /* Check the requested axis limits. Can't allow -min to be greater
+ * than -max, or have undefined log scale limits. */
+ if (((DEFINED(axisPtr->reqMin)) && (DEFINED(axisPtr->reqMax))) &&
+ (axisPtr->reqMin >= axisPtr->reqMax)) {
+ axisPtr->reqMin = axisPtr->reqMax = Blt_NaN();
+ }
+ if (axisPtr->logScale) {
+ if ((DEFINED(axisPtr->reqMin)) && (axisPtr->reqMin <= 0.0)) {
+ axisPtr->reqMin = Blt_NaN();
+ }
+ if ((DEFINED(axisPtr->reqMax)) && (axisPtr->reqMax <= 0.0)) {
+ axisPtr->reqMax = Blt_NaN();
+ }
+ }
+
+ if (min == DBL_MAX) {
+ if (DEFINED(axisPtr->reqMin)) {
+ min = axisPtr->reqMin;
+ } else {
+ min = (axisPtr->logScale) ? 0.001 : 0.0;
+ }
+ }
+ if (max == -DBL_MAX) {
+ if (DEFINED(axisPtr->reqMax)) {
+ max = axisPtr->reqMax;
+ } else {
+ max = 1.0;
+ }
+ }
+ if (min >= max) {
+ /*
+ * There is no range of data (i.e. min is not less than max), so
+ * manufacture one.
+ */
+ if (min == 0.0) {
+ min = 0.0, max = 1.0;
+ } else {
+ max = min + (FABS(min) * 0.1);
+ }
+ }
+ SetAxisRange(&axisPtr->valueRange, min, max);
+
+ /*
+ * The axis limits are either the current data range or overridden by the
+ * values selected by the user with the -min or -max options.
+ */
+ axisPtr->min = min;
+ axisPtr->max = max;
+ if (DEFINED(axisPtr->reqMin)) {
+ axisPtr->min = axisPtr->reqMin;
+ }
+ if (DEFINED(axisPtr->reqMax)) {
+ axisPtr->max = axisPtr->reqMax;
+ }
+ if (axisPtr->max < axisPtr->min) {
+ /*
+ * If the limits still don't make sense, it's because one limit
+ * configuration option (-min or -max) was set and the other default
+ * (based upon the data) is too small or large. Remedy this by making
+ * up a new min or max from the user-defined limit.
+ */
+ if (!DEFINED(axisPtr->reqMin)) {
+ axisPtr->min = axisPtr->max - (FABS(axisPtr->max) * 0.1);
+ }
+ if (!DEFINED(axisPtr->reqMax)) {
+ axisPtr->max = axisPtr->min + (FABS(axisPtr->max) * 0.1);
+ }
+ }
+ /*
+ * If a window size is defined, handle auto ranging by shifting the axis
+ * limits.
+ */
+ if ((axisPtr->windowSize > 0.0) &&
+ (!DEFINED(axisPtr->reqMin)) && (!DEFINED(axisPtr->reqMax))) {
+ if (axisPtr->shiftBy < 0.0) {
+ axisPtr->shiftBy = 0.0;
+ }
+ max = axisPtr->min + axisPtr->windowSize;
+ if (axisPtr->max >= max) {
+ if (axisPtr->shiftBy > 0.0) {
+ max = UCEIL(axisPtr->max, axisPtr->shiftBy);
+ }
+ axisPtr->min = max - axisPtr->windowSize;
+ }
+ axisPtr->max = max;
+ }
+ if ((axisPtr->max != axisPtr->prevMax) ||
+ (axisPtr->min != axisPtr->prevMin)) {
+ /* Indicate if the axis limits have changed */
+ axisPtr->flags |= DIRTY;
+ /* and save the previous minimum and maximum values */
+ axisPtr->prevMin = axisPtr->min;
+ axisPtr->prevMax = axisPtr->max;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NiceNum --
+ *
+ * Reference: Paul Heckbert, "Nice Numbers for Graph Labels",
+ * Graphics Gems, pp 61-63.
+ *
+ * Finds a "nice" number approximately equal to x.
+ *
+ *---------------------------------------------------------------------------
+ */
+static double
+NiceNum(double x, int round) /* If non-zero, round. Otherwise take
+ * ceiling of value. */
+{
+ double expt; /* Exponent of x */
+ double frac; /* Fractional part of x */
+ double nice; /* Nice, rounded fraction */
+
+ expt = floor(log10(x));
+ frac = x / EXP10(expt); /* between 1 and 10 */
+ if (round) {
+ if (frac < 1.5) {
+ nice = 1.0;
+ } else if (frac < 3.0) {
+ nice = 2.0;
+ } else if (frac < 7.0) {
+ nice = 5.0;
+ } else {
+ nice = 10.0;
+ }
+ } else {
+ if (frac <= 1.0) {
+ nice = 1.0;
+ } else if (frac <= 2.0) {
+ nice = 2.0;
+ } else if (frac <= 5.0) {
+ nice = 5.0;
+ } else {
+ nice = 10.0;
+ }
+ }
+ return nice * EXP10(expt);
+}
+
+static Ticks *
+GenerateTicks(TickSweep *sweepPtr)
+{
+ Ticks *ticksPtr;
+
+ ticksPtr = Blt_AssertMalloc(sizeof(Ticks) +
+ (sweepPtr->nSteps * sizeof(double)));
+ ticksPtr->nTicks = 0;
+
+ if (sweepPtr->step == 0.0) {
+ /* Hack: A zero step indicates to use log values. */
+ int i;
+ /* Precomputed log10 values [1..10] */
+ static double logTable[] = {
+ 0.0,
+ 0.301029995663981,
+ 0.477121254719662,
+ 0.602059991327962,
+ 0.698970004336019,
+ 0.778151250383644,
+ 0.845098040014257,
+ 0.903089986991944,
+ 0.954242509439325,
+ 1.0
+ };
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ ticksPtr->values[i] = logTable[i];
+ }
+ } else {
+ double value;
+ int i;
+
+ value = sweepPtr->initial; /* Start from smallest axis tick */
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ value = UROUND(value, sweepPtr->step);
+ ticksPtr->values[i] = value;
+ value += sweepPtr->step;
+ }
+ }
+ ticksPtr->nTicks = sweepPtr->nSteps;
+ return ticksPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LogScaleAxis --
+ *
+ * Determine the range and units of a log scaled axis.
+ *
+ * Unless the axis limits are specified, the axis is scaled
+ * automatically, where the smallest and largest major ticks encompass
+ * the range of actual data values. When an axis limit is specified,
+ * that value represents the smallest(min)/largest(max) value in the
+ * displayed range of values.
+ *
+ * Both manual and automatic scaling are affected by the step used. By
+ * default, the step is the largest power of ten to divide the range in
+ * more than one piece.
+ *
+ * Automatic scaling:
+ * Find the smallest number of units which contain the range of values.
+ * The minimum and maximum major tick values will be represent the
+ * range of values for the axis. This greatest number of major ticks
+ * possible is 10.
+ *
+ * Manual scaling:
+ * Make the minimum and maximum data values the represent the range of
+ * the values for the axis. The minimum and maximum major ticks will be
+ * inclusive of this range. This provides the largest area for plotting
+ * and the expected results when the axis min and max values have be set
+ * by the user (.e.g zooming). The maximum number of major ticks is 20.
+ *
+ * For log scale, there's the possibility that the minimum and
+ * maximum data values are the same magnitude. To represent the
+ * points properly, at least one full decade should be shown.
+ * However, if you zoom a log scale plot, the results should be
+ * predictable. Therefore, in that case, show only minor ticks.
+ * Lastly, there should be an appropriate way to handle numbers
+ * <=0.
+ *
+ * maxY
+ * | units = magnitude (of least significant digit)
+ * | high = largest unit tick < max axis value
+ * high _| low = smallest unit tick > min axis value
+ * |
+ * | range = high - low
+ * | # ticks = greatest factor of range/units
+ * _|
+ * U |
+ * n |
+ * i |
+ * t _|
+ * |
+ * |
+ * |
+ * low _|
+ * |
+ * |_minX________________maxX__
+ * | | | | |
+ * minY low high
+ * minY
+ *
+ *
+ * numTicks = Number of ticks
+ * min = Minimum value of axis
+ * max = Maximum value of axis
+ * range = Range of values (max - min)
+ *
+ * If the number of decades is greater than ten, it is assumed
+ * that the full set of log-style ticks can't be drawn properly.
+ *
+ * Results:
+ * None
+ *
+ * -------------------------------------------------------------------------- */
+static void
+LogScaleAxis(Axis *axisPtr, double min, double max)
+{
+ double range;
+ double tickMin, tickMax;
+ double majorStep, minorStep;
+ int nMajor, nMinor;
+
+ nMajor = nMinor = 0;
+ /* Suppress compiler warnings. */
+ majorStep = minorStep = 0.0;
+ tickMin = tickMax = Blt_NaN();
+ if (min < max) {
+ min = (min != 0.0) ? log10(FABS(min)) : 0.0;
+ max = (max != 0.0) ? log10(FABS(max)) : 1.0;
+
+ tickMin = floor(min);
+ tickMax = ceil(max);
+ range = tickMax - tickMin;
+
+ if (range > 10) {
+ /* There are too many decades to display a major tick at every
+ * decade. Instead, treat the axis as a linear scale. */
+ range = NiceNum(range, 0);
+ majorStep = NiceNum(range / axisPtr->reqNumMajorTicks, 1);
+ tickMin = UFLOOR(tickMin, majorStep);
+ tickMax = UCEIL(tickMax, majorStep);
+ nMajor = (int)((tickMax - tickMin) / majorStep) + 1;
+ minorStep = EXP10(floor(log10(majorStep)));
+ if (minorStep == majorStep) {
+ nMinor = 4, minorStep = 0.2;
+ } else {
+ nMinor = Round(majorStep / minorStep) - 1;
+ }
+ } else {
+ if (tickMin == tickMax) {
+ tickMax++;
+ }
+ majorStep = 1.0;
+ nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */
+
+ minorStep = 0.0; /* This is a special hack to pass
+ * information to the GenerateTicks
+ * routine. An interval of 0.0 tells 1)
+ * this is a minor sweep and 2) the axis
+ * is log scale. */
+ nMinor = 10;
+ }
+ if ((axisPtr->looseMin == AXIS_TIGHT) ||
+ ((axisPtr->looseMin == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMin)))) {
+ tickMin = min;
+ nMajor++;
+ }
+ if ((axisPtr->looseMax == AXIS_TIGHT) ||
+ ((axisPtr->looseMax == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMax)))) {
+ tickMax = max;
+ }
+ }
+ axisPtr->majorSweep.step = majorStep;
+ axisPtr->majorSweep.initial = floor(tickMin);
+ axisPtr->majorSweep.nSteps = nMajor;
+ axisPtr->minorSweep.initial = axisPtr->minorSweep.step = minorStep;
+ axisPtr->minorSweep.nSteps = nMinor;
+
+ SetAxisRange(&axisPtr->axisRange, tickMin, tickMax);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LinearScaleAxis --
+ *
+ * Determine the units of a linear scaled axis.
+ *
+ * The axis limits are either the range of the data values mapped
+ * to the axis (autoscaled), or the values specified by the -min
+ * and -max options (manual).
+ *
+ * If autoscaled, the smallest and largest major ticks will
+ * encompass the range of data values. If the -loose option is
+ * selected, the next outer ticks are choosen. If tight, the
+ * ticks are at or inside of the data limits are used.
+ *
+ * If manually set, the ticks are at or inside the data limits
+ * are used. This makes sense for zooming. You want the
+ * selected range to represent the next limit, not something a
+ * bit bigger.
+ *
+ * Note: I added an "always" value to the -loose option to force
+ * the manually selected axes to be loose. It's probably
+ * not a good idea.
+ *
+ * maxY
+ * | units = magnitude (of least significant digit)
+ * | high = largest unit tick < max axis value
+ * high _| low = smallest unit tick > min axis value
+ * |
+ * | range = high - low
+ * | # ticks = greatest factor of range/units
+ * _|
+ * U |
+ * n |
+ * i |
+ * t _|
+ * |
+ * |
+ * |
+ * low _|
+ * |
+ * |_minX________________maxX__
+ * | | | | |
+ * minY low high
+ * minY
+ *
+ * numTicks = Number of ticks
+ * min = Minimum value of axis
+ * max = Maximum value of axis
+ * range = Range of values (max - min)
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The axis tick information is set. The actual tick values will
+ * be generated later.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+LinearScaleAxis(Axis *axisPtr, double min, double max)
+{
+ double step;
+ double tickMin, tickMax;
+ double axisMin, axisMax;
+ unsigned int nTicks;
+
+ nTicks = 0;
+ step = 1.0;
+ /* Suppress compiler warning. */
+ axisMin = axisMax = tickMin = tickMax = Blt_NaN();
+ if (min < max) {
+ double range;
+
+ range = max - min;
+ /* Calculate the major tick stepping. */
+ if (axisPtr->reqStep > 0.0) {
+ /* An interval was designated by the user. Keep scaling it until
+ * it fits comfortably within the current range of the axis. */
+ step = axisPtr->reqStep;
+ while ((2 * step) >= range) {
+ step *= 0.5;
+ }
+ } else {
+ range = NiceNum(range, 0);
+ step = NiceNum(range / axisPtr->reqNumMajorTicks, 1);
+ }
+
+ /* Find the outer tick values. Add 0.0 to prevent getting -0.0. */
+ axisMin = tickMin = floor(min / step) * step + 0.0;
+ axisMax = tickMax = ceil(max / step) * step + 0.0;
+
+ nTicks = Round((tickMax - tickMin) / step) + 1;
+ }
+ axisPtr->majorSweep.step = step;
+ axisPtr->majorSweep.initial = tickMin;
+ axisPtr->majorSweep.nSteps = nTicks;
+
+ /*
+ * The limits of the axis are either the range of the data ("tight") or at
+ * the next outer tick interval ("loose"). The looseness or tightness has
+ * to do with how the axis fits the range of data values. This option is
+ * overridden when the user sets an axis limit (by either -min or -max
+ * option). The axis limit is always at the selected limit (otherwise we
+ * assume that user would have picked a different number).
+ */
+ if ((axisPtr->looseMin == AXIS_TIGHT) ||
+ ((axisPtr->looseMin == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMin)))) {
+ axisMin = min;
+ }
+ if ((axisPtr->looseMax == AXIS_TIGHT) ||
+ ((axisPtr->looseMax == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMax)))) {
+ axisMax = max;
+ }
+ SetAxisRange(&axisPtr->axisRange, axisMin, axisMax);
+
+ /* Now calculate the minor tick step and number. */
+
+ if ((axisPtr->reqNumMinorTicks > 0) && (axisPtr->flags & AXIS_AUTO_MAJOR)) {
+ nTicks = axisPtr->reqNumMinorTicks - 1;
+ step = 1.0 / (nTicks + 1);
+ } else {
+ nTicks = 0; /* No minor ticks. */
+ step = 0.5; /* Don't set the minor tick interval to
+ * 0.0. It makes the GenerateTicks
+ * routine * create minor log-scale tick
+ * marks. */
+ }
+ axisPtr->minorSweep.initial = axisPtr->minorSweep.step = step;
+ axisPtr->minorSweep.nSteps = nTicks;
+}
+
+
+static void
+SweepTicks(Axis *axisPtr)
+{
+ if (axisPtr->flags & AXIS_AUTO_MAJOR) {
+ if (axisPtr->t1Ptr != NULL) {
+ Blt_Free(axisPtr->t1Ptr);
+ }
+ axisPtr->t1Ptr = GenerateTicks(&axisPtr->majorSweep);
+ }
+ if (axisPtr->flags & AXIS_AUTO_MINOR) {
+ if (axisPtr->t2Ptr != NULL) {
+ Blt_Free(axisPtr->t2Ptr);
+ }
+ axisPtr->t2Ptr = GenerateTicks(&axisPtr->minorSweep);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ResetAxes --
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ResetAxes(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ /* FIXME: This should be called whenever the display list of
+ * elements change. Maybe yet another flag INIT_STACKS to
+ * indicate that the element display list has changed.
+ * Needs to be done before the axis limits are set.
+ */
+ Blt_InitBarSetTable(graphPtr);
+ if ((graphPtr->mode == BARS_STACKED) && (graphPtr->nBarGroups > 0)) {
+ Blt_ComputeBarStacks(graphPtr);
+ }
+ /*
+ * Step 1: Reset all axes. Initialize the data limits of the axis to
+ * impossible values.
+ */
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ axisPtr->min = axisPtr->valueRange.min = DBL_MAX;
+ axisPtr->max = axisPtr->valueRange.max = -DBL_MAX;
+ }
+
+ /*
+ * Step 2: For each element that's to be displayed, get the smallest
+ * and largest data values mapped to each X and Y-axis. This
+ * will be the axis limits if the user doesn't override them
+ * with -min and -max options.
+ */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ Region2d exts;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((graphPtr->flags & UNMAP_HIDDEN) && (elemPtr->flags & HIDE)) {
+ continue;
+ }
+ (*elemPtr->procsPtr->extentsProc) (elemPtr, &exts);
+ GetDataLimits(elemPtr->axes.x, exts.left, exts.right);
+ GetDataLimits(elemPtr->axes.y, exts.top, exts.bottom);
+ }
+ /*
+ * Step 3: Now that we know the range of data values for each axis,
+ * set axis limits and compute a sweep to generate tick values.
+ */
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+ double min, max;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ FixAxisRange(axisPtr);
+
+ /* Calculate min/max tick (major/minor) layouts */
+ min = axisPtr->min;
+ max = axisPtr->max;
+ if ((DEFINED(axisPtr->scrollMin)) && (min < axisPtr->scrollMin)) {
+ min = axisPtr->scrollMin;
+ }
+ if ((DEFINED(axisPtr->scrollMax)) && (max > axisPtr->scrollMax)) {
+ max = axisPtr->scrollMax;
+ }
+ if (axisPtr->logScale) {
+ LogScaleAxis(axisPtr, min, max);
+ } else if (axisPtr->timeScale) {
+ TimeScaleAxis(axisPtr, min, max);
+ } else {
+ LinearScaleAxis(axisPtr, min, max);
+ }
+
+ if ((axisPtr->flags & (DIRTY|AXIS_USE)) == (DIRTY|AXIS_USE)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ }
+
+ graphPtr->flags &= ~RESET_AXES;
+
+ /*
+ * When any axis changes, we need to layout the entire graph.
+ */
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | MAP_ALL |
+ REDRAW_WORLD);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ResetTextStyles --
+ *
+ * Configures axis attributes (font, line width, label, etc) and
+ * allocates a new (possibly shared) graphics context. Line cap style is
+ * projecting. This is for the problem of when a tick sits directly at
+ * the end point of the axis.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ * Axis resources are allocated (GC, font). Axis layout is deferred until
+ * the height and width of the window are known.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ResetTextStyles(Axis *axisPtr)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ Blt_Ts_ResetStyle(graphPtr->tkwin, &axisPtr->limitsTextStyle);
+
+ gcMask = (GCForeground | GCLineWidth | GCCapStyle);
+ gcValues.foreground = axisPtr->tickColor->pixel;
+ gcValues.font = Blt_FontId(axisPtr->tickFont);
+ gcValues.line_width = LineWidth(axisPtr->lineWidth);
+ gcValues.cap_style = CapProjecting;
+
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (axisPtr->tickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+ }
+ axisPtr->tickGC = newGC;
+
+ /* Assuming settings from above GC */
+ gcValues.foreground = axisPtr->activeFgColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (axisPtr->activeTickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->activeTickGC);
+ }
+ axisPtr->activeTickGC = newGC;
+
+ gcValues.background = gcValues.foreground = axisPtr->major.color->pixel;
+ gcValues.line_width = LineWidth(axisPtr->major.lineWidth);
+ gcMask = (GCForeground | GCBackground | GCLineWidth);
+ if (LineIsDashed(axisPtr->major.dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ gcMask |= GCLineStyle;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(axisPtr->major.dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &axisPtr->major.dashes);
+ }
+ if (axisPtr->major.gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, axisPtr->major.gc);
+ }
+ axisPtr->major.gc = newGC;
+
+ gcValues.background = gcValues.foreground = axisPtr->minor.color->pixel;
+ gcValues.line_width = LineWidth(axisPtr->minor.lineWidth);
+ gcMask = (GCForeground | GCBackground | GCLineWidth);
+ if (LineIsDashed(axisPtr->minor.dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ gcMask |= GCLineStyle;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(axisPtr->minor.dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &axisPtr->minor.dashes);
+ }
+ if (axisPtr->minor.gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, axisPtr->minor.gc);
+ }
+ axisPtr->minor.gc = newGC;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyAxis --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources (font, color, gc, labels, etc.) associated with the axis are
+ * deallocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DestroyAxis(Axis *axisPtr)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ int flags;
+
+ flags = Blt_GraphType(graphPtr);
+ Blt_FreeOptions(configSpecs, (char *)axisPtr, graphPtr->display, flags);
+ if (graphPtr->bindTable != NULL) {
+ Blt_DeleteBindings(graphPtr->bindTable, axisPtr);
+ }
+ if (axisPtr->link != NULL) {
+ Blt_Chain_DeleteLink(axisPtr->chain, axisPtr->link);
+ }
+ if (axisPtr->obj.name != NULL) {
+ Blt_Free(axisPtr->obj.name);
+ }
+ if (axisPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&graphPtr->axes.table, axisPtr->hashPtr);
+ }
+ Blt_Ts_FreeStyle(graphPtr->display, &axisPtr->limitsTextStyle);
+
+ if (axisPtr->tickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->tickGC);
+ }
+ if (axisPtr->activeTickGC != NULL) {
+ Tk_FreeGC(graphPtr->display, axisPtr->activeTickGC);
+ }
+ if (axisPtr->major.gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, axisPtr->major.gc);
+ }
+ if (axisPtr->minor.gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, axisPtr->minor.gc);
+ }
+ FreeTickLabels(axisPtr->tickLabels);
+ Blt_Chain_Destroy(axisPtr->tickLabels);
+ if (axisPtr->segments != NULL) {
+ Blt_Free(axisPtr->segments);
+ }
+ Blt_Free(axisPtr);
+}
+
+static void
+FreeAxis(DestroyData data)
+{
+ Axis *axisPtr = (Axis *)data;
+ DestroyAxis(axisPtr);
+}
+
+static float titleAngle[4] = /* Rotation for each axis title */
+{
+ 0.0, 90.0, 0.0, 270.0
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisOffsets --
+ *
+ * Determines the sites of the axis, major and minor ticks, and title of
+ * the axis.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+AxisOffsets(
+ Axis *axisPtr,
+ int margin,
+ int offset,
+ AxisInfo *infoPtr)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ Margin *marginPtr;
+ int pad; /* Offset of axis from interior
+ * region. This includes a possible
+ * border and the axis line width. */
+ int axisLine;
+ int t1, t2, labelOffset;
+ int tickLabel, axisPad;
+ int inset, mark;
+ int x, y;
+ float fangle;
+
+ axisPtr->titleAngle = titleAngle[margin];
+ marginPtr = graphPtr->margins + margin;
+
+ tickLabel = axisLine = t1 = t2 = 0;
+ labelOffset = AXIS_PAD_TITLE;
+ if (axisPtr->lineWidth > 0) {
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ t1 = axisPtr->tickLength;
+ t2 = (t1 * 10) / 15;
+ }
+ labelOffset = t1 + AXIS_PAD_TITLE;
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ labelOffset += axisPtr->lineWidth;
+ }
+ }
+ axisPad = 0;
+ if (graphPtr->plotRelief != TK_RELIEF_SOLID) {
+ axisPad = 0;
+ }
+ /* Adjust offset for the interior border width and the line width */
+ pad = 1;
+ if (graphPtr->plotBW > 0) {
+ pad += graphPtr->plotBW + 1;
+ }
+ pad = 0; /* FIXME: test */
+ /*
+ * Pre-calculate the x-coordinate positions of the axis, tick labels, and
+ * the individual major and minor ticks.
+ */
+ inset = pad + axisPtr->lineWidth / 2;
+ switch (margin) {
+ case MARGIN_TOP:
+ axisLine = graphPtr->top;
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ axisLine -= graphPtr->plotBW + axisPad + axisPtr->lineWidth / 2;
+ tickLabel = axisLine - 2;
+ if (axisPtr->lineWidth > 0) {
+ tickLabel -= axisPtr->tickLength;
+ }
+#ifdef notdef
+ fprintf(stderr, "axisLine=%d, axisPad=%d plotBW=%d axisPtr->lineWidth/2=%d\n", axisLine, axisPad, graphPtr->plotBW, axisPtr->lineWidth /2 );
+#endif
+ } else {
+ if (graphPtr->plotRelief == TK_RELIEF_SOLID) {
+ axisLine--;
+ }
+ axisLine -= axisPad + axisPtr->lineWidth / 2;
+ tickLabel = graphPtr->top - graphPtr->plotBW - 2;
+#ifdef notdef
+ fprintf(stderr, "axisLine=%d, axisPad=%d plotBW=%d axisPtr->lineWidth/2=%d\n", axisLine, axisPad, graphPtr->plotBW, axisPtr->lineWidth /2 );
+#endif
+ }
+ mark = graphPtr->top - offset - pad;
+ axisPtr->tickAnchor = TK_ANCHOR_S;
+ axisPtr->left = axisPtr->screenMin - inset - 2;
+ axisPtr->right = axisPtr->screenMin + axisPtr->screenRange + inset - 1;
+ if (graphPtr->stackAxes) {
+ axisPtr->top = mark - marginPtr->axesOffset;
+ } else {
+ axisPtr->top = mark - axisPtr->height;
+ }
+ axisPtr->bottom = mark;
+ if (axisPtr->titleAlternate) {
+ x = graphPtr->right + AXIS_PAD_TITLE;
+ y = mark - (axisPtr->height / 2);
+ axisPtr->titleAnchor = TK_ANCHOR_W;
+ } else {
+ x = (axisPtr->right + axisPtr->left) / 2;
+ if (graphPtr->stackAxes) {
+ y = mark - marginPtr->axesOffset + AXIS_PAD_TITLE;
+ } else {
+ y = mark - axisPtr->height + AXIS_PAD_TITLE;
+ }
+ axisPtr->titleAnchor = TK_ANCHOR_N;
+ }
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_BOTTOM:
+ /*
+ * ----------- bottom + plot borderwidth
+ * mark --------------------------------------------
+ * ===================== axisLine (linewidth)
+ * tick
+ * title
+ *
+ * ===================== axisLine (linewidth)
+ * ----------- bottom + plot borderwidth
+ * mark --------------------------------------------
+ * tick
+ * title
+ */
+ axisLine = graphPtr->bottom;
+ if (graphPtr->plotRelief == TK_RELIEF_SOLID) {
+ axisLine++;
+ }
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ axisLine += graphPtr->plotBW + axisPad + axisPtr->lineWidth / 2;
+ tickLabel = axisLine + 2;
+ if (axisPtr->lineWidth > 0) {
+ tickLabel += axisPtr->tickLength;
+ }
+ } else {
+ axisLine -= axisPad + axisPtr->lineWidth / 2;
+ tickLabel = graphPtr->bottom + graphPtr->plotBW + 2;
+ }
+ mark = graphPtr->bottom + offset;
+ fangle = FMOD(axisPtr->tickAngle, 90.0);
+ if (fangle == 0.0) {
+ axisPtr->tickAnchor = TK_ANCHOR_N;
+ } else {
+ int quadrant;
+
+ quadrant = (int)(axisPtr->tickAngle / 90.0);
+ if ((quadrant == 0) || (quadrant == 2)) {
+ axisPtr->tickAnchor = TK_ANCHOR_NE;
+ } else {
+ axisPtr->tickAnchor = TK_ANCHOR_NW;
+ }
+ }
+ axisPtr->left = axisPtr->screenMin - inset - 2;
+ axisPtr->right = axisPtr->screenMin + axisPtr->screenRange + inset - 1;
+ axisPtr->top = graphPtr->bottom + labelOffset - t1;
+ if (graphPtr->stackAxes) {
+ axisPtr->bottom = mark + marginPtr->axesOffset - 1;
+ } else {
+ axisPtr->bottom = mark + axisPtr->height - 1;
+ }
+ if (axisPtr->titleAlternate) {
+ x = graphPtr->right + AXIS_PAD_TITLE;
+ y = mark + (axisPtr->height / 2);
+ axisPtr->titleAnchor = TK_ANCHOR_W;
+ } else {
+ x = (axisPtr->right + axisPtr->left) / 2;
+ if (graphPtr->stackAxes) {
+ y = mark + marginPtr->axesOffset - AXIS_PAD_TITLE;
+ } else {
+ y = mark + axisPtr->height - AXIS_PAD_TITLE;
+ }
+ axisPtr->titleAnchor = TK_ANCHOR_S;
+ }
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_LEFT:
+ /*
+ * mark
+ * | :
+ * | :
+ * | :
+ * | :
+ * | :
+ * axisLine
+ */
+ /*
+ * Exterior axis
+ * + plotarea right
+ * |A|B|C|D|E|F|G|H
+ * |right
+ * A = plot pad
+ * B = plot border width
+ * C = axis pad
+ * D = axis line
+ * E = tick length
+ * F = tick label
+ * G = graph border width
+ * H = highlight thickness
+ */
+ /*
+ * Interior axis
+ * + plotarea right
+ * |A|B|C|D|E|F|G|H
+ * |right
+ * A = plot pad
+ * B = tick length
+ * C = axis line width
+ * D = axis pad
+ * E = plot border width
+ * F = tick label
+ * G = graph border width
+ * H = highlight thickness
+ */
+ axisLine = graphPtr->left;
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ axisLine -= graphPtr->plotBW + axisPad + axisPtr->lineWidth / 2;
+ tickLabel = axisLine - 2;
+ if (axisPtr->lineWidth > 0) {
+ tickLabel -= axisPtr->tickLength;
+ }
+ } else {
+ if (graphPtr->plotRelief == TK_RELIEF_SOLID) {
+ axisLine--;
+ }
+ axisLine += axisPad + axisPtr->lineWidth / 2;
+ tickLabel = graphPtr->left - graphPtr->plotBW - 2;
+ }
+ mark = graphPtr->left - offset;
+ axisPtr->tickAnchor = TK_ANCHOR_E;
+ if (graphPtr->stackAxes) {
+ axisPtr->left = mark - marginPtr->axesOffset;
+ } else {
+ axisPtr->left = mark - axisPtr->width;
+ }
+ axisPtr->right = mark - 3;
+ axisPtr->top = axisPtr->screenMin - inset - 2;
+ axisPtr->bottom = axisPtr->screenMin + axisPtr->screenRange + inset - 1;
+ if (axisPtr->titleAlternate) {
+ x = mark - (axisPtr->width / 2);
+ y = graphPtr->top - AXIS_PAD_TITLE;
+ axisPtr->titleAnchor = TK_ANCHOR_SW;
+ } else {
+ if (graphPtr->stackAxes) {
+ x = mark - marginPtr->axesOffset;
+ } else {
+ x = mark - axisPtr->width + AXIS_PAD_TITLE;
+ }
+ y = (axisPtr->bottom + axisPtr->top) / 2;
+ axisPtr->titleAnchor = TK_ANCHOR_W;
+ }
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_RIGHT:
+ axisLine = graphPtr->right;
+ if (graphPtr->plotRelief == TK_RELIEF_SOLID) {
+ axisLine++; /* Draw axis line within solid plot
+ * border. */
+ }
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ axisLine += graphPtr->plotBW + axisPad + axisPtr->lineWidth / 2;
+ tickLabel = axisLine + 2;
+ if (axisPtr->lineWidth > 0) {
+ tickLabel += axisPtr->tickLength;
+ }
+ } else {
+ axisLine -= axisPad + axisPtr->lineWidth / 2;
+ tickLabel = graphPtr->right + graphPtr->plotBW + 2;
+ }
+ mark = graphPtr->right + offset + pad;
+ axisPtr->tickAnchor = TK_ANCHOR_W;
+ axisPtr->left = mark;
+ if (graphPtr->stackAxes) {
+ axisPtr->right = mark + marginPtr->axesOffset - 1;
+ } else {
+ axisPtr->right = mark + axisPtr->width - 1;
+ }
+ axisPtr->top = axisPtr->screenMin - inset - 2;
+ axisPtr->bottom = axisPtr->screenMin + axisPtr->screenRange + inset -1;
+ if (axisPtr->titleAlternate) {
+ x = mark + (axisPtr->width / 2);
+ y = graphPtr->top - AXIS_PAD_TITLE;
+ axisPtr->titleAnchor = TK_ANCHOR_SE;
+ } else {
+ if (graphPtr->stackAxes) {
+ x = mark + marginPtr->axesOffset - AXIS_PAD_TITLE;
+ } else {
+ x = mark + axisPtr->width - AXIS_PAD_TITLE;
+ }
+ y = (axisPtr->bottom + axisPtr->top) / 2;
+ axisPtr->titleAnchor = TK_ANCHOR_E;
+ }
+ axisPtr->titlePos.x = x;
+ axisPtr->titlePos.y = y;
+ break;
+
+ case MARGIN_NONE:
+ axisLine = 0;
+ break;
+ }
+ if ((margin == MARGIN_LEFT) || (margin == MARGIN_TOP)) {
+ t1 = -t1, t2 = -t2;
+ labelOffset = -labelOffset;
+ }
+ infoPtr->axis = axisLine;
+ infoPtr->t1 = axisLine + t1;
+ infoPtr->t2 = axisLine + t2;
+ if (tickLabel > 0) {
+ infoPtr->label = tickLabel;
+ } else {
+ infoPtr->label = axisLine + labelOffset;
+ }
+ if ((axisPtr->flags & AXIS_EXTERIOR) == 0) {
+ /*infoPtr->label = axisLine + labelOffset - t1; */
+ infoPtr->t1 = axisLine - t1;
+ infoPtr->t2 = axisLine - t2;
+ }
+}
+
+static void
+MakeAxisLine(Axis *axisPtr, int line, Segment2d *sp)
+{
+ double min, max;
+
+ min = axisPtr->axisRange.min;
+ max = axisPtr->axisRange.max;
+ if (axisPtr->logScale) {
+ min = EXP10(min);
+ max = EXP10(max);
+ }
+ if (AxisIsHorizontal(axisPtr)) {
+ sp->p.x = Blt_HMap(axisPtr, min);
+ sp->q.x = Blt_HMap(axisPtr, max);
+ sp->p.y = sp->q.y = line;
+ } else {
+ sp->q.x = sp->p.x = line;
+ sp->p.y = Blt_VMap(axisPtr, min);
+ sp->q.y = Blt_VMap(axisPtr, max);
+ }
+}
+
+
+static void
+MakeTick(Axis *axisPtr, double value, int tick, int line, Segment2d *sp)
+{
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ if (AxisIsHorizontal(axisPtr)) {
+ sp->p.x = sp->q.x = Blt_HMap(axisPtr, value);
+ sp->p.y = line;
+ sp->q.y = tick;
+ } else {
+ sp->p.x = line;
+ sp->p.y = sp->q.y = Blt_VMap(axisPtr, value);
+ sp->q.x = tick;
+ }
+}
+
+static void
+MakeSegments(Axis *axisPtr, AxisInfo *infoPtr)
+{
+ int arraySize;
+ int nMajorTicks, nMinorTicks;
+ Segment2d *segments;
+ Segment2d *sp;
+
+ if (axisPtr->segments != NULL) {
+ Blt_Free(axisPtr->segments);
+ }
+ nMajorTicks = nMinorTicks = 0;
+ if (axisPtr->t1Ptr != NULL) {
+ nMajorTicks = axisPtr->t1Ptr->nTicks;
+ }
+ if (axisPtr->t2Ptr != NULL) {
+ nMinorTicks = axisPtr->t2Ptr->nTicks;
+ }
+ arraySize = 1 + (nMajorTicks * (nMinorTicks + 1));
+ segments = Blt_AssertMalloc(arraySize * sizeof(Segment2d));
+ sp = segments;
+ if (axisPtr->lineWidth > 0) {
+ /* Axis baseline */
+ MakeAxisLine(axisPtr, infoPtr->axis, sp);
+ sp++;
+ }
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ Blt_ChainLink link;
+ double labelPos;
+ int i;
+ int isHoriz;
+
+ isHoriz = AxisIsHorizontal(axisPtr);
+ for (i = 0; i < nMajorTicks; i++) {
+ double t1, t2;
+ int j;
+
+ t1 = axisPtr->t1Ptr->values[i];
+ /* Minor ticks */
+ for (j = 0; j < nMinorTicks; j++) {
+ t2 = t1 + (axisPtr->majorSweep.step *
+ axisPtr->t2Ptr->values[j]);
+ if (InRange(t2, &axisPtr->axisRange)) {
+ MakeTick(axisPtr, t2, infoPtr->t2, infoPtr->axis, sp);
+ sp++;
+ }
+ }
+ if (!InRange(t1, &axisPtr->axisRange)) {
+ continue;
+ }
+ /* Major tick */
+ MakeTick(axisPtr, t1, infoPtr->t1, infoPtr->axis, sp);
+ sp++;
+ }
+
+ link = Blt_Chain_FirstLink(axisPtr->tickLabels);
+ labelPos = (double)infoPtr->label;
+
+ for (i = 0; i < nMajorTicks; i++) {
+ double t1;
+ TickLabel *labelPtr;
+ Segment2d seg;
+
+ t1 = axisPtr->t1Ptr->values[i];
+ if (axisPtr->labelOffset) {
+ t1 += axisPtr->majorSweep.step * 0.5;
+ }
+ if (!InRange(t1, &axisPtr->axisRange)) {
+ continue;
+ }
+ labelPtr = Blt_Chain_GetValue(link);
+ link = Blt_Chain_NextLink(link);
+ MakeTick(axisPtr, t1, infoPtr->t1, infoPtr->axis, &seg);
+ /* Save tick label X-Y position. */
+ if (isHoriz) {
+ labelPtr->anchorPos.x = seg.p.x;
+ labelPtr->anchorPos.y = labelPos;
+ } else {
+ labelPtr->anchorPos.x = labelPos;
+ labelPtr->anchorPos.y = seg.p.y;
+ }
+ }
+ }
+ axisPtr->segments = segments;
+ axisPtr->nSegments = sp - segments;
+ assert(axisPtr->nSegments <= arraySize);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapAxis --
+ *
+ * Pre-calculates positions of the axis, ticks, and labels (to be used
+ * later when displaying the axis). Calculates the values for each major
+ * and minor tick and checks to see if they are in range (the outer ticks
+ * may be outside of the range of plotted values).
+ *
+ * Line segments for the minor and major ticks are saved into one
+ * XSegment array so that they can be drawn by a single XDrawSegments
+ * call. The positions of the tick labels are also computed and saved.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Line segments and tick labels are saved and used later to draw the
+ * axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapAxis(Axis *axisPtr, int offset, int margin)
+{
+ AxisInfo info;
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ if (AxisIsHorizontal(axisPtr)) {
+ axisPtr->screenMin = graphPtr->hOffset;
+ axisPtr->width = graphPtr->right - graphPtr->left;
+ axisPtr->screenRange = graphPtr->hRange;
+ } else {
+ axisPtr->screenMin = graphPtr->vOffset;
+ axisPtr->height = graphPtr->bottom - graphPtr->top;
+ axisPtr->screenRange = graphPtr->vRange;
+ }
+ axisPtr->screenScale = 1.0 / axisPtr->screenRange;
+ AxisOffsets(axisPtr, margin, offset, &info);
+ MakeSegments(axisPtr, &info);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapStackedAxis --
+ *
+ * Pre-calculates positions of the axis, ticks, and labels (to be used
+ * later when displaying the axis). Calculates the values for each major
+ * and minor tick and checks to see if they are in range (the outer ticks
+ * may be outside of the range of plotted values).
+ *
+ * Line segments for the minor and major ticks are saved into one XSegment
+ * array so that they can be drawn by a single XDrawSegments call. The
+ * positions of the tick labels are also computed and saved.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Line segments and tick labels are saved and used later to draw the
+ * axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapStackedAxis(Axis *axisPtr, int count, int margin)
+{
+ AxisInfo info;
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ unsigned int slice, w, h;
+
+ if ((graphPtr->margins[axisPtr->margin].axes->nLinks > 1) ||
+ (axisPtr->reqNumMajorTicks <= 0)) {
+ axisPtr->reqNumMajorTicks = 4;
+ }
+ if (AxisIsHorizontal(axisPtr)) {
+ slice = graphPtr->hRange / graphPtr->margins[margin].axes->nLinks;
+ axisPtr->screenMin = graphPtr->hOffset;
+ axisPtr->width = slice;
+ } else {
+ slice = graphPtr->vRange / graphPtr->margins[margin].axes->nLinks;
+ axisPtr->screenMin = graphPtr->vOffset;
+ axisPtr->height = slice;
+ }
+#define AXIS_PAD 2
+ Blt_GetTextExtents(axisPtr->tickFont, 0, "0", 1, &w, &h);
+ axisPtr->screenMin += (slice * count) + AXIS_PAD + h / 2;
+ axisPtr->screenRange = slice - 2 * AXIS_PAD - h;
+ axisPtr->screenScale = 1.0f / axisPtr->screenRange;
+ AxisOffsets(axisPtr, margin, 0, &info);
+ MakeSegments(axisPtr, &info);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AdjustViewport --
+ *
+ * Adjusts the offsets of the viewport according to the scroll mode. This
+ * is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ * "canvas" The viewport scrolls within the range of world
+ * coordinates. This way the viewport always displays
+ * a full page of the world. If the world is smaller
+ * than the viewport, then (bizarrely) the world and
+ * viewport are inverted so that the world moves up
+ * and down within the viewport.
+ *
+ * "listbox" The viewport can scroll beyond the range of world
+ * coordinates. Every entry can be displayed at the
+ * top of the viewport. This also means that the
+ * scrollbar thumb weirdly shrinks as the last entry
+ * is scrolled upward.
+ *
+ * Results:
+ * The corrected offset is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static double
+AdjustViewport(double offset, double windowSize)
+{
+ /*
+ * Canvas-style scrolling allows the world to be scrolled within the window.
+ */
+ if (windowSize > 1.0) {
+ if (windowSize < (1.0 - offset)) {
+ offset = 1.0 - windowSize;
+ }
+ if (offset > 0.0) {
+ offset = 0.0;
+ }
+ } else {
+ if ((offset + windowSize) > 1.0) {
+ offset = 1.0 - windowSize;
+ }
+ if (offset < 0.0) {
+ offset = 0.0;
+ }
+ }
+ return offset;
+}
+
+static int
+GetAxisScrollInfo(
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv,
+ double *offsetPtr,
+ double windowSize,
+ double scrollUnits,
+ double scale)
+{
+ const char *string;
+ char c;
+ double offset;
+ int length;
+
+ offset = *offsetPtr;
+ string = Tcl_GetStringFromObj(objv[0], &length);
+ c = string[0];
+ scrollUnits *= scale;
+ if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
+ int count;
+ double fract;
+
+ assert(objc == 3);
+ /* Scroll number unit/page */
+ if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetStringFromObj(objv[2], &length);
+ c = string[0];
+ if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
+ fract = count * scrollUnits;
+ } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (int)(count * windowSize * 0.9 + 0.5);
+ } else if ((c == 'p') && (strncmp(string, "pixels", length) == 0)) {
+ fract = count * scale;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += fract;
+ } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
+ double fract;
+
+ assert(objc == 2);
+ /* moveto fraction */
+ if (Blt_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = fract;
+ } else {
+ int count;
+ double fract;
+
+ /* Treat like "scroll units" */
+ if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count * scrollUnits;
+ offset += fract;
+ /* CHECK THIS: return TCL_OK; */
+ }
+ *offsetPtr = AdjustViewport(offset, windowSize);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawAxis --
+ *
+ * Draws the axis, ticks, and labels onto the canvas.
+ *
+ * Initializes and passes text attribute information through TextStyle
+ * structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Axis gets drawn on window.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawAxis(Axis *axisPtr, Drawable drawable)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ if (axisPtr->normalBg != NULL) {
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable,
+ axisPtr->normalBg,
+ axisPtr->left, axisPtr->top,
+ axisPtr->right - axisPtr->left,
+ axisPtr->bottom - axisPtr->top, axisPtr->borderWidth,
+ axisPtr->relief);
+ }
+ if (axisPtr->title != NULL) {
+ TextStyle ts;
+
+ Blt_Ts_InitStyle(ts);
+ Blt_Ts_SetAngle(ts, axisPtr->titleAngle);
+ Blt_Ts_SetFont(ts, axisPtr->titleFont);
+ Blt_Ts_SetPadding(ts, 1, 2, 0, 0);
+ Blt_Ts_SetAnchor(ts, axisPtr->titleAnchor);
+ Blt_Ts_SetJustify(ts, axisPtr->titleJustify);
+ if (axisPtr->flags & ACTIVE) {
+ Blt_Ts_SetForeground(ts, axisPtr->activeFgColor);
+ } else {
+ Blt_Ts_SetForeground(ts, axisPtr->titleColor);
+ }
+ Blt_Ts_SetForeground(ts, axisPtr->titleColor);
+ if ((axisPtr->titleAngle == 90.0) || (axisPtr->titleAngle == 270.0)) {
+ Blt_Ts_SetMaxLength(ts, axisPtr->height);
+ } else {
+ Blt_Ts_SetMaxLength(ts, axisPtr->width);
+ }
+ Blt_Ts_DrawText(graphPtr->tkwin, drawable, axisPtr->title, -1, &ts,
+ (int)axisPtr->titlePos.x, (int)axisPtr->titlePos.y);
+ }
+ if (axisPtr->scrollCmdObjPtr != NULL) {
+ double viewWidth, viewMin, viewMax;
+ double worldWidth, worldMin, worldMax;
+ double fract;
+ int isHoriz;
+
+ worldMin = axisPtr->valueRange.min;
+ worldMax = axisPtr->valueRange.max;
+ if (DEFINED(axisPtr->scrollMin)) {
+ worldMin = axisPtr->scrollMin;
+ }
+ if (DEFINED(axisPtr->scrollMax)) {
+ worldMax = axisPtr->scrollMax;
+ }
+ viewMin = axisPtr->min;
+ viewMax = axisPtr->max;
+ if (viewMin < worldMin) {
+ viewMin = worldMin;
+ }
+ if (viewMax > worldMax) {
+ viewMax = worldMax;
+ }
+ if (axisPtr->logScale) {
+ worldMin = log10(worldMin);
+ worldMax = log10(worldMax);
+ viewMin = log10(viewMin);
+ viewMax = log10(viewMax);
+ }
+ worldWidth = worldMax - worldMin;
+ viewWidth = viewMax - viewMin;
+ isHoriz = AxisIsHorizontal(axisPtr);
+
+ if (isHoriz != axisPtr->descending) {
+ fract = (viewMin - worldMin) / worldWidth;
+ } else {
+ fract = (worldMax - viewMax) / worldWidth;
+ }
+ fract = AdjustViewport(fract, viewWidth / worldWidth);
+
+ if (isHoriz != axisPtr->descending) {
+ viewMin = (fract * worldWidth);
+ axisPtr->min = viewMin + worldMin;
+ axisPtr->max = axisPtr->min + viewWidth;
+ viewMax = viewMin + viewWidth;
+ if (axisPtr->logScale) {
+ axisPtr->min = EXP10(axisPtr->min);
+ axisPtr->max = EXP10(axisPtr->max);
+ }
+ Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdObjPtr,
+ viewMin, viewMax, worldWidth);
+ } else {
+ viewMax = (fract * worldWidth);
+ axisPtr->max = worldMax - viewMax;
+ axisPtr->min = axisPtr->max - viewWidth;
+ viewMin = viewMax + viewWidth;
+ if (axisPtr->logScale) {
+ axisPtr->min = EXP10(axisPtr->min);
+ axisPtr->max = EXP10(axisPtr->max);
+ }
+ Blt_UpdateScrollbar(graphPtr->interp, axisPtr->scrollCmdObjPtr,
+ viewMax, viewMin, worldWidth);
+ }
+ }
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ Blt_ChainLink link;
+ TextStyle ts;
+
+ Blt_Ts_InitStyle(ts);
+ Blt_Ts_SetAngle(ts, axisPtr->tickAngle);
+ Blt_Ts_SetFont(ts, axisPtr->tickFont);
+ Blt_Ts_SetPadding(ts, 2, 0, 0, 0);
+ Blt_Ts_SetAnchor(ts, axisPtr->tickAnchor);
+ if (axisPtr->flags & ACTIVE) {
+ Blt_Ts_SetForeground(ts, axisPtr->activeFgColor);
+ } else {
+ Blt_Ts_SetForeground(ts, axisPtr->tickColor);
+ }
+ for (link = Blt_Chain_FirstLink(axisPtr->tickLabels); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ TickLabel *labelPtr;
+
+ labelPtr = Blt_Chain_GetValue(link);
+ /* Draw major tick labels */
+ Blt_DrawText(graphPtr->tkwin, drawable, labelPtr->string, &ts,
+ (int)labelPtr->anchorPos.x, (int)labelPtr->anchorPos.y);
+ }
+ }
+ if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {
+ GC gc;
+
+ if (axisPtr->flags & ACTIVE) {
+ gc = axisPtr->activeTickGC;
+ } else {
+ gc = axisPtr->tickGC;
+ }
+ /* Draw the tick marks and axis line. */
+ Blt_Draw2DSegments(graphPtr->display, drawable, gc, axisPtr->segments,
+ axisPtr->nSegments);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisToPostScript --
+ *
+ * Generates PostScript output to draw the axis, ticks, and labels.
+ *
+ * Initializes and passes text attribute information through TextStyle
+ * structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * PostScript output is left in graphPtr->interp->result;
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+AxisToPostScript(Blt_Ps ps, Axis *axisPtr)
+{
+ Blt_Ps_Format(ps, "%% Axis \"%s\"\n", axisPtr->obj.name);
+ if (axisPtr->normalBg != NULL) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(axisPtr->normalBg);
+ Blt_Ps_Fill3DRectangle(ps, border,
+ (double)axisPtr->left, (double)axisPtr->top,
+ axisPtr->right - axisPtr->left, axisPtr->bottom - axisPtr->top,
+ axisPtr->borderWidth, axisPtr->relief);
+ }
+ if (axisPtr->title != NULL) {
+ TextStyle ts;
+
+ Blt_Ts_InitStyle(ts);
+ Blt_Ts_SetAngle(ts, axisPtr->titleAngle);
+ Blt_Ts_SetFont(ts, axisPtr->titleFont);
+ Blt_Ts_SetPadding(ts, 1, 2, 0, 0);
+ Blt_Ts_SetAnchor(ts, axisPtr->titleAnchor);
+ Blt_Ts_SetJustify(ts, axisPtr->titleJustify);
+ Blt_Ts_SetForeground(ts, axisPtr->titleColor);
+ Blt_Ps_DrawText(ps, axisPtr->title, &ts, axisPtr->titlePos.x,
+ axisPtr->titlePos.y);
+ }
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ Blt_ChainLink link;
+ TextStyle ts;
+
+ Blt_Ts_InitStyle(ts);
+ Blt_Ts_SetAngle(ts, axisPtr->tickAngle);
+ Blt_Ts_SetFont(ts, axisPtr->tickFont);
+ Blt_Ts_SetPadding(ts, 2, 0, 0, 0);
+ Blt_Ts_SetAnchor(ts, axisPtr->tickAnchor);
+ Blt_Ts_SetForeground(ts, axisPtr->tickColor);
+
+ for (link = Blt_Chain_FirstLink(axisPtr->tickLabels); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ TickLabel *labelPtr;
+
+ labelPtr = Blt_Chain_GetValue(link);
+ Blt_Ps_DrawText(ps, labelPtr->string, &ts, labelPtr->anchorPos.x,
+ labelPtr->anchorPos.y);
+ }
+ }
+ if ((axisPtr->nSegments > 0) && (axisPtr->lineWidth > 0)) {
+ Blt_Ps_XSetLineAttributes(ps, axisPtr->tickColor, axisPtr->lineWidth,
+ (Blt_Dashes *)NULL, CapButt, JoinMiter);
+ Blt_Ps_Draw2DSegments(ps, axisPtr->segments, axisPtr->nSegments);
+ }
+}
+
+static void
+MakeGridLine(Axis *axisPtr, double value, Segment2d *sp)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ if (axisPtr->logScale) {
+ value = EXP10(value);
+ }
+ /* Grid lines run orthogonally to the axis */
+ if (AxisIsHorizontal(axisPtr)) {
+ sp->p.y = graphPtr->top;
+ sp->q.y = graphPtr->bottom;
+ sp->p.x = sp->q.x = Blt_HMap(axisPtr, value);
+ } else {
+ sp->p.x = graphPtr->left;
+ sp->q.x = graphPtr->right;
+ sp->p.y = sp->q.y = Blt_VMap(axisPtr, value);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapGridlines --
+ *
+ * Assembles the grid lines associated with an axis. Generates tick
+ * positions if necessary (this happens when the axis is not a logical axis
+ * too).
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapGridlines(Axis *axisPtr)
+{
+ Segment2d *s1, *s2;
+ Ticks *t1Ptr, *t2Ptr;
+ int needed;
+ int i;
+
+ if (axisPtr == NULL) {
+ return;
+ }
+ t1Ptr = axisPtr->t1Ptr;
+ if (t1Ptr == NULL) {
+ t1Ptr = GenerateTicks(&axisPtr->majorSweep);
+ }
+ t2Ptr = axisPtr->t2Ptr;
+ if (t2Ptr == NULL) {
+ t2Ptr = GenerateTicks(&axisPtr->minorSweep);
+ }
+ needed = t1Ptr->nTicks;
+ if (axisPtr->flags & AXIS_GRIDMINOR) {
+ needed += (t1Ptr->nTicks * t2Ptr->nTicks);
+ }
+ if (needed == 0) {
+ return;
+ }
+ needed = t1Ptr->nTicks;
+ if (needed != axisPtr->major.nAllocated) {
+ if (axisPtr->major.segments != NULL) {
+ Blt_Free(axisPtr->major.segments);
+ }
+ axisPtr->major.segments = Blt_AssertMalloc(sizeof(Segment2d) * needed);
+ axisPtr->major.nAllocated = needed;
+ }
+ needed = (t1Ptr->nTicks * t2Ptr->nTicks);
+ if (needed != axisPtr->minor.nAllocated) {
+ if (axisPtr->minor.segments != NULL) {
+ Blt_Free(axisPtr->minor.segments);
+ }
+ axisPtr->minor.segments = Blt_AssertMalloc(sizeof(Segment2d) * needed);
+ axisPtr->minor.nAllocated = needed;
+ }
+ s1 = axisPtr->major.segments, s2 = axisPtr->minor.segments;
+ for (i = 0; i < t1Ptr->nTicks; i++) {
+ double value;
+
+ value = t1Ptr->values[i];
+ if (axisPtr->flags & AXIS_GRIDMINOR) {
+ int j;
+
+ for (j = 0; j < t2Ptr->nTicks; j++) {
+ double subValue;
+
+ subValue = value + (axisPtr->majorSweep.step *
+ t2Ptr->values[j]);
+ if (InRange(subValue, &axisPtr->axisRange)) {
+ MakeGridLine(axisPtr, subValue, s2);
+ s2++;
+ }
+ }
+ }
+ if (InRange(value, &axisPtr->axisRange)) {
+ MakeGridLine(axisPtr, value, s1);
+ s1++;
+ }
+ }
+ if (t1Ptr != axisPtr->t1Ptr) {
+ Blt_Free(t1Ptr); /* Free generated ticks. */
+ }
+ if (t2Ptr != axisPtr->t2Ptr) {
+ Blt_Free(t2Ptr); /* Free generated ticks. */
+ }
+ axisPtr->major.nUsed = s1 - axisPtr->major.segments;
+ axisPtr->minor.nUsed = s2 - axisPtr->minor.segments;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetAxisGeometry --
+ *
+ * Results:
+ * None.
+ *
+ * Exterior axis:
+ * l r
+ * |a|b|c|d|e|f|g|h|i| j |i|h|g|f|e|d|c|d|a|
+ *
+ * Interior axis:
+ * l r
+ * |a|b|c|d|h|g|f|e| j |e|f|g|h|d|c|b|a|
+ * i.. ..i
+ * a = highlight thickness
+ * b = graph borderwidth
+ * c = axis title
+ * d = tick label
+ * e = tick
+ * f = axis line
+ * g = 1 pixel pad
+ * h = plot borderwidth
+ * i = plot pad
+ * j = plot area
+ *---------------------------------------------------------------------------
+ */
+static void
+GetAxisGeometry(Graph *graphPtr, Axis *axisPtr)
+{
+ unsigned int y;
+
+ FreeTickLabels(axisPtr->tickLabels);
+ y = 0;
+
+ if ((axisPtr->flags & AXIS_EXTERIOR) &&
+ (graphPtr->plotRelief != TK_RELIEF_SOLID)) {
+ /* Leave room for axis baseline and padding */
+ y += axisPtr->lineWidth + 2;
+ }
+
+ axisPtr->maxTickHeight = axisPtr->maxTickWidth = 0;
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ unsigned int pad;
+ unsigned int i, nLabels, nTicks;
+
+ SweepTicks(axisPtr);
+
+ nTicks = 0;
+ if (axisPtr->t1Ptr != NULL) {
+ nTicks = axisPtr->t1Ptr->nTicks;
+ }
+ assert(nTicks <= MAXTICKS);
+
+ nLabels = 0;
+ for (i = 0; i < nTicks; i++) {
+ TickLabel *labelPtr;
+ double x, x2;
+ unsigned int lw, lh; /* Label width and height. */
+
+ x2 = x = axisPtr->t1Ptr->values[i];
+ if (axisPtr->labelOffset) {
+ x2 += axisPtr->majorSweep.step * 0.5;
+ }
+ if (!InRange(x2, &axisPtr->axisRange)) {
+ continue;
+ }
+ labelPtr = MakeLabel(axisPtr, x);
+ Blt_Chain_Append(axisPtr->tickLabels, labelPtr);
+ nLabels++;
+ /*
+ * Get the dimensions of each tick label. Remember tick labels
+ * can be multi-lined and/or rotated.
+ */
+ Blt_GetTextExtents(axisPtr->tickFont, 0, labelPtr->string, -1,
+ &lw, &lh);
+ labelPtr->width = lw;
+ labelPtr->height = lh;
+
+ if (axisPtr->tickAngle != 0.0f) {
+ double rlw, rlh; /* Rotated label width and height. */
+ Blt_GetBoundingBox(lw, lh, axisPtr->tickAngle, &rlw, &rlh,NULL);
+ lw = ROUND(rlw), lh = ROUND(rlh);
+ }
+ if (axisPtr->maxTickWidth < lw) {
+ axisPtr->maxTickWidth = lw;
+ }
+ if (axisPtr->maxTickHeight < lh) {
+ axisPtr->maxTickHeight = lh;
+ }
+ }
+ assert(nLabels <= nTicks);
+
+ pad = 0;
+ if (axisPtr->flags & AXIS_EXTERIOR) {
+ /* Because the axis cap style is "CapProjecting", we need to
+ * account for an extra 1.5 linewidth at the end of each line. */
+ pad = ((axisPtr->lineWidth * 12) / 8);
+ }
+ if (AxisIsHorizontal(axisPtr)) {
+ y += axisPtr->maxTickHeight + pad;
+ } else {
+ y += axisPtr->maxTickWidth + pad;
+ if (axisPtr->maxTickWidth > 0) {
+ y += 5; /* Pad either size of label. */
+ }
+ }
+ y += 2 * AXIS_PAD_TITLE;
+ if ((axisPtr->lineWidth > 0) && (axisPtr->flags & AXIS_EXTERIOR)) {
+ /* Distance from axis line to tick label. */
+ y += axisPtr->tickLength;
+ }
+ }
+
+ if (axisPtr->title != NULL) {
+ if (axisPtr->titleAlternate) {
+ if (y < axisPtr->titleHeight) {
+ y = axisPtr->titleHeight;
+ }
+ } else {
+ y += axisPtr->titleHeight + AXIS_PAD_TITLE;
+ }
+ }
+
+ /* Correct for orientation of the axis. */
+ if (AxisIsHorizontal(axisPtr)) {
+ axisPtr->height = y;
+ } else {
+ axisPtr->width = y;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetMarginGeometry --
+ *
+ * Examines all the axes in the given margin and determines the area
+ * required to display them.
+ *
+ * Note: For multiple axes, the titles are displayed in another
+ * margin. So we must keep track of the widest title.
+ *
+ * Results:
+ * Returns the width or height of the margin, depending if it runs
+ * horizontally along the graph or vertically.
+ *
+ * Side Effects:
+ * The area width and height set in the margin. Note again that this may
+ * be corrected later (mulitple axes) to adjust for the longest title in
+ * another margin.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetMarginGeometry(Graph *graphPtr, Margin *marginPtr)
+{
+ Blt_ChainLink link;
+ unsigned int l, w, h; /* Length, width, and height. */
+ int isHoriz;
+ unsigned int nVisible;
+
+ isHoriz = HORIZMARGIN(marginPtr);
+
+ /* Count the visible axes. */
+ nVisible = 0;
+ l = w = h = 0;
+ marginPtr->maxTickWidth = marginPtr->maxTickHeight = 0;
+ if (graphPtr->stackAxes) {
+ for (link = Blt_Chain_FirstLink(marginPtr->axes); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (HIDE|AXIS_USE)) == AXIS_USE) {
+ nVisible++;
+ if (graphPtr->flags & GET_AXIS_GEOMETRY) {
+ GetAxisGeometry(graphPtr, axisPtr);
+ }
+ if (isHoriz) {
+ if (h < axisPtr->height) {
+ h = axisPtr->height;
+ }
+ } else {
+ if (w < axisPtr->width) {
+ w = axisPtr->width;
+ }
+ }
+ if (axisPtr->maxTickWidth > marginPtr->maxTickWidth) {
+ marginPtr->maxTickWidth = axisPtr->maxTickWidth;
+ }
+ if (axisPtr->maxTickHeight > marginPtr->maxTickHeight) {
+ marginPtr->maxTickHeight = axisPtr->maxTickHeight;
+ }
+ }
+ }
+ } else {
+ for (link = Blt_Chain_FirstLink(marginPtr->axes); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (HIDE|AXIS_USE)) == AXIS_USE) {
+ nVisible++;
+ if (graphPtr->flags & GET_AXIS_GEOMETRY) {
+ GetAxisGeometry(graphPtr, axisPtr);
+ }
+ if ((axisPtr->titleAlternate) && (l < axisPtr->titleWidth)) {
+ l = axisPtr->titleWidth;
+ }
+ if (isHoriz) {
+ h += axisPtr->height;
+ } else {
+ w += axisPtr->width;
+ }
+ if (axisPtr->maxTickWidth > marginPtr->maxTickWidth) {
+ marginPtr->maxTickWidth = axisPtr->maxTickWidth;
+ }
+ if (axisPtr->maxTickHeight > marginPtr->maxTickHeight) {
+ marginPtr->maxTickHeight = axisPtr->maxTickHeight;
+ }
+ }
+ }
+ }
+ /* Enforce a minimum size for margins. */
+ if (w < 3) {
+ w = 3;
+ }
+ if (h < 3) {
+ h = 3;
+ }
+ marginPtr->nAxes = nVisible;
+ marginPtr->axesTitleLength = l;
+ marginPtr->width = w;
+ marginPtr->height = h;
+ marginPtr->axesOffset = (isHoriz) ? h : w;
+ return marginPtr->axesOffset;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LayoutGraph --
+ *
+ * Calculate the layout of the graph. Based upon the data, axis limits,
+ * X and Y titles, and title height, determine the cavity left which is
+ * the plotting surface. The first step get the data and axis limits for
+ * calculating the space needed for the top, bottom, left, and right
+ * margins.
+ *
+ * 1) The LEFT margin is the area from the left border to the Y axis
+ * (not including ticks). It composes the border width, the width an
+ * optional Y axis label and its padding, and the tick numeric labels.
+ * The Y axis label is rotated 90 degrees so that the width is the
+ * font height.
+ *
+ * 2) The RIGHT margin is the area from the end of the graph
+ * to the right window border. It composes the border width,
+ * some padding, the font height (this may be dubious. It
+ * appears to provide a more even border), the max of the
+ * legend width and 1/2 max X tick number. This last part is
+ * so that the last tick label is not clipped.
+ *
+ * Window Width
+ * ___________________________________________________________
+ * | | | |
+ * | | TOP height of title | |
+ * | | | |
+ * | | x2 title | |
+ * | | | |
+ * | | height of x2-axis | |
+ * |__________|_______________________________|_______________| W
+ * | | -plotpady | | i
+ * |__________|_______________________________|_______________| n
+ * | | top right | | d
+ * | | | | o
+ * | LEFT | | RIGHT | w
+ * | | | |
+ * | y | Free area = 104% | y2 | H
+ * | | Plotting surface = 100% | | e
+ * | t | Tick length = 2 + 2% | t | i
+ * | i | | i | g
+ * | t | | t legend| h
+ * | l | | l width| t
+ * | e | | e |
+ * | height| |height |
+ * | of | | of |
+ * | y-axis| |y2-axis |
+ * | | | |
+ * | |origin 0,0 | |
+ * |__________|_left_________________bottom___|_______________|
+ * | |-plotpady | |
+ * |__________|_______________________________|_______________|
+ * | | (xoffset, yoffset) | |
+ * | | | |
+ * | | height of x-axis | |
+ * | | | |
+ * | | BOTTOM x title | |
+ * |__________|_______________________________|_______________|
+ *
+ * 3) The TOP margin is the area from the top window border to the top
+ * of the graph. It composes the border width, twice the height of
+ * the title font (if one is given) and some padding between the
+ * title.
+ *
+ * 4) The BOTTOM margin is area from the bottom window border to the
+ * X axis (not including ticks). It composes the border width, the height
+ * an optional X axis label and its padding, the height of the font
+ * of the tick labels.
+ *
+ * The plotting area is between the margins which includes the X and Y axes
+ * including the ticks but not the tick numeric labels. The length of the
+ * ticks and its padding is 5% of the entire plotting area. Hence the entire
+ * plotting area is scaled as 105% of the width and height of the area.
+ *
+ * The axis labels, ticks labels, title, and legend may or may not be
+ * displayed which must be taken into account.
+ *
+ * if reqWidth > 0 : set outer size
+ * if reqPlotWidth > 0 : set plot size
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_LayoutGraph(Graph *graphPtr)
+{
+ unsigned int titleY;
+ unsigned int left, right, top, bottom;
+ unsigned int plotWidth, plotHeight;
+ unsigned int inset, inset2;
+ int width, height;
+ int pad;
+
+ width = graphPtr->width;
+ height = graphPtr->height;
+
+ /*
+ * Step 1: Compute the amount of space needed to display the axes
+ * associated with each margin. They can be overridden by
+ * -leftmargin, -rightmargin, -bottommargin, and -topmargin
+ * graph options, respectively.
+ */
+ left = GetMarginGeometry(graphPtr, &graphPtr->leftMargin);
+ right = GetMarginGeometry(graphPtr, &graphPtr->rightMargin);
+ top = GetMarginGeometry(graphPtr, &graphPtr->topMargin);
+ bottom = GetMarginGeometry(graphPtr, &graphPtr->bottomMargin);
+
+ pad = graphPtr->bottomMargin.maxTickWidth;
+ if (pad < graphPtr->topMargin.maxTickWidth) {
+ pad = graphPtr->topMargin.maxTickWidth;
+ }
+ pad = pad / 2 + 3;
+ if (right < pad) {
+ right = pad;
+ }
+ if (left < pad) {
+ left = pad;
+ }
+ pad = graphPtr->leftMargin.maxTickHeight;
+ if (pad < graphPtr->rightMargin.maxTickHeight) {
+ pad = graphPtr->rightMargin.maxTickHeight;
+ }
+ pad = pad / 2;
+ if (top < pad) {
+ top = pad;
+ }
+ if (bottom < pad) {
+ bottom = pad;
+ }
+
+ if (graphPtr->leftMargin.reqSize > 0) {
+ left = graphPtr->leftMargin.reqSize;
+ }
+ if (graphPtr->rightMargin.reqSize > 0) {
+ right = graphPtr->rightMargin.reqSize;
+ }
+ if (graphPtr->topMargin.reqSize > 0) {
+ top = graphPtr->topMargin.reqSize;
+ }
+ if (graphPtr->bottomMargin.reqSize > 0) {
+ bottom = graphPtr->bottomMargin.reqSize;
+ }
+
+ /*
+ * Step 2: Add the graph title height to the top margin.
+ */
+ if (graphPtr->title != NULL) {
+ top += graphPtr->titleHeight + 6;
+ }
+ inset = (graphPtr->inset + graphPtr->plotBW);
+ inset2 = 2 * inset;
+
+ /*
+ * Step 3: Estimate the size of the plot area from the remaining
+ * space. This may be overridden by the -plotwidth and
+ * -plotheight graph options. We use this to compute the
+ * size of the legend.
+ */
+ if (width == 0) {
+ width = 400;
+ }
+ if (height == 0) {
+ height = 400;
+ }
+ plotWidth = (graphPtr->reqPlotWidth > 0) ? graphPtr->reqPlotWidth :
+ width - (inset2 + left + right); /* Plot width. */
+ plotHeight = (graphPtr->reqPlotHeight > 0) ? graphPtr->reqPlotHeight :
+ height - (inset2 + top + bottom); /* Plot height. */
+ Blt_MapLegend(graphPtr, plotWidth, plotHeight);
+
+ /*
+ * Step 2: Add the legend to the appropiate margin.
+ */
+ if (!Blt_Legend_IsHidden(graphPtr)) {
+ switch (Blt_Legend_Site(graphPtr)) {
+ case LEGEND_RIGHT:
+ right += Blt_Legend_Width(graphPtr) + 2;
+ break;
+ case LEGEND_LEFT:
+ left += Blt_Legend_Width(graphPtr) + 2;
+ break;
+ case LEGEND_TOP:
+ top += Blt_Legend_Height(graphPtr) + 2;
+ break;
+ case LEGEND_BOTTOM:
+ bottom += Blt_Legend_Height(graphPtr) + 2;
+ break;
+ case LEGEND_XY:
+ case LEGEND_PLOT:
+ case LEGEND_WINDOW:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ /*
+ * Recompute the plotarea or graph size, now accounting for the legend.
+ */
+ if (graphPtr->reqPlotWidth == 0) {
+ plotWidth = width - (inset2 + left + right);
+ if (plotWidth < 1) {
+ plotWidth = 1;
+ }
+ }
+ if (graphPtr->reqPlotHeight == 0) {
+ plotHeight = height - (inset2 + top + bottom);
+ if (plotHeight < 1) {
+ plotHeight = 1;
+ }
+ }
+
+ /*
+ * Step 5: If necessary, correct for the requested plot area aspect
+ * ratio.
+ */
+ if ((graphPtr->reqPlotWidth == 0) && (graphPtr->reqPlotHeight == 0) &&
+ (graphPtr->aspect > 0.0f)) {
+ float ratio;
+
+ /*
+ * Shrink one dimension of the plotarea to fit the requested
+ * width/height aspect ratio.
+ */
+ ratio = (float)plotWidth / (float)plotHeight;
+ if (ratio > graphPtr->aspect) {
+ int scaledWidth;
+
+ /* Shrink the width. */
+ scaledWidth = (int)(plotHeight * graphPtr->aspect);
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ /* Add the difference to the right margin. */
+ /* CHECK THIS: w = scaledWidth; */
+ right += (plotWidth - scaledWidth);
+ } else {
+ int scaledHeight;
+
+ /* Shrink the height. */
+ scaledHeight = (int)(plotWidth / graphPtr->aspect);
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ /* Add the difference to the top margin. */
+ /* CHECK THIS: h = scaledHeight; */
+ top += (plotHeight - scaledHeight);
+ }
+ }
+
+ /*
+ * Step 6: If there's multiple axes in a margin, the axis titles will be
+ * displayed in the adjoining margins. Make sure there's room
+ * for the longest axis titles.
+ */
+
+ if (top < graphPtr->leftMargin.axesTitleLength) {
+ top = graphPtr->leftMargin.axesTitleLength;
+ }
+ if (right < graphPtr->bottomMargin.axesTitleLength) {
+ right = graphPtr->bottomMargin.axesTitleLength;
+ }
+ if (top < graphPtr->rightMargin.axesTitleLength) {
+ top = graphPtr->rightMargin.axesTitleLength;
+ }
+ if (right < graphPtr->topMargin.axesTitleLength) {
+ right = graphPtr->topMargin.axesTitleLength;
+ }
+
+ /*
+ * Step 7: Override calculated values with requested margin sizes.
+ */
+ if (graphPtr->leftMargin.reqSize > 0) {
+ left = graphPtr->leftMargin.reqSize;
+ }
+ if (graphPtr->rightMargin.reqSize > 0) {
+ right = graphPtr->rightMargin.reqSize;
+ }
+ if (graphPtr->topMargin.reqSize > 0) {
+ top = graphPtr->topMargin.reqSize;
+ }
+ if (graphPtr->bottomMargin.reqSize > 0) {
+ bottom = graphPtr->bottomMargin.reqSize;
+ }
+ if (graphPtr->reqPlotWidth > 0) {
+ int w;
+
+ /*
+ * Width of plotarea is constained. If there's extra space, add it to
+ * th left and/or right margins. If there's too little, grow the
+ * graph width to accomodate it.
+ */
+ w = plotWidth + inset2 + left + right;
+ if (width > w) { /* Extra space in window. */
+ int extra;
+
+ extra = (width - w) / 2;
+ if (graphPtr->leftMargin.reqSize == 0) {
+ left += extra;
+ if (graphPtr->rightMargin.reqSize == 0) {
+ right += extra;
+ } else {
+ left += extra;
+ }
+ } else if (graphPtr->rightMargin.reqSize == 0) {
+ right += extra + extra;
+ }
+ } else if (width < w) {
+ width = w;
+ }
+ }
+ if (graphPtr->reqPlotHeight > 0) { /* Constrain the plotarea height. */
+ int h;
+
+ /*
+ * Height of plotarea is constained. If there's extra space,
+ * add it to th top and/or bottom margins. If there's too little,
+ * grow the graph height to accomodate it.
+ */
+ h = plotHeight + inset2 + top + bottom;
+ if (height > h) { /* Extra space in window. */
+ int extra;
+
+ extra = (height - h) / 2;
+ if (graphPtr->topMargin.reqSize == 0) {
+ top += extra;
+ if (graphPtr->bottomMargin.reqSize == 0) {
+ bottom += extra;
+ } else {
+ top += extra;
+ }
+ } else if (graphPtr->bottomMargin.reqSize == 0) {
+ bottom += extra + extra;
+ }
+ } else if (height < h) {
+ height = h;
+ }
+ }
+ graphPtr->width = width;
+ graphPtr->height = height;
+ graphPtr->left = left + inset;
+ graphPtr->top = top + inset;
+ graphPtr->right = width - right - inset;
+ graphPtr->bottom = height - bottom - inset;
+
+ graphPtr->leftMargin.width = left + graphPtr->inset;
+ graphPtr->rightMargin.width = right + graphPtr->inset;
+ graphPtr->topMargin.height = top + graphPtr->inset;
+ graphPtr->bottomMargin.height = bottom + graphPtr->inset;
+
+ graphPtr->vOffset = graphPtr->top + graphPtr->padTop;
+ graphPtr->vRange = plotHeight - PADDING(graphPtr->yPad);
+ graphPtr->hOffset = graphPtr->left + graphPtr->padLeft;
+ graphPtr->hRange = plotWidth - PADDING(graphPtr->xPad);
+
+ if (graphPtr->vRange < 1) {
+ graphPtr->vRange = 1;
+ }
+ if (graphPtr->hRange < 1) {
+ graphPtr->hRange = 1;
+ }
+ graphPtr->hScale = 1.0f / (float)graphPtr->hRange;
+ graphPtr->vScale = 1.0f / (float)graphPtr->vRange;
+
+ /*
+ * Calculate the placement of the graph title so it is centered within the
+ * space provided for it in the top margin
+ */
+ titleY = graphPtr->titleHeight;
+ graphPtr->titleY = 3 + graphPtr->inset;
+ graphPtr->titleX = (graphPtr->right + graphPtr->left) / 2;
+
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureAxis --
+ *
+ * Configures axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ * Axis layout is deferred until the height and width of the window are
+ * known.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureAxis(Axis *axisPtr)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ float angle;
+
+ /* Check the requested axis limits. Can't allow -min to be greater than
+ * -max. Do this regardless of -checklimits option. We want to always
+ * detect when the user has zoomed in beyond the precision of the data.*/
+ if (((DEFINED(axisPtr->reqMin)) && (DEFINED(axisPtr->reqMax))) &&
+ (axisPtr->reqMin >= axisPtr->reqMax)) {
+ char msg[200];
+ sprintf_s(msg, 200,
+ "impossible axis limits (-min %g >= -max %g) for \"%s\"",
+ axisPtr->reqMin, axisPtr->reqMax, axisPtr->obj.name);
+ Tcl_AppendResult(graphPtr->interp, msg, (char *)NULL);
+ return TCL_ERROR;
+ }
+ axisPtr->scrollMin = axisPtr->reqScrollMin;
+ axisPtr->scrollMax = axisPtr->reqScrollMax;
+ if (axisPtr->logScale) {
+ if (axisPtr->flags & AXIS_CHECK_LIMITS) {
+ /* Check that the logscale limits are positive. */
+ if ((DEFINED(axisPtr->reqMin)) && (axisPtr->reqMin <= 0.0)) {
+ Tcl_AppendResult(graphPtr->interp,"bad logscale -min limit \"",
+ Blt_Dtoa(graphPtr->interp, axisPtr->reqMin),
+ "\" for axis \"", axisPtr->obj.name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if ((DEFINED(axisPtr->scrollMin)) && (axisPtr->scrollMin <= 0.0)) {
+ axisPtr->scrollMin = Blt_NaN();
+ }
+ if ((DEFINED(axisPtr->scrollMax)) && (axisPtr->scrollMax <= 0.0)) {
+ axisPtr->scrollMax = Blt_NaN();
+ }
+ }
+ angle = FMOD(axisPtr->tickAngle, 360.0);
+ if (angle < 0.0f) {
+ angle += 360.0f;
+ }
+ if (axisPtr->normalBg != NULL) {
+ Blt_SetBackgroundChangedProc(axisPtr->normalBg, Blt_UpdateGraph,
+ graphPtr);
+ }
+ if (axisPtr->activeBg != NULL) {
+ Blt_SetBackgroundChangedProc(axisPtr->activeBg, Blt_UpdateGraph,
+ graphPtr);
+ }
+ axisPtr->tickAngle = angle;
+ ResetTextStyles(axisPtr);
+
+ axisPtr->titleWidth = axisPtr->titleHeight = 0;
+ if (axisPtr->title != NULL) {
+ unsigned int w, h;
+
+ Blt_GetTextExtents(axisPtr->titleFont, 0, axisPtr->title, -1, &w, &h);
+ axisPtr->titleWidth = (unsigned short int)w;
+ axisPtr->titleHeight = (unsigned short int)h;
+ }
+
+ /*
+ * Don't bother to check what configuration options have changed. Almost
+ * every option changes the size of the plotting area (except for -color
+ * and -titlecolor), requiring the graph and its contents to be completely
+ * redrawn.
+ *
+ * Recompute the scale and offset of the axis in case -min, -max options
+ * have changed.
+ */
+ graphPtr->flags |= REDRAW_WORLD;
+ graphPtr->flags |= MAP_WORLD | RESET_AXES | CACHE_DIRTY;
+ axisPtr->flags |= DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NewAxis --
+ *
+ * Create and initialize a structure containing information to display
+ * a graph axis.
+ *
+ * Results:
+ * The return value is a pointer to an Axis structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Axis *
+NewAxis(Graph *graphPtr, const char *name, int margin)
+{
+ Axis *axisPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (name[0] == '-') {
+ Tcl_AppendResult(graphPtr->interp, "name of axis \"", name,
+ "\" can't start with a '-'", (char *)NULL);
+ return NULL;
+ }
+ hPtr = Blt_CreateHashEntry(&graphPtr->axes.table, name, &isNew);
+ if (!isNew) {
+ axisPtr = Blt_GetHashValue(hPtr);
+ if ((axisPtr->flags & DELETE_PENDING) == 0) {
+ Tcl_AppendResult(graphPtr->interp, "axis \"", name,
+ "\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ axisPtr->flags &= ~DELETE_PENDING;
+ } else {
+ axisPtr = Blt_Calloc(1, sizeof(Axis));
+ if (axisPtr == NULL) {
+ Tcl_AppendResult(graphPtr->interp,
+ "can't allocate memory for axis \"", name, "\"", (char *)NULL);
+ return NULL;
+ }
+ axisPtr->obj.name = Blt_AssertStrdup(name);
+ axisPtr->hashPtr = hPtr;
+ Blt_GraphSetObjectClass(&axisPtr->obj, CID_NONE);
+ axisPtr->obj.graphPtr = graphPtr;
+ axisPtr->looseMin = axisPtr->looseMax = AXIS_TIGHT;
+ axisPtr->reqNumMinorTicks = 2;
+ axisPtr->reqNumMajorTicks = 4 /*10*/;
+ axisPtr->margin = MARGIN_NONE;
+ axisPtr->tickLength = 8;
+ axisPtr->scrollUnits = 10;
+ axisPtr->reqMin = axisPtr->reqMax = Blt_NaN();
+ axisPtr->reqScrollMin = axisPtr->reqScrollMax = Blt_NaN();
+ axisPtr->flags = (AXIS_SHOWTICKS|AXIS_GRIDMINOR|AXIS_AUTO_MAJOR|
+ AXIS_AUTO_MINOR | AXIS_EXTERIOR);
+ if (graphPtr->classId == CID_ELEM_BAR) {
+ axisPtr->flags |= AXIS_GRID;
+ }
+ if ((graphPtr->classId == CID_ELEM_BAR) &&
+ ((margin == MARGIN_TOP) || (margin == MARGIN_BOTTOM))) {
+ axisPtr->reqStep = 1.0;
+ axisPtr->reqNumMinorTicks = 0;
+ }
+ if ((margin == MARGIN_RIGHT) || (margin == MARGIN_TOP)) {
+ axisPtr->flags |= HIDE;
+ }
+ Blt_Ts_InitStyle(axisPtr->limitsTextStyle);
+ axisPtr->tickLabels = Blt_Chain_Create();
+ axisPtr->lineWidth = 1;
+ Blt_SetHashValue(hPtr, axisPtr);
+ }
+ return axisPtr;
+}
+
+static int
+GetAxisFromObj(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ Axis **axisPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+ const char *name;
+
+ *axisPtrPtr = NULL;
+ name = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&graphPtr->axes.table, name);
+ if (hPtr != NULL) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ if ((axisPtr->flags & DELETE_PENDING) == 0) {
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+ }
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find axis \"", name, "\" in \"",
+ Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+}
+
+static int
+GetAxisByClass(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ ClassId classId, Axis **axisPtrPtr)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objPtr, &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (classId != CID_NONE) {
+ if ((axisPtr->refCount == 0) || (axisPtr->obj.classId == CID_NONE)) {
+ /* Set the axis type on the first use of it. */
+ Blt_GraphSetObjectClass(&axisPtr->obj, classId);
+ } else if (axisPtr->obj.classId != classId) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "axis \"", Tcl_GetString(objPtr),
+ "\" is already in use on an opposite ",
+ axisPtr->obj.className, "-axis",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ axisPtr->refCount++;
+ }
+ *axisPtrPtr = axisPtr;
+ return TCL_OK;
+}
+
+void
+Blt_DestroyAxes(Graph *graphPtr)
+{
+ {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ axisPtr->hashPtr = NULL;
+ DestroyAxis(axisPtr);
+ }
+ }
+ Blt_DeleteHashTable(&graphPtr->axes.table);
+ {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ Blt_Chain_Destroy(graphPtr->axisChain[i]);
+ }
+ }
+ Blt_DeleteHashTable(&graphPtr->axes.tagTable);
+ Blt_Chain_Destroy(graphPtr->axes.displayList);
+}
+
+void
+Blt_ConfigureAxes(Graph *graphPtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ ConfigureAxis(axisPtr);
+ }
+}
+
+int
+Blt_DefaultAxes(Graph *graphPtr)
+{
+ int i;
+ int flags;
+
+ flags = Blt_GraphType(graphPtr);
+ for (i = 0; i < 4; i++) {
+ Blt_Chain chain;
+ Axis *axisPtr;
+
+ chain = Blt_Chain_Create();
+ graphPtr->axisChain[i] = chain;
+
+ /* Create a default axis for each chain. */
+ axisPtr = NewAxis(graphPtr, axisNames[i].name, i);
+ if (axisPtr == NULL) {
+ return TCL_ERROR;
+ }
+ axisPtr->refCount = 1; /* Default axes are assumed in use. */
+ axisPtr->margin = i;
+ axisPtr->flags |= AXIS_USE;
+ Blt_GraphSetObjectClass(&axisPtr->obj, axisNames[i].classId);
+ /*
+ * Blt_ConfigureComponentFromObj creates a temporary child window
+ * by the name of the axis. It's used so that the Tk routines
+ * that access the X resource database can describe a single
+ * component and not the entire graph.
+ */
+ if (Blt_ConfigureComponentFromObj(graphPtr->interp, graphPtr->tkwin,
+ axisPtr->obj.name, "Axis", configSpecs, 0, (Tcl_Obj **)NULL,
+ (char *)axisPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureAxis(axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ axisPtr->link = Blt_Chain_Append(chain, axisPtr);
+ axisPtr->chain = chain;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Activates the axis, drawing the axis with its -activeforeground,
+ * -activebackgound, -activerelief attributes.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new axis attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ActivateOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ const char *string;
+
+ string = Tcl_GetString(objv[2]);
+ if (string[0] == 'a') {
+ axisPtr->flags |= ACTIVE;
+ } else {
+ axisPtr->flags &= ~ACTIVE;
+ }
+ if ((axisPtr->flags & (AXIS_USE|HIDE)) == AXIS_USE) {
+ graphPtr->flags |= DRAW_MARGINS | CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*-------------------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g axis bind axisName sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+BindOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->bindTable,
+ Blt_MakeAxisTag(graphPtr, axisPtr->obj.name), objc, objv);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard TCL result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, objv[0], Blt_GraphType(graphPtr));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard TCL result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * Side Effects:
+ * Axis resources are possibly allocated (GC, font). Axis layout is
+ * deferred until the height and width of the window are known.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ int flags;
+
+ flags = BLT_CONFIG_OBJV_ONLY | Blt_GraphType(graphPtr);
+ if (objc == 0) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, (Tcl_Obj *)NULL, flags);
+ } else if (objc == 1) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)axisPtr, objv[0], flags);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, configSpecs,
+ objc, objv, (char *)axisPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureAxis(axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (axisPtr->flags & AXIS_USE) {
+ if (!Blt_ConfigModified(configSpecs, "-*color", "-background", "-bg",
+ (char *)NULL)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LimitsOp --
+ *
+ * This procedure returns a string representing the axis limits
+ * of the graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+LimitsOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ Tcl_Obj *listObjPtr;
+ double min, max;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (axisPtr->logScale) {
+ min = EXP10(axisPtr->axisRange.min);
+ max = EXP10(axisPtr->axisRange.max);
+ } else {
+ min = axisPtr->axisRange.min;
+ max = axisPtr->axisRange.max;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(min));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(max));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InvTransformOp --
+ *
+ * Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ * Returns a standard TCL result. interp->result contains
+ * the axis value. If an error occurred, TCL_ERROR is returned
+ * and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvTransformOp(Tcl_Interp *interp, Axis *axisPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ double y; /* Real graph coordinate */
+ int sy; /* Integer window coordinate*/
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Tcl_GetIntFromObj(interp, objv[0], &sy) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Is the axis vertical or horizontal?
+ *
+ * Check the site where the axis was positioned. If the axis is
+ * virtual, all we have to go on is how it was mapped to an
+ * element (using either -mapx or -mapy options).
+ */
+ if (AxisIsHorizontal(axisPtr)) {
+ y = Blt_InvHMap(axisPtr, (double)sy);
+ } else {
+ y = Blt_InvVMap(axisPtr, (double)sy);
+ }
+ Tcl_SetDoubleObj(Tcl_GetObjResult(interp), y);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MarginOp --
+ *
+ * This procedure returns a string representing the margin the axis
+ * resides. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MarginOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ const char *marginName;
+
+ marginName = "";
+ if (axisPtr->flags & AXIS_USE) {
+ marginName = axisNames[axisPtr->margin].name;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), marginName, -1);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ * Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ * Returns a standard TCL result. interp->result contains
+ * the window coordinate. If an error occurred, TCL_ERROR
+ * is returned and interp->result will contain an error
+ * message.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = axisPtr->obj.graphPtr;
+ double x;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Blt_ExprDoubleFromObj(interp, objv[0], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (AxisIsHorizontal(axisPtr)) {
+ x = Blt_HMap(axisPtr, x);
+ } else {
+ x = Blt_VMap(axisPtr, x);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), (int)x);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * This procedure returns a string representing the margin the axis
+ * resides. The format of the string is "x", "y", or "".
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is the type of
+ * axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ const char *typeName;
+
+ typeName = "";
+ if (axisPtr->flags & AXIS_USE) {
+ if (axisNames[axisPtr->margin].classId == CID_AXIS_X) {
+ typeName = "x";
+ } else if (axisNames[axisPtr->margin].classId == CID_AXIS_Y) {
+ typeName = "y";
+ }
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), typeName, -1);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UseOp --
+ *
+ * Sets the default axis for a margin.
+ *
+ * Results:
+ * A standard TCL result. If the named axis doesn't exist
+ * an error message is put in interp->result.
+ *
+ * .g xaxis use "abc def gah"
+ * .g xaxis use [lappend abc [.g axis use]]
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+UseOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr = (Graph *)axisPtr;
+ Blt_Chain chain;
+ Blt_ChainLink link;
+ Tcl_Obj **axisObjv;
+ ClassId classId;
+ int axisObjc;
+ int i;
+
+ chain = graphPtr->margins[lastMargin].axes;
+ if (objc == 0) {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (link = Blt_Chain_FirstLink(chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(axisPtr->obj.name, -1));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ if ((lastMargin == MARGIN_BOTTOM) || (lastMargin == MARGIN_TOP)) {
+ classId = (graphPtr->inverted) ? CID_AXIS_Y : CID_AXIS_X;
+ } else {
+ classId = (graphPtr->inverted) ? CID_AXIS_X : CID_AXIS_Y;
+ }
+ if (Tcl_ListObjGetElements(interp, objv[0], &axisObjc, &axisObjv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (link = Blt_Chain_FirstLink(chain); link!= NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ axisPtr->link = NULL;
+ axisPtr->flags &= ~AXIS_USE;
+ /* Clear the axis type if it's not currently used.*/
+ if (axisPtr->refCount == 0) {
+ Blt_GraphSetObjectClass(&axisPtr->obj, CID_NONE);
+ }
+ }
+ Blt_Chain_Reset(chain);
+ for (i = 0; i < axisObjc; i++) {
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, axisObjv[i], &axisPtr) != TCL_OK){
+ return TCL_ERROR;
+ }
+ if (axisPtr->obj.classId == CID_NONE) {
+ Blt_GraphSetObjectClass(&axisPtr->obj, classId);
+ } else if (axisPtr->obj.classId != classId) {
+ Tcl_AppendResult(interp, "wrong type axis \"",
+ axisPtr->obj.name, "\": can't use ",
+ axisPtr->obj.className, " type axis.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (axisPtr->link != NULL) {
+ /* Move the axis from the old margin's "use" list to the new. */
+ Blt_Chain_UnlinkLink(axisPtr->chain, axisPtr->link);
+ Blt_Chain_AppendLink(chain, axisPtr->link);
+ } else {
+ axisPtr->link = Blt_Chain_Append(chain, axisPtr);
+ }
+ axisPtr->chain = chain;
+ axisPtr->flags |= AXIS_USE;
+ }
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+ /* When any axis changes, we need to layout the entire graph. */
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+static int
+ViewOp(Tcl_Interp *interp, Axis *axisPtr, int objc, Tcl_Obj *const *objv)
+{
+ Graph *graphPtr;
+ double axisOffset, axisScale;
+ double fract;
+ double viewMin, viewMax, worldMin, worldMax;
+ double viewWidth, worldWidth;
+
+ graphPtr = axisPtr->obj.graphPtr;
+ worldMin = axisPtr->valueRange.min;
+ worldMax = axisPtr->valueRange.max;
+ /* Override data dimensions with user-selected limits. */
+ if (DEFINED(axisPtr->scrollMin)) {
+ worldMin = axisPtr->scrollMin;
+ }
+ if (DEFINED(axisPtr->scrollMax)) {
+ worldMax = axisPtr->scrollMax;
+ }
+ viewMin = axisPtr->min;
+ viewMax = axisPtr->max;
+ /* Bound the view within scroll region. */
+ if (viewMin < worldMin) {
+ viewMin = worldMin;
+ }
+ if (viewMax > worldMax) {
+ viewMax = worldMax;
+ }
+ if (axisPtr->logScale) {
+ worldMin = log10(worldMin);
+ worldMax = log10(worldMax);
+ viewMin = log10(viewMin);
+ viewMax = log10(viewMax);
+ }
+ worldWidth = worldMax - worldMin;
+ viewWidth = viewMax - viewMin;
+
+ /* Unlike horizontal axes, vertical axis values run opposite of the
+ * scrollbar first/last values. So instead of pushing the axis minimum
+ * around, we move the maximum instead. */
+ if (AxisIsHorizontal(axisPtr) != axisPtr->descending) {
+ axisOffset = viewMin - worldMin;
+ axisScale = graphPtr->hScale;
+ } else {
+ axisOffset = worldMax - viewMax;
+ axisScale = graphPtr->vScale;
+ }
+ if (objc == 4) {
+ Tcl_Obj *listObjPtr;
+ double first, last;
+
+ first = Clamp(axisOffset / worldWidth);
+ last = Clamp((axisOffset + viewWidth) / worldWidth);
+ listObjPtr = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(first));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(last));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ fract = axisOffset / worldWidth;
+ if (GetAxisScrollInfo(interp, objc, objv, &fract,
+ viewWidth / worldWidth, axisPtr->scrollUnits, axisScale) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (AxisIsHorizontal(axisPtr) != axisPtr->descending) {
+ axisPtr->reqMin = (fract * worldWidth) + worldMin;
+ axisPtr->reqMax = axisPtr->reqMin + viewWidth;
+ } else {
+ axisPtr->reqMax = worldMax - (fract * worldWidth);
+ axisPtr->reqMin = axisPtr->reqMax - viewWidth;
+ }
+ if (axisPtr->logScale) {
+ axisPtr->reqMin = EXP10(axisPtr->reqMin);
+ axisPtr->reqMax = EXP10(axisPtr->reqMax);
+ }
+ graphPtr->flags |= (GET_AXIS_GEOMETRY | LAYOUT_NEEDED | RESET_AXES);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisCreateOp --
+ *
+ * Creates a new axis.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisCreateOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+ int flags;
+
+ axisPtr = NewAxis(graphPtr, Tcl_GetString(objv[3]), MARGIN_NONE);
+ if (axisPtr == NULL) {
+ return TCL_ERROR;
+ }
+ flags = Blt_GraphType(graphPtr);
+ if ((Blt_ConfigureComponentFromObj(interp, graphPtr->tkwin,
+ axisPtr->obj.name, "Axis", configSpecs, objc - 4, objv + 4,
+ (char *)axisPtr, flags) != TCL_OK) ||
+ (ConfigureAxis(axisPtr) != TCL_OK)) {
+ DestroyAxis(axisPtr);
+ return TCL_ERROR;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), axisPtr->obj.name, -1);
+ return TCL_OK;
+}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisActivateOp --
+ *
+ * Activates the axis, drawing the axis with its -activeforeground,
+ * -activebackgound, -activerelief attributes.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new axis attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisActivateOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return ActivateOp(interp, axisPtr, objc, objv);
+}
+
+
+/*-------------------------------------------------------------------------------
+ *
+ * AxisBindOp --
+ *
+ * .g axis bind axisName sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisBindOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.tagTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ const char *tagName;
+ Tcl_Obj *objPtr;
+
+ tagName = Blt_GetHashKey(&graphPtr->axes.tagTable, hPtr);
+ objPtr = Tcl_NewStringObj(tagName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->bindTable,
+ Blt_MakeAxisTag(graphPtr, Tcl_GetString(objv[3])), objc - 4, objv + 4);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisCgetOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard TCL result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+AxisCgetOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return CgetOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisConfigureOp --
+ *
+ * Queries or resets axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * Return value is a standard TCL result. If querying configuration
+ * values, interp->result will contain the results.
+ *
+ * Side Effects:
+ * Axis resources are possibly allocated (GC, font). Axis layout is
+ * deferred until the height and width of the window are known.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisConfigureOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Tcl_Obj *const *options;
+ int i;
+ int nNames, nOpts;
+
+ /* Figure out where the option value pairs begin */
+ objc -= 3;
+ objv += 3;
+ for (i = 0; i < objc; i++) {
+ Axis *axisPtr;
+ const char *string;
+
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ if (GetAxisFromObj(interp, graphPtr, objv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of pen names specified */
+ nOpts = objc - i; /* Number of options specified */
+ options = objv + i; /* Start of options in objv */
+
+ for (i = 0; i < nNames; i++) {
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (ConfigureOp(interp, axisPtr, nOpts, options) != TCL_OK) {
+ break;
+ }
+ }
+ if (i < nNames) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisDeleteOp --
+ *
+ * Deletes one or more axes. The actual removal may be deferred until the
+ * axis is no longer used by any element. The axis can't be referenced by
+ * its name any longer and it may be recreated.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisDeleteOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[i], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ axisPtr->flags |= DELETE_PENDING;
+ if (axisPtr->refCount == 0) {
+ Tcl_EventuallyFree(axisPtr, FreeAxis);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisFocusOp --
+ *
+ * Activates the axis, drawing the axis with its -activeforeground,
+ * -activebackgound, -activerelief attributes.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new axis attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisFocusOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ if (objc > 3) {
+ Axis *axisPtr;
+ const char *string;
+
+ axisPtr = NULL;
+ string = Tcl_GetString(objv[3]);
+ if ((string[0] != '\0') &&
+ (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ graphPtr->focusPtr = NULL;
+ if ((axisPtr != NULL) &&
+ ((axisPtr->flags & (AXIS_USE|HIDE)) == AXIS_USE)) {
+ graphPtr->focusPtr = axisPtr;
+ }
+ Blt_SetFocusItem(graphPtr->bindTable, graphPtr->focusPtr, NULL);
+ }
+ /* Return the name of the axis that has focus. */
+ if (graphPtr->focusPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ graphPtr->focusPtr->obj.name, -1);
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisGetOp --
+ *
+ * Returns the name of the picked axis (using the axis bind operation).
+ * Right now, the only name accepted is "current".
+ *
+ * Results:
+ * A standard TCL result. The interpreter result will contain the name of
+ * the axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisGetOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on axes. */
+ if ((axisPtr != NULL) &&
+ ((axisPtr->obj.classId == CID_AXIS_X) ||
+ (axisPtr->obj.classId == CID_AXIS_Y) ||
+ (axisPtr->obj.classId == CID_NONE))) {
+ char c;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ c = string[0];
+ if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), axisPtr->obj.name,-1);
+ } else if ((c == 'd') && (strcmp(string, "detail") == 0)) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), axisPtr->detail, -1);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisInvTransformOp --
+ *
+ * Maps the given window coordinate into an axis-value.
+ *
+ * Results:
+ * Returns a standard TCL result. interp->result contains
+ * the axis value. If an error occurred, TCL_ERROR is returned
+ * and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisInvTransformOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return InvTransformOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisLimitsOp --
+ *
+ * This procedure returns a string representing the axis limits of the
+ * graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisLimitsOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return LimitsOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisMarginOp --
+ *
+ * This procedure returns a string representing the axis limits of the
+ * graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisMarginOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return MarginOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisNamesOp --
+ *
+ * Return a list of the names of all the axes.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+AxisNamesOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ if (axisPtr->flags & DELETE_PENDING) {
+ continue;
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(axisPtr->obj.name, -1));
+ }
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+ int i;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ for (i = 3; i < objc; i++) {
+ const char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ if (Tcl_StringMatch(axisPtr->obj.name, pattern)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(axisPtr->obj.name, -1));
+ break;
+ }
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisTransformOp --
+ *
+ * Maps the given axis-value to a window coordinate.
+ *
+ * Results:
+ * Returns the window coordinate via interp->result. If an error occurred,
+ * TCL_ERROR is returned and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisTransformOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TransformOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisMarginOp --
+ *
+ * This procedure returns a string representing the axis limits of the
+ * graph. The format of the string is { left top right bottom}.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is
+ * a list of the graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AxisTypeOp(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TypeOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+
+static int
+AxisViewOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Axis *axisPtr;
+
+ if (GetAxisFromObj(interp, graphPtr, objv[3], &axisPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return ViewOp(interp, axisPtr, objc - 4, objv + 4);
+}
+
+static Blt_OpSpec virtAxisOps[] = {
+ {"activate", 1, AxisActivateOp, 4, 4, "axisName"},
+ {"bind", 1, AxisBindOp, 3, 6, "axisName sequence command"},
+ {"cget", 2, AxisCgetOp, 5, 5, "axisName option"},
+ {"configure", 2, AxisConfigureOp, 4, 0, "axisName ?axisName?... "
+ "?option value?..."},
+ {"create", 2, AxisCreateOp, 4, 0, "axisName ?option value?..."},
+ {"deactivate", 3, AxisActivateOp, 4, 4, "axisName"},
+ {"delete", 3, AxisDeleteOp, 3, 0, "?axisName?..."},
+ {"focus", 1, AxisFocusOp, 3, 4, "?axisName?"},
+ {"get", 1, AxisGetOp, 4, 4, "name"},
+ {"invtransform", 1, AxisInvTransformOp, 5, 5, "axisName value"},
+ {"limits", 1, AxisLimitsOp, 4, 4, "axisName"},
+ {"margin", 1, AxisMarginOp, 4, 4, "axisName"},
+ {"names", 1, AxisNamesOp, 3, 0, "?pattern?..."},
+ {"transform", 2, AxisTransformOp, 5, 5, "axisName value"},
+ {"type", 2, AxisTypeOp, 4, 4, "axisName"},
+ {"view", 1, AxisViewOp, 4, 7, "axisName ?moveto fract? "
+ "?scroll number what?"},
+};
+static int nVirtAxisOps = sizeof(virtAxisOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_VirtualAxisOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphVirtualAxisProc *proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nVirtAxisOps, virtAxisOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (interp, graphPtr, objc, objv);
+ return result;
+}
+
+static Blt_OpSpec axisOps[] = {
+ {"activate", 1, ActivateOp, 3, 3, "",},
+ {"bind", 1, BindOp, 2, 5, "sequence command",},
+ {"cget", 2, CgetOp, 4, 4, "option",},
+ {"configure", 2, ConfigureOp, 3, 0, "?option value?...",},
+ {"deactivate", 1, ActivateOp, 3, 3, "",},
+ {"invtransform", 1, InvTransformOp, 4, 4, "value",},
+ {"limits", 1, LimitsOp, 3, 3, "",},
+ {"transform", 1, TransformOp, 4, 4, "value",},
+ {"use", 1, UseOp, 3, 4, "?axisName?",},
+ {"view", 1, ViewOp, 3, 6, "?moveto fract? ",},
+};
+
+static int nAxisOps = sizeof(axisOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_AxisOp(Tcl_Interp *interp, Graph *graphPtr, int margin, int objc,
+ Tcl_Obj *const *objv)
+{
+ int result;
+ GraphAxisProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nAxisOps, axisOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ if (proc == UseOp) {
+ lastMargin = margin; /* Set global variable to the margin
+ * in the argument list. Needed only
+ * for UseOp. */
+ result = (*proc)(interp, (Axis *)graphPtr, objc - 3, objv + 3);
+ } else {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetFirstAxis(graphPtr->margins[margin].axes);
+ if (axisPtr == NULL) {
+ return TCL_OK;
+ }
+ result = (*proc)(interp, axisPtr, objc - 3, objv + 3);
+ }
+ return result;
+}
+
+void
+Blt_MapAxes(Graph *graphPtr)
+{
+ int margin;
+
+ for (margin = 0; margin < 4; margin++) {
+ Blt_Chain chain;
+ Blt_ChainLink link;
+ int count, offset;
+
+ chain = graphPtr->margins[margin].axes;
+ count = offset = 0;
+ for (link = Blt_Chain_FirstLink(chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (AXIS_USE|DELETE_PENDING)) != AXIS_USE) {
+ continue;
+ }
+ if (graphPtr->stackAxes) {
+ if (axisPtr->reqNumMajorTicks <= 0) {
+ axisPtr->reqNumMajorTicks = 4;
+ }
+ MapStackedAxis(axisPtr, count, margin);
+ } else {
+ if (axisPtr->reqNumMajorTicks <= 0) {
+ axisPtr->reqNumMajorTicks = 4;
+ }
+ MapAxis(axisPtr, offset, margin);
+ }
+ if (axisPtr->flags & AXIS_GRID) {
+ MapGridlines(axisPtr);
+ }
+ offset += (AxisIsHorizontal(axisPtr))
+ ? axisPtr->height : axisPtr->width;
+ count++;
+ }
+ }
+}
+
+void
+Blt_DrawAxes(Graph *graphPtr, Drawable drawable)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->margins[i].axes);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (DELETE_PENDING|HIDE|AXIS_USE)) == AXIS_USE) {
+ DrawAxis(axisPtr, drawable);
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawGrids --
+ *
+ * Draws the grid lines associated with each axis.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawGrids(Graph *graphPtr, Drawable drawable)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->margins[i].axes); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if (axisPtr->flags & (DELETE_PENDING|HIDE)) {
+ continue;
+ }
+ if ((axisPtr->flags & AXIS_USE) && (axisPtr->flags & AXIS_GRID)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable,
+ axisPtr->major.gc, axisPtr->major.segments,
+ axisPtr->major.nUsed);
+ if (axisPtr->flags & AXIS_GRIDMINOR) {
+ Blt_Draw2DSegments(graphPtr->display, drawable,
+ axisPtr->minor.gc, axisPtr->minor.segments,
+ axisPtr->minor.nUsed);
+ }
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GridsToPostScript --
+ *
+ * Draws the grid lines associated with each axis.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_GridsToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->margins[i].axes); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (DELETE_PENDING|HIDE|AXIS_USE|AXIS_GRID)) !=
+ (AXIS_GRID|AXIS_USE)) {
+ continue;
+ }
+ Blt_Ps_Format(ps, "%% Axis %s: grid line attributes\n",
+ axisPtr->obj.name);
+ Blt_Ps_XSetLineAttributes(ps, axisPtr->major.color,
+ axisPtr->major.lineWidth, &axisPtr->major.dashes, CapButt,
+ JoinMiter);
+ Blt_Ps_Format(ps, "%% Axis %s: major grid line segments\n",
+ axisPtr->obj.name);
+ Blt_Ps_Draw2DSegments(ps, axisPtr->major.segments,
+ axisPtr->major.nUsed);
+ if (axisPtr->flags & AXIS_GRIDMINOR) {
+ Blt_Ps_XSetLineAttributes(ps, axisPtr->minor.color,
+ axisPtr->minor.lineWidth, &axisPtr->minor.dashes, CapButt,
+ JoinMiter);
+ Blt_Ps_Format(ps, "%% Axis %s: minor grid line segments\n",
+ axisPtr->obj.name);
+ Blt_Ps_Draw2DSegments(ps, axisPtr->minor.segments,
+ axisPtr->minor.nUsed);
+ }
+ }
+ }
+}
+
+void
+Blt_AxesToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ Margin *mp, *mend;
+
+ for (mp = graphPtr->margins, mend = mp + 4; mp < mend; mp++) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(mp->axes); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_Chain_GetValue(link);
+ if ((axisPtr->flags & (DELETE_PENDING|HIDE|AXIS_USE)) == AXIS_USE) {
+ AxisToPostScript(ps, axisPtr);
+ }
+ }
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawAxisLimits --
+ *
+ * Draws the min/max values of the axis in the plotting area. The text
+ * strings are formatted according to the "sprintf" format descriptors in
+ * the limitsFormats array.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws the numeric values of the axis limits into the outer regions of
+ * the plotting area.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawAxisLimits(Graph *graphPtr, Drawable drawable)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char minString[200], maxString[200];
+ int vMin, hMin, vMax, hMax;
+
+#define SPACING 8
+ vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+ hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2; /* Offsets */
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+ Dim2D textDim;
+ const char *minFmt, *maxFmt;
+ char *minPtr, *maxPtr;
+ int isHoriz;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ if (axisPtr->flags & DELETE_PENDING) {
+ continue;
+ }
+ if (axisPtr->nFormats == 0) {
+ continue;
+ }
+ isHoriz = AxisIsHorizontal(axisPtr);
+ minPtr = maxPtr = NULL;
+ minFmt = maxFmt = axisPtr->limitsFormats[0];
+ if (axisPtr->nFormats > 1) {
+ maxFmt = axisPtr->limitsFormats[1];
+ }
+ if (minFmt[0] != '\0') {
+ minPtr = minString;
+ sprintf_s(minString, 200, minFmt, axisPtr->axisRange.min);
+ }
+ if (maxFmt[0] != '\0') {
+ maxPtr = maxString;
+ sprintf_s(maxString, 200, maxFmt, axisPtr->axisRange.max);
+ }
+ if (axisPtr->descending) {
+ char *tmp;
+
+ tmp = minPtr, minPtr = maxPtr, maxPtr = tmp;
+ }
+ if (maxPtr != NULL) {
+ if (isHoriz) {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 90.0);
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_SE);
+ Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+ &axisPtr->limitsTextStyle, graphPtr->right, hMax, &textDim);
+ hMax -= (textDim.height + SPACING);
+ } else {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 0.0);
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_NW);
+ Blt_DrawText2(graphPtr->tkwin, drawable, maxPtr,
+ &axisPtr->limitsTextStyle, vMax, graphPtr->top, &textDim);
+ vMax += (textDim.width + SPACING);
+ }
+ }
+ if (minPtr != NULL) {
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_SW);
+ if (isHoriz) {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 90.0);
+ Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+ &axisPtr->limitsTextStyle, graphPtr->left, hMin, &textDim);
+ hMin -= (textDim.height + SPACING);
+ } else {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 0.0);
+ Blt_DrawText2(graphPtr->tkwin, drawable, minPtr,
+ &axisPtr->limitsTextStyle, vMin, graphPtr->bottom, &textDim);
+ vMin += (textDim.width + SPACING);
+ }
+ }
+ } /* Loop on axes */
+}
+
+void
+Blt_AxisLimitsToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ double vMin, hMin, vMax, hMax;
+ char string[200];
+
+#define SPACING 8
+ vMin = vMax = graphPtr->left + graphPtr->padLeft + 2;
+ hMin = hMax = graphPtr->bottom - graphPtr->padBottom - 2; /* Offsets */
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+ const char *minFmt, *maxFmt;
+ unsigned int textWidth, textHeight;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ if (axisPtr->flags & DELETE_PENDING) {
+ continue;
+ }
+ if (axisPtr->nFormats == 0) {
+ continue;
+ }
+ minFmt = maxFmt = axisPtr->limitsFormats[0];
+ if (axisPtr->nFormats > 1) {
+ maxFmt = axisPtr->limitsFormats[1];
+ }
+ if (*maxFmt != '\0') {
+ sprintf_s(string, 200, maxFmt, axisPtr->axisRange.max);
+ Blt_GetTextExtents(axisPtr->tickFont, 0, string, -1, &textWidth,
+ &textHeight);
+ if ((textWidth > 0) && (textHeight > 0)) {
+ if (axisPtr->obj.classId == CID_AXIS_X) {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 90.0);
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_SE);
+ Blt_Ps_DrawText(ps, string, &axisPtr->limitsTextStyle,
+ (double)graphPtr->right, hMax);
+ hMax -= (textWidth + SPACING);
+ } else {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 0.0);
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_NW);
+ Blt_Ps_DrawText(ps, string, &axisPtr->limitsTextStyle,
+ vMax, (double)graphPtr->top);
+ vMax += (textWidth + SPACING);
+ }
+ }
+ }
+ if (*minFmt != '\0') {
+ sprintf_s(string, 200, minFmt, axisPtr->axisRange.min);
+ Blt_GetTextExtents(axisPtr->tickFont, 0, string, -1, &textWidth,
+ &textHeight);
+ if ((textWidth > 0) && (textHeight > 0)) {
+ Blt_Ts_SetAnchor(axisPtr->limitsTextStyle, TK_ANCHOR_SW);
+ if (axisPtr->obj.classId == CID_AXIS_X) {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 90.0);
+ Blt_Ps_DrawText(ps, string, &axisPtr->limitsTextStyle,
+ (double)graphPtr->left, hMin);
+ hMin -= (textWidth + SPACING);
+ } else {
+ Blt_Ts_SetAngle(axisPtr->limitsTextStyle, 0.0);
+ Blt_Ps_DrawText(ps, string, &axisPtr->limitsTextStyle,
+ vMin, (double)graphPtr->bottom);
+ vMin += (textWidth + SPACING);
+ }
+ }
+ }
+ }
+}
+
+Axis *
+Blt_GetFirstAxis(Blt_Chain chain)
+{
+ Blt_ChainLink link;
+
+ link = Blt_Chain_FirstLink(chain);
+ if (link == NULL) {
+ return NULL;
+ }
+ return Blt_Chain_GetValue(link);
+}
+
+Axis *
+Blt_NearestAxis(Graph *graphPtr, int x, int y)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->axes.table, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Axis *axisPtr;
+
+ axisPtr = Blt_GetHashValue(hPtr);
+ if ((axisPtr->flags & (DELETE_PENDING|HIDE|AXIS_USE)) != AXIS_USE) {
+ continue;
+ }
+ if (axisPtr->flags & AXIS_SHOWTICKS) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(axisPtr->tickLabels); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ TickLabel *labelPtr;
+ Point2d t;
+ double rw, rh;
+ Point2d bbox[5];
+
+ labelPtr = Blt_Chain_GetValue(link);
+ Blt_GetBoundingBox(labelPtr->width, labelPtr->height,
+ axisPtr->tickAngle, &rw, &rh, bbox);
+ t = Blt_AnchorPoint(labelPtr->anchorPos.x,
+ labelPtr->anchorPos.y, rw, rh, axisPtr->tickAnchor);
+ t.x = x - t.x - (rw * 0.5);
+ t.y = y - t.y - (rh * 0.5);
+
+ bbox[4] = bbox[0];
+ if (Blt_PointInPolygon(&t, bbox, 5)) {
+ axisPtr->detail = "label";
+ return axisPtr;
+ }
+ }
+ }
+ if (axisPtr->title != NULL) { /* and then the title string. */
+ Point2d bbox[5];
+ Point2d t;
+ double rw, rh;
+ unsigned int w, h;
+
+ Blt_GetTextExtents(axisPtr->titleFont, 0, axisPtr->title,-1,&w,&h);
+ Blt_GetBoundingBox(w, h, axisPtr->titleAngle, &rw, &rh, bbox);
+ t = Blt_AnchorPoint(axisPtr->titlePos.x, axisPtr->titlePos.y,
+ rw, rh, axisPtr->titleAnchor);
+ /* Translate the point so that the 0,0 is the upper left
+ * corner of the bounding box. */
+ t.x = x - t.x - (rw * 0.5);
+ t.y = y - t.y - (rh * 0.5);
+
+ bbox[4] = bbox[0];
+ if (Blt_PointInPolygon(&t, bbox, 5)) {
+ axisPtr->detail = "title";
+ return axisPtr;
+ }
+ }
+ if (axisPtr->lineWidth > 0) { /* Check for the axis region */
+ if ((x <= axisPtr->right) && (x >= axisPtr->left) &&
+ (y <= axisPtr->bottom) && (y >= axisPtr->top)) {
+ axisPtr->detail = "line";
+ return axisPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+ClientData
+Blt_MakeAxisTag(Graph *graphPtr, const char *tagName)
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&graphPtr->axes.tagTable, tagName, &isNew);
+ return Blt_GetHashKey(&graphPtr->axes.tagTable, hPtr);
+}
+
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TimeScaleAxis --
+ *
+ * Determine the units of a linear scaled axis.
+ *
+ * The axis limits are either the range of the data values mapped
+ * to the axis (autoscaled), or the values specified by the -min
+ * and -max options (manual).
+ *
+ * If autoscaled, the smallest and largest major ticks will
+ * encompass the range of data values. If the -loose option is
+ * selected, the next outer ticks are choosen. If tight, the
+ * ticks are at or inside of the data limits are used.
+ *
+ * If manually set, the ticks are at or inside the data limits
+ * are used. This makes sense for zooming. You want the
+ * selected range to represent the next limit, not something a
+ * bit bigger.
+ *
+ * Note: I added an "always" value to the -loose option to force
+ * the manually selected axes to be loose. It's probably
+ * not a good idea.
+ *
+ * maxY
+ * | units = magnitude (of least significant digit)
+ * | high = largest unit tick < max axis value
+ * high _| low = smallest unit tick > min axis value
+ * |
+ * | range = high - low
+ * | # ticks = greatest factor of range/units
+ * _|
+ * U |
+ * n |
+ * i |
+ * t _|
+ * |
+ * |
+ * |
+ * low _|
+ * |
+ * |_minX________________maxX__
+ * | | | | |
+ * minY low high
+ * minY
+ *
+ * numTicks = Number of ticks
+ * min = Minimum value of axis
+ * max = Maximum value of axis
+ * range = Range of values (max - min)
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The axis tick information is set. The actual tick values will
+ * be generated later.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+TimeScaleAxis(Axis *axisPtr, double min, double max)
+{
+#ifdef notdef
+ double step;
+ double tickMin, tickMax;
+ double axisMin, axisMax;
+ unsigned int nTicks;
+
+ range = max - min;
+
+
+#define SECONDS_MIN (60)
+#define SECONDS_HOUR (SECONDS_MIN * 60)
+#define SECONDS_DAY (SECONDS_HOUR * 24)
+#define SECONDS_MONTH (SECONDS_DAY * 30)
+#define SECONDS_YEAR (SECONDS_MONTH * 12)
+ div = nTicks - 1;
+ if (range > (SECONDS_MONTH * 6 * div)) {
+ unit = TICK_YEAR;
+ } else if (range > (SECONDS_MONTH * 3 * div)) {
+ unit = TICKS_6MONTH;
+ first = timefloor(min, unit);
+ last = timeceil(max, unit);
+ } else if (range > (SECONDS_MONTH * 2 * div)) {
+ unit = TICKS_3MONTH;
+ first = timefloor(min, unit);
+ last = timeceil(max, unit);
+ } else if (range > (SECONDS_MONTH * div)) {
+ unit = TICKS_2MONTH;
+ first = timefloor(min, unit);
+ last = timeceil(max, unit);
+ } else if (range > (SECONDS_DAY * 15 * div)) {
+ unit = TICKS_1MONTH;
+ } else if (range > (SECONDS_DAY * 10 * div)) {
+ unit = TICKS_15DAY;
+ } else if (range > (SECONDS_DAY * div)) {
+ unit = TICKS_10DAY;
+ } else if (range > (SECONDS_HOUR * 12 * div)) {
+ unit = TICKS_1DAY;
+ } else if (range > (SECONDS_HOUR * 6 * div)) {
+ unit = TICKS_12HOUR;
+ } else if (range > (SECONDS_HOUR * 3 * div)) {
+ unit = TICKS_6HOUR;
+ } else if (range > (SECONDS_HOUR * div)) {
+ unit = TICKS_3HOUR;
+ } else if (range > (SECONDS_MIN * 30 * div)) {
+ unit = TICKS_HOUR;
+ } else if (range > (SECONDS_MIN * 20 * div)) {
+ unit = TICKS_30MIN;
+ } else if (range > (SECONDS_MIN * 15 * div)) {
+ unit = TICKS_20MIN;
+ } else if (range > (SECONDS_MIN * 10 * div)) {
+ unit = TICKS_15MIN;
+ } else if (range > (SECONDS_MIN * div)) {
+ unit = TICKS_10MIN;
+ } else if (range > (SECONDS_SEC * 30 * div)) {
+ unit = TICKS_1MIN;
+ } else if (range > (SECONDS_SEC * 20 * div)) {
+ unit = TICKS_30SEC;
+ } else if (range > (SECONDS_SEC * 15 * div)) {
+ unit = TICKS_20SEC;
+ } else if (range > (SECONDS_SEC * 10 * div)) {
+ unit = TICKS_15SEC;
+ } else if (range > (SECONDS_SEC * div)) {
+ unit = TICKS_10SEC;
+ } else {
+ unit = TICKS_1SEC;
+ }
+
+ } else {
+ unit = TICKS_SECS;
+ }
+ nTicks = 0;
+ step = 1.0;
+ /* Suppress compiler warning. */
+ axisMin = axisMax = tickMin = tickMax = Blt_NaN();
+ if (min < max) {
+ double range;
+
+ range = max - min;
+ /* Calculate the major tick stepping. */
+ if (axisPtr->reqStep > 0.0) {
+ /* An interval was designated by the user. Keep scaling it until
+ * it fits comfortably within the current range of the axis. */
+ step = axisPtr->reqStep;
+ while ((2 * step) >= range) {
+ step *= 0.5;
+ }
+ } else {
+ range = NiceNum(range, 0);
+ step = NiceNum(range / axisPtr->reqNumMajorTicks, 1);
+ }
+
+ /* Find the outer tick values. Add 0.0 to prevent getting -0.0. */
+ axisMin = tickMin = floor(min / step) * step + 0.0;
+ axisMax = tickMax = ceil(max / step) * step + 0.0;
+
+ nTicks = Round((tickMax - tickMin) / step) + 1;
+ }
+ axisPtr->majorSweep.step = step;
+ axisPtr->majorSweep.initial = tickMin;
+ axisPtr->majorSweep.nSteps = nTicks;
+
+ /*
+ * The limits of the axis are either the range of the data ("tight") or at
+ * the next outer tick interval ("loose"). The looseness or tightness has
+ * to do with how the axis fits the range of data values. This option is
+ * overridden when the user sets an axis limit (by either -min or -max
+ * option). The axis limit is always at the selected limit (otherwise we
+ * assume that user would have picked a different number).
+ */
+ if ((axisPtr->looseMin == AXIS_TIGHT) ||
+ ((axisPtr->looseMin == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMin)))) {
+ axisMin = min;
+ }
+ if ((axisPtr->looseMax == AXIS_TIGHT) ||
+ ((axisPtr->looseMax == AXIS_LOOSE) &&
+ (DEFINED(axisPtr->reqMax)))) {
+ axisMax = max;
+ }
+ SetAxisRange(&axisPtr->axisRange, axisMin, axisMax);
+
+ /* Now calculate the minor tick step and number. */
+
+ if ((axisPtr->reqNumMinorTicks > 0) && (axisPtr->flags & AXIS_AUTO_MAJOR)) {
+ nTicks = axisPtr->reqNumMinorTicks - 1;
+ step = 1.0 / (nTicks + 1);
+ } else {
+ nTicks = 0; /* No minor ticks. */
+ step = 0.5; /* Don't set the minor tick interval to
+ * 0.0. It makes the GenerateTicks
+ * routine * create minor log-scale tick
+ * marks. */
+ }
+ axisPtr->minorSweep.initial = axisPtr->minorSweep.step = step;
+ axisPtr->minorSweep.nSteps = nTicks;
+#endif
+}
+
+#ifdef notdef
+static Ticks *
+TimeGenerateTicks(TickSweep *sweepPtr)
+{
+ Ticks *ticksPtr;
+
+ ticksPtr = Blt_AssertMalloc(sizeof(Ticks) +
+ (sweepPtr->nSteps * sizeof(double)));
+ ticksPtr->nTicks = 0;
+
+ if (sweepPtr->step == 0.0) {
+ /* Hack: A zero step indicates to use log values. */
+ int i;
+ /* Precomputed log10 values [1..10] */
+ static double logTable[] = {
+ 0.0,
+ 0.301029995663981,
+ 0.477121254719662,
+ 0.602059991327962,
+ 0.698970004336019,
+ 0.778151250383644,
+ 0.845098040014257,
+ 0.903089986991944,
+ 0.954242509439325,
+ 1.0
+ };
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ ticksPtr->values[i] = logTable[i];
+ }
+ } else {
+ double value;
+ int i;
+
+ value = sweepPtr->initial; /* Start from smallest axis tick */
+ for (i = 0; i < sweepPtr->nSteps; i++) {
+ value = UROUND(value, sweepPtr->step);
+ ticksPtr->values[i] = value;
+ value += sweepPtr->step;
+ }
+ }
+ ticksPtr->nTicks = sweepPtr->nSteps;
+ return ticksPtr;
+}
+
+static double
+TimeFloor(double min, int unit)
+{
+ unsigned long ticks;
+
+ ticks = (long)floor(min);
+ localtime_r(&ticks, &tm);
+ switch(unit) {
+ case TICK_6MONTHS:
+ tm.sec = 0;
+ tm.min = 0;
+ tm.day = 0;
+ tm.
+ }
+}
+static double
+TimeCeil(double max, int unit)
+{
+
+}
+
+#endif
+
diff --git a/src/bltGrAxis.h b/src/bltGrAxis.h
new file mode 100644
index 0000000..a009aec
--- /dev/null
+++ b/src/bltGrAxis.h
@@ -0,0 +1,332 @@
+
+/*
+ * bltGrAxis.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_GR_AXIS_H
+#define _BLT_GR_AXIS_H
+
+#include "bltList.h"
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Grid --
+ *
+ * Contains attributes of describing how to draw grids (at major ticks)
+ * in the graph. Grids may be mapped to either/both X and Y axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ Blt_Dashes dashes; /* Dash style of the grid. This represents an
+ * array of alternatingly drawn pixel
+ * values. */
+ int lineWidth; /* Width of the grid lines */
+ XColor *color; /* Color of the grid lines */
+ GC gc; /* Graphics context for the grid. */
+
+ Segment2d *segments; /* Array of line segments representing the
+ * grid lines */
+ int nUsed; /* # of axis segments in use. */
+ int nAllocated; /* # of axis segments allocated. */
+} Grid;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisRange --
+ *
+ * Designates a range of values by a minimum and maximum limit.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ double min, max, range, scale;
+} AxisRange;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TickLabel --
+ *
+ * Structure containing the X-Y screen coordinates of the tick
+ * label (anchored at its center).
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ Point2d anchorPos;
+ unsigned int width, height;
+ char string[1];
+} TickLabel;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Ticks --
+ *
+ * Structure containing information where the ticks (major or
+ * minor) will be displayed on the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ unsigned int nTicks; /* # of ticks on axis */
+ double values[1]; /* Array of tick values (malloc-ed). */
+} Ticks;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TickSweep --
+ *
+ * Structure containing information where the ticks (major or
+ * minor) will be displayed on the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ double initial; /* Initial value */
+ double step; /* Size of interval */
+ unsigned int nSteps; /* Number of intervals. */
+} TickSweep;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Axis --
+ *
+ * Structure contains options controlling how the axis will be
+ * displayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in axis. */
+
+ unsigned int flags;
+
+ Blt_HashEntry *hashPtr;
+
+ /* Fields specific to axes. */
+
+ const char *detail;
+
+ int refCount; /* Number of elements referencing this
+ * axis. */
+ int logScale; /* If non-zero, generate log scale
+ * ticks for the axis. */
+ int timeScale; /* If non-zero, generate time scale
+ * ticks for the axis. This option is
+ * overridden by -logscale. */
+ int descending; /* If non-zero, display the range of
+ * values on the axis in descending
+ * order, from high to low. */
+
+ int looseMin, looseMax; /* If non-zero, axis range extends to
+ * the outer major ticks, otherwise at
+ * the limits of the data values. This
+ * is overriddened by setting the -min
+ * and -max options. */
+
+ const char *title; /* Title of the axis. */
+
+ int titleAlternate; /* Indicates whether to position the
+ * title above/left of the axis. */
+
+ Point2d titlePos; /* Position of the title */
+
+ unsigned short int titleWidth, titleHeight;
+
+
+ int lineWidth; /* Width of lines representing axis
+ * (including ticks). If zero, then
+ * no axis lines or ticks are
+ * drawn. */
+
+ const char **limitsFormats; /* One or two strings of sprintf-like
+ * formats describing how to display
+ * virtual axis limits. If NULL,
+ * display no limits. */
+ int nFormats;
+
+ TextStyle limitsTextStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the limits. */
+
+ double windowSize; /* Size of a sliding window of values
+ * used to scale the axis
+ * automatically as new data values
+ * are added. The axis will always
+ * display the latest values in this
+ * range. */
+
+ double shiftBy; /* Shift maximum by this interval. */
+
+ int tickLength; /* Length of major ticks in pixels */
+
+ const char *formatCmd; /* Specifies a TCL command, to be
+ * invoked by the axis whenever it has
+ * to generate tick labels. */
+
+ Tcl_Obj *scrollCmdObjPtr;
+ int scrollUnits;
+
+ double min, max; /* The actual axis range. */
+
+ double reqMin, reqMax; /* Requested axis bounds. Consult the
+ * axisPtr->flags field for
+ * AXIS_CONFIG_MIN and AXIS_CONFIG_MAX
+ * to see if the requested bound have
+ * been set. They override the
+ * computed range of the axis
+ * (determined by auto-scaling). */
+
+ double reqScrollMin, reqScrollMax;
+
+ double scrollMin, scrollMax; /* Defines the scrolling reqion of the
+ * axis. Normally the region is
+ * determined from the data limits. If
+ * specified, these values override
+ * the data-range. */
+
+ AxisRange valueRange; /* Range of data values of elements
+ * mapped to this axis. This is used
+ * to auto-scale the axis in "tight"
+ * mode. */
+ AxisRange axisRange; /* Smallest and largest major tick
+ * values for the axis. The tick
+ * values lie outside the range of
+ * data values. This is used to
+ * auto-scale the axis in "loose"
+ * mode. */
+
+ double prevMin, prevMax;
+
+ double reqStep; /* If > 0.0, overrides the computed major
+ * tick interval. Otherwise a stepsize
+ * is automatically calculated, based
+ * upon the range of elements mapped to the
+ * axis. The default value is 0.0. */
+
+ Ticks *t1Ptr; /* Array of major tick positions. May be
+ * set by the user or generated from the
+ * major sweep below. */
+
+ Ticks *t2Ptr; /* Array of minor tick positions. May be
+ * set by the user or generated from the
+ * minor sweep below. */
+
+ TickSweep minorSweep, majorSweep;
+
+ int reqNumMajorTicks; /* Default number of ticks to be displayed. */
+ int reqNumMinorTicks; /* If non-zero, represents the
+ * requested the number of minor ticks
+ * to be uniformally displayed along
+ * each major tick. */
+
+
+ int labelOffset; /* If non-zero, indicates that the tick
+ * label should be offset to sit in the
+ * middle of the next interval. */
+
+ /* The following fields are specific to logical axes */
+
+ int margin; /* Margin that contains this axis. */
+ Blt_ChainLink link; /* Axis link in margin list. */
+ Blt_Chain chain;
+ Segment2d *segments; /* Array of line segments representing
+ * the major and minor ticks, but also
+ * the * axis line itself. The segment
+ * coordinates * are relative to the
+ * axis. */
+ int nSegments; /* Number of segments in the above
+ * array. */
+ Blt_Chain tickLabels; /* Contains major tick label strings
+ * and their offsets along the
+ * axis. */
+ short int left, right, top, bottom; /* Region occupied by the of axis. */
+ short int width, height; /* Extents of axis */
+ short int maxTickWidth, maxTickHeight;
+ Blt_Background normalBg;
+ Blt_Background activeBg;
+ XColor *activeFgColor;
+
+ int relief;
+ int borderWidth;
+ int activeRelief;
+
+ float tickAngle;
+ Blt_Font tickFont;
+ Tk_Anchor tickAnchor;
+ Tk_Anchor reqTickAnchor;
+ XColor *tickColor;
+ GC tickGC; /* Graphics context for axis and tick
+ * labels */
+ GC activeTickGC;
+
+ double titleAngle;
+ Blt_Font titleFont;
+ Tk_Anchor titleAnchor;
+ Tk_Justify titleJustify;
+ XColor *titleColor;
+
+ Grid major, minor; /* Axis grid information. */
+
+ double screenScale;
+ int screenMin, screenRange;
+
+} Axis;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Axis2d --
+ *
+ * The pair of axes mapping a point onto the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ Axis *x, *y;
+} Axis2d;
+
+/* Axis flags: */
+
+#define AXIS_AUTO_MAJOR (1<<16) /* Auto-generate major ticks. */
+#define AXIS_AUTO_MINOR (1<<17) /* Auto-generate minor ticks. */
+#define AXIS_ONSCREEN (1<<18) /* Axis is displayed on the screen via
+ * the "use" operation */
+#define AXIS_GRID (1<<19)
+#define AXIS_GRID_MINOR (1<<20)
+#define AXIS_TICKS (1<<21)
+#define AXIS_TICKS_INTERIOR (1<<22)
+#define AXIS_CHECK_LIMITS (1<<23)
+#define AXIS_LOGSCALE (1<<24)
+#define AXIS_DECREASING (1<<25)
+
+#endif /* _BLT_GR_AXIS_H */
diff --git a/src/bltGrBind.C b/src/bltGrBind.C
new file mode 100644
index 0000000..7b288f7
--- /dev/null
+++ b/src/bltGrBind.C
@@ -0,0 +1,1825 @@
+
+/*
+ * bltBind.c --
+ *
+ * This module implements object binding procedures for the BLT toolkit.
+ *
+ * Copyright 1998 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include "bltBind.h"
+#include <bltList.h>
+
+static Tk_EventProc BindProc;
+
+typedef struct _Blt_BindTable BindTable;
+
+/* Make button presses on objects have implicit grab. */
+#define FULLY_SIMULATE_GRAB 1
+
+/*
+ * Binding table procedures.
+ */
+#define REPICK_IN_PROGRESS (1<<0)
+#define LEFT_GRABBED_ITEM (1<<1)
+
+#define ALL_BUTTONS_MASK \
+ (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)
+
+#ifndef VirtualEventMask
+#define VirtualEventMask (1L << 30)
+#endif
+
+#define ALL_VALID_EVENTS_MASK \
+ (ButtonMotionMask | Button1MotionMask | Button2MotionMask | \
+ Button3MotionMask | Button4MotionMask | Button5MotionMask | \
+ ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
+ LeaveWindowMask | KeyPressMask | KeyReleaseMask | \
+ PointerMotionMask | VirtualEventMask)
+
+static int buttonMasks[] =
+{
+ 0, /* No buttons pressed */
+ Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask,
+};
+
+
+/*
+ * How to make drag&drop work?
+ *
+ * Right now we generate pseudo <Enter> <Leave> events within button grab
+ * on an object. They're marked NotifyVirtual instead of NotifyAncestor.
+ * A better solution: generate new-style virtual <<DragEnter>>
+ * <<DragMotion>> <<DragLeave>> events. These virtual events don't have
+ * to exist as "real" event sequences, like virtual events do now.
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DoEvent --
+ *
+ * This procedure is called to invoke binding processing for a new event
+ * that is associated with the current item for a legend.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the bindings for the legend. A binding script could delete
+ * an entry, so callers should protect themselves with Tcl_Preserve and
+ * Tcl_Release.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DoEvent(
+ BindTable *bindPtr, /* Binding information for widget in which
+ * event occurred. */
+ XEvent *eventPtr, /* Real or simulated X event that is to be
+ * processed. */
+ ClientData item, /* Item picked. */
+ ClientData context) /* Context of item. */
+{
+ Blt_List tagList;
+
+ if ((bindPtr->tkwin == NULL) || (bindPtr->bindingTable == NULL)) {
+ return;
+ }
+ if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
+ item = bindPtr->focusItem;
+ context = bindPtr->focusContext;
+ }
+ if (item == NULL) {
+ return;
+ }
+ /*
+ * Invoke the binding system.
+ */
+ tagList = Blt_List_Create(BLT_ONE_WORD_KEYS);
+ if (bindPtr->tagProc == NULL) {
+ Blt_List_Append(tagList, Tk_GetUid("all"), 0);
+ Blt_List_Append(tagList, (char *)item, 0);
+ } else {
+ (*bindPtr->tagProc) (bindPtr, item, context, tagList);
+ }
+ if (Blt_List_GetLength(tagList) > 0) {
+ int nTags;
+ ClientData *tagArray;
+#define MAX_STATIC_TAGS 64
+ ClientData staticTags[MAX_STATIC_TAGS];
+ Blt_ListNode node;
+
+ tagArray = staticTags;
+ nTags = Blt_List_GetLength(tagList);
+ if (nTags >= MAX_STATIC_TAGS) {
+ tagArray = Blt_AssertMalloc(sizeof(ClientData) * nTags);
+
+ }
+ nTags = 0;
+ for (node = Blt_List_FirstNode(tagList); node != NULL;
+ node = Blt_List_NextNode(node)) {
+ tagArray[nTags++] = (ClientData)Blt_List_GetKey(node);
+ }
+ Tk_BindEvent(bindPtr->bindingTable, eventPtr, bindPtr->tkwin, nTags,
+ tagArray);
+ if (tagArray != staticTags) {
+ Blt_Free(tagArray);
+ }
+ }
+ Blt_List_Destroy(tagList);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PickCurrentItem --
+ *
+ * Find the topmost item in a legend that contains a given location and
+ * mark the the current item. If the current item has changed, generate
+ * a fake exit event on the old current item and a fake enter event on
+ * the new current item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The current item may change. If it does, then the commands associated
+ * with item entry and exit could do just about anything. A binding
+ * script could delete the legend, so callers should protect themselves
+ * with Tcl_Preserve and Tcl_Release.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+PickCurrentItem(
+ BindTable *bindPtr, /* Binding table information. */
+ XEvent *eventPtr) /* Event describing location of mouse cursor.
+ * Must be EnterWindow, LeaveWindow,
+ * ButtonRelease, or MotionNotify. */
+{
+ int buttonDown;
+ ClientData newItem, oldItem;
+ ClientData newContext;
+
+ /*
+ * Check whether or not a button is down. If so, we'll log entry and exit
+ * into and out of the current item, but not entry into any other item.
+ * This implements a form of grabbing equivalent to what the X server does
+ * for windows.
+ */
+ buttonDown = (bindPtr->state & ALL_BUTTONS_MASK);
+ if (!buttonDown) {
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ }
+
+ /*
+ * Save information about this event in the widget. The event in the
+ * widget is used for two purposes:
+ *
+ * 1. Event bindings: if the current item changes, fake events are
+ * generated to allow item-enter and item-leave bindings to trigger.
+ * 2. Reselection: if the current item gets deleted, can use the
+ * saved event to find a new current item.
+ * Translate MotionNotify events into EnterNotify events, since that's
+ * what gets reported to item handlers.
+ */
+
+ if (eventPtr != &bindPtr->pickEvent) {
+ if ((eventPtr->type == MotionNotify) ||
+ (eventPtr->type == ButtonRelease)) {
+ bindPtr->pickEvent.xcrossing.type = EnterNotify;
+ bindPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
+ bindPtr->pickEvent.xcrossing.send_event =
+ eventPtr->xmotion.send_event;
+ bindPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
+ bindPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
+ bindPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
+ bindPtr->pickEvent.xcrossing.subwindow = None;
+ bindPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
+ bindPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
+ bindPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
+ bindPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
+ bindPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
+ bindPtr->pickEvent.xcrossing.mode = NotifyNormal;
+ bindPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
+ bindPtr->pickEvent.xcrossing.same_screen
+ = eventPtr->xmotion.same_screen;
+ bindPtr->pickEvent.xcrossing.focus = False;
+ bindPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
+ } else {
+ bindPtr->pickEvent = *eventPtr;
+ }
+ }
+ bindPtr->activePick = TRUE;
+
+ /*
+ * If this is a recursive call (there's already a partially completed call
+ * pending on the stack; it's in the middle of processing a Leave event
+ * handler for the old current item) then just return; the pending call
+ * will do everything that's needed.
+ */
+ if (bindPtr->flags & REPICK_IN_PROGRESS) {
+ return;
+ }
+ /*
+ * A LeaveNotify event automatically means that there's no current item,
+ * so the check for closest item can be skipped.
+ */
+ newContext = NULL;
+ if (bindPtr->pickEvent.type != LeaveNotify) {
+ int x, y;
+
+ x = bindPtr->pickEvent.xcrossing.x;
+ y = bindPtr->pickEvent.xcrossing.y;
+ newItem = (*bindPtr->pickProc) (bindPtr->clientData, x, y, &newContext);
+ } else {
+ newItem = NULL;
+ }
+ if (((newItem == bindPtr->currentItem) &&
+ (newContext == bindPtr->currentContext)) &&
+ ((bindPtr->flags & LEFT_GRABBED_ITEM) == 0)) {
+ /*
+ * Nothing to do: the current item hasn't changed.
+ */
+ return;
+ }
+#if FULLY_SIMULATE_GRAB
+ if (((newItem != bindPtr->currentItem) ||
+ (newContext != bindPtr->currentContext)) &&
+ (buttonDown)) {
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+#ifdef notdef
+ fprintf(stderr, "pickcurrentitem: simulate grab.\n");
+#endif
+ return;
+ }
+#endif
+ /*
+ * Simulate a LeaveNotify event on the previous current item and an
+ * EnterNotify event on the new current item. Remove the "current" tag
+ * from the previous current item and place it on the new current item.
+ */
+ oldItem = bindPtr->currentItem;
+ Tcl_Preserve(oldItem);
+ Tcl_Preserve(newItem);
+
+ if ((bindPtr->currentItem != NULL) &&
+ ((newItem != bindPtr->currentItem) ||
+ (newContext != bindPtr->currentContext)) &&
+ ((bindPtr->flags & LEFT_GRABBED_ITEM) == 0)) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = LeaveNotify;
+ /*
+ * If the event's detail happens to be NotifyInferior the binding
+ * mechanism will discard the event. To be consistent, always use
+ * NotifyAncestor.
+ */
+ event.xcrossing.detail = NotifyAncestor;
+
+ bindPtr->flags |= REPICK_IN_PROGRESS;
+ DoEvent(bindPtr, &event, bindPtr->currentItem, bindPtr->currentContext);
+ bindPtr->flags &= ~REPICK_IN_PROGRESS;
+
+ /*
+ * Note: during DoEvent above, it's possible that bindPtr->newItem got
+ * reset to NULL because the item was deleted.
+ */
+ }
+ if (((newItem != bindPtr->currentItem) ||
+ (newContext != bindPtr->currentContext)) &&
+ (buttonDown)) {
+ XEvent event;
+
+ bindPtr->flags |= LEFT_GRABBED_ITEM;
+ event = bindPtr->pickEvent;
+ if ((newItem != bindPtr->newItem) ||
+ (newContext != bindPtr->newContext)) {
+ ClientData savedItem;
+ ClientData savedContext;
+
+ /*
+ * Generate <Enter> and <Leave> events for objects during button
+ * grabs. This isn't standard. But for example, it allows one to
+ * provide balloon help on the individual entries of the Hierbox
+ * widget.
+ */
+ savedItem = bindPtr->currentItem;
+ savedContext = bindPtr->currentContext;
+ if (bindPtr->newItem != NULL) {
+ event.type = LeaveNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = bindPtr->newItem;
+ DoEvent(bindPtr, &event, bindPtr->newItem, bindPtr->newContext);
+ }
+ bindPtr->newItem = newItem;
+ bindPtr->newContext = newContext;
+ if (newItem != NULL) {
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyVirtual /* Ancestor */ ;
+ bindPtr->currentItem = newItem;
+ DoEvent(bindPtr, &event, newItem, newContext);
+ }
+ bindPtr->currentItem = savedItem;
+ bindPtr->currentContext = savedContext;
+ }
+ goto done;
+ }
+ /*
+ * Special note: it's possible that
+ * bindPtr->newItem == bindPtr->currentItem
+ * here. This can happen, for example, if LEFT_GRABBED_ITEM was set.
+ */
+
+ bindPtr->flags &= ~LEFT_GRABBED_ITEM;
+ bindPtr->currentItem = bindPtr->newItem = newItem;
+ bindPtr->currentContext = bindPtr->newContext = newContext;
+ if (bindPtr->currentItem != NULL) {
+ XEvent event;
+
+ event = bindPtr->pickEvent;
+ event.type = EnterNotify;
+ event.xcrossing.detail = NotifyAncestor;
+ DoEvent(bindPtr, &event, newItem, newContext);
+#ifdef notdef
+ if ((eventPtr->type == MotionNotify) ||
+ (eventPtr->type == ButtonRelease)) {
+ fprintf(stderr, "pickcurrentitem: DoEvent Button buttondown=%d.\n",
+ buttonDown);
+ event.type = eventPtr->type;
+ event.xbutton.button = eventPtr->xbutton.button;
+ DoEvent(bindPtr, &event, newItem, newContext);
+ fprintf(stderr, "pickcurrentitem: done.\n");
+ }
+#endif
+ }
+ done:
+ Tcl_Release(newItem);
+ Tcl_Release(oldItem);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BindProc --
+ *
+ * This procedure is invoked by the Tk dispatcher to handle events
+ * associated with bindings on items.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the command invoked as part of the binding
+ * (if there was any).
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+BindProc(
+ ClientData clientData, /* Pointer to widget structure. */
+ XEvent *eventPtr) /* Pointer to X event that just happened. */
+{
+ BindTable *bindPtr = clientData;
+ int mask;
+
+ Tcl_Preserve(bindPtr->clientData);
+ /*
+ * This code below keeps track of the current modifier state in
+ * bindPtr->state. This information is used to defer repicks of the
+ * current item while buttons are down.
+ */
+ switch (eventPtr->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ mask = 0;
+ if ((eventPtr->xbutton.button >= Button1) &&
+ (eventPtr->xbutton.button <= Button5)) {
+ mask = buttonMasks[eventPtr->xbutton.button];
+ }
+ /*
+ * For button press events, repick the current item using the button
+ * state before the event, then process the event. For button release
+ * events, first process the event, then repick the current item using
+ * the button state *after* the event (the button has logically gone
+ * up before we change the current item).
+ */
+ if (eventPtr->type == ButtonPress) {
+
+ /*
+ * On a button press, first repick the current item using the
+ * button state before the event, the process the event.
+ */
+
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ bindPtr->state ^= mask;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
+ bindPtr->currentContext);
+
+ } else {
+
+ /*
+ * Button release: first process the event, with the button still
+ * considered to be down. Then repick the current item under the
+ * assumption that the button is no longer down.
+ */
+ bindPtr->state = eventPtr->xbutton.state;
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
+ bindPtr->currentContext);
+ eventPtr->xbutton.state ^= mask;
+ bindPtr->state = eventPtr->xbutton.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ eventPtr->xbutton.state ^= mask;
+ }
+ break;
+
+ case EnterNotify:
+ case LeaveNotify:
+ bindPtr->state = eventPtr->xcrossing.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ break;
+
+ case MotionNotify:
+ bindPtr->state = eventPtr->xmotion.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
+ bindPtr->currentContext);
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ bindPtr->state = eventPtr->xkey.state;
+ PickCurrentItem(bindPtr, eventPtr);
+ DoEvent(bindPtr, eventPtr, bindPtr->currentItem,
+ bindPtr->currentContext);
+ break;
+ }
+ Tcl_Release(bindPtr->clientData);
+}
+
+int
+Blt_ConfigureBindings(
+ Tcl_Interp *interp,
+ BindTable *bindPtr,
+ ClientData item,
+ int argc,
+ const char **argv)
+{
+ const char *command;
+ unsigned long mask;
+ const char *seq;
+
+ if (argc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ if (argc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, argv[0]);
+ if (command == NULL) {
+ Tcl_AppendResult(interp, "can't find event \"", argv[0], "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), command, -1);
+ return TCL_OK;
+ }
+
+ seq = argv[0];
+ command = argv[1];
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ Tcl_AppendResult(interp, "event mask can't be zero for \"", item, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+int
+Blt_ConfigureBindingsFromObj(
+ Tcl_Interp *interp,
+ BindTable *bindPtr,
+ ClientData item,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ const char *command;
+ unsigned long mask;
+ const char *seq;
+ const char *string;
+
+ if (objc == 0) {
+ Tk_GetAllBindings(interp, bindPtr->bindingTable, item);
+ return TCL_OK;
+ }
+ string = Tcl_GetString(objv[0]);
+ if (objc == 1) {
+ command = Tk_GetBinding(interp, bindPtr->bindingTable, item, string);
+ if (command == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid binding event \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), command, -1);
+ return TCL_OK;
+ }
+
+ seq = string;
+ command = Tcl_GetString(objv[1]);
+
+ if (command[0] == '\0') {
+ return Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ }
+
+ if (command[0] == '+') {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command + 1, TRUE);
+ } else {
+ mask = Tk_CreateBinding(interp, bindPtr->bindingTable, item, seq,
+ command, FALSE);
+ }
+ if (mask == 0) {
+ return TCL_ERROR;
+ }
+ if (mask & (unsigned)~ALL_VALID_EVENTS_MASK) {
+ Tk_DeleteBinding(interp, bindPtr->bindingTable, item, seq);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "requested illegal events; ",
+ "only key, button, motion, enter, leave, and virtual ",
+ "events may be used", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+Blt_BindTable
+Blt_CreateBindingTable(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ ClientData clientData,
+ Blt_BindPickProc *pickProc,
+ Blt_BindTagProc *tagProc)
+{
+ unsigned int mask;
+ BindTable *bindPtr;
+
+ bindPtr = Blt_AssertCalloc(1, sizeof(BindTable));
+ bindPtr->bindingTable = Tk_CreateBindingTable(interp);
+ bindPtr->clientData = clientData;
+ bindPtr->tkwin = tkwin;
+ bindPtr->pickProc = pickProc;
+ bindPtr->tagProc = tagProc;
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ return bindPtr;
+}
+
+void
+Blt_DestroyBindingTable(BindTable *bindPtr)
+{
+ unsigned int mask;
+
+ Tk_DeleteBindingTable(bindPtr->bindingTable);
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ Blt_Free(bindPtr);
+}
+
+void
+Blt_PickCurrentItem(BindTable *bindPtr)
+{
+ if (bindPtr->activePick) {
+ PickCurrentItem(bindPtr, &bindPtr->pickEvent);
+ }
+}
+
+void
+Blt_DeleteBindings(
+ BindTable *bindPtr,
+ ClientData object)
+{
+ Tk_DeleteAllBindings(bindPtr->bindingTable, object);
+
+ /*
+ * If this is the object currently picked, we need to repick one.
+ */
+ if (bindPtr->currentItem == object) {
+ bindPtr->currentItem = NULL;
+ bindPtr->currentContext = NULL;
+ }
+ if (bindPtr->newItem == object) {
+ bindPtr->newItem = NULL;
+ bindPtr->newContext = NULL;
+ }
+ if (bindPtr->focusItem == object) {
+ bindPtr->focusItem = NULL;
+ bindPtr->focusContext = NULL;
+ }
+}
+
+void
+Blt_MoveBindingTable(
+ BindTable *bindPtr,
+ Tk_Window tkwin)
+{
+ unsigned int mask;
+
+ mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
+ PointerMotionMask);
+ if (bindPtr->tkwin != NULL) {
+ Tk_DeleteEventHandler(bindPtr->tkwin, mask, BindProc, bindPtr);
+ }
+ Tk_CreateEventHandler(tkwin, mask, BindProc, bindPtr);
+ bindPtr->tkwin = tkwin;
+}
+
+/*
+ * The following union is used to hold the detail information from an
+ * XEvent (including Tk's XVirtualEvent extension).
+ */
+typedef union {
+ KeySym keySym; /* KeySym that corresponds to xkey.keycode. */
+ int button; /* Button that was pressed (xbutton.button). */
+ Tk_Uid name; /* Tk_Uid of virtual event. */
+ ClientData clientData; /* Used when type of Detail is unknown, and to
+ * ensure that all bytes of Detail are initialized
+ * when this structure is used in a hash key. */
+} Detail;
+
+
+/*
+ * The following structure defines a pattern, which is matched against X
+ * events as part of the process of converting X events into TCL commands.
+ */
+typedef struct {
+ int eventType; /* Type of X event, e.g. ButtonPress. */
+ int needMods; /* Mask of modifiers that must be
+ * present (0 means no modifiers are
+ * required). */
+ Detail detail; /* Additional information that must
+ * match event. Normally this is 0,
+ * meaning no additional information
+ * must match. For KeyPress and
+ * KeyRelease events, a keySym may
+ * be specified to select a
+ * particular keystroke (0 means any
+ * keystrokes). For button events,
+ * specifies a particular button (0
+ * means any buttons are OK). For virtual
+ * events, specifies the Tk_Uid of the
+ * virtual event name (never 0). */
+} Pattern;
+
+typedef struct {
+ const char *name; /* Name of modifier. */
+ int mask; /* Button/modifier mask value, such as
+ * Button1Mask. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+} EventModifier;
+
+/*
+ * Flags for EventModifier structures:
+ *
+ * DOUBLE - Non-zero means duplicate this event,
+ * e.g. for double-clicks.
+ * TRIPLE - Non-zero means triplicate this event,
+ * e.g. for triple-clicks.
+ * QUADRUPLE - Non-zero means quadruple this event,
+ * e.g. for 4-fold-clicks.
+ * MULT_CLICKS - Combination of all of above.
+ */
+
+#define DOUBLE (1<<0)
+#define TRIPLE (1<<1)
+#define QUADRUPLE (1<<2)
+#define MULT_CLICKS (DOUBLE|TRIPLE|QUADRUPLE)
+
+#define META_MASK (AnyModifier<<1)
+#define ALT_MASK (AnyModifier<<2)
+
+static EventModifier eventModifiers[] = {
+ {"Alt", ALT_MASK, 0},
+ {"Any", 0, 0}, /* Ignored: historical relic. */
+ {"B1", Button1Mask, 0},
+ {"B2", Button2Mask, 0},
+ {"B3", Button3Mask, 0},
+ {"B4", Button4Mask, 0},
+ {"B5", Button5Mask, 0},
+ {"Button1", Button1Mask, 0},
+ {"Button2", Button2Mask, 0},
+ {"Button3", Button3Mask, 0},
+ {"Button4", Button4Mask, 0},
+ {"Button5", Button5Mask, 0},
+ {"Command", Mod1Mask, 0},
+ {"Control", ControlMask, 0},
+ {"Double", 0, DOUBLE},
+ {"Lock", LockMask, 0},
+ {"M", META_MASK, 0},
+ {"M1", Mod1Mask, 0},
+ {"M2", Mod2Mask, 0},
+ {"M3", Mod3Mask, 0},
+ {"M4", Mod4Mask, 0},
+ {"M5", Mod5Mask, 0},
+ {"Meta", META_MASK, 0},
+ {"Mod1", Mod1Mask, 0},
+ {"Mod2", Mod2Mask, 0},
+ {"Mod3", Mod3Mask, 0},
+ {"Mod4", Mod4Mask, 0},
+ {"Mod5", Mod5Mask, 0},
+ {"Option", Mod2Mask, 0},
+ {"Quadruple", 0, QUADRUPLE},
+ {"Shift", ShiftMask, 0},
+ {"Triple", 0, TRIPLE},
+};
+
+typedef struct {
+ const char *name; /* Name of event. */
+ int type; /* Event type for X, such as
+ * ButtonPress. */
+ int eventMask; /* Mask bits (for XSelectInput)
+ * for this event type. */
+} EventInfo;
+
+/*
+ * Note: some of the masks below are an OR-ed combination of
+ * several masks. This is necessary because X doesn't report
+ * up events unless you also ask for down events. Also, X
+ * doesn't report button state in motion events unless you've
+ * asked about button events.
+ */
+
+static EventInfo events[] = {
+ {"Activate", ActivateNotify, ActivateMask},
+ {"Button", ButtonPress, ButtonPressMask},
+ {"ButtonPress", ButtonPress, ButtonPressMask},
+ {"ButtonRelease", ButtonRelease, ButtonPressMask|ButtonReleaseMask},
+ {"Circulate", CirculateNotify, StructureNotifyMask},
+ {"CirculateRequest", CirculateRequest, SubstructureRedirectMask},
+ {"Colormap", ColormapNotify, ColormapChangeMask},
+ {"Configure", ConfigureNotify, StructureNotifyMask},
+ {"ConfigureRequest", ConfigureRequest, SubstructureRedirectMask},
+ {"Create", CreateNotify, SubstructureNotifyMask},
+ {"Deactivate", DeactivateNotify, ActivateMask},
+ {"Destroy", DestroyNotify, StructureNotifyMask},
+ {"Enter", EnterNotify, EnterWindowMask},
+ {"Expose", Expose, ExposureMask},
+ {"FocusIn", FocusIn, FocusChangeMask},
+ {"FocusOut", FocusOut, FocusChangeMask},
+ {"Gravity", GravityNotify, StructureNotifyMask},
+ {"Key", KeyPress, KeyPressMask},
+ {"KeyPress", KeyPress, KeyPressMask},
+ {"KeyRelease", KeyRelease, KeyPressMask|KeyReleaseMask},
+ {"Leave", LeaveNotify, LeaveWindowMask},
+ {"Map", MapNotify, StructureNotifyMask},
+ {"MapRequest", MapRequest, SubstructureRedirectMask},
+ {"Motion", MotionNotify, ButtonPressMask|PointerMotionMask},
+ {"MouseWheel", MouseWheelEvent, MouseWheelMask},
+ {"Property", PropertyNotify, PropertyChangeMask},
+ {"Reparent", ReparentNotify, StructureNotifyMask},
+ {"ResizeRequest", ResizeRequest, ResizeRedirectMask},
+ {"Unmap", UnmapNotify, StructureNotifyMask},
+ {"Visibility", VisibilityNotify, VisibilityChangeMask},
+};
+
+/*
+ * The defines and table below are used to classify events into
+ * various groups. The reason for this is that logically identical
+ * fields (e.g. "state") appear at different places in different
+ * types of events. The classification masks can be used to figure
+ * out quickly where to extract information from events.
+ */
+
+#define KEY 0x1
+#define BUTTON 0x2
+#define MOTION 0x4
+#define CROSSING 0x8
+#define FOCUS 0x10
+#define EXPOSE 0x20
+#define VISIBILITY 0x40
+#define CREATE 0x80
+#define DESTROY 0x100
+#define UNMAP 0x200
+#define MAP 0x400
+#define REPARENT 0x800
+#define CONFIG 0x1000
+#define GRAVITY 0x2000
+#define CIRC 0x4000
+#define PROP 0x8000
+#define COLORMAP 0x10000
+#define VIRTUAL 0x20000
+#define ACTIVATE 0x40000
+#define MAPREQ 0x80000
+#define CONFIGREQ 0x100000
+#define RESIZEREQ 0x200000
+#define CIRCREQ 0x400000
+
+#define KEY_BUTTON_MOTION_VIRTUAL (KEY|BUTTON|MOTION|VIRTUAL)
+#define KEY_BUTTON_MOTION_CROSSING (KEY|BUTTON|MOTION|CROSSING|VIRTUAL)
+
+static int flagArray[TK_LASTEVENT+1] = {
+ /* Not used */ 0,
+ /* Not used */ 0,
+ /* KeyPress */ KEY,
+ /* KeyRelease */ KEY,
+ /* ButtonPress */ BUTTON,
+ /* ButtonRelease */ BUTTON,
+ /* MotionNotify */ MOTION,
+ /* EnterNotify */ CROSSING,
+ /* LeaveNotify */ CROSSING,
+ /* FocusIn */ FOCUS,
+ /* FocusOut */ FOCUS,
+ /* KeymapNotify */ 0,
+ /* Expose */ EXPOSE,
+ /* GraphicsExpose */ EXPOSE,
+ /* NoExpose */ 0,
+ /* VisibilityNotify */ VISIBILITY,
+ /* CreateNotify */ CREATE,
+ /* DestroyNotify */ DESTROY,
+ /* UnmapNotify */ UNMAP,
+ /* MapNotify */ MAP,
+ /* MapRequest */ MAPREQ,
+ /* ReparentNotify */ REPARENT,
+ /* ConfigureNotify */ CONFIG,
+ /* ConfigureRequest */ CONFIGREQ,
+ /* GravityNotify */ GRAVITY,
+ /* ResizeRequest */ RESIZEREQ,
+ /* CirculateNotify */ CIRC,
+ /* CirculateRequest */ 0,
+ /* PropertyNotify */ PROP,
+ /* SelectionClear */ 0,
+ /* SelectionRequest */ 0,
+ /* SelectionNotify */ 0,
+ /* ColormapNotify */ COLORMAP,
+ /* ClientMessage */ 0,
+ /* MappingNotify */ 0,
+#ifdef GenericEvent
+ /* GenericEvent */ 0,
+#endif
+ /* VirtualEvent */ VIRTUAL,
+ /* Activate */ ACTIVATE,
+ /* Deactivate */ ACTIVATE,
+ /* MouseWheel */ KEY
+};
+
+
+static EventModifier *
+FindModifier(const char *string)
+{
+ int high, low;
+ char c;
+
+ low = 0;
+ high = (sizeof(eventModifiers) / sizeof(EventModifier)) - 1;
+ c = string[0];
+ while (low <= high) {
+ EventModifier *modPtr;
+ int compare;
+ int median;
+
+ median = (low + high) >> 1;
+ modPtr = eventModifiers + median;
+
+ /* Test the first character */
+ compare = c - modPtr->name[0];
+ if (compare == 0) {
+ compare = strcmp(string, modPtr->name);
+ }
+ if (compare < 0) {
+ high = median - 1;
+ } else if (compare > 0) {
+ low = median + 1;
+ } else {
+ return modPtr; /* Modifier found. */
+ }
+ }
+ return NULL; /* Can't find modifier */
+}
+
+static EventInfo *
+FindEvent(const char *string)
+{
+ int high, low;
+ char c;
+
+ low = 0;
+ high = (sizeof(events) / sizeof(EventInfo)) - 1;
+ c = string[0];
+ while (low <= high) {
+ EventInfo *infoPtr;
+ int compare;
+ int median;
+
+ median = (low + high) >> 1;
+ infoPtr = events + median;
+
+ /* Test the first character */
+ compare = c - infoPtr->name[0];
+ if (compare == 0) {
+ compare = strcmp(string, infoPtr->name);
+ }
+ if (compare < 0) {
+ high = median - 1;
+ } else if (compare > 0) {
+ low = median + 1;
+ } else {
+ return infoPtr; /* Event found. */
+ }
+ }
+ return NULL; /* Can't find event. */
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetField --
+ *
+ * Used to parse pattern descriptions. Copies up to
+ * size characters from p to copy, stopping at end of
+ * string, space, "-", ">", or whenever size is
+ * exceeded.
+ *
+ * Results:
+ * The return value is a pointer to the character just
+ * after the last one copied (usually "-" or space or
+ * ">", but could be anything if size was exceeded).
+ * Also places NULL-terminated string (up to size
+ * character, including NULL), at copy.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+GetField(p, copy, size)
+ char *p; /* Pointer to part of pattern. */
+ char *copy; /* Place to copy field. */
+ int size; /* Maximum number of characters to
+ * copy. */
+{
+ while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
+ && (*p != '-') && (size > 1)) {
+ *copy = *p;
+ p++;
+ copy++;
+ size--;
+ }
+ *copy = '\0';
+ return p;
+}
+
+static int
+ParseEventDescription(Tcl_Interp *interp, const char **eventStringPtr,
+ Pattern *patPtr, unsigned long *eventMaskPtr)
+{
+ char *p;
+ unsigned long eventMask;
+ int count, eventFlags;
+#define FIELD_SIZE 48
+ char field[FIELD_SIZE];
+ EventInfo *infoPtr;
+
+ Tcl_DString copy;
+ Tcl_DStringInit(&copy);
+ p = Tcl_DStringAppend(&copy, *eventStringPtr, -1);
+
+ patPtr->eventType = -1;
+ patPtr->needMods = 0;
+ patPtr->detail.clientData = 0;
+
+ eventMask = 0;
+ count = 1;
+
+ /*
+ * Handle simple ASCII characters.
+ */
+
+ if (*p != '<') {
+ char string[2];
+
+ patPtr->eventType = KeyPress;
+ eventMask = KeyPressMask;
+ string[0] = *p;
+ string[1] = 0;
+ patPtr->detail.keySym = XStringToKeysym(string);
+ if (patPtr->detail.keySym == NoSymbol) {
+ if (isprint(UCHAR(*p))) {
+ patPtr->detail.keySym = *p;
+ } else {
+ char buf[64];
+
+ sprintf(buf, "bad ASCII character 0x%x", (unsigned char) *p);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ count = 0;
+ goto done;
+ }
+ }
+ p++;
+ goto end;
+ }
+
+ /*
+ * A physical event description consists of:
+ *
+ * 1. open angle bracket.
+ * 2. any number of modifiers, each followed by spaces
+ * or dashes.
+ * 3. an optional event name.
+ * 4. an option button or keysym name. Either this or
+ * item 3 *must* be present; if both are present
+ * then they are separated by spaces or dashes.
+ * 5. a close angle bracket.
+ */
+
+ p++;
+
+ while (1) {
+ EventModifier *modPtr;
+ p = GetField(p, field, FIELD_SIZE);
+ if (*p == '>') {
+ /*
+ * This solves the problem of, e.g., <Control-M> being
+ * misinterpreted as Control + Meta + missing keysym
+ * instead of Control + KeyPress + M.
+ */
+ break;
+ }
+ modPtr = FindModifier(field);
+ if (modPtr == NULL) {
+ break;
+ }
+ patPtr->needMods |= modPtr->mask;
+ if (modPtr->flags & (MULT_CLICKS)) {
+ int i = modPtr->flags & MULT_CLICKS;
+ count = 2;
+ while (i >>= 1) count++;
+ }
+ while ((*p == '-') || isspace(UCHAR(*p))) {
+ p++;
+ }
+ }
+
+ eventFlags = 0;
+ infoPtr = FindEvent(field);
+ if (infoPtr != NULL) {
+ patPtr->eventType = infoPtr->type;
+ eventFlags = flagArray[infoPtr->type];
+ eventMask = infoPtr->eventMask;
+ while ((*p == '-') || isspace(UCHAR(*p))) {
+ p++;
+ }
+ p = GetField(p, field, FIELD_SIZE);
+ }
+ if (*field != '\0') {
+ if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
+ if (eventFlags == 0) {
+ patPtr->eventType = ButtonPress;
+ eventMask = ButtonPressMask;
+ } else if (eventFlags & KEY) {
+ goto getKeysym;
+ } else if ((eventFlags & BUTTON) == 0) {
+ Tcl_AppendResult(interp, "specified button \"", field,
+ "\" for non-button event", (char *) NULL);
+ count = 0;
+ goto done;
+ }
+ patPtr->detail.button = (*field - '0');
+ } else {
+ getKeysym:
+ patPtr->detail.keySym = XStringToKeysym(field);
+ if (patPtr->detail.keySym == NoSymbol) {
+ Tcl_AppendResult(interp, "bad event type or keysym \"",
+ field, "\"", (char *)NULL);
+ count = 0;
+ goto done;
+ }
+ if (eventFlags == 0) {
+ patPtr->eventType = KeyPress;
+ eventMask = KeyPressMask;
+ } else if ((eventFlags & KEY) == 0) {
+ Tcl_AppendResult(interp, "specified keysym \"", field,
+ "\" for non-key event", (char *)NULL);
+ count = 0;
+ goto done;
+ }
+ }
+ } else if (eventFlags == 0) {
+ Tcl_AppendResult(interp, "no event type or button # or keysym",
+ (char *)NULL);
+ count = 0;
+ goto done;
+ }
+
+ while ((*p == '-') || isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p != '>') {
+ while (*p != '\0') {
+ p++;
+ if (*p == '>') {
+ Tcl_AppendResult(interp,
+ "extra characters after detail in binding",
+ (char *)NULL);
+ count = 0;
+ goto done;
+ }
+ }
+ Tcl_AppendResult(interp, "missing \">\" in binding", (char *)NULL);
+ count = 0;
+ goto done;
+ }
+ p++;
+
+end:
+ *eventStringPtr += (p - Tcl_DStringValue(&copy));
+ *eventMaskPtr |= eventMask;
+done:
+ Tcl_DStringFree(&copy);
+ return count;
+}
+
+typedef struct {
+ int numKey; /* Integer representation of a value. */
+ const char *strKey; /* String representation of a value. */
+} TkStateMap;
+
+static TkStateMap notifyMode[] = {
+ {NotifyNormal, "NotifyNormal"},
+ {NotifyGrab, "NotifyGrab"},
+ {NotifyUngrab, "NotifyUngrab"},
+ {NotifyWhileGrabbed, "NotifyWhileGrabbed"},
+ {-1, NULL}
+};
+
+static TkStateMap notifyDetail[] = {
+ {NotifyAncestor, "NotifyAncestor"},
+ {NotifyVirtual, "NotifyVirtual"},
+ {NotifyInferior, "NotifyInferior"},
+ {NotifyNonlinear, "NotifyNonlinear"},
+ {NotifyNonlinearVirtual, "NotifyNonlinearVirtual"},
+ {NotifyPointer, "NotifyPointer"},
+ {NotifyPointerRoot, "NotifyPointerRoot"},
+ {NotifyDetailNone, "NotifyDetailNone"},
+ {-1, NULL}
+};
+
+static TkStateMap circPlace[] = {
+ {PlaceOnTop, "PlaceOnTop"},
+ {PlaceOnBottom, "PlaceOnBottom"},
+ {-1, NULL}
+};
+
+static TkStateMap visNotify[] = {
+ {VisibilityUnobscured, "VisibilityUnobscured"},
+ {VisibilityPartiallyObscured, "VisibilityPartiallyObscured"},
+ {VisibilityFullyObscured, "VisibilityFullyObscured"},
+ {-1, NULL}
+};
+
+static TkStateMap configureRequestDetail[] = {
+ {None, "None"},
+ {Above, "Above"},
+ {Below, "Below"},
+ {BottomIf, "BottomIf"},
+ {TopIf, "TopIf"},
+ {Opposite, "Opposite"},
+ {-1, NULL}
+};
+
+static TkStateMap propNotify[] = {
+ {PropertyNewValue, "NewValue"},
+ {PropertyDelete, "Delete"},
+ {-1, NULL}
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * HandleEventGenerate --
+ *
+ * Helper function for the "event generate" command. Generate and
+ * process an XEvent, constructed from information parsed from the
+ * event description string and its optional arguments.
+ *
+ * argv[0] contains name of the target window.
+ * argv[1] contains pattern string for one event (e.g, <Control-v>).
+ * argv[2..argc-1] contains -field/option pairs for specifying
+ * additional detail in the generated event.
+ *
+ * Either virtual or physical events can be generated this way.
+ * The event description string must contain the specification
+ * for only one event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When constructing the event,
+ * event.xany.serial is filled with the current X serial number.
+ * event.xany.window is filled with the target window.
+ * event.xany.display is filled with the target window's display.
+ * Any other fields in eventPtr which are not specified by the pattern
+ * string or the optional arguments, are set to 0.
+ *
+ * The event may be handled sychronously or asynchronously, depending
+ * on the value specified by the optional "-when" option. The
+ * default setting is synchronous.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SendEventCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ XEvent event;
+ CONST char *p;
+ char *name;
+ Window window;
+ Display *display;
+ Tk_Window tkwin;
+ int count, flags, synch, i, number, warp;
+ Tcl_QueuePosition pos;
+ Pattern pat;
+ unsigned long eventMask;
+ static CONST char *fieldStrings[] = {
+ "-when", "-above", "-borderwidth", "-button",
+ "-count", "-delta", "-detail", "-focus",
+ "-height",
+ "-keycode", "-keysym", "-mode", "-override",
+ "-place", "-root", "-rootx", "-rooty",
+ "-sendevent", "-serial", "-state", "-subwindow",
+ "-time", "-warp", "-width", "-window",
+ "-x", "-y", NULL
+ };
+ enum field {
+ EVENT_WHEN, EVENT_ABOVE, EVENT_BORDER, EVENT_BUTTON,
+ EVENT_COUNT, EVENT_DELTA, EVENT_DETAIL, EVENT_FOCUS,
+ EVENT_HEIGHT,
+ EVENT_KEYCODE, EVENT_KEYSYM, EVENT_MODE, EVENT_OVERRIDE,
+ EVENT_PLACE, EVENT_ROOT, EVENT_ROOTX, EVENT_ROOTY,
+ EVENT_SEND, EVENT_SERIAL, EVENT_STATE, EVENT_SUBWINDOW,
+ EVENT_TIME, EVENT_WARP, EVENT_WIDTH, EVENT_WINDOW,
+ EVENT_X, EVENT_Y
+ };
+ tkwin = Tk_MainWindow(interp);
+ if (Blt_GetWindowFromObj(interp, objv[1], &window) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ name = Tcl_GetStringFromObj(objv[2], NULL);
+
+ display = Tk_Display(tkwin);
+ p = name;
+ eventMask = 0;
+ count = ParseEventDescription(interp, &p, &pat, &eventMask);
+ if (count == 0) {
+ return TCL_ERROR;
+ }
+ if (count != 1) {
+ Tcl_AppendResult(interp, "Double or Triple modifier not allowed",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (*p != '\0') {
+ Tcl_AppendResult(interp, "only one event specification allowed",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ memset((VOID *) &event, 0, sizeof(event));
+ event.xany.type = pat.eventType;
+ event.xany.serial = NextRequest(display);
+ event.xany.send_event = False;
+ event.xany.window = window;
+ event.xany.display = display;
+
+ flags = flagArray[event.xany.type];
+ if (flags & DESTROY) {
+ /*
+ * Event DestroyNotify should be generated by destroying
+ * the window.
+ */
+ XDestroyWindow(display, window);
+ return TCL_OK;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
+ event.xkey.state = pat.needMods;
+ if ((flags & KEY) && (event.xany.type != MouseWheelEvent)) {
+ TkpSetKeycodeAndState(tkwin, pat.detail.keySym, &event);
+ } else if (flags & BUTTON) {
+ event.xbutton.button = pat.detail.button;
+ } else if (flags & VIRTUAL) {
+ ((XVirtualEvent *) &event)->name = pat.detail.name;
+ }
+ }
+ if (flags & (CREATE|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) {
+ event.xcreatewindow.window = event.xany.window;
+ }
+
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.x_root = -1;
+ event.xkey.y_root = -1;
+ }
+
+ /*
+ * Process the remaining arguments to fill in additional fields
+ * of the event.
+ */
+
+ synch = 1;
+ warp = 0;
+ pos = TCL_QUEUE_TAIL;
+ for (i = 3; i < objc; i += 2) {
+ Tcl_Obj *optionPtr, *valuePtr;
+ int index;
+
+ optionPtr = objv[i];
+ valuePtr = objv[i + 1];
+
+ if (Tcl_GetIndexFromObj(interp, optionPtr, fieldStrings, "option",
+ TCL_EXACT, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc & 1) == 0) {
+ /*
+ * This test occurs after Tcl_GetIndexFromObj() so that
+ * "event generate <Button> -xyz" will return the error message
+ * that "-xyz" is a bad option, rather than that the value
+ * for "-xyz" is missing.
+ */
+
+ Tcl_AppendResult(interp, "value for \"",
+ Tcl_GetStringFromObj(optionPtr, NULL), "\" missing",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ switch ((enum field) index) {
+ case EVENT_WARP: {
+ if (Tcl_GetBooleanFromObj(interp, valuePtr, &warp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!(flags & (KEY_BUTTON_MOTION_VIRTUAL))) {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_WHEN: {
+#ifdef notdef
+ pos = (Tcl_QueuePosition) TkFindStateNumObj(interp, optionPtr,
+ queuePosition, valuePtr);
+ if ((int) pos < -1) {
+ return TCL_ERROR;
+ }
+ synch = 0;
+ if ((int) pos == -1) {
+ synch = 1;
+ }
+#endif
+ break;
+ }
+ case EVENT_ABOVE: {
+ Window window2;
+
+ if (Blt_GetWindowFromObj(interp, valuePtr, &window2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & CONFIG) {
+ event.xconfigure.above = window2;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_BORDER: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)!=TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (CREATE|CONFIG)) {
+ event.xcreatewindow.border_width = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_BUTTON: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & BUTTON) {
+ event.xbutton.button = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_COUNT: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & EXPOSE) {
+ event.xexpose.count = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_DELTA: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((flags & KEY) && (event.xkey.type == MouseWheelEvent)) {
+ event.xkey.keycode = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_DETAIL: {
+ number = TkFindStateNumObj(interp, optionPtr, notifyDetail,
+ valuePtr);
+ if (number < 0) {
+ return TCL_ERROR;
+ }
+ if (flags & FOCUS) {
+ event.xfocus.detail = number;
+ } else if (flags & CROSSING) {
+ event.xcrossing.detail = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_FOCUS: {
+ if (Tcl_GetBooleanFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & CROSSING) {
+ event.xcrossing.focus = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_HEIGHT: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & EXPOSE) {
+ event.xexpose.height = number;
+ } else if (flags & CONFIG) {
+ event.xconfigure.height = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_KEYCODE: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((flags & KEY) && (event.xkey.type != MouseWheelEvent)) {
+ event.xkey.keycode = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_KEYSYM: {
+ KeySym keysym;
+ char *value;
+
+ value = Tcl_GetStringFromObj(valuePtr, NULL);
+ keysym = TkStringToKeysym(value);
+ if (keysym == NoSymbol) {
+ Tcl_AppendResult(interp, "unknown keysym \"", value, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ TkpSetKeycodeAndState(tkwin, keysym, &event);
+ if (event.xkey.keycode == 0) {
+ Tcl_AppendResult(interp, "no keycode for keysym \"", value,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (!(flags & KEY) || (event.xkey.type == MouseWheelEvent)) {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_MODE: {
+ number = TkFindStateNumObj(interp, optionPtr, notifyMode,
+ valuePtr);
+ if (number < 0) {
+ return TCL_ERROR;
+ }
+ if (flags & CROSSING) {
+ event.xcrossing.mode = number;
+ } else if (flags & FOCUS) {
+ event.xfocus.mode = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_OVERRIDE: {
+ if (Tcl_GetBooleanFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & CREATE) {
+ event.xcreatewindow.override_redirect = number;
+ } else if (flags & MAP) {
+ event.xmap.override_redirect = number;
+ } else if (flags & REPARENT) {
+ event.xreparent.override_redirect = number;
+ } else if (flags & CONFIG) {
+ event.xconfigure.override_redirect = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_PLACE: {
+ number = TkFindStateNumObj(interp, optionPtr, circPlace,
+ valuePtr);
+ if (number < 0) {
+ return TCL_ERROR;
+ }
+ if (flags & CIRC) {
+ event.xcirculate.place = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_ROOT: {
+ Window window2;
+
+ if (Blt_GetWindowFromObj(interp, valuePtr, &window2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.root = window2;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_ROOTX: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.x_root = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_ROOTY: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.y_root = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_SEND: {
+ CONST char *value;
+
+ value = Tcl_GetStringFromObj(valuePtr, NULL);
+ if (isdigit(UCHAR(value[0]))) {
+ /*
+ * Allow arbitrary integer values for the field; they
+ * are needed by a few of the tests in the Tk test suite.
+ */
+
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_GetBooleanFromObj(interp, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ event.xany.send_event = number;
+ break;
+ }
+ case EVENT_SERIAL: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ event.xany.serial = number;
+ break;
+ }
+ case EVENT_STATE: {
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
+ event.xkey.state = number;
+ } else {
+ event.xcrossing.state = number;
+ }
+ } else if (flags & VISIBILITY) {
+ number = TkFindStateNumObj(interp, optionPtr, visNotify,
+ valuePtr);
+ if (number < 0) {
+ return TCL_ERROR;
+ }
+ event.xvisibility.state = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_SUBWINDOW: {
+ Window window2;
+
+ if (Blt_GetWindowFromObj(interp, valuePtr, &window2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.subwindow = window2;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_TIME: {
+ if (Tcl_GetIntFromObj(interp, valuePtr, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.time = (Time) number;
+ } else if (flags & PROP) {
+ event.xproperty.time = (Time) number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_WIDTH: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & EXPOSE) {
+ event.xexpose.width = number;
+ } else if (flags & (CREATE|CONFIG)) {
+ event.xcreatewindow.width = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_WINDOW: {
+ Window window2;
+
+ if (Blt_GetWindowFromObj(interp, valuePtr, &window2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (CREATE|UNMAP|MAP|REPARENT|CONFIG
+ |GRAVITY|CIRC)) {
+ event.xcreatewindow.window = window2;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_X: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.x = number;
+ /*
+ * Only modify rootx as well if it hasn't been changed.
+ */
+ if (event.xkey.x_root == -1) {
+ int rootX, rootY;
+
+ Tk_GetRootCoords(tkwin, &rootX, &rootY);
+ event.xkey.x_root = rootX + number;
+ }
+ } else if (flags & EXPOSE) {
+ event.xexpose.x = number;
+ } else if (flags & (CREATE|CONFIG|GRAVITY)) {
+ event.xcreatewindow.x = number;
+ } else if (flags & REPARENT) {
+ event.xreparent.x = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ case EVENT_Y: {
+ if (Tk_GetPixelsFromObj(interp, tkwin, valuePtr, &number)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
+ event.xkey.y = number;
+ /*
+ * Only modify rooty as well if it hasn't been changed.
+ */
+ if (event.xkey.y_root == -1) {
+ int rootX, rootY;
+
+ Tk_GetRootCoords(tkwin, &rootX, &rootY);
+ event.xkey.y_root = rootY + number;
+ }
+ } else if (flags & EXPOSE) {
+ event.xexpose.y = number;
+ } else if (flags & (CREATE|CONFIG|GRAVITY)) {
+ event.xcreatewindow.y = number;
+ } else if (flags & REPARENT) {
+ event.xreparent.y = number;
+ } else {
+ goto badopt;
+ }
+ break;
+ }
+ }
+ continue;
+
+ badopt:
+ Tcl_AppendResult(interp, name, " event doesn't accept \"",
+ Tcl_GetStringFromObj(optionPtr, NULL), "\" option",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (!XSendEvent(display, window, False, pat.eventType, &event)) {
+ fprintf(stderr, "synthethic event failed\n");
+ }
+ return TCL_OK;
+}
+
+int
+Blt_SendEventCmdInitProc(Tcl_Interp *interp)
+{
+ static Blt_InitCmdSpec cmdSpec = {
+ "sendevent", SendEventCmd,
+ };
+ return Blt_InitCmd(interp, "::blt", &cmdSpec);
+}
diff --git a/src/bltGrBind.h b/src/bltGrBind.h
new file mode 100644
index 0000000..2e2c5cb
--- /dev/null
+++ b/src/bltGrBind.h
@@ -0,0 +1,120 @@
+/*
+ * bltBind.h --
+ *
+ * Copyright 1998-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_BIND_H
+#define _BLT_BIND_H
+
+#include <bltList.h>
+
+typedef struct _Blt_BindTable *Blt_BindTable;
+
+typedef ClientData (Blt_BindPickProc)(ClientData clientData, int x, int y,
+ ClientData *contextPtr);
+
+typedef void (Blt_BindTagProc)(Blt_BindTable bindTable, ClientData object,
+ ClientData context, Blt_List list);
+
+
+/*
+ * Binding structure information:
+ */
+
+struct _Blt_BindTable {
+ unsigned int flags;
+ Tk_BindingTable bindingTable;
+ /* Table of all bindings currently defined.
+ * NULL means that no bindings exist, so the
+ * table hasn't been created. Each "object"
+ * used for this table is either a Tk_Uid for
+ * a tag or the address of an item named by
+ * id. */
+
+ ClientData currentItem; /* The item currently containing the mouse
+ * pointer, or NULL if none. */
+ ClientData currentContext; /* One word indicating what kind of object was
+ * picked. */
+
+ ClientData newItem; /* The item that is about to become the
+ * current one, or NULL. This field is used
+ * to detect deletions of the new current item
+ * pointer that occur during Leave processing
+ * of the previous current tab. */
+ ClientData newContext; /* One-word indicating what kind of object was
+ * just picked. */
+
+ ClientData focusItem;
+ ClientData focusContext;
+
+ XEvent pickEvent; /* The event upon which the current choice of
+ * the current tab is based. Must be saved so
+ * that if the current item is deleted, we can
+ * pick another. */
+ int activePick; /* The pick event has been initialized so that
+ * we can repick it */
+
+ int state; /* Last known modifier state. Used to defer
+ * picking a new current object while buttons
+ * are down. */
+
+ ClientData clientData;
+ Tk_Window tkwin;
+ Blt_BindPickProc *pickProc; /* Routine to report the item the mouse is
+ * currently over. */
+ Blt_BindTagProc *tagProc; /* Routine to report tags picked items. */
+};
+
+BLT_EXTERN void Blt_DestroyBindingTable(Blt_BindTable table);
+
+BLT_EXTERN Blt_BindTable Blt_CreateBindingTable(Tcl_Interp *interp,
+ Tk_Window tkwin, ClientData clientData, Blt_BindPickProc *pickProc,
+ Blt_BindTagProc *tagProc);
+
+BLT_EXTERN int Blt_ConfigureBindings(Tcl_Interp *interp, Blt_BindTable table,
+ ClientData item, int argc, const char **argv);
+
+BLT_EXTERN int Blt_ConfigureBindingsFromObj(Tcl_Interp *interp,
+ Blt_BindTable table, ClientData item, int objc, Tcl_Obj *const *objv);
+
+BLT_EXTERN void Blt_PickCurrentItem(Blt_BindTable table);
+
+BLT_EXTERN void Blt_DeleteBindings(Blt_BindTable table, ClientData object);
+
+BLT_EXTERN void Blt_MoveBindingTable(Blt_BindTable table, Tk_Window tkwin);
+
+#define Blt_SetFocusItem(bindPtr, object, context) \
+ ((bindPtr)->focusItem = (ClientData)(object),\
+ (bindPtr)->focusContext = (ClientData)(context))
+
+#define Blt_SetCurrentItem(bindPtr, object, context) \
+ ((bindPtr)->currentItem = (ClientData)(object),\
+ (bindPtr)->currentContext = (ClientData)(context))
+
+#define Blt_GetCurrentItem(bindPtr) ((bindPtr)->currentItem)
+#define Blt_GetCurrentContext(bindPtr) ((bindPtr)->currentContext)
+#define Blt_GetLatestItem(bindPtr) ((bindPtr)->newItem)
+
+#define Blt_GetBindingData(bindPtr) ((bindPtr)->clientData)
+
+#endif /*_BLT_BIND_H*/
diff --git a/src/bltGrElemBar.C b/src/bltGrElemBar.C
new file mode 100644
index 0000000..328275a
--- /dev/null
+++ b/src/bltGrElemBar.C
@@ -0,0 +1,2543 @@
+
+/*
+ * bltGrBar.c --
+ *
+ * This module implements barchart elements for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+#define CLAMP(x,l,h) ((x) = (((x)<(l))? (l) : ((x)>(h)) ? (h) : (x)))
+
+typedef struct {
+ float x1, y1, x2, y2;
+} BarRegion;
+
+typedef struct {
+ Point2f ul, lr;
+ Segment2d segments[4];
+ int nSegments;
+} Bar;
+
+typedef struct {
+ const char *name; /* Pen style identifier. If NULL, pen
+ * was statically allocated. */
+ ClassId classId; /* Type of pen */
+ const char *typeId; /* String token identifying the type of
+ * pen */
+ unsigned int flags; /* Indicates if the pen element is
+ * active or normal */
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications */
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+ Graph *graphPtr; /* Graph that the pen is associated
+ * with. */
+ /* Barchart specific pen fields start here. */
+ XColor *outlineColor; /* Outline (foreground) color of bar */
+ Blt_Background fill; /* 3D border and fill (background)
+ * color */
+ int borderWidth; /* 3D border width of bar */
+ int relief; /* Relief of the bar */
+ Pixmap stipple; /* Stipple */
+ GC fillGC; /* Graphics context */
+ GC outlineGC; /* GC for outline of bar. */
+
+ /* Error bar attributes. */
+ int errorBarShow; /* Describes which error bars to
+ * display: none, x, y, or * both. */
+
+ int errorBarLineWidth; /* Width of the error bar segments. */
+
+ int errorBarCapWidth;
+ XColor *errorBarColor; /* Color of the error bar. */
+
+ GC errorBarGC; /* Error bar graphics context. */
+
+ /* Show value attributes. */
+ int valueShow; /* Indicates whether to display data
+ * value. Values are x, y, or none. */
+
+ const char *valueFormat; /* A printf format string. */
+ TextStyle valueStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the value. */
+
+} BarPen;
+
+typedef struct {
+ Weight weight; /* Weight range where this pen is
+ * valid. */
+ BarPen *penPtr; /* Pen to use. */
+
+ XRectangle *bars; /* Indicates starting location in bar
+ * array for this pen. */
+ int nBars; /* # of bar segments for this pen. */
+
+ GraphSegments xeb, yeb; /* X and Y error bars. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to
+ * the current graph size. */
+ int errorBarCapWidth; /* Length of the cap ends on each error
+ * bar. */
+
+} BarStyle;
+
+typedef struct {
+ GraphObj obj; /* Must be first field in element. */
+ unsigned int flags;
+ Blt_HashEntry *hashPtr;
+
+ /* Fields specific to elements. */
+ const char *label; /* Label displayed in legend */
+ unsigned short row, col; /* Position of the entry in the
+ * legend. */
+ int legendRelief; /* Relief of label in legend. */
+ Axis2d axes; /* X-axis and Y-axis mapping the
+ * element */
+ ElemValues x, y, w; /* Contains array of floating point
+ * graph coordinate values. Also holds
+ * min/max and the number of
+ * coordinates */
+ int *activeIndices; /* Array of indices (malloc-ed) which
+ * indicate which data points are active
+ * (drawn * with "active" colors). */
+ int nActiveIndices; /* Number of active data points.
+ * Special case: if nActiveIndices < 0
+ * and the active bit is set in "flags",
+ * then all data * points are drawn
+ * active. */
+ ElementProcs *procsPtr;
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications. */
+ BarPen *activePenPtr; /* Standard Pens */
+ BarPen *normalPenPtr;
+ BarPen *builtinPenPtr;
+ Blt_Chain stylePalette; /* Palette of pens. */
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+ int state;
+ Blt_ChainLink link;
+
+ /* Fields specific to the barchart element */
+
+ float barWidth;
+ const char *groupName;
+
+ int *barToData;
+ XRectangle *bars; /* Array of rectangles comprising the bar
+ * segments of the element. */
+ int *activeToData;
+ XRectangle *activeRects;
+
+ int nBars; /* # of visible bar segments for
+ * element */
+ int nActive;
+
+ int xPad; /* Spacing on either side of bar */
+
+ ElemValues xError; /* Relative/symmetric X error values. */
+ ElemValues yError; /* Relative/symmetric Y error values. */
+ ElemValues xHigh, xLow; /* Absolute/asymmetric X-coordinate
+ * high/low error values. */
+ ElemValues yHigh, yLow; /* Absolute/asymmetric Y-coordinate
+ * high/low error values. */
+ BarPen builtinPen;
+
+ GraphSegments xeb, yeb;
+
+ int errorBarCapWidth; /* Length of cap on error bars */
+} BarElement;
+
+BLT_EXTERN Blt_CustomOption bltBarPenOption;
+BLT_EXTERN Blt_CustomOption bltValuesOption;
+BLT_EXTERN Blt_CustomOption bltValuePairsOption;
+BLT_EXTERN Blt_CustomOption bltXAxisOption;
+BLT_EXTERN Blt_CustomOption bltYAxisOption;
+BLT_EXTERN Blt_CustomOption bltColorOption;
+BLT_EXTERN Blt_CustomOption bltBarStylesOption;
+
+static Blt_OptionParseProc ObjToBarMode;
+static Blt_OptionPrintProc BarModeToObj;
+Blt_CustomOption bltBarModeOption =
+{
+ ObjToBarMode, BarModeToObj, NULL, (ClientData)0
+};
+
+#define DEF_BAR_ACTIVE_PEN "activeBar"
+#define DEF_BAR_AXIS_X "x"
+#define DEF_BAR_AXIS_Y "y"
+#define DEF_BAR_BACKGROUND "navyblue"
+#define DEF_BAR_BORDERWIDTH "2"
+#define DEF_BAR_ERRORBAR_COLOR "defcolor"
+#define DEF_BAR_ERRORBAR_LINE_WIDTH "1"
+#define DEF_BAR_ERRORBAR_CAP_WIDTH "1"
+#define DEF_BAR_FOREGROUND "blue"
+#define DEF_BAR_HIDE "no"
+#define DEF_BAR_LABEL_RELIEF "flat"
+#define DEF_BAR_NORMAL_STIPPLE ""
+#define DEF_BAR_RELIEF "raised"
+#define DEF_BAR_SHOW_ERRORBARS "both"
+#define DEF_BAR_STATE "normal"
+#define DEF_BAR_STACK (char *)NULL
+#define DEF_BAR_STYLES ""
+#define DEF_BAR_TAGS "all"
+#define DEF_BAR_WIDTH "0.0"
+
+#define DEF_PEN_ACTIVE_BACKGROUND "red"
+#define DEF_PEN_ACTIVE_FOREGROUND "pink"
+#define DEF_PEN_BORDERWIDTH "2"
+#define DEF_PEN_NORMAL_BACKGROUND "navyblue"
+#define DEF_PEN_NORMAL_FOREGROUND "blue"
+#define DEF_PEN_RELIEF "raised"
+#define DEF_PEN_STIPPLE ""
+#define DEF_PEN_TYPE "bar"
+#define DEF_PEN_VALUE_ANCHOR "s"
+#define DEF_PEN_VALUE_COLOR RGB_BLACK
+#define DEF_PEN_VALUE_FONT STD_FONT_SMALL
+#define DEF_PEN_VALUE_FORMAT "%g"
+#define DEF_PEN_SHOW_VALUES "no"
+
+static Blt_ConfigSpec barPenConfigSpecs[] =
+{
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_PEN_ACTIVE_BACKGROUND, Blt_Offset(BarPen, fill),
+ BLT_CONFIG_NULL_OK | ACTIVE_PEN},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_PEN_NORMAL_BACKGROUND, Blt_Offset(BarPen, fill),
+ BLT_CONFIG_NULL_OK | NORMAL_PEN},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_PEN_BORDERWIDTH, Blt_Offset(BarPen, borderWidth), ALL_PENS},
+ {BLT_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_BAR_ERRORBAR_COLOR, Blt_Offset(BarPen, errorBarColor), ALL_PENS,
+ &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarwidth", "errorBarWidth","ErrorBarWidth",
+ DEF_BAR_ERRORBAR_LINE_WIDTH, Blt_Offset(BarPen, errorBarLineWidth),
+ ALL_PENS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarcap", "errorBarCap", "ErrorBarCap",
+ DEF_BAR_ERRORBAR_CAP_WIDTH, Blt_Offset(BarPen, errorBarCapWidth),
+ ALL_PENS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {BLT_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_ACTIVE_FOREGROUND, Blt_Offset(BarPen, outlineColor),
+ ACTIVE_PEN | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_PEN_NORMAL_FOREGROUND, Blt_Offset(BarPen, outlineColor),
+ NORMAL_PEN | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, ALL_PENS},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_PEN_RELIEF, Blt_Offset(BarPen, relief), ALL_PENS},
+ {BLT_CONFIG_FILL, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_BAR_SHOW_ERRORBARS, Blt_Offset(BarPen, errorBarShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Blt_Offset(BarPen, valueShow),
+ ALL_PENS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMAP, "-stipple", "stipple", "Stipple", DEF_PEN_STIPPLE,
+ Blt_Offset(BarPen, stipple), ALL_PENS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL, DEF_PEN_TYPE,
+ Blt_Offset(BarPen, typeId), ALL_PENS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Blt_Offset(BarPen, valueStyle.anchor),
+ ALL_PENS},
+ {BLT_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Blt_Offset(BarPen, valueStyle.color),
+ ALL_PENS},
+ {BLT_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Blt_Offset(BarPen, valueStyle.font),
+ ALL_PENS},
+ {BLT_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Blt_Offset(BarPen, valueFormat),
+ ALL_PENS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-valuerotate", "valueRotate", "ValueRotate",
+ (char *)NULL, Blt_Offset(BarPen, valueStyle.angle), ALL_PENS},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Blt_ConfigSpec barElemConfigSpecs[] = {
+ {BLT_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_BAR_ACTIVE_PEN, Blt_Offset(BarElement, activePenPtr),
+ BLT_CONFIG_NULL_OK, &bltBarPenOption},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_BAR_BACKGROUND, Blt_Offset(BarElement, builtinPen.fill),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-barwidth", "barWidth", "BarWidth",
+ DEF_BAR_WIDTH, Blt_Offset(BarElement, barWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_BAR_TAGS,
+ Blt_Offset(BarElement, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_BAR_BORDERWIDTH, Blt_Offset(BarElement, builtinPen.borderWidth), 0},
+ {BLT_CONFIG_SYNONYM, "-color", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_BAR_ERRORBAR_COLOR,
+ Blt_Offset(BarElement, builtinPen.errorBarColor), 0, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_BAR_ERRORBAR_LINE_WIDTH,
+ Blt_Offset(BarElement, builtinPen.errorBarLineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarcap", "errorBarCap", "ErrorBarCap",
+ DEF_BAR_ERRORBAR_CAP_WIDTH,
+ Blt_Offset(BarElement, builtinPen.errorBarCapWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_CUSTOM, "-data", "data", "Data", (char *)NULL, 0, 0,
+ &bltValuePairsOption},
+ {BLT_CONFIG_SYNONYM, "-fill", "background", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_BAR_FOREGROUND, Blt_Offset(BarElement, builtinPen.outlineColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-label", "label", "Label", (char *)NULL,
+ Blt_Offset(BarElement, label), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_RELIEF, "-legendrelief", "legendRelief", "LegendRelief",
+ DEF_BAR_LABEL_RELIEF, Blt_Offset(BarElement, legendRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_BAR_HIDE,
+ Blt_Offset(BarElement, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_BAR_AXIS_X,
+ Blt_Offset(BarElement, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_BAR_AXIS_Y,
+ Blt_Offset(BarElement, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL,
+ (char *)NULL, 0, 0},
+ {BLT_CONFIG_CUSTOM, "-pen", "pen", "Pen", (char *)NULL,
+ Blt_Offset(BarElement, normalPenPtr), BLT_CONFIG_NULL_OK,
+ &bltBarPenOption},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_BAR_RELIEF, Blt_Offset(BarElement, builtinPen.relief), 0},
+ {BLT_CONFIG_FILL, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_BAR_SHOW_ERRORBARS, Blt_Offset(BarElement, builtinPen.errorBarShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Blt_Offset(BarElement, builtinPen.valueShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-stack", "stack", "Stack", DEF_BAR_STACK,
+ Blt_Offset(BarElement, groupName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_BAR_STATE,
+ Blt_Offset(BarElement, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
+ DEF_BAR_NORMAL_STIPPLE, Blt_Offset(BarElement, builtinPen.stipple),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-styles", "styles", "Styles", DEF_BAR_STYLES,
+ Blt_Offset(BarElement, stylePalette), 0, &bltBarStylesOption},
+ {BLT_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Blt_Offset(BarElement, builtinPen.valueStyle.anchor), 0},
+ {BLT_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR,
+ Blt_Offset(BarElement, builtinPen.valueStyle.color), 0},
+ {BLT_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT,
+ Blt_Offset(BarElement, builtinPen.valueStyle.font), 0},
+ {BLT_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Blt_Offset(BarElement, builtinPen.valueFormat),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-valuerotate", "valueRotate", "ValueRotate",
+ (char *)NULL, Blt_Offset(BarElement, builtinPen.valueStyle.angle), 0},
+ {BLT_CONFIG_CUSTOM, "-weights", "weights", "Weights", (char *)NULL,
+ Blt_Offset(BarElement, w), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-x", "xdata", "Xdata", (char *)NULL,
+ Blt_Offset(BarElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-y", "ydata", "Ydata", (char *)NULL,
+ Blt_Offset(BarElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xdata", "xdata", "Xdata", (char *)NULL,
+ Blt_Offset(BarElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ydata", "ydata", "Ydata", (char *)NULL,
+ Blt_Offset(BarElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL,
+ Blt_Offset(BarElement, xError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL,
+ Blt_Offset(BarElement, xHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL,
+ Blt_Offset(BarElement, xLow), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL,
+ Blt_Offset(BarElement, yError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL,
+ Blt_Offset(BarElement, yHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL,
+ Blt_Offset(BarElement, yLow), 0, &bltValuesOption},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/* Forward declarations */
+static PenConfigureProc ConfigureBarPenProc;
+static PenDestroyProc DestroyBarPenProc;
+static ElementClosestProc ClosestBarProc;
+static ElementConfigProc ConfigureBarProc;
+static ElementDestroyProc DestroyBarProc;
+static ElementDrawProc DrawActiveBarProc;
+static ElementDrawProc DrawNormalBarProc;
+static ElementDrawSymbolProc DrawSymbolProc;
+static ElementExtentsProc GetBarExtentsProc;
+static ElementToPostScriptProc ActiveBarToPostScriptProc;
+static ElementToPostScriptProc NormalBarToPostScriptProc;
+static ElementSymbolToPostScriptProc SymbolToPostScriptProc;
+static ElementMapProc MapBarProc;
+
+INLINE static int
+Round(double x)
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Custom option parse and print procedures
+ *---------------------------------------------------------------------------
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NameOfBarMode --
+ *
+ * Converts the integer representing the mode style into a string.
+ *
+ *---------------------------------------------------------------------------
+ */
+static const char *
+NameOfBarMode(BarMode mode)
+{
+ switch (mode) {
+ case BARS_INFRONT:
+ return "infront";
+ case BARS_OVERLAP:
+ return "overlap";
+ case BARS_STACKED:
+ return "stacked";
+ case BARS_ALIGNED:
+ return "aligned";
+ default:
+ return "unknown mode value";
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToMode --
+ *
+ * Converts the mode string into its numeric representation.
+ *
+ * Valid mode strings are:
+ *
+ * "infront" Draw a full bar at each point in the element.
+ *
+ * "stacked" Stack bar segments vertically. Each stack is defined
+ * by each ordinate at a particular abscissa. The height
+ * of each segment is represented by the sum the previous
+ * ordinates.
+ *
+ * "aligned" Align bar segments as smaller slices one next to
+ * the other. Like "stacks", aligned segments are
+ * defined by each ordinate at a particular abscissa.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToBarMode(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* Mode style string */
+ char *widgRec, /* Cubicle structure record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ BarMode *modePtr = (BarMode *)(widgRec + offset);
+ int length;
+ char c;
+ char *string;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'n') && (strncmp(string, "normal", length) == 0)) {
+ *modePtr = BARS_INFRONT;
+ } else if ((c == 'i') && (strncmp(string, "infront", length) == 0)) {
+ *modePtr = BARS_INFRONT;
+ } else if ((c == 's') && (strncmp(string, "stacked", length) == 0)) {
+ *modePtr = BARS_STACKED;
+ } else if ((c == 'a') && (strncmp(string, "aligned", length) == 0)) {
+ *modePtr = BARS_ALIGNED;
+ } else if ((c == 'o') && (strncmp(string, "overlap", length) == 0)) {
+ *modePtr = BARS_OVERLAP;
+ } else {
+ Tcl_AppendResult(interp, "bad mode argument \"", string, "\": should"
+ "be \"infront\", \"stacked\", \"overlap\", or \"aligned\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BarModeToObj --
+ *
+ * Returns the mode style string based upon the mode flags.
+ *
+ * Results:
+ * The mode style string is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+BarModeToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Row/column structure record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ BarMode mode = *(BarMode *)(widgRec + offset);
+
+ return Tcl_NewStringObj(NameOfBarMode(mode), -1);
+}
+
+
+/*
+ * Zero out the style's number of bars and errorbars.
+ */
+static void
+ResetStylePalette(Blt_Chain stylePalette)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->xeb.length = stylePtr->yeb.length = 0;
+ stylePtr->nBars = 0;
+ }
+}
+
+static int
+ConfigureBarPen(Graph *graphPtr, BarPen *penPtr)
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ long defColor;
+ int screenNum;
+
+ screenNum = Tk_ScreenNumber(graphPtr->tkwin);
+ gcMask = GCForeground | GCLineWidth;
+ gcValues.line_width = LineWidth(penPtr->errorBarLineWidth);
+
+ if (penPtr->outlineColor != NULL) {
+ defColor = penPtr->outlineColor->pixel;
+ gcValues.foreground = penPtr->outlineColor->pixel;
+ } else if (penPtr->fill != NULL) {
+ defColor = Blt_BackgroundBorderColor(penPtr->fill)->pixel;
+ gcValues.foreground = defColor;
+ } else {
+ defColor = BlackPixel(graphPtr->display, screenNum);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (penPtr->outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->outlineGC);
+ }
+ penPtr->outlineGC = newGC;
+
+ newGC = NULL;
+ if (penPtr->stipple != None) {
+ /* Handle old-style -stipple specially. */
+ gcMask = GCForeground | GCBackground | GCFillStyle | GCStipple;
+ gcValues.foreground = BlackPixel(graphPtr->display, screenNum);
+ gcValues.background = WhitePixel(graphPtr->display, screenNum);
+ if (penPtr->fill != NULL) {
+ gcValues.foreground =
+ Blt_BackgroundBorderColor(penPtr->fill)->pixel;
+ } else if (penPtr->outlineColor != NULL) {
+ gcValues.foreground = penPtr->outlineColor->pixel;
+ }
+ gcValues.stipple = penPtr->stipple;
+ gcValues.fill_style = FillStippled;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (penPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->fillGC);
+ }
+ penPtr->fillGC = newGC;
+
+ gcMask = GCForeground | GCLineWidth;
+ if (penPtr->errorBarColor == COLOR_DEFAULT) {
+ gcValues.foreground = defColor;
+ } else {
+ gcValues.foreground = penPtr->errorBarColor->pixel;
+ }
+ gcValues.line_width = LineWidth(penPtr->errorBarLineWidth);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (penPtr->errorBarGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->errorBarGC);
+ }
+ penPtr->errorBarGC = newGC;
+ return TCL_OK;
+}
+
+static void
+DestroyBarPen(Graph *graphPtr, BarPen *penPtr)
+{
+ Blt_Ts_FreeStyle(graphPtr->display, &penPtr->valueStyle);
+ if (penPtr->outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->outlineGC);
+ }
+ if (penPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->fillGC);
+ }
+ if (penPtr->errorBarGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->errorBarGC);
+ }
+}
+
+static int
+ConfigureBarPenProc(Graph *graphPtr, Pen *basePtr)
+{
+ return ConfigureBarPen(graphPtr, (BarPen *)basePtr);
+}
+
+static void
+DestroyBarPenProc(Graph *graphPtr, Pen *basePtr)
+{
+ DestroyBarPen(graphPtr, (BarPen *)basePtr);
+}
+
+
+static void
+InitializeBarPen(BarPen *penPtr)
+{
+ /* Generic fields common to all pen types. */
+ penPtr->configProc = ConfigureBarPenProc;
+ penPtr->destroyProc = DestroyBarPenProc;
+ penPtr->flags = NORMAL_PEN;
+ penPtr->configSpecs = barPenConfigSpecs;
+
+ /* Initialize fields specific to bar pens. */
+ Blt_Ts_InitStyle(penPtr->valueStyle);
+ penPtr->relief = TK_RELIEF_RAISED;
+ penPtr->valueShow = SHOW_NONE;
+ penPtr->borderWidth = 2;
+ penPtr->errorBarShow = SHOW_BOTH;
+}
+
+Pen *
+Blt_BarPen(const char *penName)
+{
+ BarPen *penPtr;
+
+ penPtr = Blt_AssertCalloc(1, sizeof(BarPen));
+ InitializeBarPen(penPtr);
+ penPtr->name = Blt_AssertStrdup(penName);
+ if (strcmp(penName, "activeBar") == 0) {
+ penPtr->flags = ACTIVE_PEN;
+ }
+ return (Pen *)penPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CheckBarStacks --
+ *
+ * Check that the data limits are not superseded by the heights of
+ * stacked bar segments. The heights are calculated by
+ * Blt_ComputeStacks.
+ *
+ * Results:
+ * If the y-axis limits need to be adjusted for stacked segments,
+ * *minPtr* or *maxPtr* are updated.
+ *
+ * Side effects:
+ * Autoscaling of the y-axis is affected.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+CheckBarStacks(Graph *graphPtr, Axis2d *pairPtr, double *minPtr, double *maxPtr)
+{
+ BarGroup *gp, *gend;
+
+ if ((graphPtr->mode != BARS_STACKED) || (graphPtr->nBarGroups == 0)) {
+ return;
+ }
+ for (gp = graphPtr->barGroups, gend = gp + graphPtr->nBarGroups; gp < gend;
+ gp++) {
+ if ((gp->axes.x == pairPtr->x) && (gp->axes.y == pairPtr->y)) {
+ /*
+ * Check if any of the y-values (because of stacking) are greater
+ * than the current limits of the graph.
+ */
+ if (gp->sum < 0.0f) {
+ if (*minPtr > gp->sum) {
+ *minPtr = gp->sum;
+ }
+ } else {
+ if (*maxPtr < gp->sum) {
+ *maxPtr = gp->sum;
+ }
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureBarProc --
+ *
+ * Sets up the appropriate configuration parameters in the GC. It is
+ * assumed the parameters have been previously set by a call to
+ * Blt_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard TCL result. If TCL_ERROR is returned,
+ * then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as bar foreground/background color and
+ * stipple etc. get set in a new GC.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureBarProc(Graph *graphPtr, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ Blt_ChainLink link;
+ BarStyle *stylePtr;
+
+ if (ConfigureBarPen(graphPtr, elemPtr->builtinPenPtr)!= TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Point to the static normal pen if no external pens have been selected.
+ */
+ link = Blt_Chain_FirstLink(elemPtr->stylePalette);
+ if (link == NULL) {
+ link = Blt_Chain_AllocLink(sizeof(BarStyle));
+ Blt_Chain_LinkAfter(elemPtr->stylePalette, link, NULL);
+ }
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->penPtr = NORMALPEN(elemPtr);
+
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-barwidth", "-*data",
+ "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
+ elemPtr->flags |= MAP_ITEM;
+ }
+ return TCL_OK;
+}
+
+static void
+GetBarExtentsProc(Element *basePtr, Region2d *regPtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ Graph *graphPtr;
+ double middle, barWidth;
+ int nPoints;
+
+ graphPtr = elemPtr->obj.graphPtr;
+ regPtr->top = regPtr->left = DBL_MAX;
+ regPtr->bottom = regPtr->right = -DBL_MAX;
+
+ nPoints = NUMBEROFPOINTS(elemPtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ barWidth = graphPtr->barWidth;
+ if (elemPtr->barWidth > 0.0f) {
+ barWidth = elemPtr->barWidth;
+ }
+ middle = 0.5;
+ regPtr->left = elemPtr->x.min - middle;
+ regPtr->right = elemPtr->x.max + middle;
+
+ regPtr->top = elemPtr->y.min;
+ regPtr->bottom = elemPtr->y.max;
+ if (regPtr->bottom < graphPtr->baseline) {
+ regPtr->bottom = graphPtr->baseline;
+ }
+ /*
+ * Handle stacked bar elements specially.
+ *
+ * If element is stacked, the sum of its ordinates may be outside the
+ * minimum/maximum limits of the element's data points.
+ */
+ if ((graphPtr->mode == BARS_STACKED) && (graphPtr->nBarGroups > 0)) {
+ CheckBarStacks(graphPtr, &elemPtr->axes, &regPtr->top, &regPtr->bottom);
+ }
+ /* Warning: You get what you deserve if the x-axis is logScale */
+ if (elemPtr->axes.x->logScale) {
+ regPtr->left = Blt_FindElemValuesMinimum(&elemPtr->x, DBL_MIN) +
+ middle;
+ }
+ /* Fix y-min limits for barchart */
+ if (elemPtr->axes.y->logScale) {
+ if ((regPtr->top <= 0.0) || (regPtr->top > 1.0)) {
+ regPtr->top = 1.0;
+ }
+ } else {
+ if (regPtr->top > 0.0) {
+ regPtr->top = 0.0;
+ }
+ }
+ /* Correct the extents for error bars if they exist. */
+ if (elemPtr->xError.nValues > 0) {
+ int i;
+
+ /* Correct the data limits for error bars */
+ nPoints = MIN(elemPtr->xError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ double x;
+
+ x = elemPtr->x.values[i] + elemPtr->xError.values[i];
+ if (x > regPtr->right) {
+ regPtr->right = x;
+ }
+ x = elemPtr->x.values[i] - elemPtr->xError.values[i];
+ if (elemPtr->axes.x->logScale) {
+ if (x < 0.0) {
+ x = -x; /* Mirror negative values, instead of
+ * ignoring them. */
+ }
+ if ((x > DBL_MIN) && (x < regPtr->left)) {
+ regPtr->left = x;
+ }
+ } else if (x < regPtr->left) {
+ regPtr->left = x;
+ }
+ }
+ } else {
+ if ((elemPtr->xHigh.nValues > 0) &&
+ (elemPtr->xHigh.max > regPtr->right)) {
+ regPtr->right = elemPtr->xHigh.max;
+ }
+ if (elemPtr->xLow.nValues > 0) {
+ double left;
+
+ if ((elemPtr->xLow.min <= 0.0) &&
+ (elemPtr->axes.x->logScale)) {
+ left = Blt_FindElemValuesMinimum(&elemPtr->xLow, DBL_MIN);
+ } else {
+ left = elemPtr->xLow.min;
+ }
+ if (left < regPtr->left) {
+ regPtr->left = left;
+ }
+ }
+ }
+ if (elemPtr->yError.nValues > 0) {
+ int i;
+
+ nPoints = MIN(elemPtr->yError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ double y;
+
+ y = elemPtr->y.values[i] + elemPtr->yError.values[i];
+ if (y > regPtr->bottom) {
+ regPtr->bottom = y;
+ }
+ y = elemPtr->y.values[i] - elemPtr->yError.values[i];
+ if (elemPtr->axes.y->logScale) {
+ if (y < 0.0) {
+ y = -y; /* Mirror negative values, instead of
+ * ignoring them. */
+ }
+ if ((y > DBL_MIN) && (y < regPtr->left)) {
+ regPtr->top = y;
+ }
+ } else if (y < regPtr->top) {
+ regPtr->top = y;
+ }
+ }
+ } else {
+ if ((elemPtr->yHigh.nValues > 0) &&
+ (elemPtr->yHigh.max > regPtr->bottom)) {
+ regPtr->bottom = elemPtr->yHigh.max;
+ }
+ if (elemPtr->yLow.nValues > 0) {
+ double top;
+
+ if ((elemPtr->yLow.min <= 0.0) &&
+ (elemPtr->axes.y->logScale)) {
+ top = Blt_FindElemValuesMinimum(&elemPtr->yLow, DBL_MIN);
+ } else {
+ top = elemPtr->yLow.min;
+ }
+ if (top < regPtr->top) {
+ regPtr->top = top;
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestBar --
+ *
+ * Find the bar segment closest to the window coordinates point
+ * specified.
+ *
+ * Note: This does not return the height of the stacked segment
+ * (in graph coordinates) properly.
+ *
+ * Results:
+ * Returns 1 if the point is width any bar segment, otherwise 0.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ClosestBarProc(
+ Graph *graphPtr, /* Not used. */
+ Element *basePtr, /* Bar element */
+ ClosestSearch *searchPtr) /* Information about closest point in
+ * element */
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ XRectangle *bp;
+ double minDist;
+ int imin;
+ int i;
+
+ minDist = searchPtr->dist;
+ imin = 0;
+
+ for (bp = elemPtr->bars, i = 0; i < elemPtr->nBars; i++, bp++) {
+ Point2d *pp, *pend;
+ Point2d outline[5];
+ double left, right, top, bottom;
+
+ if (PointInRectangle(bp, searchPtr->x, searchPtr->y)) {
+ imin = elemPtr->barToData[i];
+ minDist = 0.0;
+ break;
+ }
+ left = bp->x, top = bp->y;
+ right = (double)(bp->x + bp->width);
+ bottom = (double)(bp->y + bp->height);
+ outline[4].x = outline[3].x = outline[0].x = left;
+ outline[4].y = outline[1].y = outline[0].y = top;
+ outline[2].x = outline[1].x = right;
+ outline[3].y = outline[2].y = bottom;
+
+ for (pp = outline, pend = outline + 4; pp < pend; pp++) {
+ Point2d t;
+ double dist;
+
+ t = Blt_GetProjection(searchPtr->x, searchPtr->y, pp, pp + 1);
+ if (t.x > right) {
+ t.x = right;
+ } else if (t.x < left) {
+ t.x = left;
+ }
+ if (t.y > bottom) {
+ t.y = bottom;
+ } else if (t.y < top) {
+ t.y = top;
+ }
+ dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y));
+ if (dist < minDist) {
+ minDist = dist;
+ imin = elemPtr->barToData[i];
+ }
+ }
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->elemPtr = (Element *)elemPtr;
+ searchPtr->dist = minDist;
+ searchPtr->index = imin;
+ searchPtr->point.x = (double)elemPtr->x.values[imin];
+ searchPtr->point.y = (double)elemPtr->y.values[imin];
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ * Reorders the both arrays of points and errorbars to merge pens.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The old arrays are freed and new ones allocated containing
+ * the reordered points and errorbars.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MergePens(BarElement *elemPtr, BarStyle **dataToStyle)
+{
+ if (Blt_Chain_GetLength(elemPtr->stylePalette) < 2) {
+ Blt_ChainLink link;
+ BarStyle *stylePtr;
+
+ link = Blt_Chain_FirstLink(elemPtr->stylePalette);
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->nBars = elemPtr->nBars;
+ stylePtr->bars = elemPtr->bars;
+ stylePtr->symbolSize = elemPtr->bars->width / 2;
+ stylePtr->xeb.length = elemPtr->xeb.length;
+ stylePtr->xeb.segments = elemPtr->xeb.segments;
+ stylePtr->yeb.length = elemPtr->yeb.length;
+ stylePtr->yeb.segments = elemPtr->yeb.segments;
+ return;
+ }
+ /* We have more than one style. Group bar segments of like pen styles
+ * together. */
+
+ if (elemPtr->nBars > 0) {
+ Blt_ChainLink link;
+ XRectangle *bars, *bp;
+ int *ip, *barToData;
+
+ bars = Blt_AssertMalloc(elemPtr->nBars * sizeof(XRectangle));
+ barToData = Blt_AssertMalloc(elemPtr->nBars * sizeof(int));
+ bp = bars, ip = barToData;
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->symbolSize = bp->width / 2;
+ stylePtr->bars = bp;
+ for (i = 0; i < elemPtr->nBars; i++) {
+ int iData;
+
+ iData = elemPtr->barToData[i];
+ if (dataToStyle[iData] == stylePtr) {
+ *bp++ = elemPtr->bars[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->nBars = bp - stylePtr->bars;
+ }
+ Blt_Free(elemPtr->bars);
+ Blt_Free(elemPtr->barToData);
+ elemPtr->bars = bars;
+ elemPtr->barToData = barToData;
+ }
+
+ if (elemPtr->xeb.length > 0) {
+ Blt_ChainLink link;
+ Segment2d *bars, *sp;
+ int *map, *ip;
+
+ bars = Blt_AssertMalloc(elemPtr->xeb.length * sizeof(Segment2d));
+ map = Blt_AssertMalloc(elemPtr->xeb.length * sizeof(int));
+ sp = bars, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->xeb.segments = sp;
+ for (i = 0; i < elemPtr->xeb.length; i++) {
+ int iData;
+
+ iData = elemPtr->xeb.map[i];
+ if (dataToStyle[iData] == stylePtr) {
+ *sp++ = elemPtr->xeb.segments[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->xeb.length = sp - stylePtr->xeb.segments;
+ }
+ Blt_Free(elemPtr->xeb.segments);
+ elemPtr->xeb.segments = bars;
+ Blt_Free(elemPtr->xeb.map);
+ elemPtr->xeb.map = map;
+ }
+ if (elemPtr->yeb.length > 0) {
+ Blt_ChainLink link;
+ Segment2d *bars, *sp;
+ int *map, *ip;
+
+ bars = Blt_AssertMalloc(elemPtr->yeb.length * sizeof(Segment2d));
+ map = Blt_AssertMalloc(elemPtr->yeb.length * sizeof(int));
+ sp = bars, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->yeb.segments = sp;
+ for (i = 0; i < elemPtr->yeb.length; i++) {
+ int iData;
+
+ iData = elemPtr->yeb.map[i];
+ if (dataToStyle[iData] == stylePtr) {
+ *sp++ = elemPtr->yeb.segments[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->yeb.length = sp - stylePtr->yeb.segments;
+ }
+ Blt_Free(elemPtr->yeb.segments);
+ elemPtr->yeb.segments = bars;
+ Blt_Free(elemPtr->yeb.map);
+ elemPtr->yeb.map = map;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapActiveBars --
+ *
+ * Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the active point array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapActiveBars(BarElement *elemPtr)
+{
+ if (elemPtr->activeRects != NULL) {
+ Blt_Free(elemPtr->activeRects);
+ elemPtr->activeRects = NULL;
+ }
+ if (elemPtr->activeToData != NULL) {
+ Blt_Free(elemPtr->activeToData);
+ elemPtr->activeToData = NULL;
+ }
+ elemPtr->nActive = 0;
+
+ if (elemPtr->nActiveIndices > 0) {
+ XRectangle *activeRects;
+ int *activeToData;
+ int i;
+ int count;
+
+ activeRects = Blt_AssertMalloc(sizeof(XRectangle) *
+ elemPtr->nActiveIndices);
+ activeToData = Blt_AssertMalloc(sizeof(int) *
+ elemPtr->nActiveIndices);
+ count = 0;
+ for (i = 0; i < elemPtr->nBars; i++) {
+ int *ip, *iend;
+
+ for (ip = elemPtr->activeIndices,
+ iend = ip + elemPtr->nActiveIndices; ip < iend; ip++) {
+ if (elemPtr->barToData[i] == *ip) {
+ activeRects[count] = elemPtr->bars[i];
+ activeToData[count] = i;
+ count++;
+ }
+ }
+ }
+ elemPtr->nActive = count;
+ elemPtr->activeRects = activeRects;
+ elemPtr->activeToData = activeToData;
+ }
+ elemPtr->flags &= ~ACTIVE_PENDING;
+}
+
+static void
+ResetBar(BarElement *elemPtr)
+{
+ /* Release any storage associated with the display of the bar */
+ ResetStylePalette(elemPtr->stylePalette);
+ if (elemPtr->activeRects != NULL) {
+ Blt_Free(elemPtr->activeRects);
+ }
+ if (elemPtr->activeToData != NULL) {
+ Blt_Free(elemPtr->activeToData);
+ }
+ if (elemPtr->xeb.segments != NULL) {
+ Blt_Free(elemPtr->xeb.segments);
+ }
+ if (elemPtr->xeb.map != NULL) {
+ Blt_Free(elemPtr->xeb.map);
+ }
+ if (elemPtr->yeb.segments != NULL) {
+ Blt_Free(elemPtr->yeb.segments);
+ }
+ if (elemPtr->yeb.map != NULL) {
+ Blt_Free(elemPtr->yeb.map);
+ }
+ if (elemPtr->bars != NULL) {
+ Blt_Free(elemPtr->bars);
+ }
+ if (elemPtr->barToData != NULL) {
+ Blt_Free(elemPtr->barToData);
+ }
+ elemPtr->activeToData = elemPtr->xeb.map = elemPtr->yeb.map =
+ elemPtr->barToData = NULL;
+ elemPtr->activeRects = elemPtr->bars = NULL;
+ elemPtr->xeb.segments = elemPtr->yeb.segments = NULL;
+ elemPtr->nActive = elemPtr->xeb.length = elemPtr->yeb.length =
+ elemPtr->nBars = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_MapErrorBars --
+ *
+ * Creates two arrays of points and pen indices, filled with the screen
+ * coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapErrorBars(Graph *graphPtr, BarElement *elemPtr, BarStyle **dataToStyle)
+{
+ int n, nPoints;
+ Region2d reg;
+
+ Blt_GraphExtents(graphPtr, &reg);
+ nPoints = NUMBEROFPOINTS(elemPtr);
+ if (elemPtr->xError.nValues > 0) {
+ n = MIN(elemPtr->xError.nValues, nPoints);
+ } else {
+ n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, nPoints);
+ }
+ if (n > 0) {
+ Segment2d *bars;
+ Segment2d *segPtr;
+ int *map;
+ int *indexPtr;
+ int i;
+
+ segPtr = bars = Blt_AssertMalloc(n * 3 * sizeof(Segment2d));
+ indexPtr = map = Blt_AssertMalloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ double x, y;
+ double high, low;
+ BarStyle *stylePtr;
+
+ x = elemPtr->x.values[i];
+ y = elemPtr->y.values[i];
+ stylePtr = dataToStyle[i];
+ if ((FINITE(x)) && (FINITE(y))) {
+ if (elemPtr->xError.nValues > 0) {
+ high = x + elemPtr->xError.values[i];
+ low = x - elemPtr->xError.values[i];
+ } else {
+ high = elemPtr->xHigh.values[i];
+ low = elemPtr->xLow.values[i];
+ }
+ if ((FINITE(high)) && (FINITE(low))) {
+ Point2d p, q;
+
+ p = Blt_Map2D(graphPtr, high, y, &elemPtr->axes);
+ q = Blt_Map2D(graphPtr, low, y, &elemPtr->axes);
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Left cap */
+ segPtr->p.x = segPtr->q.x = p.x;
+ segPtr->p.y = p.y - stylePtr->errorBarCapWidth;
+ segPtr->q.y = p.y + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Right cap */
+ segPtr->p.x = segPtr->q.x = q.x;
+ segPtr->p.y = q.y - stylePtr->errorBarCapWidth;
+ segPtr->q.y = q.y + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->xeb.segments = bars;
+ elemPtr->xeb.length = segPtr - bars;
+ elemPtr->xeb.map = map;
+ }
+ if (elemPtr->yError.nValues > 0) {
+ n = MIN(elemPtr->yError.nValues, nPoints);
+ } else {
+ n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, nPoints);
+ }
+ if (n > 0) {
+ Segment2d *bars;
+ Segment2d *segPtr;
+ int *map;
+ int *indexPtr;
+ int i;
+
+ segPtr = bars = Blt_AssertMalloc(n * 3 * sizeof(Segment2d));
+ indexPtr = map = Blt_AssertMalloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ double x, y;
+ double high, low;
+ BarStyle *stylePtr;
+
+ x = elemPtr->x.values[i];
+ y = elemPtr->y.values[i];
+ stylePtr = dataToStyle[i];
+ if ((FINITE(x)) && (FINITE(y))) {
+ if (elemPtr->yError.nValues > 0) {
+ high = y + elemPtr->yError.values[i];
+ low = y - elemPtr->yError.values[i];
+ } else {
+ high = elemPtr->yHigh.values[i];
+ low = elemPtr->yLow.values[i];
+ }
+ if ((FINITE(high)) && (FINITE(low))) {
+ Point2d p, q;
+
+ p = Blt_Map2D(graphPtr, x, high, &elemPtr->axes);
+ q = Blt_Map2D(graphPtr, x, low, &elemPtr->axes);
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Top cap. */
+ segPtr->p.y = segPtr->q.y = p.y;
+ segPtr->p.x = p.x - stylePtr->errorBarCapWidth;
+ segPtr->q.x = p.x + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Bottom cap. */
+ segPtr->p.y = segPtr->q.y = q.y;
+ segPtr->p.x = q.x - stylePtr->errorBarCapWidth;
+ segPtr->q.x = q.x + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&reg, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->yeb.segments = bars;
+ elemPtr->yeb.length = segPtr - bars;
+ elemPtr->yeb.map = map;
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapBarProc --
+ *
+ * Calculates the actual window coordinates of the bar element. The
+ * window coordinates are saved in the bar element structure.
+ *
+ * Results:
+ * None.
+ *
+ * Notes:
+ * A bar can have multiple segments (more than one x,y pairs). In this
+ * case, the bar can be represented as either a set of non-contiguous
+ * bars or a single multi-segmented (stacked) bar.
+ *
+ * The x-axis layout for a barchart may be presented in one of two ways.
+ * If abscissas are used, the bars are placed at those coordinates.
+ * Otherwise, the range will represent the number of values.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapBarProc(Graph *graphPtr, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ BarStyle **dataToStyle;
+ double *x, *y;
+ double barWidth, barOffset;
+ double baseline, ybot;
+ int *barToData; /* Maps bars to data point indices */
+ int invertBar;
+ int nPoints, count;
+ XRectangle *rp, *bars;
+ int i;
+ int size;
+
+ ResetBar(elemPtr);
+ nPoints = NUMBEROFPOINTS(elemPtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ barWidth = graphPtr->barWidth;
+ barWidth = (elemPtr->barWidth > 0.0f)
+ ? elemPtr->barWidth : graphPtr->barWidth;
+ baseline = (elemPtr->axes.y->logScale) ? 0.0 : graphPtr->baseline;
+ barOffset = barWidth * 0.5;
+
+ /*
+ * Create an array of bars representing the screen coordinates of all the
+ * segments in the bar.
+ */
+ bars = Blt_AssertCalloc(nPoints, sizeof(XRectangle));
+ barToData = Blt_AssertCalloc(nPoints, sizeof(int));
+
+ x = elemPtr->x.values, y = elemPtr->y.values;
+ count = 0;
+ for (rp = bars, i = 0; i < nPoints; i++) {
+ Point2d c1, c2; /* Two opposite corners of the rectangle
+ * in graph coordinates. */
+ double dx, dy;
+ int height;
+ double right, left, top, bottom;
+
+ if (((x[i] - barWidth) > elemPtr->axes.x->axisRange.max) ||
+ ((x[i] + barWidth) < elemPtr->axes.x->axisRange.min)) {
+ continue; /* Abscissa is out of range of the
+ * x-axis */
+ }
+ c1.x = x[i] - barOffset;
+ c1.y = y[i];
+ c2.x = c1.x + barWidth;
+ c2.y = baseline;
+
+ /*
+ * If the mode is "aligned" or "stacked" we need to adjust the x or y
+ * coordinates of the two corners.
+ */
+
+ if ((graphPtr->nBarGroups > 0) && (graphPtr->mode != BARS_INFRONT) &&
+ (!graphPtr->stackAxes)) {
+ Blt_HashEntry *hPtr;
+ BarSetKey key;
+
+ key.value = (float)x[i];
+ key.axes = elemPtr->axes;
+ key.axes.y = NULL;
+ hPtr = Blt_FindHashEntry(&graphPtr->setTable, (char *)&key);
+ if (hPtr != NULL) {
+ Blt_HashTable *tablePtr;
+ const char *name;
+
+ tablePtr = Blt_GetHashValue(hPtr);
+ name = (elemPtr->groupName != NULL) ? elemPtr->groupName :
+ elemPtr->axes.y->obj.name;
+ hPtr = Blt_FindHashEntry(tablePtr, name);
+ if (hPtr != NULL) {
+ BarGroup *groupPtr;
+ double slice, width, offset;
+
+ groupPtr = Blt_GetHashValue(hPtr);
+ slice = barWidth / (double)graphPtr->maxBarSetSize;
+ offset = (slice * groupPtr->index);
+ if (graphPtr->maxBarSetSize > 1) {
+ offset += slice * 0.05;
+ slice *= 0.90;
+ }
+ switch (graphPtr->mode) {
+ case BARS_STACKED:
+ groupPtr->count++;
+ c2.y = groupPtr->lastY;
+ c1.y += c2.y;
+ groupPtr->lastY = c1.y;
+ c1.x += offset;
+ c2.x = c1.x + slice;
+ break;
+
+ case BARS_ALIGNED:
+ slice /= groupPtr->nSegments;
+ c1.x += offset + (slice * groupPtr->count);
+ c2.x = c1.x + slice;
+ groupPtr->count++;
+ break;
+
+ case BARS_OVERLAP:
+ slice /= (groupPtr->nSegments + 1);
+ width = slice + slice;
+ groupPtr->count++;
+ c1.x += offset +
+ (slice * (groupPtr->nSegments - groupPtr->count));
+ c2.x = c1.x + width;
+ break;
+
+ case BARS_INFRONT:
+ break;
+ }
+ }
+ }
+ }
+ invertBar = FALSE;
+ if (c1.y < c2.y) {
+ double temp;
+
+ /* Handle negative bar values by swapping ordinates */
+ temp = c1.y, c1.y = c2.y, c2.y = temp;
+ invertBar = TRUE;
+ }
+ /*
+ * Get the two corners of the bar segment and compute the rectangle
+ */
+ ybot = c2.y;
+ c1 = Blt_Map2D(graphPtr, c1.x, c1.y, &elemPtr->axes);
+ c2 = Blt_Map2D(graphPtr, c2.x, c2.y, &elemPtr->axes);
+ if ((ybot == 0.0) && (elemPtr->axes.y->logScale)) {
+ c2.y = graphPtr->bottom;
+ }
+
+ if (c2.y < c1.y) {
+ double t;
+ t = c1.y, c1.y = c2.y, c2.y = t;
+ }
+ if (c2.x < c1.x) {
+ double t;
+ t = c1.x, c1.x = c2.x, c2.x = t;
+ }
+ if ((c1.x > graphPtr->right) || (c2.x < graphPtr->left) ||
+ (c1.y > graphPtr->bottom) || (c2.y < graphPtr->top)) {
+ continue;
+ }
+ /* Bound the bars horizontally by the width of the graph window */
+ /* Bound the bars vertically by the position of the axis. */
+ if (graphPtr->stackAxes) {
+ top = elemPtr->axes.y->screenMin;
+ bottom = elemPtr->axes.y->screenMin + elemPtr->axes.y->screenRange;
+ left = graphPtr->left;
+ right = graphPtr->right;
+ } else {
+ left = top = 0;
+ bottom = right = 10000;
+ /* Shouldn't really have a call to Tk_Width or Tk_Height in
+ * mapping routine. We only want to clamp the bar segment to the
+ * size of the window if we're actually mapped onscreen. */
+ if (Tk_Height(graphPtr->tkwin) > 1) {
+ bottom = Tk_Height(graphPtr->tkwin);
+ }
+ if (Tk_Width(graphPtr->tkwin) > 1) {
+ right = Tk_Width(graphPtr->tkwin);
+ }
+ }
+ CLAMP(c1.y, top, bottom);
+ CLAMP(c2.y, top, bottom);
+ CLAMP(c1.x, left, right);
+ CLAMP(c2.x, left, right);
+ dx = FABS(c1.x - c2.x);
+ dy = FABS(c1.y - c2.y);
+ if ((dx == 0) || (dy == 0)) {
+ continue;
+ }
+ height = (int)dy;
+ if (invertBar) {
+ rp->y = (short int)MIN(c1.y, c2.y);
+ } else {
+ rp->y = (short int)(MAX(c1.y, c2.y)) - height;
+ }
+ rp->x = (short int)MIN(c1.x, c2.x);
+ rp->width = (short int)dx + 1;
+ rp->width |= 0x1;
+ if (rp->width < 1) {
+ rp->width = 1;
+ }
+ rp->height = height + 1;
+ if (rp->height < 1) {
+ rp->height = 1;
+ }
+ barToData[count] = i; /* Save the data index corresponding to
+ * the rectangle */
+ count++;
+ rp++;
+ }
+ elemPtr->nBars = count;
+ elemPtr->bars = bars;
+ elemPtr->barToData = barToData;
+ if (elemPtr->nActiveIndices > 0) {
+ MapActiveBars(elemPtr);
+ }
+
+ size = 20;
+ if (count > 0) {
+ size = bars->width;
+ }
+ {
+ Blt_ChainLink link;
+
+ /* Set the symbol size of all the pen styles. */
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->symbolSize = size;
+ stylePtr->errorBarCapWidth =
+ (stylePtr->penPtr->errorBarCapWidth > 0)
+ ? stylePtr->penPtr->errorBarCapWidth : (size * 66666) / 100000;
+ stylePtr->errorBarCapWidth /= 2;
+ }
+ }
+ dataToStyle = (BarStyle **)Blt_StyleMap((Element *)elemPtr);
+ if (((elemPtr->yHigh.nValues > 0) && (elemPtr->yLow.nValues > 0)) ||
+ ((elemPtr->xHigh.nValues > 0) && (elemPtr->xLow.nValues > 0)) ||
+ (elemPtr->xError.nValues > 0) || (elemPtr->yError.nValues > 0)) {
+ MapErrorBars(graphPtr, elemPtr, dataToStyle);
+ }
+ MergePens(elemPtr, dataToStyle);
+ Blt_Free(dataToStyle);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawSymbolProc --
+ *
+ * Draw a symbol centered at the given x,y window coordinate based upon
+ * the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when calculating the
+ * centered position of the symbol.
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawSymbolProc(Graph *graphPtr, Drawable drawable, Element *basePtr,
+ int x, int y, int size)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ BarPen *penPtr;
+ int radius;
+
+ penPtr = NORMALPEN(elemPtr);
+ if ((penPtr->fill == NULL) && (penPtr->outlineColor == NULL)) {
+ return;
+ }
+ radius = (size / 2);
+ size--;
+
+ x -= radius;
+ y -= radius;
+ if (penPtr->fillGC != NULL) {
+ XSetTSOrigin(graphPtr->display, penPtr->fillGC, x, y);
+ }
+ if (penPtr->stipple != None) {
+ XFillRectangle(graphPtr->display, drawable, penPtr->fillGC, x, y,
+ size, size);
+ } else {
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, penPtr->fill,
+ x, y, size, size, penPtr->borderWidth, penPtr->relief);
+ }
+ XDrawRectangle(graphPtr->display, drawable, penPtr->outlineGC, x, y,
+ size, size);
+ if (penPtr->fillGC != NULL) {
+ XSetTSOrigin(graphPtr->display, penPtr->fillGC, 0, 0);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawBarSegments --
+ *
+ * Draws each of the rectangular segments for the element.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawBarSegments(Graph *graphPtr, Drawable drawable, BarPen *penPtr,
+ XRectangle *bars, int nBars)
+{
+ TkRegion rgn;
+
+ {
+ XRectangle clip;
+ clip.x = graphPtr->left;
+ clip.y = graphPtr->top;
+ clip.width = graphPtr->right - graphPtr->left + 1;
+ clip.height = graphPtr->bottom - graphPtr->top + 1;
+ rgn = TkCreateRegion();
+ TkUnionRectWithRegion(&clip, rgn, rgn);
+ }
+ if (penPtr->fill != NULL) {
+ XRectangle *rp, *rend;
+ int hasOutline;
+ int relief;
+
+ relief = (penPtr->relief == TK_RELIEF_SOLID)
+ ? TK_RELIEF_FLAT: penPtr->relief;
+ hasOutline = ((relief == TK_RELIEF_FLAT) &&
+ (penPtr->outlineColor != NULL));
+ if (penPtr->stipple != None) {
+ TkSetRegion(graphPtr->display, penPtr->fillGC, rgn);
+ }
+ Blt_SetBackgroundClipRegion(graphPtr->tkwin, penPtr->fill, rgn);
+ if (hasOutline) {
+ TkSetRegion(graphPtr->display, penPtr->outlineGC, rgn);
+ }
+ for (rp = bars, rend = rp + nBars; rp < rend; rp++) {
+ if (penPtr->stipple != None) {
+ XFillRectangle(graphPtr->display, drawable, penPtr->fillGC,
+ rp->x, rp->y, rp->width, rp->height);
+ } else {
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable,
+ penPtr->fill, rp->x, rp->y, rp->width, rp->height,
+ penPtr->borderWidth, relief);
+ }
+ if (hasOutline) {
+ XDrawRectangle(graphPtr->display, drawable, penPtr->outlineGC,
+ rp->x, rp->y, rp->width, rp->height);
+ }
+ }
+ Blt_UnsetBackgroundClipRegion(graphPtr->tkwin, penPtr->fill);
+ if (hasOutline) {
+ XSetClipMask(graphPtr->display, penPtr->outlineGC, None);
+ }
+ if (penPtr->stipple != None) {
+ XSetClipMask(graphPtr->display, penPtr->fillGC, None);
+ }
+ } else if (penPtr->outlineColor != NULL) {
+ TkSetRegion(graphPtr->display, penPtr->outlineGC, rgn);
+ XDrawRectangles(graphPtr->display, drawable, penPtr->outlineGC, bars,
+ nBars);
+ XSetClipMask(graphPtr->display, penPtr->outlineGC, None);
+ }
+ TkDestroyRegion(rgn);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawBarValues --
+ *
+ * Draws the numeric value of the bar.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawBarValues(Graph *graphPtr, Drawable drawable, BarElement *elemPtr,
+ BarPen *penPtr, XRectangle *bars, int nBars, int *barToData)
+{
+ XRectangle *rp, *rend;
+ int count;
+ const char *fmt;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (rp = bars, rend = rp + nBars; rp < rend; rp++) {
+ Point2d anchorPos;
+ double x, y;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+
+ x = elemPtr->x.values[barToData[count]];
+ y = elemPtr->y.values[barToData[count]];
+
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ strcat(string, ",");
+ sprintf_s(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y);
+ }
+ if (graphPtr->inverted) {
+ anchorPos.y = rp->y + rp->height * 0.5;
+ anchorPos.x = rp->x + rp->width;
+ if (x < graphPtr->baseline) {
+ anchorPos.x -= rp->width;
+ }
+ } else {
+ anchorPos.x = rp->x + rp->width * 0.5;
+ anchorPos.y = rp->y;
+ if (y < graphPtr->baseline) {
+ anchorPos.y += rp->height;
+ }
+ }
+ Blt_DrawText(graphPtr->tkwin, drawable, string, &penPtr->valueStyle,
+ (int)anchorPos.x, (int)anchorPos.y);
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawNormalBar --
+ *
+ * Draws the rectangle representing the bar element. If the relief
+ * option is set to "raised" or "sunken" and the bar borderwidth is set
+ * (borderwidth > 0), a 3D border is drawn around the bar.
+ *
+ * Don't draw bars that aren't visible (i.e. within the limits of the
+ * axis).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawNormalBarProc(Graph *graphPtr, Drawable drawable, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ int count;
+ Blt_ChainLink link;
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+ BarPen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = stylePtr->penPtr;
+ if (stylePtr->nBars > 0) {
+ DrawBarSegments(graphPtr, drawable, penPtr, stylePtr->bars,
+ stylePtr->nBars);
+ }
+ if ((stylePtr->xeb.length > 0) && (penPtr->errorBarShow & SHOW_X)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC,
+ stylePtr->xeb.segments, stylePtr->xeb.length);
+ }
+ if ((stylePtr->yeb.length > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC,
+ stylePtr->yeb.segments, stylePtr->yeb.length);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawBarValues(graphPtr, drawable, elemPtr, penPtr,
+ stylePtr->bars, stylePtr->nBars,
+ elemPtr->barToData + count);
+ }
+ count += stylePtr->nBars;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawActiveBar --
+ *
+ * Draws bars representing the active segments of the bar element. If
+ * the -relief option is set (other than "flat") and the borderwidth is
+ * greater than 0, a 3D border is drawn around the each bar segment.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawActiveBarProc(Graph *graphPtr, Drawable drawable, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+
+ if (elemPtr->activePenPtr != NULL) {
+ BarPen *penPtr = elemPtr->activePenPtr;
+
+ if (elemPtr->nActiveIndices > 0) {
+ if (elemPtr->flags & ACTIVE_PENDING) {
+ MapActiveBars(elemPtr);
+ }
+ DrawBarSegments(graphPtr, drawable, penPtr, elemPtr->activeRects,
+ elemPtr->nActive);
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawBarValues(graphPtr, drawable, elemPtr, penPtr,
+ elemPtr->activeRects, elemPtr->nActive,
+ elemPtr->activeToData);
+ }
+ } else if (elemPtr->nActiveIndices < 0) {
+ DrawBarSegments(graphPtr, drawable, penPtr, elemPtr->bars,
+ elemPtr->nBars);
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawBarValues(graphPtr, drawable, elemPtr, penPtr,
+ elemPtr->bars, elemPtr->nBars, elemPtr->barToData);
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * Draw a symbol centered at the given x,y window coordinate based upon
+ * the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when calculating the
+ * centered position of the symbol.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+SymbolToPostScriptProc(
+ Graph *graphPtr,
+ Blt_Ps ps,
+ Element *basePtr,
+ double x, double y,
+ int size)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ BarPen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ if ((penPtr->fill == NULL) && (penPtr->outlineColor == NULL)) {
+ return;
+ }
+ /*
+ * Build a PostScript procedure to draw the fill and outline of the symbol
+ * after the path of the symbol shape has been formed
+ */
+ Blt_Ps_Append(ps, "\n"
+ "/DrawSymbolProc {\n"
+ "gsave\n ");
+ if (penPtr->stipple != None) {
+ if (penPtr->fill != NULL) {
+ Blt_Ps_XSetBackground(ps, Blt_BackgroundBorderColor(penPtr->fill));
+ Blt_Ps_Append(ps, " gsave fill grestore\n ");
+ }
+ if (penPtr->outlineColor != NULL) {
+ Blt_Ps_XSetForeground(ps, penPtr->outlineColor);
+ } else {
+ Blt_Ps_XSetForeground(ps, Blt_BackgroundBorderColor(penPtr->fill));
+ }
+ Blt_Ps_XSetStipple(ps, graphPtr->display, penPtr->stipple);
+ } else if (penPtr->outlineColor != NULL) {
+ Blt_Ps_XSetForeground(ps, penPtr->outlineColor);
+ Blt_Ps_Append(ps, " fill\n");
+ }
+ Blt_Ps_Append(ps, " grestore\n");
+ Blt_Ps_Append(ps, "} def\n\n");
+ Blt_Ps_Format(ps, "%g %g %d Sq\n", x, y, size);
+}
+
+static void
+SegmentsToPostScript(Graph *graphPtr, Blt_Ps ps, BarPen *penPtr,
+ XRectangle *bars, int nBars)
+{
+ XRectangle *rp, *rend;
+
+ if ((penPtr->fill == NULL) && (penPtr->outlineColor == NULL)) {
+ return;
+ }
+ for (rp = bars, rend = rp + nBars; rp < rend; rp++) {
+ if ((rp->width < 1) || (rp->height < 1)) {
+ continue;
+ }
+ if (penPtr->stipple != None) {
+ Blt_Ps_Rectangle(ps, rp->x, rp->y, rp->width - 1, rp->height - 1);
+ if (penPtr->fill != NULL) {
+ Blt_Ps_XSetBackground(ps,Blt_BackgroundBorderColor(penPtr->fill));
+ Blt_Ps_Append(ps, "gsave fill grestore\n");
+ }
+ if (penPtr->outlineColor != NULL) {
+ Blt_Ps_XSetForeground(ps, penPtr->outlineColor);
+ } else {
+ Blt_Ps_XSetForeground(ps,Blt_BackgroundBorderColor(penPtr->fill));
+ }
+ Blt_Ps_XSetStipple(ps, graphPtr->display, penPtr->stipple);
+ } else if (penPtr->outlineColor != NULL) {
+ Blt_Ps_XSetForeground(ps, penPtr->outlineColor);
+ Blt_Ps_XFillRectangle(ps, (double)rp->x, (double)rp->y,
+ (int)rp->width - 1, (int)rp->height - 1);
+ }
+ if ((penPtr->fill != NULL) && (penPtr->borderWidth > 0) &&
+ (penPtr->relief != TK_RELIEF_FLAT)) {
+ Blt_Ps_Draw3DRectangle(ps, Blt_BackgroundBorder(penPtr->fill),
+ (double)rp->x, (double)rp->y, (int)rp->width, (int)rp->height,
+ penPtr->borderWidth, penPtr->relief);
+ }
+ }
+}
+
+static void
+BarValuesToPostScript(Graph *graphPtr, Blt_Ps ps, BarElement *elemPtr,
+ BarPen *penPtr, XRectangle *bars, int nBars,
+ int *barToData)
+{
+ XRectangle *rp, *rend;
+ int count;
+ const char *fmt;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ double x, y;
+ Point2d anchorPos;
+
+ count = 0;
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ for (rp = bars, rend = rp + nBars; rp < rend; rp++) {
+ x = elemPtr->x.values[barToData[count]];
+ y = elemPtr->y.values[barToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ strcat(string, ",");
+ sprintf_s(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y);
+ }
+ if (graphPtr->inverted) {
+ anchorPos.y = rp->y + rp->height * 0.5;
+ anchorPos.x = rp->x + rp->width;
+ if (x < graphPtr->baseline) {
+ anchorPos.x -= rp->width;
+ }
+ } else {
+ anchorPos.x = rp->x + rp->width * 0.5;
+ anchorPos.y = rp->y;
+ if (y < graphPtr->baseline) {
+ anchorPos.y += rp->height;
+ }
+ }
+ Blt_Ps_DrawText(ps, string, &penPtr->valueStyle, anchorPos.x,
+ anchorPos.y);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ActiveBarToPostScript --
+ *
+ * Similar to the NormalBarToPostScript procedure, generates PostScript
+ * commands to display the bars representing the active bar segments of
+ * the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+ActiveBarToPostScriptProc(Graph *graphPtr, Blt_Ps ps, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+
+ if (elemPtr->activePenPtr != NULL) {
+ BarPen *penPtr = elemPtr->activePenPtr;
+
+ if (elemPtr->nActiveIndices > 0) {
+ if (elemPtr->flags & ACTIVE_PENDING) {
+ MapActiveBars(elemPtr);
+ }
+ SegmentsToPostScript(graphPtr, ps, penPtr, elemPtr->activeRects,
+ elemPtr->nActive);
+ if (penPtr->valueShow != SHOW_NONE) {
+ BarValuesToPostScript(graphPtr, ps, elemPtr, penPtr,
+ elemPtr->activeRects, elemPtr->nActive, elemPtr->activeToData);
+ }
+ } else if (elemPtr->nActiveIndices < 0) {
+ SegmentsToPostScript(graphPtr, ps, penPtr, elemPtr->bars,
+ elemPtr->nBars);
+ if (penPtr->valueShow != SHOW_NONE) {
+ BarValuesToPostScript(graphPtr, ps, elemPtr, penPtr,
+ elemPtr->bars, elemPtr->nBars, elemPtr->barToData);
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NormalBarToPostScript --
+ *
+ * Generates PostScript commands to form the bars representing the
+ * segments of the bar element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+NormalBarToPostScriptProc(Graph *graphPtr, Blt_Ps ps, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+ Blt_ChainLink link;
+ int count;
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ BarStyle *stylePtr;
+ BarPen *penPtr;
+ XColor *colorPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = stylePtr->penPtr;
+ if (stylePtr->nBars > 0) {
+ SegmentsToPostScript(graphPtr, ps, penPtr, stylePtr->bars,
+ stylePtr->nBars);
+ }
+ colorPtr = penPtr->errorBarColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = penPtr->outlineColor;
+ }
+ if ((stylePtr->xeb.length > 0) && (penPtr->errorBarShow & SHOW_X)) {
+ Blt_Ps_XSetLineAttributes(ps, colorPtr, penPtr->errorBarLineWidth,
+ NULL, CapButt, JoinMiter);
+ Blt_Ps_Draw2DSegments(ps, stylePtr->xeb.segments,
+ stylePtr->xeb.length);
+ }
+ if ((stylePtr->yeb.length > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+ Blt_Ps_XSetLineAttributes(ps, colorPtr, penPtr->errorBarLineWidth,
+ NULL, CapButt, JoinMiter);
+ Blt_Ps_Draw2DSegments(ps, stylePtr->yeb.segments,
+ stylePtr->yeb.length);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ BarValuesToPostScript(graphPtr, ps, elemPtr, penPtr,
+ stylePtr->bars, stylePtr->nBars, elemPtr->barToData + count);
+ }
+ count += stylePtr->nBars;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyBar --
+ *
+ * Release memory and resources allocated for the bar element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the bar element is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+DestroyBarProc(Graph *graphPtr, Element *basePtr)
+{
+ BarElement *elemPtr = (BarElement *)basePtr;
+
+ DestroyBarPen(graphPtr, elemPtr->builtinPenPtr);
+ if (elemPtr->activePenPtr != NULL) {
+ Blt_FreePen((Pen *)elemPtr->activePenPtr);
+ }
+ ResetBar(elemPtr);
+ if (elemPtr->stylePalette != NULL) {
+ Blt_FreeStylePalette(elemPtr->stylePalette);
+ Blt_Chain_Destroy(elemPtr->stylePalette);
+ }
+ if (elemPtr->activeIndices != NULL) {
+ Blt_Free(elemPtr->activeIndices);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_BarElement --
+ *
+ * Allocate memory and initialize methods for the new bar element.
+ *
+ * Results:
+ * The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the bar element structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static ElementProcs barProcs = {
+ ClosestBarProc,
+ ConfigureBarProc,
+ DestroyBarProc,
+ DrawActiveBarProc,
+ DrawNormalBarProc,
+ DrawSymbolProc,
+ GetBarExtentsProc,
+ ActiveBarToPostScriptProc,
+ NormalBarToPostScriptProc,
+ SymbolToPostScriptProc,
+ MapBarProc,
+};
+
+
+Element *
+Blt_BarElement(Graph *graphPtr, const char *name, ClassId classId)
+{
+ BarElement *elemPtr;
+
+ elemPtr = Blt_AssertCalloc(1, sizeof(BarElement));
+ elemPtr->procsPtr = &barProcs;
+ elemPtr->configSpecs = barElemConfigSpecs;
+ elemPtr->legendRelief = TK_RELIEF_FLAT;
+ Blt_GraphSetObjectClass(&elemPtr->obj, classId);
+ elemPtr->obj.name = Blt_AssertStrdup(name);
+ elemPtr->obj.graphPtr = graphPtr;
+ /* By default, an element's name and label are the same. */
+ elemPtr->label = Blt_AssertStrdup(name);
+ elemPtr->builtinPenPtr = &elemPtr->builtinPen;
+ InitializeBarPen(elemPtr->builtinPenPtr);
+ elemPtr->stylePalette = Blt_Chain_Create();
+ bltBarStylesOption.clientData = (ClientData)sizeof(BarStyle);
+ return (Element *)elemPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_InitBarSetTable --
+ *
+ * Generate a table of abscissa frequencies. Duplicate x-coordinates
+ * (depending upon the bar drawing mode) indicate that something special
+ * should be done with each bar segment mapped to the same abscissa
+ * (i.e. it should be stacked, aligned, or overlay-ed with other segments)
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the bar element structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_InitBarSetTable(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+ int nStacks, nSegs;
+ Blt_HashTable setTable;
+ int sum, max;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ /*
+ * Free resources associated with a previous frequency table. This includes
+ * the array of frequency information and the table itself
+ */
+ Blt_DestroyBarSets(graphPtr);
+ if (graphPtr->mode == BARS_INFRONT) {
+ return; /* No set table is needed for
+ * "infront" mode */
+ }
+ Blt_InitHashTable(&graphPtr->setTable, sizeof(BarSetKey) / sizeof(int));
+
+ /*
+ * Initialize a hash table and fill it with unique abscissas. Keep track
+ * of the frequency of each x-coordinate and how many abscissas have
+ * duplicate mappings.
+ */
+ Blt_InitHashTable(&setTable, sizeof(BarSetKey) / sizeof(int));
+ nSegs = nStacks = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ BarElement *elemPtr;
+ double *x, *xend;
+ int nPoints;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((elemPtr->flags & HIDE) || (elemPtr->obj.classId != CID_ELEM_BAR)) {
+ continue;
+ }
+ nSegs++;
+ nPoints = NUMBEROFPOINTS(elemPtr);
+ for (x = elemPtr->x.values, xend = x + nPoints; x < xend; x++) {
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ BarSetKey key;
+ int isNew;
+ size_t count;
+ const char *name;
+
+ key.value = *x;
+ key.axes = elemPtr->axes;
+ key.axes.y = NULL;
+ hPtr = Blt_CreateHashEntry(&setTable, (char *)&key, &isNew);
+ if (isNew) {
+ tablePtr = Blt_AssertMalloc(sizeof(Blt_HashTable));
+ Blt_InitHashTable(tablePtr, BLT_STRING_KEYS);
+ Blt_SetHashValue(hPtr, tablePtr);
+ } else {
+ tablePtr = Blt_GetHashValue(hPtr);
+ }
+ name = (elemPtr->groupName != NULL) ? elemPtr->groupName :
+ elemPtr->axes.y->obj.name;
+ hPtr = Blt_CreateHashEntry(tablePtr, name, &isNew);
+ if (isNew) {
+ count = 1;
+ } else {
+ count = (size_t)Blt_GetHashValue(hPtr);
+ count++;
+ }
+ Blt_SetHashValue(hPtr, (ClientData)count);
+ }
+ }
+ if (setTable.numEntries == 0) {
+ return; /* No bar elements to be displayed */
+ }
+ sum = max = 0;
+ for (hPtr = Blt_FirstHashEntry(&setTable, &iter); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&iter)) {
+ Blt_HashTable *tablePtr;
+ Blt_HashEntry *hPtr2;
+ BarSetKey *keyPtr;
+ int isNew;
+
+ keyPtr = (BarSetKey *)Blt_GetHashKey(&setTable, hPtr);
+ hPtr2 = Blt_CreateHashEntry(&graphPtr->setTable, (char *)keyPtr,&isNew);
+ tablePtr = Blt_GetHashValue(hPtr);
+ Blt_SetHashValue(hPtr2, tablePtr);
+ if (max < tablePtr->numEntries) {
+ max = tablePtr->numEntries; /* # of stacks in group. */
+ }
+ sum += tablePtr->numEntries;
+ }
+ Blt_DeleteHashTable(&setTable);
+ if (sum > 0) {
+ BarGroup *groupPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ graphPtr->barGroups = Blt_AssertCalloc(sum, sizeof(BarGroup));
+ groupPtr = graphPtr->barGroups;
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->setTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ BarSetKey *keyPtr;
+ Blt_HashTable *tablePtr;
+ Blt_HashEntry *hPtr2;
+ Blt_HashSearch iter2;
+ size_t xcount;
+
+ tablePtr = Blt_GetHashValue(hPtr);
+ keyPtr = (BarSetKey *)Blt_GetHashKey(&setTable, hPtr);
+ xcount = 0;
+ for (hPtr2 = Blt_FirstHashEntry(tablePtr, &iter2); hPtr2!=NULL;
+ hPtr2 = Blt_NextHashEntry(&iter2)) {
+ size_t count;
+
+ count = (size_t)Blt_GetHashValue(hPtr2);
+ groupPtr->nSegments = count;
+ groupPtr->axes = keyPtr->axes;
+ Blt_SetHashValue(hPtr2, groupPtr);
+ groupPtr->index = xcount++;
+ groupPtr++;
+ }
+ }
+ }
+ graphPtr->maxBarSetSize = max;
+ graphPtr->nBarGroups = sum;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ComputeStacks --
+ *
+ * Determine the height of each stack of bar segments. A stack is created
+ * by designating two or more points with the same abscissa. Each ordinate
+ * defines the height of a segment in the stack. This procedure simply
+ * looks at all the data points summing the heights of each stacked
+ * segment. The sum is saved in the frequency information table. This
+ * value will be used to calculate the y-axis limits (data limits aren't
+ * sufficient).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The heights of each stack is computed. CheckBarGroups will use this
+ * information to adjust the y-axis limits if necessary.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ComputeBarStacks(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ if ((graphPtr->mode != BARS_STACKED) || (graphPtr->nBarGroups == 0)) {
+ return;
+ }
+
+ /* Initialize the stack sums to zero. */
+ {
+ BarGroup *gp, *gend;
+
+ for (gp = graphPtr->barGroups, gend = gp + graphPtr->nBarGroups;
+ gp < gend; gp++) {
+ gp->sum = 0.0;
+ }
+ }
+
+ /* Consider each bar x-y coordinate. Add the ordinates of duplicate
+ * abscissas. */
+
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ BarElement *elemPtr;
+ double *x, *y, *xend;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((elemPtr->flags & HIDE) || (elemPtr->obj.classId != CID_ELEM_BAR)) {
+ continue;
+ }
+ for (x = elemPtr->x.values, y = elemPtr->y.values,
+ xend = x + NUMBEROFPOINTS(elemPtr); x < xend; x++, y++) {
+ BarSetKey key;
+ BarGroup *groupPtr;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ const char *name;
+
+ key.value = *x;
+ key.axes = elemPtr->axes;
+ key.axes.y = NULL;
+ hPtr = Blt_FindHashEntry(&graphPtr->setTable, (char *)&key);
+ if (hPtr == NULL) {
+ continue;
+ }
+ tablePtr = Blt_GetHashValue(hPtr);
+ name = (elemPtr->groupName != NULL) ? elemPtr->groupName :
+ elemPtr->axes.y->obj.name;
+ hPtr = Blt_FindHashEntry(tablePtr, name);
+ if (hPtr == NULL) {
+ continue;
+ }
+ groupPtr = Blt_GetHashValue(hPtr);
+ groupPtr->sum += *y;
+ }
+ }
+}
+
+void
+Blt_ResetBarGroups(Graph *graphPtr)
+{
+ BarGroup *gp, *gend;
+
+ for (gp = graphPtr->barGroups, gend = gp + graphPtr->nBarGroups; gp < gend;
+ gp++) {
+ gp->lastY = 0.0;
+ gp->count = 0;
+ }
+}
+
+void
+Blt_DestroyBarSets(Graph *graphPtr)
+{
+ Blt_HashSearch iter;
+ Blt_HashEntry *hPtr;
+
+ if (graphPtr->barGroups != NULL) {
+ Blt_Free(graphPtr->barGroups);
+ graphPtr->barGroups = NULL;
+ }
+ graphPtr->nBarGroups = 0;
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->setTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Blt_HashTable *tablePtr;
+
+ tablePtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashTable(tablePtr);
+ Blt_Free(tablePtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->setTable);
+ Blt_InitHashTable(&graphPtr->setTable, sizeof(BarSetKey) / sizeof(int));
+}
diff --git a/src/bltGrElemLine.C b/src/bltGrElemLine.C
new file mode 100644
index 0000000..82234da
--- /dev/null
+++ b/src/bltGrElemLine.C
@@ -0,0 +1,5347 @@
+
+/*
+ * bltGrLine.c --
+ *
+ * This module implements line graph and stripchart elements for the BLT graph
+ * widget.
+ *
+ * Copyright (c) 1993 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+#include "bltGrElem.h"
+#include "tkDisplay.h"
+#include "bltBgStyle.h"
+#include "bltImage.h"
+#include "bltBitmap.h"
+
+#define COLOR_DEFAULT (XColor *)1
+#define PATTERN_SOLID ((Pixmap)1)
+
+#define PEN_INCREASING 1 /* Draw line segments for only those
+ * data points whose abscissas are
+ * monotonically increasing in
+ * order. */
+#define PEN_DECREASING 2 /* Lines will be drawn between only
+ * those points whose abscissas are
+ * decreasing in order. */
+
+#define PEN_BOTH_DIRECTIONS (PEN_INCREASING | PEN_DECREASING)
+
+/* Lines will be drawn between points regardless of the ordering of the
+ * abscissas */
+
+#define BROKEN_TRACE(dir,last,next) \
+ (((((dir) & PEN_DECREASING) == 0) && ((next) < (last))) || \
+ ((((dir) & PEN_INCREASING) == 0) && ((next) > (last))))
+
+#define DRAW_SYMBOL(linePtr) \
+ (((linePtr)->symbolCounter % (linePtr)->symbolInterval) == 0)
+
+typedef enum {
+ PEN_SMOOTH_LINEAR, /* Line segments */
+ PEN_SMOOTH_STEP, /* Step-and-hold */
+ PEN_SMOOTH_NATURAL, /* Natural cubic spline */
+ PEN_SMOOTH_QUADRATIC, /* Quadratic spline */
+ PEN_SMOOTH_CATROM, /* Catrom parametric spline */
+ PEN_SMOOTH_LAST /* Sentinel */
+} Smoothing;
+
+typedef struct {
+ const char *name;
+ Smoothing value;
+} SmoothingInfo;
+
+static SmoothingInfo smoothingInfo[] = {
+ { "none", PEN_SMOOTH_LINEAR },
+ { "linear", PEN_SMOOTH_LINEAR },
+ { "step", PEN_SMOOTH_STEP },
+ { "natural", PEN_SMOOTH_NATURAL },
+ { "cubic", PEN_SMOOTH_NATURAL },
+ { "quadratic", PEN_SMOOTH_QUADRATIC },
+ { "catrom", PEN_SMOOTH_CATROM },
+ { (char *)NULL, PEN_SMOOTH_LAST }
+};
+
+
+typedef struct {
+ Point2d *screenPts; /* Array of transformed coordinates */
+ int nScreenPts; /* Number of coordinates */
+ int *styleMap; /* Index of pen styles */
+ int *map; /* Maps segments/traces to data
+ * points */
+} MapInfo;
+
+/* Symbol types for line elements */
+typedef enum {
+ SYMBOL_NONE,
+ SYMBOL_SQUARE,
+ SYMBOL_CIRCLE,
+ SYMBOL_DIAMOND,
+ SYMBOL_PLUS,
+ SYMBOL_CROSS,
+ SYMBOL_SPLUS,
+ SYMBOL_SCROSS,
+ SYMBOL_TRIANGLE,
+ SYMBOL_ARROW,
+ SYMBOL_BITMAP,
+ SYMBOL_IMAGE
+} SymbolType;
+
+typedef struct {
+ const char *name;
+ unsigned int minChars;
+ SymbolType type;
+} GraphSymbolType;
+
+static GraphSymbolType graphSymbols[] = {
+ { "arrow", 1, SYMBOL_ARROW, },
+ { "circle", 2, SYMBOL_CIRCLE, },
+ { "cross", 2, SYMBOL_CROSS, },
+ { "diamond", 1, SYMBOL_DIAMOND, },
+ { "none", 1, SYMBOL_NONE, },
+ { "plus", 1, SYMBOL_PLUS, },
+ { "scross", 2, SYMBOL_SCROSS, },
+ { "splus", 2, SYMBOL_SPLUS, },
+ { "square", 2, SYMBOL_SQUARE, },
+ { "triangle", 1, SYMBOL_TRIANGLE, },
+ { NULL, 0, 0 },
+};
+
+typedef struct {
+ SymbolType type; /* Type of symbol to be drawn/printed */
+
+ int size; /* Requested size of symbol in pixels */
+
+ XColor *outlineColor; /* Outline color */
+
+ int outlineWidth; /* Width of the outline */
+
+ GC outlineGC; /* Outline graphics context */
+
+ XColor *fillColor; /* Normal fill color */
+
+ GC fillGC; /* Fill graphics context */
+
+ Tk_Image image; /* This is used of image symbols. */
+
+ /* The last two fields are used only for bitmap symbols. */
+
+ Pixmap bitmap; /* Bitmap to determine
+ * foreground/background pixels of the
+ * symbol */
+ Pixmap mask; /* Bitmap representing the transparent
+ * pixels of the symbol */
+} Symbol;
+
+typedef struct {
+ int start; /* Index into the X-Y coordinate arrays
+ * indicating where trace starts. */
+ GraphPoints screenPts; /* Array of screen coordinates
+ * (malloc-ed) representing the
+ * trace. */
+} Trace;
+
+typedef struct {
+ const char *name; /* Pen style identifier. If NULL pen
+ * was statically allocated. */
+ ClassId classId; /* Type of pen */
+ const char *typeId; /* String token identifying the type of
+ * pen */
+ unsigned int flags; /* Indicates if the pen element is
+ * active or normal */
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+ Graph *graphPtr; /* Graph that the pen is associated
+ * with. */
+
+ /* Symbol attributes. */
+ Symbol symbol; /* Element symbol type */
+
+ /* Trace attributes. */
+ int traceWidth; /* Width of the line segments. If
+ * lineWidth is 0, no line will be
+ * drawn, only symbols. */
+
+ Blt_Dashes traceDashes; /* Dash on-off list value */
+
+ XColor *traceColor; /* Line segment color */
+
+ XColor *traceOffColor; /* Line segment dash gap color */
+
+ GC traceGC; /* Line segment graphics context */
+
+ /* Error bar attributes. */
+ int errorBarShow; /* Describes which error bars to display:
+ * none, x, y, or * both. */
+
+ int errorBarLineWidth; /* Width of the error bar segments. */
+
+ int errorBarCapWidth; /* Width of the cap on error bars. */
+
+ XColor *errorBarColor; /* Color of the error bar. */
+
+ GC errorBarGC; /* Error bar graphics context. */
+
+ /* Show value attributes. */
+ int valueShow; /* Indicates whether to display data
+ * value. Values are x, y, both, or
+ * none. */
+ const char *valueFormat; /* A printf format string. */
+
+ TextStyle valueStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the value. */
+} LinePen;
+
+typedef struct {
+ Weight weight; /* Weight range where this pen is
+ * valid. */
+ LinePen *penPtr; /* Pen to use. */
+ GraphPoints symbolPts;
+
+ GraphSegments lines; /* Points to start of the line segments
+ * for this pen. */
+ GraphSegments xeb, yeb; /* X and Y axis error bars. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to
+ * the current graph size. */
+ int errorBarCapWidth; /* Length of the cap ends on each error
+ * bar. */
+} LineStyle;
+
+typedef struct {
+ GraphObj obj; /* Must be first field in element. */
+ unsigned int flags;
+ Blt_HashEntry *hashPtr;
+
+ /* Fields specific to elements. */
+ const char *label; /* Label displayed in legend */
+ unsigned short row, col; /* Position of the entry in the
+ * legend. */
+ int legendRelief; /* Relief of label in legend. */
+ Axis2d axes; /* X-axis and Y-axis mapping the
+ * element */
+ ElemValues x, y, w; /* Contains array of floating point
+ * graph coordinate values. Also holds
+ * min/max * and the number of
+ * coordinates */
+ int *activeIndices; /* Array of indices (malloc-ed) which
+ * indicate which data points are active
+ * (drawn * with "active" colors). */
+ int nActiveIndices; /* Number of active data points.
+ * Special case: if nActiveIndices < 0
+ * and the * active bit is set in
+ * "flags", then all data points are
+ * drawn active. */
+ ElementProcs *procsPtr;
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications. */
+ LinePen *activePenPtr; /* Standard Pens */
+ LinePen *normalPenPtr;
+ LinePen *builtinPenPtr;
+ Blt_Chain styles; /* Palette of pens. */
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+ int state;
+ Blt_ChainLink link; /* Element's link in display list. */
+
+ /* The line element specific fields start here. */
+
+ ElemValues xError; /* Relative/symmetric X error values. */
+ ElemValues yError; /* Relative/symmetric Y error values. */
+ ElemValues xHigh, xLow; /* Absolute/asymmetric X-coordinate
+ * high/low error values. */
+ ElemValues yHigh, yLow; /* Absolute/asymmetric Y-coordinate
+ * high/low error values. */
+ LinePen builtinPen;
+ int errorBarCapWidth; /* Length of cap on error bars */
+
+ /* Line smoothing */
+ Smoothing reqSmooth; /* Requested smoothing function to use
+ * for connecting the data points */
+ Smoothing smooth; /* Smoothing function used. */
+ float rTolerance; /* Tolerance to reduce the number of
+ * points displayed. */
+
+ /* Drawing-related data structures. */
+
+ /* Area-under-curve fill attributes. */
+ XColor *fillFgColor;
+ XColor *fillBgColor;
+ GC fillGC;
+
+ Blt_Background fillBg; /* Background for fill area. */
+
+ Point2d *fillPts; /* Array of points used to draw polygon
+ * to fill area under the curve */
+ int nFillPts;
+
+ /* Symbol points */
+ GraphPoints symbolPts;
+
+ /* Active symbol points */
+ GraphPoints activePts;
+ GraphSegments xeb, yeb; /* Point to start of this pen's X-error
+ * bar segments in the element's
+ * array. */
+ int reqMaxSymbols;
+ int symbolInterval;
+ int symbolCounter;
+
+ /* X-Y graph-specific fields */
+
+ int penDir; /* Indicates if a change in the pen
+ * direction should be considered a
+ * retrace (line segment is not
+ * drawn). */
+ Blt_Chain traces; /* List of traces (a trace is a series
+ * of contiguous line segments). New
+ * traces are generated when either
+ * the next segment changes the pen
+ * direction, or the end point is
+ * clipped by the plotting area. */
+
+ /* Stripchart-specific fields */
+
+ GraphSegments lines; /* Holds the the line segments of the
+ * element trace. The segments are
+ * grouped by pen style. */
+} LineElement;
+
+static Blt_OptionParseProc ObjToSmoothProc;
+static Blt_OptionPrintProc SmoothToObjProc;
+static Blt_CustomOption smoothOption =
+{
+ ObjToSmoothProc, SmoothToObjProc, NULL, (ClientData)0
+};
+
+static Blt_OptionParseProc ObjToPenDirProc;
+static Blt_OptionPrintProc PenDirToObjProc;
+static Blt_CustomOption penDirOption =
+{
+ ObjToPenDirProc, PenDirToObjProc, NULL, (ClientData)0
+};
+
+static Blt_OptionFreeProc FreeSymbolProc;
+static Blt_OptionParseProc ObjToSymbolProc;
+static Blt_OptionPrintProc SymbolToObjProc;
+static Blt_CustomOption symbolOption =
+{
+ ObjToSymbolProc, SymbolToObjProc, FreeSymbolProc, (ClientData)0
+};
+
+BLT_EXTERN Blt_CustomOption bltLineStylesOption;
+BLT_EXTERN Blt_CustomOption bltColorOption;
+BLT_EXTERN Blt_CustomOption bltValuesOption;
+BLT_EXTERN Blt_CustomOption bltValuePairsOption;
+BLT_EXTERN Blt_CustomOption bltLinePenOption;
+BLT_EXTERN Blt_CustomOption bltXAxisOption;
+BLT_EXTERN Blt_CustomOption bltYAxisOption;
+
+#define DEF_LINE_ACTIVE_PEN "activeLine"
+#define DEF_LINE_AXIS_X "x"
+#define DEF_LINE_AXIS_Y "y"
+#define DEF_LINE_DASHES (char *)NULL
+#define DEF_LINE_DATA (char *)NULL
+#define DEF_LINE_FILL_COLOR "defcolor"
+#define DEF_LINE_HIDE "no"
+#define DEF_LINE_LABEL (char *)NULL
+#define DEF_LINE_LABEL_RELIEF "flat"
+#define DEF_LINE_MAX_SYMBOLS "0"
+#define DEF_LINE_OFFDASH_COLOR (char *)NULL
+#define DEF_LINE_OUTLINE_COLOR "defcolor"
+#define DEF_LINE_OUTLINE_WIDTH "1"
+#define DEF_LINE_PATTERN_BG (char *)NULL
+#define DEF_LINE_PATTERN_FG "black"
+#define DEF_LINE_PEN_COLOR RGB_NAVYBLUE
+#define DEF_LINE_PEN_DIRECTION "both"
+#define DEF_LINE_PEN_WIDTH "1"
+#define DEF_LINE_PIXELS "0.1i"
+#define DEF_LINE_REDUCE "0.0"
+#define DEF_LINE_SCALE_SYMBOLS "yes"
+#define DEF_LINE_SMOOTH "linear"
+#define DEF_LINE_STATE "normal"
+#define DEF_LINE_STIPPLE (char *)NULL
+#define DEF_LINE_STYLES ""
+#define DEF_LINE_SYMBOL "circle"
+#define DEF_LINE_TAGS "all"
+#define DEF_LINE_X_DATA (char *)NULL
+#define DEF_LINE_Y_DATA (char *)NULL
+
+#define DEF_LINE_ERRORBAR_COLOR "defcolor"
+#define DEF_LINE_ERRORBAR_LINE_WIDTH "2"
+#define DEF_LINE_ERRORBAR_CAP_WIDTH "2"
+#define DEF_LINE_SHOW_ERRORBARS "both"
+
+#define DEF_PEN_ACTIVE_COLOR RGB_BLUE
+#define DEF_PEN_DASHES (char *)NULL
+#define DEF_PEN_FILL_COLOR "defcolor"
+#define DEF_PEN_LINE_WIDTH "1"
+#define DEF_PEN_NORMAL_COLOR RGB_NAVYBLUE
+#define DEF_PEN_OFFDASH_COLOR (char *)NULL
+#define DEF_PEN_OUTLINE_COLOR "defcolor"
+#define DEF_PEN_OUTLINE_WIDTH "1"
+#define DEF_PEN_PIXELS "0.1i"
+#define DEF_PEN_SYMBOL "circle"
+#define DEF_PEN_TYPE "line"
+#define DEF_PEN_VALUE_ANCHOR "s"
+#define DEF_PEN_VALUE_COLOR RGB_BLACK
+#define DEF_PEN_VALUE_FONT STD_FONT_NUMBERS
+#define DEF_PEN_VALUE_FORMAT "%g"
+#define DEF_PEN_VALUE_ANGLE (char *)NULL
+#define DEF_PEN_SHOW_VALUES "no"
+
+static Blt_ConfigSpec lineElemConfigSpecs[] =
+{
+ {BLT_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Blt_Offset(LineElement, activePenPtr),
+ BLT_CONFIG_NULL_OK, &bltLinePenOption},
+ {BLT_CONFIG_COLOR, "-areaforeground", "areaForeground", "AreaForeground",
+ DEF_LINE_PATTERN_FG, Blt_Offset(LineElement, fillFgColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BACKGROUND, "-areabackground", "areaBackground",
+ "AreaBackground", DEF_LINE_PATTERN_BG, Blt_Offset(LineElement, fillBg),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_LINE_TAGS,
+ Blt_Offset(LineElement, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-color", "color", "Color", DEF_LINE_PEN_COLOR,
+ Blt_Offset(LineElement, builtinPen.traceColor), 0},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes", DEF_LINE_DASHES,
+ Blt_Offset(LineElement, builtinPen.traceDashes), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-data", "data", "Data", DEF_LINE_DATA, 0, 0,
+ &bltValuePairsOption},
+ {BLT_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR,
+ Blt_Offset(LineElement, builtinPen.errorBarColor), 0, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_LINE_WIDTH,
+ Blt_Offset(LineElement, builtinPen.errorBarLineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarcap", "errorBarCap", "ErrorBarCap",
+ DEF_LINE_ERRORBAR_CAP_WIDTH,
+ Blt_Offset(LineElement, builtinPen.errorBarCapWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_LINE_FILL_COLOR,
+ Blt_Offset(LineElement, builtinPen.symbol.fillColor),
+ BLT_CONFIG_NULL_OK, &bltColorOption},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_LINE_HIDE,
+ Blt_Offset(LineElement, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_STRING, "-label", "label", "Label", (char *)NULL,
+ Blt_Offset(LineElement, label), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_RELIEF, "-legendrelief", "legendRelief", "LegendRelief",
+ DEF_LINE_LABEL_RELIEF, Blt_Offset(LineElement, legendRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Blt_Offset(LineElement, builtinPen.traceWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_LINE_AXIS_X, Blt_Offset(LineElement, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_LINE_AXIS_Y, Blt_Offset(LineElement, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_INT_NNEG, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Blt_Offset(LineElement, reqMaxSymbols),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR,
+ Blt_Offset(LineElement, builtinPen.traceOffColor),
+ BLT_CONFIG_NULL_OK, &bltColorOption},
+ {BLT_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR,
+ Blt_Offset(LineElement, builtinPen.symbol.outlineColor),
+ 0, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH,
+ Blt_Offset(LineElement, builtinPen.symbol.outlineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-pen", "pen", "Pen", (char *)NULL,
+ Blt_Offset(LineElement, normalPenPtr), BLT_CONFIG_NULL_OK,
+ &bltLinePenOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-pixels", "pixels", "Pixels", DEF_LINE_PIXELS,
+ Blt_Offset(LineElement, builtinPen.symbol.size), GRAPH | STRIPCHART},
+ {BLT_CONFIG_FLOAT, "-reduce", "reduce", "Reduce",
+ DEF_LINE_REDUCE, Blt_Offset(LineElement, rTolerance),
+ GRAPH | STRIPCHART | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Blt_Offset(LineElement, scaleSymbols),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS,
+ Blt_Offset(LineElement, builtinPen.errorBarShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Blt_Offset(LineElement, builtinPen.valueShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth", DEF_LINE_SMOOTH,
+ Blt_Offset(LineElement, reqSmooth), BLT_CONFIG_DONT_SET_DEFAULT,
+ &smoothOption},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_LINE_STATE,
+ Blt_Offset(LineElement, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-styles", "styles", "Styles", DEF_LINE_STYLES,
+ Blt_Offset(LineElement, styles), 0, &bltLineStylesOption},
+ {BLT_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol", DEF_LINE_SYMBOL,
+ Blt_Offset(LineElement, builtinPen.symbol),
+ BLT_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {BLT_CONFIG_CUSTOM, "-trace", "trace", "Trace", DEF_LINE_PEN_DIRECTION,
+ Blt_Offset(LineElement, penDir),
+ BLT_CONFIG_DONT_SET_DEFAULT, &penDirOption},
+ {BLT_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Blt_Offset(LineElement, builtinPen.valueStyle.anchor), 0},
+ {BLT_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR,
+ Blt_Offset(LineElement, builtinPen.valueStyle.color), 0},
+ {BLT_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT,
+ Blt_Offset(LineElement, builtinPen.valueStyle.font), 0},
+ {BLT_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Blt_Offset(LineElement, builtinPen.valueFormat),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ANGLE,
+ Blt_Offset(LineElement, builtinPen.valueStyle.angle), 0},
+ {BLT_CONFIG_CUSTOM, "-weights", "weights", "Weights", (char *)NULL,
+ Blt_Offset(LineElement, w), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-x", "xData", "XData", (char *)NULL,
+ Blt_Offset(LineElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xdata", "xData", "XData", (char *)NULL,
+ Blt_Offset(LineElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL,
+ Blt_Offset(LineElement, xError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL,
+ Blt_Offset(LineElement, xHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL,
+ Blt_Offset(LineElement, xLow), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-y", "yData", "YData", (char *)NULL,
+ Blt_Offset(LineElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ydata", "yData", "YData", (char *)NULL,
+ Blt_Offset(LineElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL,
+ Blt_Offset(LineElement, yError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL,
+ Blt_Offset(LineElement, yHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL,
+ Blt_Offset(LineElement, yLow), 0, &bltValuesOption},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Blt_ConfigSpec stripElemConfigSpecs[] =
+{
+ {BLT_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Blt_Offset(LineElement, activePenPtr),
+ BLT_CONFIG_NULL_OK, &bltLinePenOption},
+ {BLT_CONFIG_COLOR, "-areaforeground", "areaForeground", "areaForeground",
+ DEF_LINE_PATTERN_FG, Blt_Offset(LineElement, fillFgColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BACKGROUND, "-areabackground", "areaBackground",
+ "areaBackground", DEF_LINE_PATTERN_BG, Blt_Offset(LineElement, fillBg),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_LINE_TAGS,
+ Blt_Offset(LineElement, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_COLOR, Blt_Offset(LineElement, builtinPen.traceColor), 0},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes",
+ DEF_LINE_DASHES, Blt_Offset(LineElement, builtinPen.traceDashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-data", "data", "Data", DEF_LINE_DATA, 0, 0,
+ &bltValuePairsOption},
+ {BLT_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR,
+ Blt_Offset(LineElement, builtinPen.errorBarColor), 0, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_LINE_WIDTH,
+ Blt_Offset(LineElement, builtinPen.errorBarLineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarcap", "errorBarCap", "ErrorBarCap",
+ DEF_LINE_ERRORBAR_CAP_WIDTH,
+ Blt_Offset(LineElement, builtinPen.errorBarCapWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_LINE_FILL_COLOR,
+ Blt_Offset(LineElement, builtinPen.symbol.fillColor),
+ BLT_CONFIG_NULL_OK, &bltColorOption},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_LINE_HIDE,
+ Blt_Offset(LineElement, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_STRING, "-label", "label", "Label", (char *)NULL,
+ Blt_Offset(LineElement, label), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_RELIEF, "-legendrelief", "legendRelief", "LegendRelief",
+ DEF_LINE_LABEL_RELIEF, Blt_Offset(LineElement, legendRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Blt_Offset(LineElement, builtinPen.traceWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_LINE_AXIS_X,
+ Blt_Offset(LineElement, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_LINE_AXIS_Y,
+ Blt_Offset(LineElement, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_INT_NNEG, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Blt_Offset(LineElement, reqMaxSymbols),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR, Blt_Offset(LineElement, builtinPen.traceOffColor),
+ BLT_CONFIG_NULL_OK, &bltColorOption},
+ {BLT_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR,
+ Blt_Offset(LineElement, builtinPen.symbol.outlineColor), 0,
+ &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH,
+ Blt_Offset(LineElement, builtinPen.symbol.outlineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-pen", "pen", "Pen", (char *)NULL,
+ Blt_Offset(LineElement, normalPenPtr), BLT_CONFIG_NULL_OK,
+ &bltLinePenOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-pixels", "pixels", "Pixels", DEF_LINE_PIXELS,
+ Blt_Offset(LineElement, builtinPen.symbol.size), 0},
+ {BLT_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Blt_Offset(LineElement, scaleSymbols),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS,
+ Blt_Offset(LineElement, builtinPen.errorBarShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Blt_Offset(LineElement, builtinPen.valueShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth", DEF_LINE_SMOOTH,
+ Blt_Offset(LineElement, reqSmooth), BLT_CONFIG_DONT_SET_DEFAULT,
+ &smoothOption},
+ {BLT_CONFIG_CUSTOM, "-styles", "styles", "Styles", DEF_LINE_STYLES,
+ Blt_Offset(LineElement, styles), 0, &bltLineStylesOption},
+ {BLT_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol", DEF_LINE_SYMBOL,
+ Blt_Offset(LineElement, builtinPen.symbol),
+ BLT_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {BLT_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Blt_Offset(LineElement, builtinPen.valueStyle.anchor), 0},
+ {BLT_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR,
+ Blt_Offset(LineElement, builtinPen.valueStyle.color), 0},
+ {BLT_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT,
+ Blt_Offset(LineElement, builtinPen.valueStyle.font), 0},
+ {BLT_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Blt_Offset(LineElement, builtinPen.valueFormat),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ANGLE,
+ Blt_Offset(LineElement, builtinPen.valueStyle.angle),0},
+ {BLT_CONFIG_CUSTOM, "-weights", "weights", "Weights", (char *)NULL,
+ Blt_Offset(LineElement, w), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-x", "xData", "XData", (char *)NULL,
+ Blt_Offset(LineElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xdata", "xData", "XData", (char *)NULL,
+ Blt_Offset(LineElement, x), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-y", "yData", "YData", (char *)NULL,
+ Blt_Offset(LineElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL,
+ Blt_Offset(LineElement, xError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ydata", "yData", "YData", (char *)NULL,
+ Blt_Offset(LineElement, y), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL,
+ Blt_Offset(LineElement, yError), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL,
+ Blt_Offset(LineElement, xHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL,
+ Blt_Offset(LineElement, xLow), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL,
+ Blt_Offset(LineElement, xHigh), 0, &bltValuesOption},
+ {BLT_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL,
+ Blt_Offset(LineElement, yLow), 0, &bltValuesOption},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Blt_ConfigSpec linePenConfigSpecs[] =
+{
+ {BLT_CONFIG_COLOR, "-color", "color", "Color", DEF_PEN_ACTIVE_COLOR,
+ Blt_Offset(LinePen, traceColor), ACTIVE_PEN},
+ {BLT_CONFIG_COLOR, "-color", "color", "Color", DEF_PEN_NORMAL_COLOR,
+ Blt_Offset(LinePen, traceColor), NORMAL_PEN},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes", DEF_PEN_DASHES,
+ Blt_Offset(LinePen, traceDashes), BLT_CONFIG_NULL_OK | ALL_PENS},
+ {BLT_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Blt_Offset(LinePen, errorBarColor),
+ ALL_PENS, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_LINE_WIDTH, Blt_Offset(LinePen, errorBarLineWidth),
+ ALL_PENS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-errorbarcap", "errorBarCap", "ErrorBarCap",
+ DEF_LINE_ERRORBAR_CAP_WIDTH, Blt_Offset(LinePen, errorBarCapWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_PEN_FILL_COLOR,
+ Blt_Offset(LinePen, symbol.fillColor), BLT_CONFIG_NULL_OK | ALL_PENS,
+ &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ (char *)NULL, Blt_Offset(LinePen, traceWidth),
+ ALL_PENS| BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash", DEF_PEN_OFFDASH_COLOR,
+ Blt_Offset(LinePen, traceOffColor), BLT_CONFIG_NULL_OK | ALL_PENS,
+ &bltColorOption},
+ {BLT_CONFIG_CUSTOM, "-outline", "outline", "Outline", DEF_PEN_OUTLINE_COLOR,
+ Blt_Offset(LinePen, symbol.outlineColor), ALL_PENS, &bltColorOption},
+ {BLT_CONFIG_PIXELS_NNEG, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_PEN_OUTLINE_WIDTH, Blt_Offset(LinePen, symbol.outlineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT | ALL_PENS},
+ {BLT_CONFIG_PIXELS_NNEG, "-pixels", "pixels", "Pixels", DEF_PEN_PIXELS,
+ Blt_Offset(LinePen, symbol.size), ALL_PENS},
+ {BLT_CONFIG_FILL, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Blt_Offset(LinePen, errorBarShow),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FILL, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Blt_Offset(LinePen, valueShow),
+ ALL_PENS | BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol", DEF_PEN_SYMBOL,
+ Blt_Offset(LinePen, symbol), BLT_CONFIG_DONT_SET_DEFAULT | ALL_PENS,
+ &symbolOption},
+ {BLT_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL, DEF_PEN_TYPE,
+ Blt_Offset(Pen, typeId), ALL_PENS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Blt_Offset(LinePen, valueStyle.anchor), ALL_PENS},
+ {BLT_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Blt_Offset(LinePen, valueStyle.color), ALL_PENS},
+ {BLT_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Blt_Offset(LinePen, valueStyle.font), ALL_PENS},
+ {BLT_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Blt_Offset(LinePen, valueFormat),
+ ALL_PENS | BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_FLOAT, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ANGLE, Blt_Offset(LinePen, valueStyle.angle), ALL_PENS},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef double (DistanceProc)(int x, int y, Point2d *p, Point2d *q, Point2d *t);
+
+/* Forward declarations */
+static PenConfigureProc ConfigurePenProc;
+static PenDestroyProc DestroyPenProc;
+static ElementClosestProc ClosestLineProc;
+static ElementConfigProc ConfigureLineProc;
+static ElementDestroyProc DestroyLineProc;
+static ElementDrawProc DrawActiveLineProc;
+static ElementDrawProc DrawNormalLineProc;
+static ElementDrawSymbolProc DrawSymbolProc;
+static ElementExtentsProc GetLineExtentsProc;
+static ElementToPostScriptProc ActiveLineToPostScriptProc;
+static ElementToPostScriptProc NormalLineToPostScriptProc;
+static ElementSymbolToPostScriptProc SymbolToPostScriptProc;
+static ElementMapProc MapLineProc;
+static DistanceProc DistanceToYProc;
+static DistanceProc DistanceToXProc;
+static DistanceProc DistanceToLineProc;
+static Blt_BackgroundChangedProc BackgroundChangedProc;
+
+#ifdef WIN32
+
+static int tkpWinRopModes[] =
+{
+ R2_BLACK, /* GXclear */
+ R2_MASKPEN, /* GXand */
+ R2_MASKPENNOT, /* GXandReverse */
+ R2_COPYPEN, /* GXcopy */
+ R2_MASKNOTPEN, /* GXandInverted */
+ R2_NOT, /* GXnoop */
+ R2_XORPEN, /* GXxor */
+ R2_MERGEPEN, /* GXor */
+ R2_NOTMERGEPEN, /* GXnor */
+ R2_NOTXORPEN, /* GXequiv */
+ R2_NOT, /* GXinvert */
+ R2_MERGEPENNOT, /* GXorReverse */
+ R2_NOTCOPYPEN, /* GXcopyInverted */
+ R2_MERGENOTPEN, /* GXorInverted */
+ R2_NOTMASKPEN, /* GXnand */
+ R2_WHITE /* GXset */
+};
+
+#endif
+
+#ifndef notdef
+INLINE static int
+Round(double x)
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+#else
+#define Round Round
+#endif
+/*
+ *---------------------------------------------------------------------------
+ * Custom configuration option (parse and print) routines
+ *---------------------------------------------------------------------------
+ */
+
+static void
+DestroySymbol(Display *display, Symbol *symbolPtr)
+{
+ if (symbolPtr->image != NULL) {
+ Tk_FreeImage(symbolPtr->image);
+ symbolPtr->image = NULL;
+ }
+ if (symbolPtr->bitmap != None) {
+ Tk_FreeBitmap(display, symbolPtr->bitmap);
+ symbolPtr->bitmap = None;
+ }
+ if (symbolPtr->mask != None) {
+ Tk_FreeBitmap(display, symbolPtr->mask);
+ symbolPtr->mask = None;
+ }
+ symbolPtr->type = SYMBOL_NONE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(
+ ClientData clientData,
+ int x, int y, int w, int h, /* Not used. */
+ int imageWidth, int imageHeight) /* Not used. */
+{
+ Element *elemPtr;
+ Graph *graphPtr;
+
+ elemPtr = clientData;
+ elemPtr->flags |= MAP_ITEM;
+ graphPtr = elemPtr->obj.graphPtr;
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*ARGSUSED*/
+static void
+FreeSymbolProc(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+
+ DestroySymbol(display, symbolPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToSymbol --
+ *
+ * Convert the string representation of a line style or symbol name into
+ * its numeric form.
+ *
+ * Results:
+ * The return value is a standard TCL result. The symbol type is written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSymbolProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing symbol type */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+ const char *string;
+
+ {
+ int length;
+ GraphSymbolType *p;
+ char c;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ if (length == 0) {
+ DestroySymbol(Tk_Display(tkwin), symbolPtr);
+ symbolPtr->type = SYMBOL_NONE;
+ return TCL_OK;
+ }
+ c = string[0];
+ for (p = graphSymbols; p->name != NULL; p++) {
+ if (length < p->minChars) {
+ continue;
+ }
+ if ((c == p->name[0]) && (strncmp(string, p->name, length) == 0)) {
+ DestroySymbol(Tk_Display(tkwin), symbolPtr);
+ symbolPtr->type = p->type;
+ return TCL_OK;
+ }
+ }
+ }
+ {
+ Tk_Image tkImage;
+ Element *elemPtr = (Element *)widgRec;
+
+ tkImage = Tk_GetImage(interp, tkwin, string, ImageChangedProc, elemPtr);
+ if (tkImage != NULL) {
+ DestroySymbol(Tk_Display(tkwin), symbolPtr);
+ symbolPtr->image = tkImage;
+ symbolPtr->type = SYMBOL_IMAGE;
+ return TCL_OK;
+ }
+ }
+ {
+ Pixmap bitmap, mask;
+ Tcl_Obj **objv;
+ int objc;
+
+ if ((Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK) ||
+ (objc > 2)) {
+ goto error;
+ }
+ bitmap = mask = None;
+ if (objc > 0) {
+ bitmap = Tk_AllocBitmapFromObj((Tcl_Interp *)NULL, tkwin, objv[0]);
+ if (bitmap == None) {
+ goto error;
+ }
+ }
+ if (objc > 1) {
+ mask = Tk_AllocBitmapFromObj((Tcl_Interp *)NULL, tkwin, objv[1]);
+ if (mask == None) {
+ goto error;
+ }
+ }
+ DestroySymbol(Tk_Display(tkwin), symbolPtr);
+ symbolPtr->bitmap = bitmap;
+ symbolPtr->mask = mask;
+ symbolPtr->type = SYMBOL_BITMAP;
+ return TCL_OK;
+ }
+ error:
+ Tcl_AppendResult(interp, "bad symbol \"", string,
+ "\": should be \"none\", \"circle\", \"square\", \"diamond\", "
+ "\"plus\", \"cross\", \"splus\", \"scross\", \"triangle\", "
+ "\"arrow\" or the name of a bitmap", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SymbolToObj --
+ *
+ * Convert the symbol value into a string.
+ *
+ * Results:
+ * The string representing the symbol type or line style is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SymbolToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+
+ if (symbolPtr->type == SYMBOL_BITMAP) {
+ Tcl_Obj *listObjPtr, *objPtr;
+ const char *name;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ name = Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->bitmap);
+ objPtr = Tcl_NewStringObj(name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ if (symbolPtr->mask == None) {
+ objPtr = Tcl_NewStringObj("", -1);
+ } else {
+ name = Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->mask);
+ objPtr = Tcl_NewStringObj(name, -1);
+ }
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ return listObjPtr;
+ } else {
+ GraphSymbolType *p;
+
+ for (p = graphSymbols; p->name != NULL; p++) {
+ if (p->type == symbolPtr->type) {
+ return Tcl_NewStringObj(p->name, -1);
+ }
+ }
+ return Tcl_NewStringObj("?unknown symbol type?", -1);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NameOfSmooth --
+ *
+ * Converts the smooth value into its string representation.
+ *
+ * Results:
+ * The static string representing the smooth type is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static const char *
+NameOfSmooth(Smoothing value)
+{
+ SmoothingInfo *siPtr;
+
+ for (siPtr = smoothingInfo; siPtr->name != NULL; siPtr++) {
+ if (siPtr->value == value) {
+ return siPtr->name;
+ }
+ }
+ return "unknown smooth value";
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToSmooth --
+ *
+ * Convert the string representation of a line style or smooth name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard TCL result. The smooth type is
+ * written into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSmoothProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing smooth type */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Smoothing *valuePtr = (Smoothing *)(widgRec + offset);
+ SmoothingInfo *siPtr;
+ const char *string;
+ char c;
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ for (siPtr = smoothingInfo; siPtr->name != NULL; siPtr++) {
+ if ((c == siPtr->name[0]) && (strcmp(string, siPtr->name) == 0)) {
+ *valuePtr = siPtr->value;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "bad smooth value \"", string, "\": should be \
+linear, step, natural, or quadratic", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SmoothToObj --
+ *
+ * Convert the smooth value into a string.
+ *
+ * Results:
+ * The string representing the smooth type or line style is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SmoothToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int smooth = *(int *)(widgRec + offset);
+
+ return Tcl_NewStringObj(NameOfSmooth(smooth), -1);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPenDir --
+ *
+ * Convert the string representation of a line style or symbol name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard TCL result. The symbol type is
+ * written into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToPenDirProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing pen direction */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int *penDirPtr = (int *)(widgRec + offset);
+ int length;
+ char c;
+ char *string;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if ((c == 'i') && (strncmp(string, "increasing", length) == 0)) {
+ *penDirPtr = PEN_INCREASING;
+ } else if ((c == 'd') && (strncmp(string, "decreasing", length) == 0)) {
+ *penDirPtr = PEN_DECREASING;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *penDirPtr = PEN_BOTH_DIRECTIONS;
+ } else {
+ Tcl_AppendResult(interp, "bad trace value \"", string,
+ "\" : should be \"increasing\", \"decreasing\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NameOfPenDir --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The static string representing the pen direction is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static const char *
+NameOfPenDir(int penDir)
+{
+ switch (penDir) {
+ case PEN_INCREASING:
+ return "increasing";
+ case PEN_DECREASING:
+ return "decreasing";
+ case PEN_BOTH_DIRECTIONS:
+ return "both";
+ default:
+ return "unknown trace direction";
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PenDirToObj --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The string representing the pen drawing direction is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PenDirToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int penDir = *(int *)(widgRec + offset);
+
+ return Tcl_NewStringObj(NameOfPenDir(penDir), -1);
+}
+
+
+/*
+ * Reset the number of points and segments, in case there are no segments or
+ * points
+ */
+static void
+ResetStylePalette(Blt_Chain styles)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->lines.length = stylePtr->symbolPts.length = 0;
+ stylePtr->xeb.length = stylePtr->yeb.length = 0;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigurePenProc --
+ *
+ * Sets up the appropriate configuration parameters in the GC. It is
+ * assumed the parameters have been previously set by a call to
+ * Blt_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard TCL result. If TCL_ERROR is returned,
+ * then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePenProc(Graph *graphPtr, Pen *penPtr)
+{
+ LinePen *lpPtr = (LinePen *)penPtr;
+ unsigned long gcMask;
+ GC newGC;
+ XGCValues gcValues;
+ XColor *colorPtr;
+
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.outlineColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.foreground = colorPtr->pixel;
+ if (lpPtr->symbol.type == SYMBOL_BITMAP) {
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ /*
+ * Set a clip mask if either
+ * 1) no background color was designated or
+ * 2) a masking bitmap was specified.
+ *
+ * These aren't necessarily the bitmaps we'll be using for clipping. But
+ * this makes it unlikely that anyone else will be sharing this GC when
+ * we set the clip origin (at the time the bitmap is drawn).
+ */
+ if (colorPtr != NULL) {
+ gcValues.background = colorPtr->pixel;
+ gcMask |= GCBackground;
+ if (lpPtr->symbol.mask != None) {
+ gcValues.clip_mask = lpPtr->symbol.mask;
+ gcMask |= GCClipMask;
+ }
+ } else {
+ gcValues.clip_mask = lpPtr->symbol.bitmap;
+ gcMask |= GCClipMask;
+ }
+ }
+ gcValues.line_width = LineWidth(lpPtr->symbol.outlineWidth);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+ }
+ lpPtr->symbol.outlineGC = newGC;
+
+ /* Fill GC for symbols: GCForeground is fill color */
+
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ newGC = NULL;
+ if (colorPtr != NULL) {
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (lpPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+ }
+ lpPtr->symbol.fillGC = newGC;
+
+ /* Line segments */
+
+ gcMask = (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle |
+ GCJoinStyle);
+ gcValues.cap_style = CapButt;
+ gcValues.join_style = JoinRound;
+ gcValues.line_style = LineSolid;
+ gcValues.line_width = LineWidth(lpPtr->traceWidth);
+
+ colorPtr = lpPtr->traceOffColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ if (colorPtr != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = colorPtr->pixel;
+ }
+ gcValues.foreground = lpPtr->traceColor->pixel;
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ gcValues.line_width = lpPtr->traceWidth;
+ gcValues.line_style =
+ (colorPtr == NULL) ? LineOnOffDash : LineDoubleDash;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+ }
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ lpPtr->traceDashes.offset = lpPtr->traceDashes.values[0] / 2;
+ Blt_SetDashes(graphPtr->display, newGC, &lpPtr->traceDashes);
+ }
+ lpPtr->traceGC = newGC;
+
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->errorBarColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.line_width = LineWidth(lpPtr->errorBarLineWidth);
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->errorBarGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->errorBarGC);
+ }
+ lpPtr->errorBarGC = newGC;
+
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyPenProc --
+ *
+ * Release memory and resources allocated for the style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the pen style is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DestroyPenProc(Graph *graphPtr, Pen *basePtr)
+{
+ LinePen *penPtr = (LinePen *)basePtr;
+
+ Blt_Ts_FreeStyle(graphPtr->display, &penPtr->valueStyle);
+ if (penPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->symbol.outlineGC);
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->symbol.fillGC);
+ }
+ if (penPtr->errorBarGC != NULL) {
+ Tk_FreeGC(graphPtr->display, penPtr->errorBarGC);
+ }
+ if (penPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, penPtr->traceGC);
+ }
+ if (penPtr->symbol.bitmap != None) {
+ Tk_FreeBitmap(graphPtr->display, penPtr->symbol.bitmap);
+ penPtr->symbol.bitmap = None;
+ }
+ if (penPtr->symbol.mask != None) {
+ Tk_FreeBitmap(graphPtr->display, penPtr->symbol.mask);
+ penPtr->symbol.mask = None;
+ }
+}
+
+
+static void
+InitLinePen(LinePen *penPtr)
+{
+ Blt_Ts_InitStyle(penPtr->valueStyle);
+ penPtr->errorBarLineWidth = 2;
+ penPtr->errorBarShow = SHOW_BOTH;
+ penPtr->configProc = ConfigurePenProc;
+ penPtr->configSpecs = linePenConfigSpecs;
+ penPtr->destroyProc = DestroyPenProc;
+ penPtr->flags = NORMAL_PEN;
+ penPtr->name = "";
+ penPtr->symbol.bitmap = penPtr->symbol.mask = None;
+ penPtr->symbol.outlineColor = penPtr->symbol.fillColor = COLOR_DEFAULT;
+ penPtr->symbol.outlineWidth = penPtr->traceWidth = 1;
+ penPtr->symbol.type = SYMBOL_CIRCLE;
+ penPtr->valueShow = SHOW_NONE;
+}
+
+Pen *
+Blt_LinePen(const char *penName)
+{
+ LinePen *penPtr;
+
+ penPtr = Blt_AssertCalloc(1, sizeof(LinePen));
+ InitLinePen(penPtr);
+ penPtr->name = Blt_AssertStrdup(penName);
+ penPtr->classId = CID_ELEM_LINE;
+ if (strcmp(penName, "activeLine") == 0) {
+ penPtr->flags = ACTIVE_PEN;
+ }
+ return (Pen *)penPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * In this section, the routines deal with building and filling the
+ * element's data structures with transformed screen coordinates. They are
+ * triggered from TranformLine which is called whenever the data or
+ * coordinates axes have changed and new screen coordinates need to be
+ * calculated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ScaleSymbol --
+ *
+ * Returns the scaled size for the line element. Scaling depends upon when
+ * the base line ranges for the element were set and the current range of
+ * the graph.
+ *
+ * Results:
+ * The new size of the symbol, after considering how much the graph has
+ * been scaled, is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ScaleSymbol(LineElement *elemPtr, int normalSize)
+{
+ int maxSize;
+ double scale;
+ int newSize;
+
+ scale = 1.0;
+ if (elemPtr->scaleSymbols) {
+ double xRange, yRange;
+
+ xRange = (elemPtr->axes.x->max - elemPtr->axes.x->min);
+ yRange = (elemPtr->axes.y->max - elemPtr->axes.y->min);
+ if (elemPtr->flags & SCALE_SYMBOL) {
+ /* Save the ranges as a baseline for future scaling. */
+ elemPtr->xRange = xRange;
+ elemPtr->yRange = yRange;
+ elemPtr->flags &= ~SCALE_SYMBOL;
+ } else {
+ double xScale, yScale;
+
+ /* Scale the symbol by the smallest change in the X or Y axes */
+ xScale = elemPtr->xRange / xRange;
+ yScale = elemPtr->yRange / yRange;
+ scale = MIN(xScale, yScale);
+ }
+ }
+ newSize = Round(normalSize * scale);
+
+ /*
+ * Don't let the size of symbols go unbounded. Both X and Win32 drawing
+ * routines assume coordinates to be a signed short int.
+ */
+ maxSize = (int)MIN(elemPtr->obj.graphPtr->hRange,
+ elemPtr->obj.graphPtr->vRange);
+ if (newSize > maxSize) {
+ newSize = maxSize;
+ }
+
+ /* Make the symbol size odd so that its center is a single pixel. */
+ newSize |= 0x01;
+ return newSize;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetScreenPoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates from
+ * the data points. Coordinates with Inf, -Inf, or NaN values are
+ * removed.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *
+ * Future ideas:
+ * Allow bad values to be removed (as done currently) or break
+ * into separate traces. Smoothing would be affected.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GetScreenPoints(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ double *x, *y;
+ int i, np;
+ int count;
+ Point2d *points;
+ int *map;
+
+ np = NUMBEROFPOINTS(elemPtr);
+ x = elemPtr->x.values;
+ y = elemPtr->y.values;
+ points = Blt_AssertMalloc(sizeof(Point2d) * np);
+ map = Blt_AssertMalloc(sizeof(int) * np);
+
+ count = 0; /* Count the valid screen coordinates */
+ if (graphPtr->inverted) {
+ for (i = 0; i < np; i++) {
+ if ((FINITE(x[i])) && (FINITE(y[i]))) {
+ points[count].x = Blt_HMap(elemPtr->axes.y, y[i]);
+ points[count].y = Blt_VMap(elemPtr->axes.x, x[i]);
+ map[count] = i;
+ count++;
+ }
+ }
+ } else {
+ for (i = 0; i < np; i++) {
+ if ((FINITE(x[i])) && (FINITE(y[i]))) {
+ points[count].x = Blt_HMap(elemPtr->axes.x, x[i]);
+ points[count].y = Blt_VMap(elemPtr->axes.y, y[i]);
+ map[count] = i;
+ count++;
+ }
+ }
+ }
+ mapPtr->screenPts = points;
+ mapPtr->nScreenPts = count;
+ mapPtr->map = map;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ReducePoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates from
+ * the data points.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ReducePoints(MapInfo *mapPtr, double tolerance)
+{
+ int i, np;
+ Point2d *screenPts;
+ int *map, *simple;
+
+ simple = Blt_AssertMalloc(mapPtr->nScreenPts * sizeof(int));
+ map = Blt_AssertMalloc(mapPtr->nScreenPts * sizeof(int));
+ screenPts = Blt_AssertMalloc(mapPtr->nScreenPts * sizeof(Point2d));
+ np = Blt_SimplifyLine(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1,
+ tolerance, simple);
+ for (i = 0; i < np; i++) {
+ int k;
+
+ k = simple[i];
+ screenPts[i] = mapPtr->screenPts[k];
+ map[i] = mapPtr->map[k];
+ }
+#ifdef notdef
+ if (np < mapPtr->nScreenPts) {
+ fprintf(stderr, "reduced from %d to %d\n", mapPtr->nScreenPts, np);
+ }
+#endif
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->map);
+ Blt_Free(simple);
+ mapPtr->screenPts = screenPts;
+ mapPtr->map = map;
+ mapPtr->nScreenPts = np;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GenerateSteps --
+ *
+ * Resets the coordinate and pen index arrays adding new points for
+ * step-and-hold type smoothing.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and pen indices
+ * are updated.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GenerateSteps(MapInfo *mapPtr)
+{
+ int newSize;
+ int i, count;
+ Point2d *screenPts;
+ int *map;
+
+ newSize = ((mapPtr->nScreenPts - 1) * 2) + 1;
+ screenPts = Blt_AssertMalloc(newSize * sizeof(Point2d));
+ map = Blt_AssertMalloc(sizeof(int) * newSize);
+ screenPts[0] = mapPtr->screenPts[0];
+ map[0] = 0;
+
+ count = 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++) {
+ screenPts[count + 1] = mapPtr->screenPts[i];
+
+ /* Hold last y-coordinate, use new x-coordinate */
+ screenPts[count].x = screenPts[count + 1].x;
+ screenPts[count].y = screenPts[count - 1].y;
+
+ /* Use the same style for both the hold and the step points */
+ map[count] = map[count + 1] = mapPtr->map[i];
+ count += 2;
+ }
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->map);
+ mapPtr->map = map;
+ mapPtr->screenPts = screenPts;
+ mapPtr->nScreenPts = newSize;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GenerateSpline --
+ *
+ * Computes a spline based upon the data points, returning a new (larger)
+ * coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data map are updated
+ * based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GenerateSpline(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Point2d *origPts, *iPts;
+ int *map;
+ int extra;
+ int niPts, nOrigPts;
+ int result;
+ int i, j, count;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ if (origPts[j].x <= origPts[i].x) {
+ return; /* Points are not monotonically
+ * increasing */
+ }
+ }
+ if (((origPts[0].x > (double)graphPtr->right)) ||
+ ((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr->left))) {
+ return; /* All points are clipped */
+ }
+
+ /*
+ * The spline is computed in screen coordinates instead of data points so
+ * that we can select the abscissas of the interpolated points from each
+ * pixel horizontally across the plotting area.
+ */
+ extra = (graphPtr->right - graphPtr->left) + 1;
+ if (extra < 1) {
+ return;
+ }
+ niPts = nOrigPts + extra + 1;
+ iPts = Blt_AssertMalloc(niPts * sizeof(Point2d));
+ map = Blt_AssertMalloc(sizeof(int) * niPts);
+ /* Populate the x2 array with both the original X-coordinates and extra
+ * X-coordinates for each horizontal pixel that the line segment
+ * contains. */
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+
+ /* Add the original x-coordinate */
+ iPts[count].x = origPts[i].x;
+
+ /* Include the starting offset of the point in the offset array */
+ map[count] = mapPtr->map[i];
+ count++;
+
+ /* Is any part of the interval (line segment) in the plotting area? */
+ if ((origPts[j].x >= (double)graphPtr->left) ||
+ (origPts[i].x <= (double)graphPtr->right)) {
+ double x, last;
+
+ x = origPts[i].x + 1.0;
+
+ /*
+ * Since the line segment may be partially clipped on the left or
+ * right side, the points to interpolate are always interior to
+ * the plotting area.
+ *
+ * left right
+ * x1----|---------------------------|---x2
+ *
+ * Pick the max of the starting X-coordinate and the left edge and
+ * the min of the last X-coordinate and the right edge.
+ */
+ x = MAX(x, (double)graphPtr->left);
+ last = MIN(origPts[j].x, (double)graphPtr->right);
+
+ /* Add the extra x-coordinates to the interval. */
+ while (x < last) {
+ map[count] = mapPtr->map[i];
+ iPts[count++].x = x;
+ x++;
+ }
+ }
+ }
+ niPts = count;
+ result = FALSE;
+ if (elemPtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalSpline(origPts, nOrigPts, iPts, niPts);
+ } else if (elemPtr->smooth == PEN_SMOOTH_QUADRATIC) {
+ result = Blt_QuadraticSpline(origPts, nOrigPts, iPts, niPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We'll fallback to the current
+ * coordinates and do no smoothing (standard line segments). */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ Blt_Free(iPts);
+ Blt_Free(map);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->map);
+ mapPtr->map = map;
+ mapPtr->screenPts = iPts;
+ mapPtr->nScreenPts = niPts;
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GenerateParametricSpline --
+ *
+ * Computes a spline based upon the data points, returning a new (larger)
+ * coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data map are updated
+ * based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GenerateParametricSpline(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Region2d exts;
+ Point2d *origPts, *iPts;
+ int *map;
+ int niPts, nOrigPts;
+ int result;
+ int i, j, count;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+
+ Blt_GraphExtents(graphPtr, &exts);
+
+ /*
+ * Populate the x2 array with both the original X-coordinates and extra
+ * X-coordinates for each horizontal pixel that the line segment contains.
+ */
+ count = 1;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ Point2d p, q;
+
+ p = origPts[i];
+ q = origPts[j];
+ count++;
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ count += (int)(hypot(q.x - p.x, q.y - p.y) * 0.5);
+ }
+ }
+ niPts = count;
+ iPts = Blt_AssertMalloc(niPts * sizeof(Point2d));
+ map = Blt_AssertMalloc(sizeof(int) * niPts);
+
+ /*
+ * FIXME: This is just plain wrong. The spline should be computed
+ * and evaluated in separate steps. This will mean breaking
+ * up this routine since the catrom coefficients can be
+ * independently computed for original data point. This
+ * also handles the problem of allocating enough points
+ * since evaluation is independent of the number of points
+ * to be evalualted. The interpolated
+ * line segments should be clipped, not the original segments.
+ */
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ Point2d p, q;
+ double d;
+
+ p = origPts[i];
+ q = origPts[j];
+
+ d = hypot(q.x - p.x, q.y - p.y);
+ /* Add the original x-coordinate */
+ iPts[count].x = (double)i;
+ iPts[count].y = 0.0;
+
+ /* Include the starting offset of the point in the offset array */
+ map[count] = mapPtr->map[i];
+ count++;
+
+ /* Is any part of the interval (line segment) in the plotting
+ * area? */
+
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ double dp, dq;
+
+ /* Distance of original point to p. */
+ dp = hypot(p.x - origPts[i].x, p.y - origPts[i].y);
+ /* Distance of original point to q. */
+ dq = hypot(q.x - origPts[i].x, q.y - origPts[i].y);
+ dp += 2.0;
+ while(dp <= dq) {
+ /* Point is indicated by its interval and parameter t. */
+ iPts[count].x = (double)i;
+ iPts[count].y = dp / d;
+ map[count] = mapPtr->map[i];
+ count++;
+ dp += 2.0;
+ }
+ }
+ }
+ iPts[count].x = (double)i;
+ iPts[count].y = 0.0;
+ map[count] = mapPtr->map[i];
+ count++;
+ niPts = count;
+ result = FALSE;
+ if (elemPtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalParametricSpline(origPts, nOrigPts, &exts, FALSE,
+ iPts, niPts);
+ } else if (elemPtr->smooth == PEN_SMOOTH_CATROM) {
+ result = Blt_CatromParametricSpline(origPts, nOrigPts, iPts, niPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We will fall back to the current
+ * coordinates and do no smoothing (standard line segments). */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ Blt_Free(iPts);
+ Blt_Free(map);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->map);
+ mapPtr->map = map;
+ mapPtr->screenPts = iPts;
+ mapPtr->nScreenPts = niPts;
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapSymbols --
+ *
+ * Creates two arrays of points and pen map, filled with the screen
+ * coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapSymbols(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Region2d exts;
+ Point2d *pp, *points;
+ int *map;
+ int i, count;
+
+ points = Blt_AssertMalloc(sizeof(Point2d) * mapPtr->nScreenPts);
+ map = Blt_AssertMalloc(sizeof(int) * mapPtr->nScreenPts);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of visible points */
+
+ for (pp = mapPtr->screenPts, i = 0; i < mapPtr->nScreenPts; i++, pp++) {
+ if (PointInRegion(&exts, pp->x, pp->y)) {
+ points[count].x = pp->x;
+ points[count].y = pp->y;
+ map[count] = mapPtr->map[i];
+ count++;
+ }
+ }
+ elemPtr->symbolPts.points = points;
+ elemPtr->symbolPts.length = count;
+ elemPtr->symbolPts.map = map;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapActiveSymbols --
+ *
+ * Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the active point array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapActiveSymbols(Graph *graphPtr, LineElement *elemPtr)
+{
+ Point2d *points;
+ Region2d exts;
+ int *map;
+ int count, i, np;
+
+ if (elemPtr->activePts.points != NULL) {
+ Blt_Free(elemPtr->activePts.points);
+ elemPtr->activePts.points = NULL;
+ }
+ if (elemPtr->activePts.map != NULL) {
+ Blt_Free(elemPtr->activePts.map);
+ elemPtr->activePts.map = NULL;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ points = Blt_AssertMalloc(sizeof(Point2d) * elemPtr->nActiveIndices);
+ map = Blt_AssertMalloc(sizeof(int) * elemPtr->nActiveIndices);
+ np = NUMBEROFPOINTS(elemPtr);
+ count = 0; /* Count the visible active points */
+ for (i = 0; i < elemPtr->nActiveIndices; i++) {
+ double x, y;
+ int iPoint;
+
+ iPoint = elemPtr->activeIndices[i];
+ if (iPoint >= np) {
+ continue; /* Index not available */
+ }
+ x = elemPtr->x.values[iPoint];
+ y = elemPtr->y.values[iPoint];
+ points[count] = Blt_Map2D(graphPtr, x, y, &elemPtr->axes);
+ map[count] = iPoint;
+ if (PointInRegion(&exts, points[count].x, points[count].y)) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ elemPtr->activePts.points = points;
+ elemPtr->activePts.map = map;
+ } else {
+ /* No active points were visible. */
+ Blt_Free(points);
+ Blt_Free(map);
+ }
+ elemPtr->activePts.length = count;
+ elemPtr->flags &= ~ACTIVE_PENDING;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapStrip --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapStrip(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Region2d exts;
+ Segment2d *lines;
+ int *indices, *indexPtr;
+ Point2d *pend, *pp;
+ Segment2d *sp;
+ int count;
+
+ indices = Blt_AssertMalloc(sizeof(int) * mapPtr->nScreenPts);
+
+ /*
+ * Create array to hold points for line segments (not polyline
+ * coordinates). So allocate twice the number of points.
+ */
+ sp = lines = Blt_AssertMalloc(mapPtr->nScreenPts * sizeof(Segment2d));
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of segments. */
+ indexPtr = mapPtr->map;
+ for (pp = mapPtr->screenPts, pend = pp + (mapPtr->nScreenPts - 1);
+ pp < pend; pp++, indexPtr++) {
+ sp->p = pp[0], sp->q = pp[1];
+ if (Blt_LineRectClip(&exts, &sp->p, &sp->q)) {
+ sp++;
+ indices[count] = *indexPtr;
+ count++;
+ }
+ }
+ elemPtr->lines.map = indices;
+ elemPtr->lines.length = count;
+ elemPtr->lines.segments = lines;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ * Reorders the both arrays of points and segments to merge pens.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The old arrays are freed and new ones allocated containing
+ * the reordered points and segments.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MergePens(LineElement *elemPtr, LineStyle **styleMap)
+{
+ if (Blt_Chain_GetLength(elemPtr->styles) < 2) {
+ Blt_ChainLink link;
+ LineStyle *stylePtr;
+
+ link = Blt_Chain_FirstLink(elemPtr->styles);
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->errorBarCapWidth = elemPtr->errorBarCapWidth;
+ stylePtr->lines.length = elemPtr->lines.length;
+ stylePtr->lines.segments = elemPtr->lines.segments;
+ stylePtr->symbolPts.length = elemPtr->symbolPts.length;
+ stylePtr->symbolPts.points = elemPtr->symbolPts.points;
+ stylePtr->xeb.length = elemPtr->xeb.length;
+ stylePtr->xeb.segments = elemPtr->xeb.segments;
+ stylePtr->yeb.length = elemPtr->yeb.length;
+ stylePtr->yeb.segments = elemPtr->yeb.segments;
+ return;
+ }
+
+ /* We have more than one style. Group line segments and points of like pen
+ * styles. */
+ if (elemPtr->lines.length > 0) {
+ Blt_ChainLink link;
+ Segment2d *sp, *segments;
+ int *ip;
+ int *map;
+
+ segments = Blt_AssertMalloc(elemPtr->lines.length * sizeof(Segment2d));
+ map = Blt_AssertMalloc(elemPtr->lines.length * sizeof(int));
+ sp = segments, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->lines.segments = sp;
+ for (i = 0; i < elemPtr->lines.length; i++) {
+ int iData;
+
+ iData = elemPtr->lines.map[i];
+ if (styleMap[iData] == stylePtr) {
+ *sp++ = elemPtr->lines.segments[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->lines.length = sp - stylePtr->lines.segments;
+ }
+ Blt_Free(elemPtr->lines.segments);
+ elemPtr->lines.segments = segments;
+ Blt_Free(elemPtr->lines.map);
+ elemPtr->lines.map = map;
+ }
+ if (elemPtr->symbolPts.length > 0) {
+ Blt_ChainLink link;
+ int *ip;
+ Point2d *points, *pp;
+ int *map;
+
+ points = Blt_AssertMalloc(elemPtr->symbolPts.length * sizeof(Point2d));
+ map = Blt_AssertMalloc(elemPtr->symbolPts.length * sizeof(int));
+ pp = points, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->symbolPts.points = pp;
+ for (i = 0; i < elemPtr->symbolPts.length; i++) {
+ int iData;
+
+ iData = elemPtr->symbolPts.map[i];
+ if (styleMap[iData] == stylePtr) {
+ *pp++ = elemPtr->symbolPts.points[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->symbolPts.length = pp - stylePtr->symbolPts.points;
+ }
+ Blt_Free(elemPtr->symbolPts.points);
+ Blt_Free(elemPtr->symbolPts.map);
+ elemPtr->symbolPts.points = points;
+ elemPtr->symbolPts.map = map;
+ }
+ if (elemPtr->xeb.length > 0) {
+ Segment2d *segments, *sp;
+ int *map, *ip;
+ Blt_ChainLink link;
+
+ segments = Blt_AssertMalloc(elemPtr->xeb.length * sizeof(Segment2d));
+ map = Blt_AssertMalloc(elemPtr->xeb.length * sizeof(int));
+ sp = segments, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->xeb.segments = sp;
+ for (i = 0; i < elemPtr->xeb.length; i++) {
+ int iData;
+
+ iData = elemPtr->xeb.map[i];
+ if (styleMap[iData] == stylePtr) {
+ *sp++ = elemPtr->xeb.segments[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->xeb.length = sp - stylePtr->xeb.segments;
+ }
+ Blt_Free(elemPtr->xeb.segments);
+ Blt_Free(elemPtr->xeb.map);
+ elemPtr->xeb.segments = segments;
+ elemPtr->xeb.map = map;
+ }
+ if (elemPtr->yeb.length > 0) {
+ Segment2d *segments, *sp;
+ int *map, *ip;
+ Blt_ChainLink link;
+
+ segments = Blt_AssertMalloc(elemPtr->yeb.length * sizeof(Segment2d));
+ map = Blt_AssertMalloc(elemPtr->yeb.length * sizeof(int));
+ sp = segments, ip = map;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ int i;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->yeb.segments = sp;
+ for (i = 0; i < elemPtr->yeb.length; i++) {
+ int iData;
+
+ iData = elemPtr->yeb.map[i];
+ if (styleMap[iData] == stylePtr) {
+ *sp++ = elemPtr->yeb.segments[i];
+ *ip++ = iData;
+ }
+ }
+ stylePtr->yeb.length = sp - stylePtr->yeb.segments;
+ }
+ Blt_Free(elemPtr->yeb.segments);
+ elemPtr->yeb.segments = segments;
+ Blt_Free(elemPtr->yeb.map);
+ elemPtr->yeb.map = map;
+ }
+}
+
+#define CLIP_TOP (1<<0)
+#define CLIP_BOTTOM (1<<1)
+#define CLIP_RIGHT (1<<2)
+#define CLIP_LEFT (1<<3)
+
+INLINE static int
+OutCode(Region2d *extsPtr, Point2d *p)
+{
+ int code;
+
+ code = 0;
+ if (p->x > extsPtr->right) {
+ code |= CLIP_RIGHT;
+ } else if (p->x < extsPtr->left) {
+ code |= CLIP_LEFT;
+ }
+ if (p->y > extsPtr->bottom) {
+ code |= CLIP_BOTTOM;
+ } else if (p->y < extsPtr->top) {
+ code |= CLIP_TOP;
+ }
+ return code;
+}
+
+static int
+ClipSegment(
+ Region2d *extsPtr,
+ int code1, int code2,
+ Point2d *p, Point2d *q)
+{
+ int inside, outside;
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+
+ /*
+ * In the worst case, we'll clip the line segment against each of the four
+ * sides of the bounding rectangle.
+ */
+ while ((!outside) && (!inside)) {
+ if (code1 == 0) {
+ Point2d *tmp;
+ int code;
+
+ /* Swap pointers and out codes */
+ tmp = p, p = q, q = tmp;
+ code = code1, code1 = code2, code2 = code;
+ }
+ if (code1 & CLIP_LEFT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->left - p->x) / (q->x - p->x);
+ p->x = extsPtr->left;
+ } else if (code1 & CLIP_RIGHT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->right - p->x) / (q->x - p->x);
+ p->x = extsPtr->right;
+ } else if (code1 & CLIP_BOTTOM) {
+ p->x += (q->x - p->x) *
+ (extsPtr->bottom - p->y) / (q->y - p->y);
+ p->y = extsPtr->bottom;
+ } else if (code1 & CLIP_TOP) {
+ p->x += (q->x - p->x) *
+ (extsPtr->top - p->y) / (q->y - p->y);
+ p->y = extsPtr->top;
+ }
+ code1 = OutCode(extsPtr, p);
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+ }
+ return (!inside);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SaveTrace --
+ *
+ * Creates a new trace and inserts it into the line's list of traces.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+SaveTrace(
+ LineElement *elemPtr,
+ int start, /* Starting index of the trace in data point
+ * array. Used to figure out closest point */
+ int length, /* Number of points forming the trace */
+ MapInfo *mapPtr)
+{
+ Trace *tracePtr;
+ Point2d *screenPts;
+ int *map;
+ int i, j;
+
+ tracePtr = Blt_AssertMalloc(sizeof(Trace));
+ screenPts = Blt_AssertMalloc(sizeof(Point2d) * length);
+ map = Blt_AssertMalloc(sizeof(int) * length);
+
+ /* Copy the screen coordinates of the trace into the point array */
+
+ if (mapPtr->map != NULL) {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ map[i] = mapPtr->map[j];
+ }
+ } else {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ map[i] = j;
+ }
+ }
+ tracePtr->screenPts.length = length;
+ tracePtr->screenPts.points = screenPts;
+ tracePtr->screenPts.map = map;
+ tracePtr->start = start;
+ if (elemPtr->traces == NULL) {
+ elemPtr->traces = Blt_Chain_Create();
+ }
+ Blt_Chain_Append(elemPtr->traces, tracePtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeTraces --
+ *
+ * Deletes all the traces for the line.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeTraces(LineElement *elemPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(elemPtr->traces); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Trace *tracePtr;
+
+ tracePtr = Blt_Chain_GetValue(link);
+ Blt_Free(tracePtr->screenPts.map);
+ Blt_Free(tracePtr->screenPts.points);
+ Blt_Free(tracePtr);
+ }
+ Blt_Chain_Destroy(elemPtr->traces);
+ elemPtr->traces = NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapTraces --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapTraces(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Point2d *p, *q;
+ Region2d exts;
+ int code1;
+ int i;
+ int start, count;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 1;
+ code1 = OutCode(&exts, mapPtr->screenPts);
+ p = mapPtr->screenPts;
+ q = p + 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++, p++, q++) {
+ Point2d s;
+ int code2;
+ int broken, offscreen;
+
+ s.x = s.y = 0;
+ code2 = OutCode(&exts, q);
+ if (code2 != 0) {
+ /* Save the coordinates of the last point, before clipping */
+ s = *q;
+ }
+ broken = BROKEN_TRACE(elemPtr->penDir, p->x, q->x);
+ offscreen = ClipSegment(&exts, code1, code2, p, q);
+ if (broken || offscreen) {
+
+ /*
+ * The last line segment is either totally clipped by the plotting
+ * area or the x-direction is wrong, breaking the trace. Either
+ * way, save information about the last trace (if one exists),
+ * discarding the current line segment
+ */
+
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(elemPtr, start, count, mapPtr);
+ count = 1;
+ }
+ } else {
+ count++; /* Add the point to the trace. */
+ if (code2 != 0) {
+
+ /*
+ * If the last point is clipped, this means that the trace is
+ * broken after this point. Restore the original coordinate
+ * (before clipping) after saving the trace.
+ */
+
+ start = i - (count - 1);
+ SaveTrace(elemPtr, start, count, mapPtr);
+ mapPtr->screenPts[i] = s;
+ count = 1;
+ }
+ }
+ code1 = code2;
+ }
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(elemPtr, start, count, mapPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapFillArea --
+ *
+ * Creates an array of points that represent a polygon that fills
+ * the area under the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon point array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapFillArea(Graph *graphPtr, LineElement *elemPtr, MapInfo *mapPtr)
+{
+ Point2d *origPts, *clipPts;
+ Region2d exts;
+ int np;
+
+ if (elemPtr->fillPts != NULL) {
+ Blt_Free(elemPtr->fillPts);
+ elemPtr->fillPts = NULL;
+ elemPtr->nFillPts = 0;
+ }
+ if (mapPtr->nScreenPts < 3) {
+ return;
+ }
+ np = mapPtr->nScreenPts + 3;
+ Blt_GraphExtents(graphPtr, &exts);
+
+ origPts = Blt_AssertMalloc(sizeof(Point2d) * np);
+ if (graphPtr->inverted) {
+ double minX;
+ int i;
+
+ minX = (double)elemPtr->axes.y->screenMin;
+ for (i = 0; i < mapPtr->nScreenPts; i++) {
+ origPts[i].x = mapPtr->screenPts[i].x + 1;
+ origPts[i].y = mapPtr->screenPts[i].y;
+ if (origPts[i].x < minX) {
+ minX = origPts[i].x;
+ }
+ }
+ /* Add edges to make (if necessary) the polygon fill to the bottom of
+ * plotting window */
+ origPts[i].x = minX;
+ origPts[i].y = origPts[i - 1].y;
+ i++;
+ origPts[i].x = minX;
+ origPts[i].y = origPts[0].y;
+ i++;
+ origPts[i] = origPts[0];
+ } else {
+ double maxY;
+ int i;
+
+ maxY = (double)elemPtr->axes.y->bottom;
+ for (i = 0; i < mapPtr->nScreenPts; i++) {
+ origPts[i].x = mapPtr->screenPts[i].x + 1;
+ origPts[i].y = mapPtr->screenPts[i].y;
+ if (origPts[i].y > maxY) {
+ maxY = origPts[i].y;
+ }
+ }
+ /* Add edges to extend the fill polygon to the bottom of plotting
+ * window */
+ origPts[i].x = origPts[i - 1].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i].x = origPts[0].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i] = origPts[0];
+ }
+
+ clipPts = Blt_AssertMalloc(sizeof(Point2d) * np * 3);
+ np = Blt_PolyRectClip(&exts, origPts, np - 1, clipPts);
+
+ Blt_Free(origPts);
+ if (np < 3) {
+ Blt_Free(clipPts);
+ } else {
+ elemPtr->fillPts = clipPts;
+ elemPtr->nFillPts = np;
+ }
+}
+
+static void
+ResetLine(LineElement *elemPtr)
+{
+ FreeTraces(elemPtr);
+ ResetStylePalette(elemPtr->styles);
+ if (elemPtr->symbolPts.points != NULL) {
+ Blt_Free(elemPtr->symbolPts.points);
+ }
+ if (elemPtr->symbolPts.map != NULL) {
+ Blt_Free(elemPtr->symbolPts.map);
+ }
+ if (elemPtr->lines.segments != NULL) {
+ Blt_Free(elemPtr->lines.segments);
+ }
+ if (elemPtr->lines.map != NULL) {
+ Blt_Free(elemPtr->lines.map);
+ }
+ if (elemPtr->activePts.points != NULL) {
+ Blt_Free(elemPtr->activePts.points);
+ }
+ if (elemPtr->activePts.map != NULL) {
+ Blt_Free(elemPtr->activePts.map);
+ }
+ if (elemPtr->xeb.segments != NULL) {
+ Blt_Free(elemPtr->xeb.segments);
+ }
+ if (elemPtr->xeb.map != NULL) {
+ Blt_Free(elemPtr->xeb.map);
+ }
+ if (elemPtr->yeb.segments != NULL) {
+ Blt_Free(elemPtr->yeb.segments);
+ }
+ if (elemPtr->yeb.map != NULL) {
+ Blt_Free(elemPtr->yeb.map);
+ }
+ elemPtr->xeb.segments = elemPtr->yeb.segments = elemPtr->lines.segments = NULL;
+ elemPtr->symbolPts.points = elemPtr->activePts.points = NULL;
+ elemPtr->lines.map = elemPtr->symbolPts.map = elemPtr->xeb.map =
+ elemPtr->yeb.map = elemPtr->activePts.map = NULL;
+ elemPtr->activePts.length = elemPtr->symbolPts.length =
+ elemPtr->lines.length = elemPtr->xeb.length = elemPtr->yeb.length = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapErrorBars --
+ *
+ * Creates two arrays of points and pen indices, filled with the screen
+ * coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapErrorBars(Graph *graphPtr, LineElement *elemPtr, LineStyle **styleMap)
+{
+ int n, np;
+ Region2d exts;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ np = NUMBEROFPOINTS(elemPtr);
+ if (elemPtr->xError.nValues > 0) {
+ n = MIN(elemPtr->xError.nValues, np);
+ } else {
+ n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, np);
+ }
+ if (n > 0) {
+ Segment2d *errorBars;
+ Segment2d *segPtr;
+ int *errorToData;
+ int *indexPtr;
+ int i;
+
+ segPtr = errorBars = Blt_AssertMalloc(n * 3 * sizeof(Segment2d));
+ indexPtr = errorToData = Blt_AssertMalloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ double x, y;
+ double high, low;
+ LineStyle *stylePtr;
+
+ x = elemPtr->x.values[i];
+ y = elemPtr->y.values[i];
+ stylePtr = styleMap[i];
+ if ((FINITE(x)) && (FINITE(y))) {
+ if (elemPtr->xError.nValues > 0) {
+ high = x + elemPtr->xError.values[i];
+ low = x - elemPtr->xError.values[i];
+ } else {
+ high = elemPtr->xHigh.values[i];
+ low = elemPtr->xLow.values[i];
+ }
+ if ((FINITE(high)) && (FINITE(low))) {
+ Point2d p, q;
+
+ p = Blt_Map2D(graphPtr, high, y, &elemPtr->axes);
+ q = Blt_Map2D(graphPtr, low, y, &elemPtr->axes);
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Left cap */
+ segPtr->p.x = segPtr->q.x = p.x;
+ segPtr->p.y = p.y - stylePtr->errorBarCapWidth;
+ segPtr->q.y = p.y + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Right cap */
+ segPtr->p.x = segPtr->q.x = q.x;
+ segPtr->p.y = q.y - stylePtr->errorBarCapWidth;
+ segPtr->q.y = q.y + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->xeb.segments = errorBars;
+ elemPtr->xeb.length = segPtr - errorBars;
+ elemPtr->xeb.map = errorToData;
+ }
+ if (elemPtr->yError.nValues > 0) {
+ n = MIN(elemPtr->yError.nValues, np);
+ } else {
+ n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, np);
+ }
+ if (n > 0) {
+ Segment2d *errorBars;
+ Segment2d *segPtr;
+ int *errorToData;
+ int *indexPtr;
+ int i;
+
+ segPtr = errorBars = Blt_AssertMalloc(n * 3 * sizeof(Segment2d));
+ indexPtr = errorToData = Blt_AssertMalloc(n * 3 * sizeof(int));
+ for (i = 0; i < n; i++) {
+ double x, y;
+ double high, low;
+ LineStyle *stylePtr;
+
+ x = elemPtr->x.values[i];
+ y = elemPtr->y.values[i];
+ stylePtr = styleMap[i];
+ if ((FINITE(x)) && (FINITE(y))) {
+ if (elemPtr->yError.nValues > 0) {
+ high = y + elemPtr->yError.values[i];
+ low = y - elemPtr->yError.values[i];
+ } else {
+ high = elemPtr->yHigh.values[i];
+ low = elemPtr->yLow.values[i];
+ }
+ if ((FINITE(high)) && (FINITE(low))) {
+ Point2d p, q;
+
+ p = Blt_Map2D(graphPtr, x, high, &elemPtr->axes);
+ q = Blt_Map2D(graphPtr, x, low, &elemPtr->axes);
+ segPtr->p = p;
+ segPtr->q = q;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Top cap. */
+ segPtr->p.y = segPtr->q.y = p.y;
+ segPtr->p.x = p.x - stylePtr->errorBarCapWidth;
+ segPtr->q.x = p.x + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ /* Bottom cap. */
+ segPtr->p.y = segPtr->q.y = q.y;
+ segPtr->p.x = q.x - stylePtr->errorBarCapWidth;
+ segPtr->q.x = q.x + stylePtr->errorBarCapWidth;
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ *indexPtr++ = i;
+ }
+ }
+ }
+ }
+ elemPtr->yeb.segments = errorBars;
+ elemPtr->yeb.length = segPtr - errorBars;
+ elemPtr->yeb.map = errorToData;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapLineProc --
+ *
+ * Calculates the actual window coordinates of the line element. The
+ * window coordinates are saved in an allocated point array.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is (re)allocated for the point array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapLineProc(Graph *graphPtr, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ MapInfo mi;
+ int size, np;
+ LineStyle **styleMap;
+ Blt_ChainLink link;
+
+ ResetLine(elemPtr);
+ np = NUMBEROFPOINTS(elemPtr);
+ if (np < 1) {
+ return; /* No data points */
+ }
+ GetScreenPoints(graphPtr, elemPtr, &mi);
+ MapSymbols(graphPtr, elemPtr, &mi);
+
+ if ((elemPtr->flags & ACTIVE_PENDING) && (elemPtr->nActiveIndices > 0)) {
+ MapActiveSymbols(graphPtr, elemPtr);
+ }
+ /*
+ * Map connecting line segments if they are to be displayed.
+ */
+ elemPtr->smooth = elemPtr->reqSmooth;
+ if ((np > 1) && ((graphPtr->classId == CID_ELEM_STRIP) ||
+ (elemPtr->builtinPen.traceWidth > 0))) {
+ /*
+ * Do smoothing if necessary. This can extend the coordinate array,
+ * so both mi.points and mi.nPoints may change.
+ */
+ switch (elemPtr->smooth) {
+ case PEN_SMOOTH_STEP:
+ GenerateSteps(&mi);
+ break;
+
+ case PEN_SMOOTH_NATURAL:
+ case PEN_SMOOTH_QUADRATIC:
+ if (mi.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ } else {
+ GenerateSpline(graphPtr, elemPtr, &mi);
+ }
+ break;
+
+ case PEN_SMOOTH_CATROM:
+ if (mi.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ } else {
+ GenerateParametricSpline(graphPtr, elemPtr, &mi);
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (elemPtr->rTolerance > 0.0) {
+ ReducePoints(&mi, elemPtr->rTolerance);
+ }
+ if (elemPtr->fillBg != NULL) {
+ MapFillArea(graphPtr, elemPtr, &mi);
+ }
+ if (graphPtr->classId == CID_ELEM_STRIP) {
+ MapStrip(graphPtr, elemPtr, &mi);
+ } else {
+ MapTraces(graphPtr, elemPtr, &mi);
+ }
+ }
+ Blt_Free(mi.screenPts);
+ Blt_Free(mi.map);
+
+ /* Set the symbol size of all the pen styles. */
+ for (link = Blt_Chain_FirstLink(elemPtr->styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ size = ScaleSymbol(elemPtr, penPtr->symbol.size);
+ stylePtr->symbolSize = size;
+ stylePtr->errorBarCapWidth = (penPtr->errorBarCapWidth > 0)
+ ? penPtr->errorBarCapWidth : Round(size * 0.6666666);
+ stylePtr->errorBarCapWidth /= 2;
+ }
+ styleMap = (LineStyle **)Blt_StyleMap((Element *)elemPtr);
+ if (((elemPtr->yHigh.nValues > 0) && (elemPtr->yLow.nValues > 0)) ||
+ ((elemPtr->xHigh.nValues > 0) && (elemPtr->xLow.nValues > 0)) ||
+ (elemPtr->xError.nValues > 0) || (elemPtr->yError.nValues > 0)) {
+ MapErrorBars(graphPtr, elemPtr, styleMap);
+ }
+ MergePens(elemPtr, styleMap);
+ Blt_Free(styleMap);
+}
+
+static double
+DistanceToLineProc(
+ int x, int y, /* Sample X-Y coordinate. */
+ Point2d *p, Point2d *q, /* End points of the line segment. */
+ Point2d *t) /* (out) Point on line segment. */
+{
+ double right, left, top, bottom;
+
+ *t = Blt_GetProjection(x, y, p, q);
+ if (p->x > q->x) {
+ right = p->x, left = q->x;
+ } else {
+ left = p->x, right = q->x;
+ }
+ if (p->y > q->y) {
+ bottom = p->y, top = q->y;
+ } else {
+ top = p->y, bottom = q->y;
+ }
+ if (t->x > right) {
+ t->x = right;
+ } else if (t->x < left) {
+ t->x = left;
+ }
+ if (t->y > bottom) {
+ t->y = bottom;
+ } else if (t->y < top) {
+ t->y = top;
+ }
+ return hypot((t->x - x), (t->y - y));
+}
+
+static double
+DistanceToXProc(
+ int x, int y, /* Search X-Y coordinate. */
+ Point2d *p,
+ Point2d *q, /* End points of the line segment. */
+ Point2d *t) /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double d;
+
+ if (p->x > q->x) {
+ if ((x > p->x) || (x < q->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ } else {
+ if ((x > q->x) || (x < p->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->x = (double)x;
+ if (FABS(dx) < DBL_EPSILON) {
+ double d1, d2;
+ /*
+ * Same X-coordinate indicates a vertical line. Pick the closest end
+ * point.
+ */
+ d1 = p->y - y;
+ d2 = q->y - y;
+ if (FABS(d1) < FABS(d2)) {
+ t->y = p->y, d = d1;
+ } else {
+ t->y = q->y, d = d2;
+ }
+ } else if (FABS(dy) < DBL_EPSILON) {
+ /* Horizontal line. */
+ t->y = p->y, d = p->y - y;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->y = (x * m) + b;
+ d = y - t->y;
+ }
+ return FABS(d);
+}
+
+static double
+DistanceToYProc(
+ int x, int y, /* Search X-Y coordinate. */
+ Point2d *p, Point2d *q, /* End points of the line segment. */
+ Point2d *t) /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double d;
+
+ if (p->y > q->y) {
+ if ((y > p->y) || (y < q->y)) {
+ return DBL_MAX;
+ }
+ } else {
+ if ((y > q->y) || (y < p->y)) {
+ return DBL_MAX;
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->y = y;
+ if (FABS(dy) < DBL_EPSILON) {
+ double d1, d2;
+
+ /* Save Y-coordinate indicates an horizontal line. Pick the closest end
+ * point. */
+ d1 = p->x - x;
+ d2 = q->x - x;
+ if (FABS(d1) < FABS(d2)) {
+ t->x = p->x, d = d1;
+ } else {
+ t->x = q->x, d = d2;
+ }
+ } else if (FABS(dx) < DBL_EPSILON) {
+ /* Vertical line. */
+ t->x = p->x, d = p->x - x;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->x = (y - b) / m;
+ d = x - t->x;
+ }
+ return FABS(d);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestTrace --
+ *
+ * Find the line segment closest to the given window coordinate in the
+ * element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding it is
+ * returned via searchPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ClosestTrace(
+ Graph *graphPtr, /* Graph widget record */
+ LineElement *elemPtr,
+ ClosestSearch *searchPtr, /* Info about closest point in
+ * element */
+ DistanceProc *distProc)
+{
+ Blt_ChainLink link;
+ Point2d closest;
+ double dMin;
+ int iClose;
+
+ iClose = -1; /* Suppress compiler warning. */
+ dMin = searchPtr->dist;
+ closest.x = closest.y = 0; /* Suppress compiler warning. */
+ for (link = Blt_Chain_FirstLink(elemPtr->traces); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Trace *tracePtr;
+ Point2d *p, *pend;
+
+ tracePtr = Blt_Chain_GetValue(link);
+ for (p = tracePtr->screenPts.points,
+ pend = p + (tracePtr->screenPts.length - 1); p < pend; p++) {
+ Point2d b;
+ double d;
+
+ d = (*distProc)(searchPtr->x, searchPtr->y, p, p + 1, &b);
+ if (d < dMin) {
+ closest = b;
+ iClose = tracePtr->screenPts.map[p-tracePtr->screenPts.points];
+ dMin = d;
+ }
+ }
+ }
+ if (dMin < searchPtr->dist) {
+ searchPtr->dist = dMin;
+ searchPtr->elemPtr = (Element *)elemPtr;
+ searchPtr->index = iClose;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &elemPtr->axes);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestStrip --
+ *
+ * Find the line segment closest to the given window coordinate in the
+ * element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding it is
+ * returned via searchPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ClosestStrip(
+ Graph *graphPtr, /* Graph widget record */
+ LineElement *elemPtr, /* Line element record */
+ ClosestSearch *searchPtr, /* Info about closest point in
+ * element */
+ DistanceProc *distProc)
+{
+ Point2d closest;
+ double dMin;
+ int count;
+ int iClose;
+ Segment2d *sp;
+
+ iClose = 0;
+ dMin = searchPtr->dist;
+ closest.x = closest.y = 0;
+ for (sp = elemPtr->lines.segments, count = 0;
+ count < elemPtr->lines.length; count++, sp++) {
+ double d;
+ Point2d b;
+
+ d = (*distProc)(searchPtr->x, searchPtr->y, &sp->p, &sp->q, &b);
+ if (d < dMin) {
+ closest = b;
+ iClose = elemPtr->lines.map[count];
+ dMin = d;
+ }
+ }
+ if (dMin < searchPtr->dist) {
+ searchPtr->dist = dMin;
+ searchPtr->elemPtr = (Element *)elemPtr;
+ searchPtr->index = iClose;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &elemPtr->axes);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestPoint --
+ *
+ * Find the element whose data point is closest to the given screen
+ * coordinate.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ClosestPoint(
+ LineElement *elemPtr, /* Line element to be searched. */
+ ClosestSearch *searchPtr) /* Assorted information related to
+ * searching for the closest point */
+{
+ double dMin;
+ int count, iClose;
+ Point2d *pp;
+
+ dMin = searchPtr->dist;
+ iClose = 0;
+
+ /*
+ * Instead of testing each data point in graph coordinates, look at the
+ * array of mapped screen coordinates. The advantages are
+ * 1) only examine points that are visible (unclipped), and
+ * 2) the computed distance is already in screen coordinates.
+ */
+ for (pp = elemPtr->symbolPts.points, count = 0;
+ count < elemPtr->symbolPts.length; count++, pp++) {
+ double dx, dy;
+ double d;
+
+ dx = (double)(searchPtr->x - pp->x);
+ dy = (double)(searchPtr->y - pp->y);
+ if (searchPtr->along == SEARCH_BOTH) {
+ d = hypot(dx, dy);
+ } else if (searchPtr->along == SEARCH_X) {
+ d = dx;
+ } else if (searchPtr->along == SEARCH_Y) {
+ d = dy;
+ } else {
+ /* This can't happen */
+ continue;
+ }
+ if (d < dMin) {
+ iClose = elemPtr->symbolPts.map[count];
+ dMin = d;
+ }
+ }
+ if (dMin < searchPtr->dist) {
+ searchPtr->elemPtr = (Element *)elemPtr;
+ searchPtr->dist = dMin;
+ searchPtr->index = iClose;
+ searchPtr->point.x = elemPtr->x.values[iClose];
+ searchPtr->point.y = elemPtr->y.values[iClose];
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetLineExtentsProc --
+ *
+ * Retrieves the range of the line element
+ *
+ * Results:
+ * Returns the number of data points in the element.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GetLineExtentsProc(Element *basePtr, Region2d *extsPtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ int np;
+
+ extsPtr->top = extsPtr->left = DBL_MAX;
+ extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+ np = NUMBEROFPOINTS(elemPtr);
+ if (np < 1) {
+ return;
+ }
+ extsPtr->right = elemPtr->x.max;
+ if ((elemPtr->x.min <= 0.0) && (elemPtr->axes.x->logScale)) {
+ extsPtr->left = Blt_FindElemValuesMinimum(&elemPtr->x, DBL_MIN);
+ } else {
+ extsPtr->left = elemPtr->x.min;
+ }
+ extsPtr->bottom = elemPtr->y.max;
+ if ((elemPtr->y.min <= 0.0) && (elemPtr->axes.y->logScale)) {
+ extsPtr->top = Blt_FindElemValuesMinimum(&elemPtr->y, DBL_MIN);
+ } else {
+ extsPtr->top = elemPtr->y.min;
+ }
+
+ /* Correct the data limits for error bars */
+
+ if (elemPtr->xError.nValues > 0) {
+ int i;
+
+ np = MIN(elemPtr->xError.nValues, np);
+ for (i = 0; i < np; i++) {
+ double x;
+
+ x = elemPtr->x.values[i] + elemPtr->xError.values[i];
+ if (x > extsPtr->right) {
+ extsPtr->right = x;
+ }
+ x = elemPtr->x.values[i] - elemPtr->xError.values[i];
+ if (elemPtr->axes.x->logScale) {
+ if (x < 0.0) {
+ x = -x; /* Mirror negative values, instead of
+ * ignoring them. */
+ }
+ if ((x > DBL_MIN) && (x < extsPtr->left)) {
+ extsPtr->left = x;
+ }
+ } else if (x < extsPtr->left) {
+ extsPtr->left = x;
+ }
+ }
+ } else {
+ if ((elemPtr->xHigh.nValues > 0) &&
+ (elemPtr->xHigh.max > extsPtr->right)) {
+ extsPtr->right = elemPtr->xHigh.max;
+ }
+ if (elemPtr->xLow.nValues > 0) {
+ double left;
+
+ if ((elemPtr->xLow.min <= 0.0) &&
+ (elemPtr->axes.x->logScale)) {
+ left = Blt_FindElemValuesMinimum(&elemPtr->xLow, DBL_MIN);
+ } else {
+ left = elemPtr->xLow.min;
+ }
+ if (left < extsPtr->left) {
+ extsPtr->left = left;
+ }
+ }
+ }
+
+ if (elemPtr->yError.nValues > 0) {
+ int i;
+
+ np = MIN(elemPtr->yError.nValues, np);
+ for (i = 0; i < np; i++) {
+ double y;
+
+ y = elemPtr->y.values[i] + elemPtr->yError.values[i];
+ if (y > extsPtr->bottom) {
+ extsPtr->bottom = y;
+ }
+ y = elemPtr->y.values[i] - elemPtr->yError.values[i];
+ if (elemPtr->axes.y->logScale) {
+ if (y < 0.0) {
+ y = -y; /* Mirror negative values, instead of
+ * ignoring them. */
+ }
+ if ((y > DBL_MIN) && (y < extsPtr->left)) {
+ extsPtr->top = y;
+ }
+ } else if (y < extsPtr->top) {
+ extsPtr->top = y;
+ }
+ }
+ } else {
+ if ((elemPtr->yHigh.nValues > 0) &&
+ (elemPtr->yHigh.max > extsPtr->bottom)) {
+ extsPtr->bottom = elemPtr->yHigh.max;
+ }
+ if (elemPtr->yLow.nValues > 0) {
+ double top;
+
+ if ((elemPtr->yLow.min <= 0.0) &&
+ (elemPtr->axes.y->logScale)) {
+ top = Blt_FindElemValuesMinimum(&elemPtr->yLow, DBL_MIN);
+ } else {
+ top = elemPtr->yLow.min;
+ }
+ if (top < extsPtr->top) {
+ extsPtr->top = top;
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BackgroundChangedProc
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+BackgroundChangedProc(ClientData clientData)
+{
+ Element *elemPtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = elemPtr->obj.graphPtr;
+ if (graphPtr->tkwin != NULL) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureLineProc --
+ *
+ * Sets up the appropriate configuration parameters in the GC. It is
+ * assumed the parameters have been previously set by a call to
+ * Blt_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard TCL result. If TCL_ERROR is returned,
+ * then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLineProc(Graph *graphPtr, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+ Blt_ChainLink link;
+ LineStyle *stylePtr;
+
+ if (ConfigurePenProc(graphPtr, (Pen *)&elemPtr->builtinPen) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Point to the static normal/active pens if no external pens have been
+ * selected.
+ */
+ link = Blt_Chain_FirstLink(elemPtr->styles);
+ if (link == NULL) {
+ link = Blt_Chain_AllocLink(sizeof(LineStyle));
+ Blt_Chain_LinkAfter(elemPtr->styles, link, NULL);
+ }
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->penPtr = NORMALPEN(elemPtr);
+
+ if (elemPtr->fillBg != NULL) {
+ Blt_SetBackgroundChangedProc(elemPtr->fillBg, BackgroundChangedProc,
+ elemPtr);
+ }
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = 0;
+ if (elemPtr->fillFgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = elemPtr->fillFgColor->pixel;
+ }
+ if (elemPtr->fillBgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = elemPtr->fillBgColor->pixel;
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (elemPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, elemPtr->fillGC);
+ }
+ elemPtr->fillGC = newGC;
+
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-scalesymbols",
+ (char *)NULL)) {
+ elemPtr->flags |= (MAP_ITEM | SCALE_SYMBOL);
+ }
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-pixels", "-trace",
+ "-*data", "-smooth", "-map*", "-label", "-hide", "-x", "-y",
+ "-areabackground", (char *)NULL)) {
+ elemPtr->flags |= MAP_ITEM;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestLineProc --
+ *
+ * Find the closest point or line segment (if interpolated) to the given
+ * window coordinate in the line element.
+ *
+ * Results:
+ * Returns the distance of the closest point among other information.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ClosestLineProc(Graph *graphPtr, Element *basePtr, ClosestSearch *searchPtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ int mode;
+
+ mode = searchPtr->mode;
+ if (mode == SEARCH_AUTO) {
+ LinePen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ mode = SEARCH_POINTS;
+ if ((NUMBEROFPOINTS(elemPtr) > 1) && (penPtr->traceWidth > 0)) {
+ mode = SEARCH_TRACES;
+ }
+ }
+ if (mode == SEARCH_POINTS) {
+ ClosestPoint(elemPtr, searchPtr);
+ } else {
+ DistanceProc *distProc;
+ int found;
+
+ if (searchPtr->along == SEARCH_X) {
+ distProc = DistanceToXProc;
+ } else if (searchPtr->along == SEARCH_Y) {
+ distProc = DistanceToYProc;
+ } else {
+ distProc = DistanceToLineProc;
+ }
+ if (elemPtr->obj.classId == CID_ELEM_STRIP) {
+ found = ClosestStrip(graphPtr, elemPtr, searchPtr, distProc);
+ } else {
+ found = ClosestTrace(graphPtr, elemPtr, searchPtr, distProc);
+ }
+ if ((!found) && (searchPtr->along != SEARCH_BOTH)) {
+ ClosestPoint(elemPtr, searchPtr);
+ }
+ }
+}
+
+/*
+ * XDrawLines() points: XMaxRequestSize(dpy) - 3
+ * XFillPolygon() points: XMaxRequestSize(dpy) - 4
+ * XDrawSegments() segments: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XFillRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawArcs() or XFillArcs() arcs: (XMaxRequestSize(dpy) - 3) / 3
+ */
+
+#define MAX_DRAWLINES(d) Blt_MaxRequestSize(d, sizeof(XPoint))
+#define MAX_DRAWPOLYGON(d) Blt_MaxRequestSize(d, sizeof(XPoint))
+#define MAX_DRAWSEGMENTS(d) Blt_MaxRequestSize(d, sizeof(XSegment))
+#define MAX_DRAWRECTANGLES(d) Blt_MaxRequestSize(d, sizeof(XRectangle))
+#define MAX_DRAWARCS(d) Blt_MaxRequestSize(d, sizeof(XArc))
+
+#ifdef WIN32
+
+static void
+DrawCircles(
+ Display *display,
+ Drawable drawable,
+ LineElement *elemPtr,
+ LinePen *penPtr,
+ int nSymbolPts,
+ Point2d *symbolPts,
+ int radius)
+{
+ HBRUSH brush, oldBrush;
+ HPEN pen, oldPen;
+ HDC dc;
+ TkWinDCState state;
+
+ if (drawable == None) {
+ return; /* Huh? */
+ }
+ if ((penPtr->symbol.fillGC == NULL) &&
+ (penPtr->symbol.outlineWidth == 0)) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ /* SetROP2(dc, tkpWinRopModes[penPtr->symbol.fillGC->function]); */
+ if (penPtr->symbol.fillGC != NULL) {
+ brush = CreateSolidBrush(penPtr->symbol.fillGC->foreground);
+ } else {
+ brush = GetStockBrush(NULL_BRUSH);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ pen = Blt_GCToPen(dc, penPtr->symbol.outlineGC);
+ } else {
+ pen = GetStockPen(NULL_PEN);
+ }
+ oldPen = SelectPen(dc, pen);
+ oldBrush = SelectBrush(dc, brush);
+ {
+ Point2d *pp, *pend;
+
+ for (pp = symbolPts, pend = pp + nSymbolPts; pp < pend; pp++) {
+ int rndx, rndy;
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ Ellipse(dc, rndx - radius, rndy - radius, rndx + radius + 1,
+ rndy + radius + 1);
+ }
+ }
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ DeletePen(SelectPen(dc, oldPen));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawCircles(Display *display, Drawable drawable, LineElement *elemPtr,
+ LinePen *penPtr, int nSymbolPts, Point2d *symbolPts, int radius)
+{
+ int i;
+ XArc *arcs; /* Array of arcs (circle) */
+ int reqSize;
+ int s;
+ int count;
+
+ s = radius + radius;
+ arcs = Blt_AssertMalloc(nSymbolPts * sizeof(XArc));
+
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *pend;
+ XArc *ap;
+
+ ap = arcs;
+ count = 0;
+ for (pp = symbolPts, pend = pp + nSymbolPts; pp < pend; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ ap->x = Round(pp->x) - radius;
+ ap->y = Round(pp->y) - radius;
+ ap->width = ap->height = (unsigned short)s;
+ ap->angle1 = 0;
+ ap->angle2 = 23040;
+ ap++, count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *pend;
+ XArc *ap;
+
+ ap = arcs;
+ for (pp = symbolPts, pend = pp + nSymbolPts; pp < pend; pp++) {
+ ap->x = Round(pp->x) - radius;
+ ap->y = Round(pp->y) - radius;
+ ap->width = ap->height = (unsigned short)s;
+ ap->angle1 = 0;
+ ap->angle2 = 23040;
+ ap++;
+ }
+ count = nSymbolPts;
+ }
+ reqSize = MAX_DRAWARCS(display);
+ for (i = 0; i < count; i += reqSize) {
+ int n;
+
+ n = ((i + reqSize) > count) ? (count - i) : reqSize;
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillArcs(display, drawable, penPtr->symbol.fillGC, arcs + i, n);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawArcs(display, drawable, penPtr->symbol.outlineGC, arcs + i, n);
+ }
+ }
+ Blt_Free(arcs);
+}
+
+#endif
+
+static void
+DrawSquares(Display *display, Drawable drawable, LineElement *elemPtr,
+ LinePen *penPtr, int nSymbolPts, Point2d *symbolPts, int r)
+{
+ XRectangle *rectangles;
+ XRectangle *rp, *rend;
+ int reqSize;
+ int s, count;
+
+ s = r + r;
+ rectangles = Blt_AssertMalloc(nSymbolPts * sizeof(XRectangle));
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *pend;
+ XRectangle *rp;
+
+ count = 0;
+ rp = rectangles;
+ for (pp = symbolPts, pend = pp + nSymbolPts; pp < pend; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ rp->x = Round(pp->x) - r;
+ rp->y = Round(pp->y) - r;
+ rp->width = rp->height = (unsigned short)s;
+ rp++, count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *pend;
+ XRectangle *rp;
+
+ rp = rectangles;
+ for (pp = symbolPts, pend = pp + nSymbolPts; pp < pend; pp++) {
+ rp->x = Round(pp->x) - r;
+ rp->y = Round(pp->y) - r;
+ rp->width = rp->height = (unsigned short)s;
+ rp++;
+ }
+ count = nSymbolPts;
+ }
+ reqSize = MAX_DRAWRECTANGLES(display) - 3;
+ for (rp = rectangles, rend = rp + count; rp < rend; rp += reqSize) {
+ int n;
+
+ n = rend - rp;
+ if (n > reqSize) {
+ n = reqSize;
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillRectangles(display, drawable, penPtr->symbol.fillGC, rp, n);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawRectangles(display, drawable, penPtr->symbol.outlineGC, rp, n);
+ }
+ }
+ Blt_Free(rectangles);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawSymbols --
+ *
+ * Draw the symbols centered at the each given x,y coordinate in the array
+ * of points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at each coordinate given. If active, only those
+ * coordinates which are currently active are drawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawSymbols(
+ Graph *graphPtr, /* Graph widget record */
+ Drawable drawable, /* Pixmap or window to draw into */
+ LineElement *elemPtr,
+ LinePen *penPtr,
+ int size, /* Size of element */
+ int nSymbolPts, /* Number of coordinates in array */
+ Point2d *symbolPts) /* Array of x,y coordinates for line */
+{
+ XPoint pattern[13]; /* Template for polygon symbols */
+ int r1, r2;
+ int count;
+#define SQRT_PI 1.77245385090552
+#define S_RATIO 0.886226925452758
+
+ if (size < 3) {
+ if (penPtr->symbol.fillGC != NULL) {
+ Point2d *pp, *endp;
+ XPoint *points, *xpp;
+
+ xpp = points = Blt_AssertMalloc(nSymbolPts * sizeof(XPoint));
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ xpp->x = Round(pp->x);
+ xpp->y = Round(pp->y);
+ xpp++;
+ }
+ XDrawPoints(graphPtr->display, drawable, penPtr->symbol.fillGC,
+ points, nSymbolPts, CoordModeOrigin);
+ Blt_Free(points);
+ }
+ return;
+ }
+ r1 = (int)ceil(size * 0.5);
+ r2 = (int)ceil(size * S_RATIO * 0.5);
+
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break;
+
+ case SYMBOL_SQUARE:
+ DrawSquares(graphPtr->display, drawable, elemPtr, penPtr, nSymbolPts,
+ symbolPts, r2);
+ break;
+
+ case SYMBOL_CIRCLE:
+ DrawCircles(graphPtr->display, drawable, elemPtr, penPtr, nSymbolPts,
+ symbolPts, r1);
+ break;
+
+ case SYMBOL_SPLUS:
+ case SYMBOL_SCROSS:
+ {
+ XSegment *segments; /* Array of line segments (splus,
+ * scross) */
+ int i;
+ int reqSize, nSegs;
+
+ if (penPtr->symbol.type == SYMBOL_SCROSS) {
+ r2 = Round((double)r2 * M_SQRT1_2);
+ pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2;
+ pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2;
+ } else {
+ pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0;
+ pattern[0].x = pattern[2].y = -r2;
+ pattern[1].x = pattern[3].y = r2;
+ }
+ segments = Blt_AssertMalloc(nSymbolPts * 2 * sizeof(XSegment));
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+ XSegment *sp;
+
+ sp = segments;
+ count = 0;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ int rndx, rndy;
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ sp->x1 = pattern[0].x + rndx;
+ sp->y1 = pattern[0].y + rndy;
+ sp->x2 = pattern[1].x + rndx;
+ sp->y2 = pattern[1].y + rndy;
+ sp++;
+ sp->x1 = pattern[2].x + rndx;
+ sp->y1 = pattern[2].y + rndy;
+ sp->x2 = pattern[3].x + rndx;
+ sp->y2 = pattern[3].y + rndy;
+ sp++;
+ count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+ XSegment *sp;
+
+ sp = segments;
+ count = nSymbolPts;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int rndx, rndy;
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ sp->x1 = pattern[0].x + rndx;
+ sp->y1 = pattern[0].y + rndy;
+ sp->x2 = pattern[1].x + rndx;
+ sp->y2 = pattern[1].y + rndy;
+ sp++;
+ sp->x1 = pattern[2].x + rndx;
+ sp->y1 = pattern[2].y + rndy;
+ sp->x2 = pattern[3].x + rndx;
+ sp->y2 = pattern[3].y + rndy;
+ sp++;
+ }
+ }
+ nSegs = count * 2;
+ /* Always draw skinny symbols regardless of the outline width */
+ reqSize = MAX_DRAWSEGMENTS(graphPtr->display);
+ for (i = 0; i < nSegs; i += reqSize) {
+ int chunk;
+
+ chunk = ((i + reqSize) > nSegs) ? (nSegs - i) : reqSize;
+ XDrawSegments(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, segments + i, chunk);
+ }
+ Blt_Free(segments);
+ }
+ break;
+
+ case SYMBOL_PLUS:
+ case SYMBOL_CROSS:
+ {
+ XPoint *polygon;
+ int d; /* Small delta for cross/plus
+ * thickness */
+
+ d = (r2 / 3);
+
+ /*
+ *
+ * 2 3 The plus/cross symbol is a closed polygon
+ * of 12 points. The diagram to the left
+ * 0,12 1 4 5 represents the positions of the points
+ * x,y which are computed below. The extra
+ * 11 10 7 6 (thirteenth) point connects the first and
+ * last points.
+ * 9 8
+ */
+
+ pattern[0].x = pattern[11].x = pattern[12].x = -r2;
+ pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d;
+ pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d;
+ pattern[5].x = pattern[6].x = r2;
+ pattern[2].y = pattern[3].y = -r2;
+ pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y =
+ pattern[12].y = -d;
+ pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d;
+ pattern[9].y = pattern[8].y = r2;
+
+ if (penPtr->symbol.type == SYMBOL_CROSS) {
+ int i;
+
+ /* For the cross symbol, rotate the points by 45 degrees. */
+ for (i = 0; i < 12; i++) {
+ double dx, dy;
+
+ dx = (double)pattern[i].x * M_SQRT1_2;
+ dy = (double)pattern[i].y * M_SQRT1_2;
+ pattern[i].x = Round(dx - dy);
+ pattern[i].y = Round(dx + dy);
+ }
+ pattern[12] = pattern[0];
+ }
+ polygon = Blt_AssertMalloc(nSymbolPts * 13 * sizeof(XPoint));
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ count = 0;
+ xpp = polygon;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ int i;
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 13; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ xpp = polygon;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int i;
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 13; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ }
+ count = nSymbolPts;
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ int i;
+ XPoint *xpp;
+
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 13) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, xpp, 13, Complex,
+ CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ int i;
+ XPoint *xpp;
+
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 13) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, xpp, 13, CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_DIAMOND:
+ {
+ XPoint *polygon;
+
+ /*
+ *
+ * The plus symbol is a closed polygon
+ * 1 of 4 points. The diagram to the left
+ * represents the positions of the points
+ * 0,4 x,y 2 which are computed below. The extra
+ * (fifth) point connects the first and
+ * 3 last points.
+ *
+ */
+ pattern[1].y = pattern[0].x = -r1;
+ pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0;
+ pattern[3].y = pattern[2].x = r1;
+ pattern[4] = pattern[0];
+
+ polygon = Blt_AssertMalloc(nSymbolPts * 5 * sizeof(XPoint));
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ xpp = polygon;
+ count = 0;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int i;
+
+ if (DRAW_SYMBOL(elemPtr)) {
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 5; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ xpp = polygon;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int i;
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 5; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ }
+ count = nSymbolPts;
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ XPoint *xpp;
+ int i;
+
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 5) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, xpp, 5, Convex, CoordModeOrigin);
+
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XPoint *xpp;
+ int i;
+
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 5) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, xpp, 5, CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ {
+ XPoint *polygon;
+ double b;
+ int b2, h1, h2;
+#define H_RATIO 1.1663402261671607
+#define B_RATIO 1.3467736870885982
+#define TAN30 0.57735026918962573
+#define COS30 0.86602540378443871
+
+ b = Round(size * B_RATIO * 0.7);
+ b2 = Round(b * 0.5);
+ h2 = Round(TAN30 * b2);
+ h1 = Round(b2 / COS30);
+ /*
+ *
+ * The triangle symbol is a closed polygon
+ * 0,3 of 3 points. The diagram to the left
+ * represents the positions of the points
+ * x,y which are computed below. The extra
+ * (fourth) point connects the first and
+ * 2 1 last points.
+ *
+ */
+
+ if (penPtr->symbol.type == SYMBOL_ARROW) {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = -h2;
+ pattern[2].x = -b2;
+ } else {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = -h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = h2;
+ pattern[2].x = -b2;
+ }
+ polygon = Blt_AssertMalloc(nSymbolPts * 4 * sizeof(XPoint));
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ xpp = polygon;
+ count = 0;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int i;
+
+ if (DRAW_SYMBOL(elemPtr)) {
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 4; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ count++;
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+ XPoint *xpp;
+
+ xpp = polygon;
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int i;
+ int rndx, rndy;
+
+ rndx = Round(pp->x), rndy = Round(pp->y);
+ for (i = 0; i < 4; i++) {
+ xpp->x = pattern[i].x + rndx;
+ xpp->y = pattern[i].y + rndy;
+ xpp++;
+ }
+ }
+ count = nSymbolPts;
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ XPoint *xpp;
+ int i;
+
+ xpp = polygon;
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 4) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, xpp, 4, Convex, CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XPoint *xpp;
+ int i;
+
+ xpp = polygon;
+ for (xpp = polygon, i = 0; i < count; i++, xpp += 4) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, xpp, 4, CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_IMAGE:
+ {
+ int w, h;
+ int dx, dy;
+
+ Tk_SizeOfImage(penPtr->symbol.image, &w, &h);
+
+ dx = w / 2;
+ dy = h / 2;
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ int x, y;
+
+ x = Round(pp->x) - dx;
+ y = Round(pp->y) - dy;
+ Tk_RedrawImage(penPtr->symbol.image, 0, 0, w, h,
+ drawable, x, y);
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int x, y;
+
+ x = Round(pp->x) - dx;
+ y = Round(pp->y) - dy;
+ Tk_RedrawImage(penPtr->symbol.image, 0, 0, w, h,
+ drawable, x, y);
+ }
+ }
+ }
+ break;
+
+ case SYMBOL_BITMAP:
+ {
+ Pixmap bitmap, mask;
+ int w, h, bw, bh;
+ double scale, sx, sy;
+ int dx, dy;
+
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap, &w, &h);
+ mask = None;
+
+ /*
+ * Compute the size of the scaled bitmap. Stretch the bitmap to fit
+ * a nxn bounding box.
+ */
+ sx = (double)size / (double)w;
+ sy = (double)size / (double)h;
+ scale = MIN(sx, sy);
+ bw = (int)(w * scale);
+ bh = (int)(h * scale);
+
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, None);
+ if (penPtr->symbol.mask != None) {
+ mask = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.mask,
+ w, h, bw, bh);
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, mask);
+ }
+ bitmap = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.bitmap,
+ w, h, bw, bh);
+ if (penPtr->symbol.fillGC == NULL) {
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC,
+ bitmap);
+ }
+ dx = bw / 2;
+ dy = bh / 2;
+ if (elemPtr->symbolInterval > 0) {
+ Point2d *pp, *endp;
+
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ if (DRAW_SYMBOL(elemPtr)) {
+ int x, y;
+
+ x = Round(pp->x) - dx;
+ y = Round(pp->y) - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask !=None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bw, bh, x, y, 1);
+ }
+ elemPtr->symbolCounter++;
+ }
+ } else {
+ Point2d *pp, *endp;
+
+ for (pp = symbolPts, endp = pp + nSymbolPts; pp < endp; pp++) {
+ int x, y;
+
+ x = Round(pp->x) - dx;
+ y = Round(pp->y) - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bw, bh, x, y, 1);
+ }
+ }
+ Tk_FreePixmap(graphPtr->display, bitmap);
+ if (mask != None) {
+ Tk_FreePixmap(graphPtr->display, mask);
+ }
+ }
+ break;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawSymbolProc --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawSymbolProc(
+ Graph *graphPtr, /* Graph widget record */
+ Drawable drawable, /* Pixmap or window to draw into */
+ Element *basePtr, /* Line element information */
+ int x, int y, /* Center position of symbol */
+ int size) /* Size of symbol. */
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ LinePen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to give a
+ * thicker appearance. This is only for the legend entry. This routine
+ * is never called for drawing the actual line segments.
+ */
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size, y,
+ x + size, y);
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size, y + 1,
+ x + size, y + 1);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2d point;
+
+ point.x = x, point.y = y;
+ DrawSymbols(graphPtr, drawable, elemPtr, penPtr, size, 1, &point);
+ }
+}
+
+#ifdef WIN32
+
+static void
+DrawTraces(
+ Graph *graphPtr,
+ Drawable drawable, /* Pixmap or window to draw into */
+ LineElement *elemPtr,
+ LinePen *penPtr)
+{
+ Blt_ChainLink link;
+ HBRUSH brush, oldBrush;
+ HDC dc;
+ HPEN pen, oldPen;
+ POINT *points;
+ TkWinDCState state;
+ int np;
+
+ /*
+ * Depending if the line is wide (> 1 pixel), arbitrarily break the line in
+ * sections of 100 points. This bit of weirdness has to do with wide
+ * geometric pens. The longer the polyline, the slower it draws. The trade
+ * off is that we lose dash and cap uniformity for unbearably slow polyline
+ * draws.
+ */
+ if (penPtr->traceGC->line_width > 1) {
+ np = 100;
+ } else {
+ np = Blt_MaxRequestSize(graphPtr->display, sizeof(POINT)) - 1;
+ }
+ points = Blt_AssertMalloc((np + 1) * sizeof(POINT));
+
+ dc = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
+
+ pen = Blt_GCToPen(dc, penPtr->traceGC);
+ oldPen = SelectPen(dc, pen);
+ brush = CreateSolidBrush(penPtr->traceGC->foreground);
+ oldBrush = SelectBrush(dc, brush);
+ SetROP2(dc, tkpWinRopModes[penPtr->traceGC->function]);
+
+ for (link = Blt_Chain_FirstLink(elemPtr->traces); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ POINT *p;
+ Trace *tracePtr;
+ int count, remaining;
+
+ tracePtr = Blt_Chain_GetValue(link);
+
+ /*
+ * If the trace has to be split into separate XDrawLines calls, then the
+ * end point of the current trace is also the starting point of the new
+ * split.
+ */
+
+ /* Step 1. Convert and draw the first section of the trace.
+ * It may contain the entire trace. */
+
+ for (p = points, count = 0;
+ count < MIN(np, tracePtr->screenPts.length);
+ count++, p++) {
+ p->x = Round(tracePtr->screenPts.points[count].x);
+ p->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ Polyline(dc, points, count);
+
+ /* Step 2. Next handle any full-size chunks left. */
+
+ while ((count + np) < tracePtr->screenPts.length) {
+ int j;
+
+ /* Start with the last point of the previous trace. */
+ points[0].x = points[np - 1].x;
+ points[0].y = points[np - 1].y;
+
+ for (p = points + 1, j = 0; j < np; j++, count++, p++) {
+ p->x = Round(tracePtr->screenPts.points[count].x);
+ p->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ Polyline(dc, points, np + 1);
+ }
+
+ /* Step 3. Convert and draw the remaining points. */
+
+ remaining = tracePtr->screenPts.length - count;
+ if (remaining > 0) {
+ /* Start with the last point of the previous trace. */
+ points[0].x = points[np - 1].x;
+ points[0].y = points[np - 1].y;
+
+ for (p = points + 1; count < tracePtr->screenPts.length;
+ count++, p++) {
+ p->x = Round(tracePtr->screenPts.points[count].x);
+ p->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ Polyline(dc, points, remaining + 1);
+ }
+ }
+ Blt_Free(points);
+ DeletePen(SelectPen(dc, oldPen));
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawTraces(Graph *graphPtr, Drawable drawable, LineElement *elemPtr,
+ LinePen *penPtr)
+{
+ Blt_ChainLink link;
+ XPoint *points;
+ int np;
+
+ np = Blt_MaxRequestSize(graphPtr->display, sizeof(XPoint)) - 1;
+ points = Blt_AssertMalloc((np + 1) * sizeof(XPoint));
+
+ for (link = Blt_Chain_FirstLink(elemPtr->traces); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ XPoint *xpp;
+ Trace *tracePtr;
+ int remaining, count;
+ int n;
+
+ tracePtr = Blt_Chain_GetValue(link);
+
+ /*
+ * If the trace has to be split into separate XDrawLines calls, then the
+ * end point of the current trace is also the starting point of the new
+ * split.
+ */
+ /* Step 1. Convert and draw the first section of the trace.
+ * It may contain the entire trace. */
+
+ n = MIN(np, tracePtr->screenPts.length);
+ for (xpp = points, count = 0; count < n; count++, xpp++) {
+ xpp->x = Round(tracePtr->screenPts.points[count].x);
+ xpp->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points,
+ count, CoordModeOrigin);
+
+ /* Step 2. Next handle any full-size chunks left. */
+
+ while ((count + np) < tracePtr->screenPts.length) {
+ int j;
+
+ /* Start with the last point of the previous trace. */
+ points[0].x = points[np - 1].x;
+ points[0].y = points[np - 1].y;
+
+ for (xpp = points + 1, j = 0; j < np; j++, count++, xpp++) {
+ xpp->x = Round(tracePtr->screenPts.points[count].x);
+ xpp->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points,
+ np + 1, CoordModeOrigin);
+ }
+
+ /* Step 3. Convert and draw the remaining points. */
+
+ remaining = tracePtr->screenPts.length - count;
+ if (remaining > 0) {
+ /* Start with the last point of the previous trace. */
+ points[0].x = points[np - 1].x;
+ points[0].y = points[np - 1].y;
+ for (xpp = points + 1; count < tracePtr->screenPts.length; count++,
+ xpp++) {
+ xpp->x = Round(tracePtr->screenPts.points[count].x);
+ xpp->y = Round(tracePtr->screenPts.points[count].y);
+ }
+ XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points,
+ remaining + 1, CoordModeOrigin);
+ }
+ }
+ Blt_Free(points);
+}
+#endif /* WIN32 */
+
+static void
+DrawValues(Graph *graphPtr, Drawable drawable, LineElement *elemPtr,
+ LinePen *penPtr, int length, Point2d *points, int *map)
+{
+ Point2d *pp, *endp;
+ double *xval, *yval;
+ const char *fmt;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ int count;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ xval = elemPtr->x.values, yval = elemPtr->y.values;
+ for (pp = points, endp = points + length; pp < endp; pp++) {
+ double x, y;
+
+ x = xval[map[count]];
+ y = yval[map[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ strcat(string, ",");
+ sprintf_s(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y);
+ }
+ Blt_DrawText(graphPtr->tkwin, drawable, string, &penPtr->valueStyle,
+ Round(pp->x), Round(pp->y));
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawActiveLineProc --
+ *
+ * Draws the connected line(s) representing the element. If the line is
+ * made up of non-line symbols and the line width parameter has been set
+ * (linewidth > 0), the element will also be drawn as a line (with the
+ * linewidth requested). The line may consist of separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawActiveLineProc(Graph *graphPtr, Drawable drawable, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ LinePen *penPtr = (LinePen *)elemPtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, penPtr->symbol.size);
+
+ /*
+ * nActiveIndices
+ * > 0 Some points are active. Uses activeArr.
+ * < 0 All points are active.
+ * == 0 No points are active.
+ */
+ if (elemPtr->nActiveIndices > 0) {
+ if (elemPtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, elemPtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, elemPtr, penPtr, symbolSize,
+ elemPtr->activePts.length, elemPtr->activePts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, elemPtr, penPtr,
+ elemPtr->activePts.length,
+ elemPtr->activePts.points,
+ elemPtr->activePts.map);
+ }
+ } else if (elemPtr->nActiveIndices < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (elemPtr->lines.length > 0) {
+ Blt_Draw2DSegments(graphPtr->display, drawable,
+ penPtr->traceGC, elemPtr->lines.segments,
+ elemPtr->lines.length);
+ } else if (Blt_Chain_GetLength(elemPtr->traces) > 0) {
+ DrawTraces(graphPtr, drawable, elemPtr, penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, elemPtr, penPtr, symbolSize,
+ elemPtr->symbolPts.length, elemPtr->symbolPts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, elemPtr, penPtr,
+ elemPtr->symbolPts.length, elemPtr->symbolPts.points,
+ elemPtr->symbolPts.map);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawNormalLine --
+ *
+ * Draws the connected line(s) representing the element. If the line is
+ * made up of non-line symbols and the line width parameter has been set
+ * (linewidth > 0), the element will also be drawn as a line (with the
+ * linewidth requested). The line may consist of separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawNormalLineProc(Graph *graphPtr, Drawable drawable, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ Blt_ChainLink link;
+ unsigned int count;
+
+ /* Fill area under the curve */
+ if (elemPtr->fillPts != NULL) {
+ XPoint *points;
+ Point2d *endp, *pp;
+
+ points = Blt_AssertMalloc(sizeof(XPoint) * elemPtr->nFillPts);
+ count = 0;
+ for (pp = elemPtr->fillPts, endp = pp + elemPtr->nFillPts;
+ pp < endp; pp++) {
+ points[count].x = Round(pp->x);
+ points[count].y = Round(pp->y);
+ count++;
+ }
+ if (elemPtr->fillBg != NULL) {
+ Blt_SetBackgroundOrigin(graphPtr->tkwin, elemPtr->fillBg, 0, 0);
+ Blt_FillBackgroundPolygon(graphPtr->tkwin, drawable,
+ elemPtr->fillBg, points, elemPtr->nFillPts, 0, TK_RELIEF_FLAT);
+ }
+ Blt_Free(points);
+ }
+
+ /* Lines: stripchart segments or graph traces. */
+ if (elemPtr->lines.length > 0) {
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ if ((stylePtr->lines.length > 0) &&
+ (penPtr->errorBarLineWidth > 0)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->traceGC,
+ stylePtr->lines.segments, stylePtr->lines.length);
+ }
+ }
+ } else {
+ LinePen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ if ((Blt_Chain_GetLength(elemPtr->traces) > 0) &&
+ (penPtr->traceWidth > 0)) {
+ DrawTraces(graphPtr, drawable, elemPtr, penPtr);
+ }
+ }
+
+ if (elemPtr->reqMaxSymbols > 0) {
+ int total;
+
+ total = 0;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ total += stylePtr->symbolPts.length;
+ }
+ elemPtr->symbolInterval = total / elemPtr->reqMaxSymbols;
+ elemPtr->symbolCounter = 0;
+ }
+
+ /* Symbols, error bars, values. */
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ if ((stylePtr->xeb.length > 0) && (penPtr->errorBarShow & SHOW_X)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC,
+ stylePtr->xeb.segments, stylePtr->xeb.length);
+ }
+ if ((stylePtr->yeb.length > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC,
+ stylePtr->yeb.segments, stylePtr->yeb.length);
+ }
+ if ((stylePtr->symbolPts.length > 0) &&
+ (penPtr->symbol.type != SYMBOL_NONE)) {
+ DrawSymbols(graphPtr, drawable, elemPtr, penPtr,
+ stylePtr->symbolSize, stylePtr->symbolPts.length,
+ stylePtr->symbolPts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, elemPtr, penPtr,
+ stylePtr->symbolPts.length, stylePtr->symbolPts.points,
+ elemPtr->symbolPts.map + count);
+ }
+ count += stylePtr->symbolPts.length;
+ }
+ elemPtr->symbolInterval = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetSymbolPostScriptInfo --
+ *
+ * Set up the PostScript environment with the macros and attributes needed
+ * to draw the symbols of the element.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GetSymbolPostScriptInfo(
+ Graph *graphPtr,
+ Blt_Ps ps,
+ LinePen *penPtr,
+ int size)
+{
+ XColor *outlineColor, *fillColor, *defaultColor;
+
+ /* Set line and foreground attributes */
+ outlineColor = penPtr->symbol.outlineColor;
+ fillColor = penPtr->symbol.fillColor;
+ defaultColor = penPtr->traceColor;
+
+ if (fillColor == COLOR_DEFAULT) {
+ fillColor = defaultColor;
+ }
+ if (outlineColor == COLOR_DEFAULT) {
+ outlineColor = defaultColor;
+ }
+ if (penPtr->symbol.type == SYMBOL_NONE) {
+ Blt_Ps_XSetLineAttributes(ps, defaultColor, penPtr->traceWidth + 2,
+ &penPtr->traceDashes, CapButt, JoinMiter);
+ } else {
+ Blt_Ps_XSetLineWidth(ps, penPtr->symbol.outlineWidth);
+ Blt_Ps_XSetDashes(ps, (Blt_Dashes *)NULL);
+ }
+
+ /*
+ * Build a PostScript procedure to draw the symbols. For bitmaps, paint
+ * both the bitmap and its mask. Otherwise fill and stroke the path formed
+ * already.
+ */
+ Blt_Ps_Append(ps, "\n/DrawSymbolProc {\n");
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break; /* Do nothing */
+ case SYMBOL_BITMAP:
+ {
+ int w, h;
+ double sx, sy, scale;
+
+ /*
+ * Compute how much to scale the bitmap. Don't let the scaled
+ * bitmap exceed the bounding square for the symbol.
+ */
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap, &w, &h);
+ sx = (double)size / (double)w;
+ sy = (double)size / (double)h;
+ scale = MIN(sx, sy);
+
+ if ((penPtr->symbol.mask != None) && (fillColor != NULL)) {
+ Blt_Ps_VarAppend(ps, "\n % Bitmap mask is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.mask),
+ "\"\n\n ", (char *)NULL);
+ Blt_Ps_XSetBackground(ps, fillColor);
+ Blt_Ps_DrawBitmap(ps, graphPtr->display, penPtr->symbol.mask,
+ scale, scale);
+ }
+ Blt_Ps_VarAppend(ps, "\n % Bitmap symbol is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.bitmap),
+ "\"\n\n ", (char *)NULL);
+ Blt_Ps_XSetForeground(ps, outlineColor);
+ Blt_Ps_DrawBitmap(ps, graphPtr->display, penPtr->symbol.bitmap,
+ scale, scale);
+ }
+ break;
+ default:
+ if (fillColor != NULL) {
+ Blt_Ps_Append(ps, " ");
+ Blt_Ps_XSetBackground(ps, fillColor);
+ Blt_Ps_Append(ps, " gsave fill grestore\n");
+ }
+ if ((outlineColor != NULL) && (penPtr->symbol.outlineWidth > 0)) {
+ Blt_Ps_Append(ps, " ");
+ Blt_Ps_XSetForeground(ps, outlineColor);
+ Blt_Ps_Append(ps, " stroke\n");
+ }
+ break;
+ }
+ Blt_Ps_Append(ps, "} def\n\n");
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SymbolsToPostScript --
+ *
+ * Draw a symbol centered at the given x,y window coordinate based upon the
+ * element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when calculating the
+ * centered position of the symbol.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+SymbolsToPostScript(
+ Graph *graphPtr,
+ Blt_Ps ps,
+ LinePen *penPtr,
+ int size,
+ int nSymbolPts,
+ Point2d *symbolPts)
+{
+ double symbolSize;
+ static const char *symbolMacros[] =
+ {
+ "Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm",
+ (char *)NULL,
+ };
+ GetSymbolPostScriptInfo(graphPtr, ps, penPtr, size);
+
+ symbolSize = (double)size;
+ switch (penPtr->symbol.type) {
+ case SYMBOL_SQUARE:
+ case SYMBOL_CROSS:
+ case SYMBOL_PLUS:
+ case SYMBOL_SCROSS:
+ case SYMBOL_SPLUS:
+ symbolSize = (double)Round(size * S_RATIO);
+ break;
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ symbolSize = (double)Round(size * 0.7);
+ break;
+ case SYMBOL_DIAMOND:
+ symbolSize = (double)Round(size * M_SQRT1_2);
+ break;
+
+ default:
+ break;
+ }
+ {
+ Point2d *pp, *endp;
+
+ for (pp = symbolPts, endp = symbolPts + nSymbolPts; pp < endp; pp++) {
+ Blt_Ps_Format(ps, "%g %g %g %s\n", pp->x, pp->y,
+ symbolSize, symbolMacros[penPtr->symbol.type]);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SymbolToPostScriptProc --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+SymbolToPostScriptProc(
+ Graph *graphPtr, /* Graph widget record */
+ Blt_Ps ps,
+ Element *basePtr, /* Line element information */
+ double x, double y, /* Center position of symbol */
+ int size) /* Size of element */
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ LinePen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to give a
+ * thicker appearance. This is only for the legend entry. This routine
+ * is never called for drawing the actual line segments.
+ */
+ Blt_Ps_XSetLineAttributes(ps, penPtr->traceColor,
+ penPtr->traceWidth, &penPtr->traceDashes, CapButt, JoinMiter);
+ Blt_Ps_Format(ps, "%g %g %d Li\n", x, y, size + size);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2d point;
+
+ point.x = x, point.y = y;
+ SymbolsToPostScript(graphPtr, ps, penPtr, size, 1, &point);
+ }
+}
+
+static void
+SetLineAttributes(Blt_Ps ps, LinePen *penPtr)
+{
+ /* Set the attributes of the line (color, dashes, linewidth) */
+ Blt_Ps_XSetLineAttributes(ps, penPtr->traceColor,
+ penPtr->traceWidth, &penPtr->traceDashes, CapButt, JoinMiter);
+ if ((LineIsDashed(penPtr->traceDashes)) &&
+ (penPtr->traceOffColor != NULL)) {
+ Blt_Ps_Append(ps, "/DashesProc {\n gsave\n ");
+ Blt_Ps_XSetBackground(ps, penPtr->traceOffColor);
+ Blt_Ps_Append(ps, " ");
+ Blt_Ps_XSetDashes(ps, (Blt_Dashes *)NULL);
+ Blt_Ps_Append(ps, "stroke\n grestore\n} def\n");
+ } else {
+ Blt_Ps_Append(ps, "/DashesProc {} def\n");
+ }
+}
+
+static void
+TracesToPostScript(Blt_Ps ps, LineElement *elemPtr, LinePen *penPtr)
+{
+ Blt_ChainLink link;
+
+ SetLineAttributes(ps, penPtr);
+ for (link = Blt_Chain_FirstLink(elemPtr->traces); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Trace *tracePtr;
+
+ tracePtr = Blt_Chain_GetValue(link);
+ if (tracePtr->screenPts.length > 0) {
+ Blt_Ps_Append(ps, "% start trace\n");
+ Blt_Ps_DrawPolyline(ps, tracePtr->screenPts.points,
+ tracePtr->screenPts.length);
+ Blt_Ps_Append(ps, "% end trace\n");
+ }
+ }
+}
+
+
+static void
+ValuesToPostScript(Blt_Ps ps, LineElement *elemPtr, LinePen *penPtr,
+ int nSymbolPts, Point2d *symbolPts, int *pointToData)
+{
+ Point2d *pp, *endp;
+ int count;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ const char *fmt;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (pp = symbolPts, endp = symbolPts + nSymbolPts; pp < endp; pp++) {
+ double x, y;
+
+ x = elemPtr->x.values[pointToData[count]];
+ y = elemPtr->y.values[pointToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf_s(string, TCL_DOUBLE_SPACE, fmt, x);
+ strcat(string, ",");
+ sprintf_s(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y);
+ }
+ Blt_Ps_DrawText(ps, string, &penPtr->valueStyle, pp->x, pp->y);
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ActiveLineToPostScript --
+ *
+ * Generates PostScript commands to draw as "active" the points (symbols)
+ * and or line segments (trace) representing the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ActiveLineToPostScriptProc(Graph *graphPtr, Blt_Ps ps, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ LinePen *penPtr = (LinePen *)elemPtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, penPtr->symbol.size);
+ if (elemPtr->nActiveIndices > 0) {
+ if (elemPtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, elemPtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, ps, penPtr, symbolSize,
+ elemPtr->activePts.length, elemPtr->activePts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(ps, elemPtr, penPtr, elemPtr->activePts.length,
+ elemPtr->activePts.points, elemPtr->activePts.map);
+ }
+ } else if (elemPtr->nActiveIndices < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (elemPtr->lines.length > 0) {
+ SetLineAttributes(ps, penPtr);
+ Blt_Ps_Draw2DSegments(ps, elemPtr->lines.segments,
+ elemPtr->lines.length);
+ }
+ if (Blt_Chain_GetLength(elemPtr->traces) > 0) {
+ TracesToPostScript(ps, elemPtr, (LinePen *)penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, ps, penPtr, symbolSize,
+ elemPtr->symbolPts.length, elemPtr->symbolPts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(ps, elemPtr, penPtr, elemPtr->symbolPts.length,
+ elemPtr->symbolPts.points, elemPtr->symbolPts.map);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NormalLineToPostScriptProc --
+ *
+ * Similar to the DrawLine procedure, prints PostScript related commands to
+ * form the connected line(s) representing the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+NormalLineToPostScriptProc(Graph *graphPtr, Blt_Ps ps, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ Blt_ChainLink link;
+ unsigned int count;
+
+ /* Draw fill area */
+ if (elemPtr->fillPts != NULL) {
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_Ps_Append(ps, "% start fill area\n");
+ Blt_Ps_Polyline(ps, elemPtr->fillPts, elemPtr->nFillPts);
+
+ /* If the background fill color was specified, draw the polygon in a
+ * solid fashion with that color. */
+ if (elemPtr->fillBgColor != NULL) {
+ Blt_Ps_XSetBackground(ps, elemPtr->fillBgColor);
+ Blt_Ps_Append(ps, "gsave fill grestore\n");
+ }
+ Blt_Ps_XSetForeground(ps, elemPtr->fillFgColor);
+ if (elemPtr->fillBg != NULL) {
+ Blt_Ps_Append(ps, "gsave fill grestore\n");
+ /* TBA: Transparent tiling is the hard part. */
+ } else {
+ Blt_Ps_Append(ps, "gsave fill grestore\n");
+ }
+ Blt_Ps_Append(ps, "% end fill area\n");
+ }
+
+ /* Draw lines (strip chart) or traces (xy graph) */
+ if (elemPtr->lines.length > 0) {
+ for (link = Blt_Chain_FirstLink(elemPtr->styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ if ((stylePtr->lines.length > 0) && (penPtr->traceWidth > 0)) {
+ SetLineAttributes(ps, penPtr);
+ Blt_Ps_Append(ps, "% start segments\n");
+ Blt_Ps_Draw2DSegments(ps, stylePtr->lines.segments,
+ stylePtr->lines.length);
+ Blt_Ps_Append(ps, "% end segments\n");
+ }
+ }
+ } else {
+ LinePen *penPtr;
+
+ penPtr = NORMALPEN(elemPtr);
+ if ((Blt_Chain_GetLength(elemPtr->traces) > 0) &&
+ (penPtr->traceWidth > 0)) {
+ TracesToPostScript(ps, elemPtr, penPtr);
+ }
+ }
+
+ /* Draw symbols, error bars, values. */
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(elemPtr->styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+ XColor *colorPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ colorPtr = penPtr->errorBarColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = penPtr->traceColor;
+ }
+ if ((stylePtr->xeb.length > 0) && (penPtr->errorBarShow & SHOW_X)) {
+ Blt_Ps_XSetLineAttributes(ps, colorPtr, penPtr->errorBarLineWidth,
+ NULL, CapButt, JoinMiter);
+ Blt_Ps_Draw2DSegments(ps, stylePtr->xeb.segments,
+ stylePtr->xeb.length);
+ }
+ if ((stylePtr->yeb.length > 0) && (penPtr->errorBarShow & SHOW_Y)) {
+ Blt_Ps_XSetLineAttributes(ps, colorPtr, penPtr->errorBarLineWidth,
+ NULL, CapButt, JoinMiter);
+ Blt_Ps_Draw2DSegments(ps, stylePtr->yeb.segments,
+ stylePtr->yeb.length);
+ }
+ if ((stylePtr->symbolPts.length > 0) &&
+ (penPtr->symbol.type != SYMBOL_NONE)) {
+ SymbolsToPostScript(graphPtr, ps, penPtr, stylePtr->symbolSize,
+ stylePtr->symbolPts.length, stylePtr->symbolPts.points);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(ps, elemPtr, penPtr, stylePtr->symbolPts.length,
+ stylePtr->symbolPts.points, elemPtr->symbolPts.map + count);
+ }
+ count += stylePtr->symbolPts.length;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyLineProc --
+ *
+ * Release memory and resources allocated for the line element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the line element is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static void
+DestroyLineProc(Graph *graphPtr, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+
+ DestroyPenProc(graphPtr, (Pen *)&elemPtr->builtinPen);
+ if (elemPtr->activePenPtr != NULL) {
+ Blt_FreePen((Pen *)elemPtr->activePenPtr);
+ }
+ ResetLine(elemPtr);
+ if (elemPtr->styles != NULL) {
+ Blt_FreeStylePalette(elemPtr->styles);
+ Blt_Chain_Destroy(elemPtr->styles);
+ }
+ if (elemPtr->activeIndices != NULL) {
+ Blt_Free(elemPtr->activeIndices);
+ }
+ if (elemPtr->fillPts != NULL) {
+ Blt_Free(elemPtr->fillPts);
+ }
+ if (elemPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, elemPtr->fillGC);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LineElement --
+ *
+ * Allocate memory and initialize methods for the new line element.
+ *
+ * Results:
+ * The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line element structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static ElementProcs lineProcs =
+{
+ ClosestLineProc, /* Finds the closest element/data
+ * point */
+ ConfigureLineProc, /* Configures the element. */
+ DestroyLineProc, /* Destroys the element. */
+ DrawActiveLineProc, /* Draws active element */
+ DrawNormalLineProc, /* Draws normal element */
+ DrawSymbolProc, /* Draws the element symbol. */
+ GetLineExtentsProc, /* Find the extents of the element's
+ * data. */
+ ActiveLineToPostScriptProc, /* Prints active element. */
+ NormalLineToPostScriptProc, /* Prints normal element. */
+ SymbolToPostScriptProc, /* Prints the line's symbol. */
+ MapLineProc /* Compute element's screen
+ * coordinates. */
+};
+
+Element *
+Blt_LineElement(Graph *graphPtr, const char *name, ClassId classId)
+{
+ LineElement *elemPtr;
+
+ elemPtr = Blt_AssertCalloc(1, sizeof(LineElement));
+ elemPtr->procsPtr = &lineProcs;
+ if (classId == CID_ELEM_LINE) {
+ elemPtr->configSpecs = lineElemConfigSpecs;
+ } else {
+ elemPtr->configSpecs = stripElemConfigSpecs;
+ }
+ elemPtr->obj.name = Blt_AssertStrdup(name);
+ Blt_GraphSetObjectClass(&elemPtr->obj, classId);
+ elemPtr->flags = SCALE_SYMBOL;
+ elemPtr->obj.graphPtr = graphPtr;
+ /* By default an element's name and label are the same. */
+ elemPtr->label = Blt_AssertStrdup(name);
+ elemPtr->legendRelief = TK_RELIEF_FLAT;
+ elemPtr->penDir = PEN_BOTH_DIRECTIONS;
+ elemPtr->styles = Blt_Chain_Create();
+ elemPtr->builtinPenPtr = &elemPtr->builtinPen;
+ elemPtr->reqSmooth = PEN_SMOOTH_LINEAR;
+ InitLinePen(elemPtr->builtinPenPtr);
+ bltLineStylesOption.clientData = (ClientData)sizeof(LineStyle);
+ return (Element *)elemPtr;
+}
+
+#ifdef notdef
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapLineProc --
+ *
+ * Calculates the actual window coordinates of the line element. The
+ * window coordinates are saved in an allocated point array.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is (re)allocated for the point array.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapLineProc(Graph *graphPtr, Element *basePtr)
+{
+ LineElement *elemPtr = (LineElement *)basePtr;
+ MapInfo mi;
+ int size, np;
+ LineStyle **styleMap;
+ Blt_ChainLink link;
+
+ ResetLine(elemPtr);
+ np = NUMBEROFPOINTS(elemPtr);
+ if (np < 1) {
+ return; /* No data points */
+ }
+ GetScreenPoints(graphPtr, elemPtr, &mi);
+ MapSymbols(graphPtr, elemPtr, &mi);
+
+ if ((elemPtr->flags & ACTIVE_PENDING) && (elemPtr->nActiveIndices > 0)) {
+ MapActiveSymbols(graphPtr, elemPtr);
+ }
+ /*
+ * Map connecting line segments if they are to be displayed.
+ */
+ elemPtr->smooth = elemPtr->reqSmooth;
+ if ((np > 1) && ((graphPtr->classId == CID_ELEM_STRIP) ||
+ (elemPtr->builtinPen.traceWidth > 0))) {
+
+ /*
+ * Do smoothing if necessary. This can extend the coordinate array,
+ * so both mi.points and mi.nPoints may change.
+ */
+ switch (elemPtr->smooth) {
+ case PEN_SMOOTH_STEP:
+ GenerateSteps(&mi);
+ break;
+
+ case PEN_SMOOTH_NATURAL:
+ case PEN_SMOOTH_QUADRATIC:
+ if (mi.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ } else {
+ GenerateSpline(graphPtr, elemPtr, &mi);
+ }
+ break;
+
+ case PEN_SMOOTH_CATROM:
+ if (mi.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ elemPtr->smooth = PEN_SMOOTH_LINEAR;
+ } else {
+ GenerateParametricSpline(graphPtr, elemPtr, &mi);
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (elemPtr->rTolerance > 0.0) {
+ ReducePoints(&mi, elemPtr->rTolerance);
+ }
+ if (elemPtr->fillBg != NULL) {
+ MapFillArea(graphPtr, elemPtr, &mi);
+ }
+ if (graphPtr->classId == CID_ELEM_STRIP) {
+ MapStrip(graphPtr, elemPtr, &mi);
+ } else {
+ MapTraces(graphPtr, elemPtr, &mi);
+ }
+ }
+ Blt_Free(mi.screenPts);
+ Blt_Free(mi.map);
+
+ /* Set the symbol size of all the pen styles. */
+ for (link = Blt_Chain_FirstLink(elemPtr->styles); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ LineStyle *stylePtr;
+ LinePen *penPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ penPtr = (LinePen *)stylePtr->penPtr;
+ size = ScaleSymbol(elemPtr, penPtr->symbol.size);
+ stylePtr->symbolSize = size;
+ stylePtr->errorBarCapWidth = (penPtr->errorBarCapWidth > 0)
+ ? penPtr->errorBarCapWidth : Round(size * 0.6666666);
+ stylePtr->errorBarCapWidth /= 2;
+ }
+ styleMap = (LineStyle **)Blt_StyleMap((Element *)elemPtr);
+ if (((elemPtr->yHigh.nValues > 0) && (elemPtr->yLow.nValues > 0)) ||
+ ((elemPtr->xHigh.nValues > 0) && (elemPtr->xLow.nValues > 0)) ||
+ (elemPtr->xError.nValues > 0) || (elemPtr->yError.nValues > 0)) {
+ MapErrorBars(graphPtr, elemPtr, styleMap);
+ }
+ MergePens(elemPtr, styleMap);
+ Blt_Free(styleMap);
+}
+#endif
diff --git a/src/bltGrElemLineSpline.C b/src/bltGrElemLineSpline.C
new file mode 100644
index 0000000..3f3b621
--- /dev/null
+++ b/src/bltGrElemLineSpline.C
@@ -0,0 +1,1399 @@
+
+#include "bltInt.h"
+#include "bltOp.h"
+#include <bltVector.h>
+
+typedef int (SplineProc)(Point2d origPts[], int nOrigPts, Point2d intpPts[],
+ int nIntpPts);
+
+typedef double TriDiagonalMatrix[3];
+typedef struct {
+ double b, c, d;
+} Cubic2D;
+
+typedef struct {
+ double b, c, d, e, f;
+} Quint2D;
+
+/*
+ * Quadratic spline parameters
+ */
+#define E1 param[0]
+#define E2 param[1]
+#define V1 param[2]
+#define V2 param[3]
+#define W1 param[4]
+#define W2 param[5]
+#define Z1 param[6]
+#define Z2 param[7]
+#define Y1 param[8]
+#define Y2 param[9]
+
+static Tcl_ObjCmdProc SplineCmd;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Search --
+ *
+ * Conducts a binary search for a value. This routine is called
+ * only if key is between x(0) and x(len - 1).
+ *
+ * Results:
+ * Returns the index of the largest value in xtab for which
+ * x[i] < key.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+Search(
+ Point2d points[], /* Contains the abscissas of the data
+ * points of interpolation. */
+ int nPoints, /* Dimension of x. */
+ double key, /* Value whose relative position in
+ * x is to be located. */
+ int *foundPtr) /* (out) Returns 1 if s is found in
+ * x and 0 otherwise. */
+{
+ int high, low, mid;
+
+ low = 0;
+ high = nPoints - 1;
+
+ while (high >= low) {
+ mid = (high + low) / 2;
+ if (key > points[mid].x) {
+ low = mid + 1;
+ } else if (key < points[mid].x) {
+ high = mid - 1;
+ } else {
+ *foundPtr = 1;
+ return mid;
+ }
+ }
+ *foundPtr = 0;
+ return low;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadChoose --
+ *
+ * Determines the case needed for the computation of the parame-
+ * ters of the quadratic spline.
+ *
+ * Results:
+ * Returns a case number (1-4) which controls how the parameters
+ * of the quadratic spline are evaluated.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+QuadChoose(
+ Point2d *p, /* Coordinates of one of the points of
+ * interpolation */
+ Point2d *q, /* Coordinates of one of the points of
+ * interpolation */
+ double m1, /* Derivative condition at point P */
+ double m2, /* Derivative condition at point Q */
+ double epsilon) /* Error tolerance used to distinguish
+ * cases when m1 or m2 is relatively
+ * close to the slope or twice the
+ * slope of the line segment joining
+ * the points P and Q. If
+ * epsilon is not 0.0, then epsilon
+ * should be greater than or equal to
+ * machine epsilon. */
+{
+ double slope;
+
+ /* Calculate the slope of the line joining P and Q. */
+ slope = (q->y - p->y) / (q->x - p->x);
+
+ if (slope != 0.0) {
+ double relerr;
+ double mref, mref1, mref2, prod1, prod2;
+
+ prod1 = slope * m1;
+ prod2 = slope * m2;
+
+ /* Find the absolute values of the slopes slope, m1, and m2. */
+ mref = FABS(slope);
+ mref1 = FABS(m1);
+ mref2 = FABS(m2);
+
+ /*
+ * If the relative deviation of m1 or m2 from slope is less than
+ * epsilon, then choose case 2 or case 3.
+ */
+ relerr = epsilon * mref;
+ if ((FABS(slope - m1) > relerr) && (FABS(slope - m2) > relerr) &&
+ (prod1 >= 0.0) && (prod2 >= 0.0)) {
+ double prod;
+
+ prod = (mref - mref1) * (mref - mref2);
+ if (prod < 0.0) {
+ /*
+ * l1, the line through (x1,y1) with slope m1, and l2,
+ * the line through (x2,y2) with slope m2, intersect
+ * at a point whose abscissa is between x1 and x2.
+ * The abscissa becomes a knot of the spline.
+ */
+ return 1;
+ }
+ if (mref1 > (mref * 2.0)) {
+ if (mref2 <= ((2.0 - epsilon) * mref)) {
+ return 3;
+ }
+ } else if (mref2 <= (mref * 2.0)) {
+ /*
+ * Both l1 and l2 cross the line through
+ * (x1+x2)/2.0,y1 and (x1+x2)/2.0,y2, which is the
+ * midline of the rectangle formed by P and Q or both
+ * m1 and m2 have signs different than the sign of
+ * slope, or one of m1 and m2 has opposite sign from
+ * slope and l1 and l2 intersect to the left of x1 or
+ * to the right of x2. The point (x1+x2)/2. is a knot
+ * of the spline.
+ */
+ return 2;
+ } else if (mref1 <= ((2.0 - epsilon) * mref)) {
+ /*
+ * In cases 3 and 4, sign(m1)=sign(m2)=sign(slope).
+ * Either l1 or l2 crosses the midline, but not both.
+ * Choose case 4 if mref1 is greater than
+ * (2.-epsilon)*mref; otherwise, choose case 3.
+ */
+ return 3;
+ }
+ /*
+ * If neither l1 nor l2 crosses the midline, the spline
+ * requires two knots between x1 and x2.
+ */
+ return 4;
+ } else {
+ /*
+ * The sign of at least one of the slopes m1 or m2 does not
+ * agree with the sign of *slope*.
+ */
+ if ((prod1 < 0.0) && (prod2 < 0.0)) {
+ return 2;
+ } else if (prod1 < 0.0) {
+ if (mref2 > ((epsilon + 1.0) * mref)) {
+ return 1;
+ } else {
+ return 2;
+ }
+ } else if (mref1 > ((epsilon + 1.0) * mref)) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+ } else if ((m1 * m2) >= 0.0) {
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadCases --
+ *
+ * Computes the knots and other parameters of the spline on the
+ * interval PQ.
+ *
+ *
+ * On input--
+ *
+ * P and Q are the coordinates of the points of interpolation.
+ *
+ * m1 is the slope at P.
+ *
+ * m2 is the slope at Q.
+ *
+ * ncase controls the number and location of the knots.
+ *
+ *
+ * On output--
+ *
+ * (v1,v2),(w1,w2),(z1,z2), and (e1,e2) are the coordinates of
+ * the knots and other parameters of the spline on P.
+ * (e1,e2) and Q are used only if ncase=4.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+QuadCases(Point2d *p, Point2d *q, double m1, double m2, double param[],
+ int which)
+{
+ if ((which == 3) || (which == 4)) { /* Parameters used in both 3 and 4 */
+ double mbar1, mbar2, mbar3, c1, d1, h1, j1, k1;
+
+ c1 = p->x + (q->y - p->y) / m1;
+ d1 = q->x + (p->y - q->y) / m2;
+ h1 = c1 * 2.0 - p->x;
+ j1 = d1 * 2.0 - q->x;
+ mbar1 = (q->y - p->y) / (h1 - p->x);
+ mbar2 = (p->y - q->y) / (j1 - q->x);
+
+ if (which == 4) { /* Case 4. */
+ Y1 = (p->x + c1) / 2.0;
+ V1 = (p->x + Y1) / 2.0;
+ V2 = m1 * (V1 - p->x) + p->y;
+ Z1 = (d1 + q->x) / 2.0;
+ W1 = (q->x + Z1) / 2.0;
+ W2 = m2 * (W1 - q->x) + q->y;
+ mbar3 = (W2 - V2) / (W1 - V1);
+ Y2 = mbar3 * (Y1 - V1) + V2;
+ Z2 = mbar3 * (Z1 - V1) + V2;
+ E1 = (Y1 + Z1) / 2.0;
+ E2 = mbar3 * (E1 - V1) + V2;
+ } else { /* Case 3. */
+ k1 = (p->y - q->y + q->x * mbar2 - p->x * mbar1) / (mbar2 - mbar1);
+ if (FABS(m1) > FABS(m2)) {
+ Z1 = (k1 + p->x) / 2.0;
+ } else {
+ Z1 = (k1 + q->x) / 2.0;
+ }
+ V1 = (p->x + Z1) / 2.0;
+ V2 = p->y + m1 * (V1 - p->x);
+ W1 = (q->x + Z1) / 2.0;
+ W2 = q->y + m2 * (W1 - q->x);
+ Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+ }
+ } else if (which == 2) { /* Case 2. */
+ Z1 = (p->x + q->x) / 2.0;
+ V1 = (p->x + Z1) / 2.0;
+ V2 = p->y + m1 * (V1 - p->x);
+ W1 = (Z1 + q->x) / 2.0;
+ W2 = q->y + m2 * (W1 - q->x);
+ Z2 = (V2 + W2) / 2.0;
+ } else { /* Case 1. */
+ double ztwo;
+
+ Z1 = (p->y - q->y + m2 * q->x - m1 * p->x) / (m2 - m1);
+ ztwo = p->y + m1 * (Z1 - p->x);
+ V1 = (p->x + Z1) / 2.0;
+ V2 = (p->y + ztwo) / 2.0;
+ W1 = (Z1 + q->x) / 2.0;
+ W2 = (ztwo + q->y) / 2.0;
+ Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1);
+ }
+}
+
+static int
+QuadSelect(Point2d *p, Point2d *q, double m1, double m2, double epsilon,
+ double param[])
+{
+ int ncase;
+
+ ncase = QuadChoose(p, q, m1, m2, epsilon);
+ QuadCases(p, q, m1, m2, param, ncase);
+ return ncase;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadGetImage --
+ *
+ *---------------------------------------------------------------------------
+ */
+INLINE static double
+QuadGetImage(double p1, double p2, double p3, double x1, double x2, double x3)
+{
+ double A, B, C;
+ double y;
+
+ A = x1 - x2;
+ B = x2 - x3;
+ C = x1 - x3;
+
+ y = (p1 * (A * A) + p2 * 2.0 * B * A + p3 * (B * B)) / (C * C);
+ return y;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadSpline --
+ *
+ * Finds the image of a point in x.
+ *
+ * On input
+ *
+ * x Contains the value at which the spline is evaluated.
+ * leftX, leftY
+ * Coordinates of the left-hand data point used in the
+ * evaluation of x values.
+ * rightX, rightY
+ * Coordinates of the right-hand data point used in the
+ * evaluation of x values.
+ * Z1, Z2, Y1, Y2, E2, W2, V2
+ * Parameters of the spline.
+ * ncase Controls the evaluation of the spline by indicating
+ * whether one or two knots were placed in the interval
+ * (xtabs,xtabs1).
+ *
+ * Results:
+ * The image of the spline at x.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+QuadSpline(
+ Point2d *intp, /* Value at which spline is evaluated */
+ Point2d *left, /* Point to the left of the data point to
+ * be evaluated */
+ Point2d *right, /* Point to the right of the data point to
+ * be evaluated */
+ double param[], /* Parameters of the spline */
+ int ncase) /* Controls the evaluation of the
+ * spline by indicating whether one or
+ * two knots were placed in the
+ * interval (leftX,rightX) */
+{
+ double y;
+
+ if (ncase == 4) {
+ /*
+ * Case 4: More than one knot was placed in the interval.
+ */
+
+ /*
+ * Determine the location of data point relative to the 1st knot.
+ */
+ if (Y1 > intp->x) {
+ y = QuadGetImage(left->y, V2, Y2, Y1, intp->x, left->x);
+ } else if (Y1 < intp->x) {
+ /*
+ * Determine the location of the data point relative to
+ * the 2nd knot.
+ */
+ if (Z1 > intp->x) {
+ y = QuadGetImage(Y2, E2, Z2, Z1, intp->x, Y1);
+ } else if (Z1 < intp->x) {
+ y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+ } else {
+ y = Z2;
+ }
+ } else {
+ y = Y2;
+ }
+ } else {
+
+ /*
+ * Cases 1, 2, or 3:
+ *
+ * Determine the location of the data point relative to the
+ * knot.
+ */
+ if (Z1 < intp->x) {
+ y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1);
+ } else if (Z1 > intp->x) {
+ y = QuadGetImage(left->y, V2, Z2, Z1, intp->x, left->x);
+ } else {
+ y = Z2;
+ }
+ }
+ intp->y = y;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadSlopes --
+ *
+ * Calculates the derivative at each of the data points. The
+ * slopes computed will insure that an osculatory quadratic
+ * spline will have one additional knot between two adjacent
+ * points of interpolation. Convexity and monotonicity are
+ * preserved wherever these conditions are compatible with the
+ * data.
+ *
+ * Results:
+ * The output array "m" is filled with the derivates at each
+ * data point.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+QuadSlopes(Point2d *points, double *m, int nPoints)
+{
+ double xbar, xmid, xhat, ydif1, ydif2;
+ double yxmid;
+ double m1, m2;
+ double m1s, m2s;
+ int i, n, l;
+
+ m1s = m2s = m1 = m2 = 0;
+ for (l = 0, i = 1, n = 2; i < (nPoints - 1); l++, i++, n++) {
+ /*
+ * Calculate the slopes of the two lines joining three
+ * consecutive data points.
+ */
+ ydif1 = points[i].y - points[l].y;
+ ydif2 = points[n].y - points[i].y;
+ m1 = ydif1 / (points[i].x - points[l].x);
+ m2 = ydif2 / (points[n].x - points[i].x);
+ if (i == 1) {
+ m1s = m1, m2s = m2; /* Save slopes of starting point */
+ }
+ /*
+ * If one of the preceding slopes is zero or if they have opposite
+ * sign, assign the value zero to the derivative at the middle
+ * point.
+ */
+ if ((m1 == 0.0) || (m2 == 0.0) || ((m1 * m2) <= 0.0)) {
+ m[i] = 0.0;
+ } else if (FABS(m1) > FABS(m2)) {
+ /*
+ * Calculate the slope by extending the line with slope m1.
+ */
+ xbar = ydif2 / m1 + points[i].x;
+ xhat = (xbar + points[n].x) / 2.0;
+ m[i] = ydif2 / (xhat - points[i].x);
+ } else {
+ /*
+ * Calculate the slope by extending the line with slope m2.
+ */
+ xbar = -ydif1 / m2 + points[i].x;
+ xhat = (points[l].x + xbar) / 2.0;
+ m[i] = ydif1 / (points[i].x - xhat);
+ }
+ }
+
+ /* Calculate the slope at the last point, x(n). */
+ i = nPoints - 2;
+ n = nPoints - 1;
+ if ((m1 * m2) < 0.0) {
+ m[n] = m2 * 2.0;
+ } else {
+ xmid = (points[i].x + points[n].x) / 2.0;
+ yxmid = m[i] * (xmid - points[i].x) + points[i].y;
+ m[n] = (points[n].y - yxmid) / (points[n].x - xmid);
+ if ((m[n] * m2) < 0.0) {
+ m[n] = 0.0;
+ }
+ }
+
+ /* Calculate the slope at the first point, x(0). */
+ if ((m1s * m2s) < 0.0) {
+ m[0] = m1s * 2.0;
+ } else {
+ xmid = (points[0].x + points[1].x) / 2.0;
+ yxmid = m[1] * (xmid - points[1].x) + points[1].y;
+ m[0] = (yxmid - points[0].y) / (xmid - points[0].x);
+ if ((m[0] * m1s) < 0.0) {
+ m[0] = 0.0;
+ }
+ }
+
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * QuadEval --
+ *
+ * QuadEval controls the evaluation of an osculatory quadratic
+ * spline. The user may provide his own slopes at the points of
+ * interpolation or use the subroutine 'QuadSlopes' to calculate
+ * slopes which are consistent with the shape of the data.
+ *
+ * ON INPUT--
+ * intpPts must be a nondecreasing vector of points at which the
+ * spline will be evaluated.
+ * origPts contains the abscissas of the data points to be
+ * interpolated. xtab must be increasing.
+ * y contains the ordinates of the data points to be
+ * interpolated.
+ * m contains the slope of the spline at each point of
+ * interpolation.
+ * nPoints number of data points (dimension of xtab and y).
+ * numEval is the number of points of evaluation (dimension of
+ * xval and yval).
+ * epsilon is a relative error tolerance used in subroutine
+ * 'QuadChoose' to distinguish the situation m(i) or
+ * m(i+1) is relatively close to the slope or twice
+ * the slope of the linear segment between xtab(i) and
+ * xtab(i+1). If this situation occurs, roundoff may
+ * cause a change in convexity or monotonicity of the
+ * resulting spline and a change in the case number
+ * provided by 'QuadChoose'. If epsilon is not equal to zero,
+ * then epsilon should be greater than or equal to machine
+ * epsilon.
+ * ON OUTPUT--
+ * yval contains the images of the points in xval.
+ * err is one of the following error codes:
+ * 0 - QuadEval ran normally.
+ * 1 - xval(i) is less than xtab(1) for at least one
+ * i or xval(i) is greater than xtab(num) for at
+ * least one i. QuadEval will extrapolate to provide
+ * function values for these abscissas.
+ * 2 - xval(i+1) < xval(i) for some i.
+ *
+ *
+ * QuadEval calls the following subroutines or functions:
+ * Search
+ * QuadCases
+ * QuadChoose
+ * QuadSpline
+ *---------------------------------------------------------------------------
+ */
+static int
+QuadEval(
+ Point2d origPts[],
+ int nOrigPts,
+ Point2d intpPts[],
+ int nIntpPts,
+ double *m, /* Slope of the spline at each point
+ * of interpolation. */
+ double epsilon) /* Relative error tolerance (see choose) */
+{
+ int error;
+ int i, j;
+ double param[10];
+ int ncase;
+ int start, end;
+ int l, p;
+ int n;
+ int found;
+
+ /* Initialize indices and set error result */
+ error = 0;
+ l = nOrigPts - 1;
+ p = l - 1;
+ ncase = 1;
+
+ /*
+ * Determine if abscissas of new vector are non-decreasing.
+ */
+ for (j = 1; j < nIntpPts; j++) {
+ if (intpPts[j].x < intpPts[j - 1].x) {
+ return 2;
+ }
+ }
+ /*
+ * Determine if any of the points in xval are LESS than the
+ * abscissa of the first data point.
+ */
+ for (start = 0; start < nIntpPts; start++) {
+ if (intpPts[start].x >= origPts[0].x) {
+ break;
+ }
+ }
+ /*
+ * Determine if any of the points in xval are GREATER than the
+ * abscissa of the l data point.
+ */
+ for (end = nIntpPts - 1; end >= 0; end--) {
+ if (intpPts[end].x <= origPts[l].x) {
+ break;
+ }
+ }
+
+ if (start > 0) {
+ error = 1; /* Set error value to indicate that
+ * extrapolation has occurred. */
+ /*
+ * Calculate the images of points of evaluation whose abscissas
+ * are less than the abscissa of the first data point.
+ */
+ ncase = QuadSelect(origPts, origPts + 1, m[0], m[1], epsilon, param);
+ for (j = 0; j < (start - 1); j++) {
+ QuadSpline(intpPts + j, origPts, origPts + 1, param, ncase);
+ }
+ if (nIntpPts == 1) {
+ return error;
+ }
+ }
+ if ((nIntpPts == 1) && (end != (nIntpPts - 1))) {
+ goto noExtrapolation;
+ }
+
+ /*
+ * Search locates the interval in which the first in-range
+ * point of evaluation lies.
+ */
+
+ i = Search(origPts, nOrigPts, intpPts[start].x, &found);
+
+ n = i + 1;
+ if (n >= nOrigPts) {
+ n = nOrigPts - 1;
+ i = nOrigPts - 2;
+ }
+ /*
+ * If the first in-range point of evaluation is equal to one
+ * of the data points, assign the appropriate value from y.
+ * Continue until a point of evaluation is found which is not
+ * equal to a data point.
+ */
+ if (found) {
+ do {
+ intpPts[start].y = origPts[i].y;
+ start++;
+ if (start >= nIntpPts) {
+ return error;
+ }
+ } while (intpPts[start - 1].x == intpPts[start].x);
+
+ for (;;) {
+ if (intpPts[start].x < origPts[n].x) {
+ break; /* Break out of for-loop */
+ }
+ if (intpPts[start].x == origPts[n].x) {
+ do {
+ intpPts[start].y = origPts[n].y;
+ start++;
+ if (start >= nIntpPts) {
+ return error;
+ }
+ } while (intpPts[start].x == intpPts[start - 1].x);
+ }
+ i++;
+ n++;
+ }
+ }
+ /*
+ * Calculate the images of all the points which lie within
+ * range of the data.
+ */
+ if ((i > 0) || (error != 1)) {
+ ncase = QuadSelect(origPts + i, origPts + n, m[i], m[n],
+ epsilon, param);
+ }
+ for (j = start; j <= end; j++) {
+ /*
+ * If xx(j) - x(n) is negative, do not recalculate
+ * the parameters for this section of the spline since
+ * they are already known.
+ */
+ if (intpPts[j].x == origPts[n].x) {
+ intpPts[j].y = origPts[n].y;
+ continue;
+ } else if (intpPts[j].x > origPts[n].x) {
+ double delta;
+
+ /* Determine that the routine is in the correct part of
+ the spline. */
+ do {
+ i++, n++;
+ delta = intpPts[j].x - origPts[n].x;
+ } while (delta > 0.0);
+
+ if (delta < 0.0) {
+ ncase = QuadSelect(origPts + i, origPts + n, m[i],
+ m[n], epsilon, param);
+ } else if (delta == 0.0) {
+ intpPts[j].y = origPts[n].y;
+ continue;
+ }
+ }
+ QuadSpline(intpPts + j, origPts + i, origPts + n, param, ncase);
+ }
+
+ if (end == (nIntpPts - 1)) {
+ return error;
+ }
+ if ((n == l) && (intpPts[end].x != origPts[l].x)) {
+ goto noExtrapolation;
+ }
+
+ error = 1; /* Set error value to indicate that
+ * extrapolation has occurred. */
+ ncase = QuadSelect(origPts + p, origPts + l, m[p], m[l], epsilon, param);
+
+ noExtrapolation:
+ /*
+ * Calculate the images of the points of evaluation whose
+ * abscissas are greater than the abscissa of the last data point.
+ */
+ for (j = (end + 1); j < nIntpPts; j++) {
+ QuadSpline(intpPts + j, origPts + p, origPts + l, param, ncase);
+ }
+ return error;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Shape preserving quadratic splines
+ * by D.F.Mcallister & J.A.Roulier
+ * Coded by S.L.Dodd & M.Roulier
+ * N.C.State University
+ *
+ *---------------------------------------------------------------------------
+ */
+/*
+ * Driver routine for quadratic spline package
+ * On input--
+ * X,Y Contain n-long arrays of data (x is increasing)
+ * XM Contains m-long array of x values (increasing)
+ * eps Relative error tolerance
+ * n Number of input data points
+ * m Number of output data points
+ * On output--
+ * work Contains the value of the first derivative at each data point
+ * ym Contains the interpolated spline value at each data point
+ */
+int
+Blt_QuadraticSpline(Point2d *origPts, int nOrigPts, Point2d *intpPts,
+ int nIntpPts)
+{
+ double epsilon;
+ double *work;
+ int result;
+
+ work = Blt_AssertMalloc(nOrigPts * sizeof(double));
+ epsilon = 0.0; /* TBA: adjust error via command-line option */
+ /* allocate space for vectors used in calculation */
+ QuadSlopes(origPts, work, nOrigPts);
+ result = QuadEval(origPts, nOrigPts, intpPts, nIntpPts, work, epsilon);
+ Blt_Free(work);
+ if (result > 1) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Reference:
+ * Numerical Analysis, R. Burden, J. Faires and A. Reynolds.
+ * Prindle, Weber & Schmidt 1981 pp 112
+ *
+ * Parameters:
+ * origPts - vector of points, assumed to be sorted along x.
+ * intpPts - vector of new points.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_NaturalSpline(Point2d *origPts, int nOrigPts, Point2d *intpPts,
+ int nIntpPts)
+{
+ Cubic2D *eq;
+ Point2d *ip, *iend;
+ TriDiagonalMatrix *A;
+ double *dx; /* vector of deltas in x */
+ double x, dy, alpha;
+ int isKnot;
+ int i, j, n;
+
+ dx = Blt_AssertMalloc(sizeof(double) * nOrigPts);
+ /* Calculate vector of differences */
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dx[i] = origPts[j].x - origPts[i].x;
+ if (dx[i] < 0.0) {
+ return 0;
+ }
+ }
+ n = nOrigPts - 1; /* Number of intervals. */
+ A = Blt_AssertMalloc(sizeof(TriDiagonalMatrix) * nOrigPts);
+ if (A == NULL) {
+ Blt_Free(dx);
+ return 0;
+ }
+ /* Vectors to solve the tridiagonal matrix */
+ A[0][0] = A[n][0] = 1.0;
+ A[0][1] = A[n][1] = 0.0;
+ A[0][2] = A[n][2] = 0.0;
+
+ /* Calculate the intermediate results */
+ for (i = 0, j = 1; j < n; j++, i++) {
+ alpha = 3.0 * ((origPts[j + 1].y / dx[j]) - (origPts[j].y / dx[i]) -
+ (origPts[j].y / dx[j]) + (origPts[i].y / dx[i]));
+ A[j][0] = 2 * (dx[j] + dx[i]) - dx[i] * A[i][1];
+ A[j][1] = dx[j] / A[j][0];
+ A[j][2] = (alpha - dx[i] * A[i][2]) / A[j][0];
+ }
+
+ eq = Blt_Malloc(sizeof(Cubic2D) * nOrigPts);
+ if (eq == NULL) {
+ Blt_Free(A);
+ Blt_Free(dx);
+ return FALSE;
+ }
+ eq[0].c = eq[n].c = 0.0;
+ for (j = n, i = n - 1; i >= 0; i--, j--) {
+ eq[i].c = A[i][2] - A[i][1] * eq[j].c;
+ dy = origPts[i+1].y - origPts[i].y;
+ eq[i].b = (dy) / dx[i] - dx[i] * (eq[j].c + 2.0 * eq[i].c) / 3.0;
+ eq[i].d = (eq[j].c - eq[i].c) / (3.0 * dx[i]);
+ }
+ Blt_Free(A);
+ Blt_Free(dx);
+
+ /* Now calculate the new values */
+ for (ip = intpPts, iend = ip + nIntpPts; ip < iend; ip++) {
+ ip->y = 0.0;
+ x = ip->x;
+
+ /* Is it outside the interval? */
+ if ((x < origPts[0].x) || (x > origPts[n].x)) {
+ continue;
+ }
+ /* Search for the interval containing x in the point array */
+ i = Search(origPts, nOrigPts, x, &isKnot);
+ if (isKnot) {
+ ip->y = origPts[i].y;
+ } else {
+ i--;
+ x -= origPts[i].x;
+ ip->y = origPts[i].y + x * (eq[i].b + x * (eq[i].c + x * eq[i].d));
+ }
+ }
+ Blt_Free(eq);
+ return TRUE;
+}
+
+static Blt_OpSpec splineOps[] =
+{
+ {"natural", 1, Blt_NaturalSpline, 6, 6, "x y splx sply",},
+ {"quadratic", 1, Blt_QuadraticSpline, 6, 6, "x y splx sply",},
+};
+static int nSplineOps = sizeof(splineOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+SplineCmd(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ SplineProc *proc;
+ Blt_Vector *x, *y, *splX, *splY;
+ double *xArr, *yArr;
+ int i;
+ Point2d *origPts, *intpPts;
+ int nOrigPts, nIntpPts;
+
+ proc = Blt_GetOpFromObj(interp, nSplineOps, splineOps, BLT_OP_ARG1,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ if ((Blt_GetVectorFromObj(interp, objv[2], &x) != TCL_OK) ||
+ (Blt_GetVectorFromObj(interp, objv[3], &y) != TCL_OK) ||
+ (Blt_GetVectorFromObj(interp, objv[4], &splX) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ nOrigPts = Blt_VecLength(x);
+ if (nOrigPts < 3) {
+ Tcl_AppendResult(interp, "length of vector \"", Tcl_GetString(objv[2]),
+ "\" is < 3", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 1; i < nOrigPts; i++) {
+ if (Blt_VecData(x)[i] < Blt_VecData(x)[i - 1]) {
+ Tcl_AppendResult(interp, "x vector \"", Tcl_GetString(objv[2]),
+ "\" must be monotonically increasing", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ /* Check that all the data points aren't the same. */
+ if (Blt_VecData(x)[i - 1] <= Blt_VecData(x)[0]) {
+ Tcl_AppendResult(interp, "x vector \"", Tcl_GetString(objv[2]),
+ "\" must be monotonically increasing", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nOrigPts != Blt_VecLength(y)) {
+ Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[2]),
+ "\" and \"", Tcl_GetString(objv[3]),
+ " have different lengths", (char *)NULL);
+ return TCL_ERROR;
+ }
+ nIntpPts = Blt_VecLength(splX);
+ if (Blt_GetVectorFromObj(interp, objv[5], &splY) != TCL_OK) {
+ /*
+ * If the named vector to hold the ordinates of the spline
+ * doesn't exist, create one the same size as the vector
+ * containing the abscissas.
+ */
+ if (Blt_CreateVector(interp, Tcl_GetString(objv[5]), nIntpPts, &splY)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if (nIntpPts != Blt_VecLength(splY)) {
+ /*
+ * The x and y vectors differ in size. Make the number of ordinates
+ * the same as the number of abscissas.
+ */
+ if (Blt_ResizeVector(splY, nIntpPts) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ origPts = Blt_Malloc(sizeof(Point2d) * nOrigPts);
+ if (origPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nOrigPts),
+ "\" points", (char *)NULL);
+ return TCL_ERROR;
+ }
+ intpPts = Blt_Malloc(sizeof(Point2d) * nIntpPts);
+ if (intpPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate \"", Blt_Itoa(nIntpPts),
+ "\" points", (char *)NULL);
+ Blt_Free(origPts);
+ return TCL_ERROR;
+ }
+ xArr = Blt_VecData(x);
+ yArr = Blt_VecData(y);
+ for (i = 0; i < nOrigPts; i++) {
+ origPts[i].x = xArr[i];
+ origPts[i].y = yArr[i];
+ }
+ xArr = Blt_VecData(splX);
+ yArr = Blt_VecData(splY);
+ for (i = 0; i < nIntpPts; i++) {
+ intpPts[i].x = xArr[i];
+ intpPts[i].y = yArr[i];
+ }
+ if (!(*proc) (origPts, nOrigPts, intpPts, nIntpPts)) {
+ Tcl_AppendResult(interp, "error generating spline for \"",
+ Blt_NameOfVector(splY), "\"", (char *)NULL);
+ Blt_Free(origPts);
+ Blt_Free(intpPts);
+ return TCL_ERROR;
+ }
+ yArr = Blt_VecData(splY);
+ for (i = 0; i < nIntpPts; i++) {
+ yArr[i] = intpPts[i].y;
+ }
+ Blt_Free(origPts);
+ Blt_Free(intpPts);
+
+ /* Finally update the vector. The size of the vector hasn't
+ * changed, just the data. Reset the vector using TCL_STATIC to
+ * indicate this. */
+ if (Blt_ResetVector(splY, Blt_VecData(splY), Blt_VecLength(splY),
+ Blt_VecSize(splY), TCL_STATIC) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_SplineCmdInitProc(Tcl_Interp *interp)
+{
+ static Blt_InitCmdSpec cmdSpec = {"spline", SplineCmd,};
+
+ return Blt_InitCmd(interp, "::blt", &cmdSpec);
+}
+
+
+#define SQR(x) ((x)*(x))
+
+typedef struct {
+ double t; /* Arc length of interval. */
+ double x; /* 2nd derivative of X with respect to T */
+ double y; /* 2nd derivative of Y with respect to T */
+} CubicSpline;
+
+
+/*
+ * The following two procedures solve the special linear system which arise
+ * in cubic spline interpolation. If x is assumed cyclic ( x[i]=x[n+i] ) the
+ * equations can be written as (i=0,1,...,n-1):
+ * m[i][0] * x[i-1] + m[i][1] * x[i] + m[i][2] * x[i+1] = b[i] .
+ * In matrix notation one gets A * x = b, where the matrix A is tridiagonal
+ * with additional elements in the upper right and lower left position:
+ * A[i][0] = A_{i,i-1} for i=1,2,...,n-1 and m[0][0] = A_{0,n-1} ,
+ * A[i][1] = A_{i, i } for i=0,1,...,n-1
+ * A[i][2] = A_{i,i+1} for i=0,1,...,n-2 and m[n-1][2] = A_{n-1,0}.
+ * A should be symmetric (A[i+1][0] == A[i][2]) and positive definite.
+ * The size of the system is given in n (n>=1).
+ *
+ * In the first procedure the Cholesky decomposition A = C^T * D * C
+ * (C is upper triangle with unit diagonal, D is diagonal) is calculated.
+ * Return TRUE if decomposition exist.
+ */
+static int
+SolveCubic1(TriDiagonalMatrix A[], int n)
+{
+ int i;
+ double m_ij, m_n, m_nn, d;
+
+ if (n < 1) {
+ return FALSE; /* Dimension should be at least 1 */
+ }
+ d = A[0][1]; /* D_{0,0} = A_{0,0} */
+ if (d <= 0.0) {
+ return FALSE; /* A (or D) should be positive definite */
+ }
+ m_n = A[0][0]; /* A_{0,n-1} */
+ m_nn = A[n - 1][1]; /* A_{n-1,n-1} */
+ for (i = 0; i < n - 2; i++) {
+ m_ij = A[i][2]; /* A_{i,1} */
+ A[i][2] = m_ij / d; /* C_{i,i+1} */
+ A[i][0] = m_n / d; /* C_{i,n-1} */
+ m_nn -= A[i][0] * m_n; /* to get C_{n-1,n-1} */
+ m_n = -A[i][2] * m_n; /* to get C_{i+1,n-1} */
+ d = A[i + 1][1] - A[i][2] * m_ij; /* D_{i+1,i+1} */
+ if (d <= 0.0) {
+ return FALSE; /* Elements of D should be positive */
+ }
+ A[i + 1][1] = d;
+ }
+ if (n >= 2) { /* Complete last column */
+ m_n += A[n - 2][2]; /* add A_{n-2,n-1} */
+ A[n - 2][0] = m_n / d; /* C_{n-2,n-1} */
+ A[n - 1][1] = d = m_nn - A[n - 2][0] * m_n; /* D_{n-1,n-1} */
+ if (d <= 0.0) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * The second procedure solves the linear system, with the Cholesky
+ * decomposition calculated above (in m[][]) and the right side b given
+ * in x[]. The solution x overwrites the right side in x[].
+ */
+static void
+SolveCubic2(TriDiagonalMatrix A[], CubicSpline spline[], int nIntervals)
+{
+ int i;
+ double x, y;
+ int n, m;
+
+ n = nIntervals - 2;
+ m = nIntervals - 1;
+
+ /* Division by transpose of C : b = C^{-T} * b */
+ x = spline[m].x;
+ y = spline[m].y;
+ for (i = 0; i < n; i++) {
+ spline[i + 1].x -= A[i][2] * spline[i].x; /* C_{i,i+1} * x(i) */
+ spline[i + 1].y -= A[i][2] * spline[i].y; /* C_{i,i+1} * x(i) */
+ x -= A[i][0] * spline[i].x; /* C_{i,n-1} * x(i) */
+ y -= A[i][0] * spline[i].y; /* C_{i,n-1} * x(i) */
+ }
+ if (n >= 0) {
+ /* C_{n-2,n-1} * x_{n-1} */
+ spline[m].x = x - A[n][0] * spline[n].x;
+ spline[m].y = y - A[n][0] * spline[n].y;
+ }
+ /* Division by D: b = D^{-1} * b */
+ for (i = 0; i < nIntervals; i++) {
+ spline[i].x /= A[i][1];
+ spline[i].y /= A[i][1];
+ }
+
+ /* Division by C: b = C^{-1} * b */
+ x = spline[m].x;
+ y = spline[m].y;
+ if (n >= 0) {
+ /* C_{n-2,n-1} * x_{n-1} */
+ spline[n].x -= A[n][0] * x;
+ spline[n].y -= A[n][0] * y;
+ }
+ for (i = (n - 1); i >= 0; i--) {
+ /* C_{i,i+1} * x_{i+1} + C_{i,n-1} * x_{n-1} */
+ spline[i].x -= A[i][2] * spline[i + 1].x + A[i][0] * x;
+ spline[i].y -= A[i][2] * spline[i + 1].y + A[i][0] * y;
+ }
+}
+
+/*
+ * Find second derivatives (x''(t_i),y''(t_i)) of cubic spline interpolation
+ * through list of points (x_i,y_i). The parameter t is calculated as the
+ * length of the linear stroke. The number of points must be at least 3.
+ * Note: For CLOSED_CONTOURs the first and last point must be equal.
+ */
+static CubicSpline *
+CubicSlopes(
+ Point2d points[],
+ int nPoints, /* Number of points (nPoints>=3) */
+ int isClosed, /* CLOSED_CONTOUR or OPEN_CONTOUR */
+ double unitX,
+ double unitY) /* Unit length in x and y (norm=1) */
+{
+ CubicSpline *spline;
+ CubicSpline *s1, *s2;
+ int n, i;
+ double norm, dx, dy;
+ TriDiagonalMatrix *A; /* The tri-diagonal matrix is saved here. */
+
+ spline = Blt_Malloc(sizeof(CubicSpline) * nPoints);
+ if (spline == NULL) {
+ return NULL;
+ }
+ A = Blt_Malloc(sizeof(TriDiagonalMatrix) * nPoints);
+ if (A == NULL) {
+ Blt_Free(spline);
+ return NULL;
+ }
+ /*
+ * Calculate first differences in (dxdt2[i], y[i]) and interval lengths
+ * in dist[i]:
+ */
+ s1 = spline;
+ for (i = 0; i < nPoints - 1; i++) {
+ s1->x = points[i+1].x - points[i].x;
+ s1->y = points[i+1].y - points[i].y;
+
+ /*
+ * The Norm of a linear stroke is calculated in "normal coordinates"
+ * and used as interval length:
+ */
+ dx = s1->x / unitX;
+ dy = s1->y / unitY;
+ s1->t = sqrt(dx * dx + dy * dy);
+
+ s1->x /= s1->t; /* first difference, with unit norm: */
+ s1->y /= s1->t; /* || (dxdt2[i], y[i]) || = 1 */
+ s1++;
+ }
+
+ /*
+ * Setup linear System: Ax = b
+ */
+ n = nPoints - 2; /* Without first and last point */
+ if (isClosed) {
+ /* First and last points must be equal for CLOSED_CONTOURs */
+ spline[nPoints - 1].t = spline[0].t;
+ spline[nPoints - 1].x = spline[0].x;
+ spline[nPoints - 1].y = spline[0].y;
+ n++; /* Add last point (= first point) */
+ }
+ s1 = spline, s2 = s1 + 1;
+ for (i = 0; i < n; i++) {
+ /* Matrix A, mainly tridiagonal with cyclic second index
+ ("j = j+n mod n")
+ */
+ A[i][0] = s1->t; /* Off-diagonal element A_{i,i-1} */
+ A[i][1] = 2.0 * (s1->t + s2->t); /* A_{i,i} */
+ A[i][2] = s2->t; /* Off-diagonal element A_{i,i+1} */
+
+ /* Right side b_x and b_y */
+ s1->x = (s2->x - s1->x) * 6.0;
+ s1->y = (s2->y - s1->y) * 6.0;
+
+ /*
+ * If the linear stroke shows a cusp of more than 90 degree,
+ * the right side is reduced to avoid oscillations in the
+ * spline:
+ */
+ /*
+ * The Norm of a linear stroke is calculated in "normal coordinates"
+ * and used as interval length:
+ */
+ dx = s1->x / unitX;
+ dy = s1->y / unitY;
+ norm = sqrt(dx * dx + dy * dy) / 8.5;
+ if (norm > 1.0) {
+ /* The first derivative will not be continuous */
+ s1->x /= norm;
+ s1->y /= norm;
+ }
+ s1++, s2++;
+ }
+
+ if (!isClosed) {
+ /* Third derivative is set to zero at both ends */
+ A[0][1] += A[0][0]; /* A_{0,0} */
+ A[0][0] = 0.0; /* A_{0,n-1} */
+ A[n-1][1] += A[n-1][2]; /* A_{n-1,n-1} */
+ A[n-1][2] = 0.0; /* A_{n-1,0} */
+ }
+ /* Solve linear systems for dxdt2[] and y[] */
+
+ if (SolveCubic1(A, n)) { /* Cholesky decomposition */
+ SolveCubic2(A, spline, n); /* A * dxdt2 = b_x */
+ } else { /* Should not happen, but who knows ... */
+ Blt_Free(A);
+ Blt_Free(spline);
+ return NULL;
+ }
+ /* Shift all second derivatives one place right and update the ends. */
+ s2 = spline + n, s1 = s2 - 1;
+ for (/* empty */; s2 > spline; s2--, s1--) {
+ s2->x = s1->x;
+ s2->y = s1->y;
+ }
+ if (isClosed) {
+ spline[0].x = spline[n].x;
+ spline[0].y = spline[n].y;
+ } else {
+ /* Third derivative is 0.0 for the first and last interval. */
+ spline[0].x = spline[1].x;
+ spline[0].y = spline[1].y;
+ spline[n + 1].x = spline[n].x;
+ spline[n + 1].y = spline[n].y;
+ }
+ Blt_Free( A);
+ return spline;
+}
+
+
+/*
+ * Calculate interpolated values of the spline function (defined via p_cntr
+ * and the second derivatives dxdt2[] and dydt2[]). The number of tabulated
+ * values is n. On an equidistant grid n_intpol values are calculated.
+ */
+static int
+CubicEval(Point2d *origPts, int nOrigPts, Point2d *intpPts, int nIntpPts,
+ CubicSpline *spline)
+{
+ double t, tSkip, tMax;
+ Point2d q;
+ int i, j, count;
+
+ /* Sum the lengths of all the segments (intervals). */
+ tMax = 0.0;
+ for (i = 0; i < nOrigPts - 1; i++) {
+ tMax += spline[i].t;
+ }
+
+ /* Need a better way of doing this... */
+
+ /* The distance between interpolated points */
+ tSkip = (1. - 1e-7) * tMax / (nIntpPts - 1);
+
+ t = 0.0; /* Spline parameter value. */
+ q = origPts[0];
+ count = 0;
+
+ intpPts[count++] = q; /* First point. */
+ t += tSkip;
+
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ Point2d p;
+ double d, hx, dx0, dx01, hy, dy0, dy01;
+
+ d = spline[i].t; /* Interval length */
+ p = q;
+ q = origPts[i+1];
+ hx = (q.x - p.x) / d;
+ hy = (q.y - p.y) / d;
+ dx0 = (spline[j].x + 2 * spline[i].x) / 6.0;
+ dy0 = (spline[j].y + 2 * spline[i].y) / 6.0;
+ dx01 = (spline[j].x - spline[i].x) / (6.0 * d);
+ dy01 = (spline[j].y - spline[i].y) / (6.0 * d);
+ while (t <= spline[i].t) { /* t in current interval ? */
+ p.x += t * (hx + (t - d) * (dx0 + t * dx01));
+ p.y += t * (hy + (t - d) * (dy0 + t * dy01));
+ intpPts[count++] = p;
+ t += tSkip;
+ }
+ /* Parameter t relative to start of next interval */
+ t -= spline[i].t;
+ }
+ return count;
+}
+
+/*
+ * Generate a cubic spline curve through the points (x_i,y_i) which are
+ * stored in the linked list p_cntr.
+ * The spline is defined as a 2d-function s(t) = (x(t),y(t)), where the
+ * parameter t is the length of the linear stroke.
+ */
+int
+Blt_NaturalParametricSpline(Point2d *origPts, int nOrigPts, Region2d *extsPtr,
+ int isClosed, Point2d *intpPts, int nIntpPts)
+{
+ double unitX, unitY; /* To define norm (x,y)-plane */
+ CubicSpline *spline;
+ int result;
+
+ if (nOrigPts < 3) {
+ return 0;
+ }
+ if (isClosed) {
+ origPts[nOrigPts].x = origPts[0].x;
+ origPts[nOrigPts].y = origPts[0].y;
+ nOrigPts++;
+ }
+ /* Width and height of the grid is used at unit length (2d-norm) */
+ unitX = extsPtr->right - extsPtr->left;
+ unitY = extsPtr->bottom - extsPtr->top;
+
+ if (unitX < FLT_EPSILON) {
+ unitX = FLT_EPSILON;
+ }
+ if (unitY < FLT_EPSILON) {
+ unitY = FLT_EPSILON;
+ }
+ /* Calculate parameters for cubic spline:
+ * t = arc length of interval.
+ * dxdt2 = second derivatives of x with respect to t,
+ * dydt2 = second derivatives of y with respect to t,
+ */
+ spline = CubicSlopes(origPts, nOrigPts, isClosed, unitX, unitY);
+ if (spline == NULL) {
+ return 0;
+ }
+ result= CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline);
+ Blt_Free(spline);
+ return result;
+}
+
+static INLINE void
+CatromCoeffs(Point2d *p, Point2d *a, Point2d *b, Point2d *c, Point2d *d)
+{
+ a->x = -p[0].x + 3.0 * p[1].x - 3.0 * p[2].x + p[3].x;
+ b->x = 2.0 * p[0].x - 5.0 * p[1].x + 4.0 * p[2].x - p[3].x;
+ c->x = -p[0].x + p[2].x;
+ d->x = 2.0 * p[1].x;
+ a->y = -p[0].y + 3.0 * p[1].y - 3.0 * p[2].y + p[3].y;
+ b->y = 2.0 * p[0].y - 5.0 * p[1].y + 4.0 * p[2].y - p[3].y;
+ c->y = -p[0].y + p[2].y;
+ d->y = 2.0 * p[1].y;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ParametricCatromSpline --
+ *
+ * Computes a spline based upon the data points, returning a new (larger)
+ * coordinate array of points.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_CatromParametricSpline(Point2d *points, int nPoints, Point2d *intpPts,
+ int nIntpPts)
+{
+ int i;
+ Point2d *origPts;
+ double t;
+ int interval;
+ Point2d a, b, c, d;
+
+ assert(nPoints > 0);
+
+ /*
+ * The spline is computed in screen coordinates instead of data points so
+ * that we can select the abscissas of the interpolated points from each
+ * pixel horizontally across the plotting area.
+ */
+ origPts = Blt_AssertMalloc((nPoints + 4) * sizeof(Point2d));
+ memcpy(origPts + 1, points, sizeof(Point2d) * nPoints);
+
+ origPts[0] = origPts[1];
+ origPts[nPoints + 2] = origPts[nPoints + 1] = origPts[nPoints];
+
+ for (i = 0; i < nIntpPts; i++) {
+ interval = (int)intpPts[i].x;
+ t = intpPts[i].y;
+ assert(interval < nPoints);
+ CatromCoeffs(origPts + interval, &a, &b, &c, &d);
+ intpPts[i].x = (d.x + t * (c.x + t * (b.x + t * a.x))) / 2.0;
+ intpPts[i].y = (d.y + t * (c.y + t * (b.y + t * a.y))) / 2.0;
+ }
+ Blt_Free(origPts);
+ return 1;
+}
diff --git a/src/bltGrElemOp.C b/src/bltGrElemOp.C
new file mode 100644
index 0000000..6f509ac
--- /dev/null
+++ b/src/bltGrElemOp.C
@@ -0,0 +1,2456 @@
+
+/*
+ * bltGrElem.c --
+ *
+ * This module implements generic elements for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+#include <bltDataTable.h>
+
+#define GRAPH_KEY "BLT Graph Data"
+
+/* Ignore elements that aren't in the display list or have been deleted. */
+#define IGNORE_ELEMENT(e) (((e)->link == NULL) || ((e)->flags & DELETE_PENDING))
+
+typedef struct {
+ Blt_Table table;
+ int refCount;
+} TableClient;
+
+static Blt_OptionParseProc ObjToAlong;
+static Blt_OptionPrintProc AlongToObj;
+static Blt_CustomOption alongOption =
+{
+ ObjToAlong, AlongToObj, NULL, (ClientData)0
+};
+static Blt_OptionFreeProc FreeValues;
+static Blt_OptionParseProc ObjToValues;
+static Blt_OptionPrintProc ValuesToObj;
+Blt_CustomOption bltValuesOption =
+{
+ ObjToValues, ValuesToObj, FreeValues, (ClientData)0
+};
+static Blt_OptionFreeProc FreeValuePairs;
+static Blt_OptionParseProc ObjToValuePairs;
+static Blt_OptionPrintProc ValuePairsToObj;
+Blt_CustomOption bltValuePairsOption =
+{
+ ObjToValuePairs, ValuePairsToObj, FreeValuePairs, (ClientData)0
+};
+
+static Blt_OptionFreeProc FreeStyles;
+static Blt_OptionParseProc ObjToStyles;
+static Blt_OptionPrintProc StylesToObj;
+Blt_CustomOption bltLineStylesOption =
+{
+ ObjToStyles, StylesToObj, FreeStyles, (ClientData)0,
+};
+
+Blt_CustomOption bltBarStylesOption =
+{
+ ObjToStyles, StylesToObj, FreeStyles, (ClientData)0,
+};
+
+#include "bltGrElem.h"
+
+static Blt_VectorChangedProc VectorChangedProc;
+
+static void FindRange(ElemValues *valuesPtr);
+static void FreeDataValues(ElemValues *valuesPtr);
+static Tcl_FreeProc FreeElement;
+
+typedef int (GraphElementProc)(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyTableClients --
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+void
+Blt_DestroyTableClients(Graph *graphPtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->dataTables, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ TableClient *clientPtr;
+
+ clientPtr = Blt_GetHashValue(hPtr);
+ if (clientPtr->table != NULL) {
+ Blt_Table_Close(clientPtr->table);
+ }
+ Blt_Free(clientPtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->dataTables);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ * Custom option parse and print procedures
+ *---------------------------------------------------------------------------
+ */
+static int
+GetPenStyleFromObj(
+ Tcl_Interp *interp,
+ Graph *graphPtr,
+ Tcl_Obj *objPtr,
+ ClassId classId,
+ PenStyle *stylePtr)
+{
+ Pen *penPtr;
+ Tcl_Obj **objv;
+ int objc;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc != 1) && (objc != 3)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad style entry \"",
+ Tcl_GetString(objPtr),
+ "\": should be \"penName\" or \"penName min max\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ if (Blt_GetPenFromObj(interp, graphPtr, objv[0], classId, &penPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 3) {
+ double min, max;
+
+ if ((Tcl_GetDoubleFromObj(interp, objv[1], &min) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[2], &max) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ SetWeight(stylePtr->weight, min, max);
+ }
+ stylePtr->penPtr = penPtr;
+ return TCL_OK;
+}
+
+static void
+FreeVectorSource(ElemValues *valuesPtr)
+{
+ if (valuesPtr->vectorSource.vector != NULL) {
+ Blt_SetVectorChangedProc(valuesPtr->vectorSource.vector, NULL, NULL);
+ Blt_FreeVectorId(valuesPtr->vectorSource.vector);
+ valuesPtr->vectorSource.vector = NULL;
+ }
+}
+
+static int
+FetchVectorValues(Tcl_Interp *interp, ElemValues *valuesPtr, Blt_Vector *vector)
+{
+ double *array;
+
+ if (valuesPtr->values == NULL) {
+ array = Blt_Malloc(Blt_VecLength(vector) * sizeof(double));
+ } else {
+ array = Blt_Realloc(valuesPtr->values,
+ Blt_VecLength(vector) * sizeof(double));
+ }
+ if (array == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ memcpy(array, Blt_VecData(vector), sizeof(double) * Blt_VecLength(vector));
+ valuesPtr->min = Blt_VecMin(vector);
+ valuesPtr->max = Blt_VecMax(vector);
+ valuesPtr->values = array;
+ valuesPtr->nValues = Blt_VecLength(vector);
+ /* FindRange(valuesPtr); */
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorChangedProc --
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+VectorChangedProc(
+ Tcl_Interp *interp,
+ ClientData clientData,
+ Blt_VectorNotify notify)
+{
+ ElemValues *valuesPtr = clientData;
+
+ if (notify == BLT_VECTOR_NOTIFY_DESTROY) {
+ FreeDataValues(valuesPtr);
+ } else {
+ Blt_Vector *vector;
+
+ Blt_GetVectorById(interp, valuesPtr->vectorSource.vector, &vector);
+ if (FetchVectorValues(NULL, valuesPtr, vector) != TCL_OK) {
+ return;
+ }
+ }
+ {
+ Element *elemPtr = valuesPtr->elemPtr;
+ Graph *graphPtr;
+
+ graphPtr = elemPtr->obj.graphPtr;
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ if (!IGNORE_ELEMENT(elemPtr)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ }
+}
+
+static int
+GetVectorData(Tcl_Interp *interp, ElemValues *valuesPtr, const char *vecName)
+{
+ Blt_Vector *vecPtr;
+ VectorDataSource *srcPtr;
+
+ srcPtr = &valuesPtr->vectorSource;
+ srcPtr->vector = Blt_AllocVectorId(interp, vecName);
+ if (Blt_GetVectorById(interp, srcPtr->vector, &vecPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (FetchVectorValues(interp, valuesPtr, vecPtr) != TCL_OK) {
+ FreeVectorSource(valuesPtr);
+ return TCL_ERROR;
+ }
+ Blt_SetVectorChangedProc(srcPtr->vector, VectorChangedProc, valuesPtr);
+ valuesPtr->type = ELEM_SOURCE_VECTOR;
+ return TCL_OK;
+}
+
+static int
+FetchTableValues(Tcl_Interp *interp, ElemValues *valuesPtr, Blt_TableColumn col)
+{
+ long i, j;
+ double *array;
+ Blt_Table table;
+
+ table = valuesPtr->tableSource.table;
+ array = Blt_Malloc(sizeof(double) * Blt_Table_NumRows(table));
+ if (array == NULL) {
+ return TCL_ERROR;
+ }
+ for (j = 0, i = 1; i <= Blt_Table_NumRows(table); i++) {
+ Blt_TableRow row;
+ double value;
+
+ row = Blt_Table_FindRowByIndex(table, i);
+ value = Blt_Table_GetDouble(table, row, col);
+ if (FINITE(value)) {
+ array[j] = value;
+ j++;
+ }
+ }
+ if (valuesPtr->values != NULL) {
+ Blt_Free(valuesPtr->values);
+ }
+ valuesPtr->nValues = j;
+ valuesPtr->values = array;
+ FindRange(valuesPtr);
+ return TCL_OK;
+}
+
+static void
+FreeTableSource(ElemValues *valuesPtr)
+{
+ TableDataSource *srcPtr;
+
+ srcPtr = &valuesPtr->tableSource;
+ if (srcPtr->trace != NULL) {
+ Blt_Table_DeleteTrace(srcPtr->trace);
+ }
+ if (srcPtr->notifier != NULL) {
+ Blt_Table_DeleteNotifier(srcPtr->notifier);
+ }
+ if (srcPtr->hashPtr != NULL) {
+ TableClient *clientPtr;
+
+ clientPtr = Blt_GetHashValue(srcPtr->hashPtr);
+ clientPtr->refCount--;
+ if (clientPtr->refCount == 0) {
+ Graph *graphPtr;
+
+ graphPtr = valuesPtr->elemPtr->obj.graphPtr;
+ if (srcPtr->table != NULL) {
+ Blt_Table_Close(srcPtr->table);
+ }
+ Blt_Free(clientPtr);
+ Blt_DeleteHashEntry(&graphPtr->dataTables, srcPtr->hashPtr);
+ srcPtr->hashPtr = NULL;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TableNotifyProc --
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+TableNotifyProc(ClientData clientData, Blt_TableNotifyEvent *eventPtr)
+{
+ ElemValues *valuesPtr = clientData;
+ Element *elemPtr;
+ Graph *graphPtr;
+
+ elemPtr = valuesPtr->elemPtr;
+ graphPtr = elemPtr->obj.graphPtr;
+ if ((eventPtr->type == TABLE_NOTIFY_COLUMN_DELETED) ||
+ (FetchTableValues(graphPtr->interp, valuesPtr,
+ (Blt_TableColumn)eventPtr->header)) != TCL_OK) {
+ FreeTableSource(valuesPtr);
+ return TCL_ERROR;
+ }
+ /* Always redraw the element. */
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ if (!IGNORE_ELEMENT(elemPtr)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TableTraceProc --
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+TableTraceProc(ClientData clientData, Blt_TableTraceEvent *eventPtr)
+{
+ ElemValues *valuesPtr = clientData;
+ Element *elemPtr;
+ Graph *graphPtr;
+
+ elemPtr = valuesPtr->elemPtr;
+ graphPtr = elemPtr->obj.graphPtr;
+ assert((Blt_TableColumn)eventPtr->column == valuesPtr->tableSource.column);
+
+ if (FetchTableValues(eventPtr->interp, valuesPtr, eventPtr->column)
+ != TCL_OK) {
+ FreeTableSource(valuesPtr);
+ return TCL_ERROR;
+ }
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ if (!IGNORE_ELEMENT(elemPtr)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ return TCL_OK;
+}
+
+static int
+GetTableData(Tcl_Interp *interp, ElemValues *valuesPtr, const char *tableName,
+ Tcl_Obj *colObjPtr)
+{
+ TableDataSource *srcPtr;
+ TableClient *clientPtr;
+ int isNew;
+ Graph *graphPtr;
+
+ memset(&valuesPtr->tableSource, 0, sizeof(TableDataSource));
+ srcPtr = &valuesPtr->tableSource;
+ graphPtr = valuesPtr->elemPtr->obj.graphPtr;
+ /* See if the graph is already using this table. */
+ srcPtr->hashPtr = Blt_CreateHashEntry(&graphPtr->dataTables, tableName,
+ &isNew);
+ if (isNew) {
+ if (Blt_Table_Open(interp, tableName, &srcPtr->table) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ clientPtr = Blt_AssertMalloc(sizeof(TableClient));
+ clientPtr->table = srcPtr->table;
+ clientPtr->refCount = 1;
+ Blt_SetHashValue(srcPtr->hashPtr, clientPtr);
+ } else {
+ clientPtr = Blt_GetHashValue(srcPtr->hashPtr);
+ srcPtr->table = clientPtr->table;
+ clientPtr->refCount++;
+ }
+ srcPtr->column = Blt_Table_FindColumn(interp, srcPtr->table, colObjPtr);
+ if (srcPtr->column == NULL) {
+ goto error;
+ }
+ if (FetchTableValues(interp, valuesPtr, srcPtr->column) != TCL_OK) {
+ goto error;
+ }
+ srcPtr->notifier = Blt_Table_CreateColumnNotifier(interp, srcPtr->table,
+ srcPtr->column, TABLE_NOTIFY_COLUMN_CHANGED, TableNotifyProc,
+ (Blt_TableNotifierDeleteProc *)NULL, valuesPtr);
+ srcPtr->trace = Blt_Table_CreateColumnTrace(srcPtr->table, srcPtr->column,
+ (TABLE_TRACE_WRITES | TABLE_TRACE_UNSETS | TABLE_TRACE_CREATES), TableTraceProc,
+ (Blt_TableTraceDeleteProc *)NULL, valuesPtr);
+ valuesPtr->type = ELEM_SOURCE_TABLE;
+ return TCL_OK;
+ error:
+ FreeTableSource(valuesPtr);
+ return TCL_ERROR;
+}
+
+static int
+ParseValues(Tcl_Interp *interp, Tcl_Obj *objPtr, int *nValuesPtr,
+ double **arrayPtr)
+{
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *arrayPtr = NULL;
+ *nValuesPtr = 0;
+ if (objc > 0) {
+ double *array;
+ double *p;
+ int i;
+
+ array = Blt_Malloc(sizeof(double) * objc);
+ if (array == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (p = array, i = 0; i < objc; i++, p++) {
+ if (Blt_ExprDoubleFromObj(interp, objv[i], p) != TCL_OK) {
+ Blt_Free(array);
+ return TCL_ERROR;
+ }
+ }
+ *arrayPtr = array;
+ *nValuesPtr = objc;
+ }
+ return TCL_OK;
+}
+
+static void
+FreeDataValues(ElemValues *valuesPtr)
+{
+ switch (valuesPtr->type) {
+ case ELEM_SOURCE_VECTOR:
+ FreeVectorSource(valuesPtr); break;
+ case ELEM_SOURCE_TABLE:
+ FreeTableSource(valuesPtr); break;
+ case ELEM_SOURCE_VALUES:
+ break;
+ }
+ if (valuesPtr->values != NULL) {
+ Blt_Free(valuesPtr->values);
+ }
+ valuesPtr->values = NULL;
+ valuesPtr->nValues = 0;
+ valuesPtr->type = ELEM_SOURCE_VALUES;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FindRange --
+ *
+ * Find the minimum, positive minimum, and maximum values in a given
+ * vector and store the results in the vector structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Minimum, positive minimum, and maximum values are stored in the
+ * vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FindRange(ElemValues *valuesPtr)
+{
+ int i;
+ double *x;
+ double min, max;
+
+ if ((valuesPtr->nValues < 1) || (valuesPtr->values == NULL)) {
+ return; /* This shouldn't ever happen. */
+ }
+ x = valuesPtr->values;
+
+ min = DBL_MAX, max = -DBL_MAX;
+ for(i = 0; i < valuesPtr->nValues; i++) {
+ if (FINITE(x[i])) {
+ min = max = x[i];
+ break;
+ }
+ }
+ /* Initialize values to track the vector range */
+ for (/* empty */; i < valuesPtr->nValues; i++) {
+ if (FINITE(x[i])) {
+ if (x[i] < min) {
+ min = x[i];
+ } else if (x[i] > max) {
+ max = x[i];
+ }
+ }
+ }
+ valuesPtr->min = min, valuesPtr->max = max;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_FindElemValuesMinimum --
+ *
+ * Find the minimum, positive minimum, and maximum values in a given
+ * vector and store the results in the vector structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Minimum, positive minimum, and maximum values are stored in the
+ * vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+double
+Blt_FindElemValuesMinimum(ElemValues *valuesPtr, double minLimit)
+{
+ int i;
+ double min;
+
+ min = DBL_MAX;
+ for (i = 0; i < valuesPtr->nValues; i++) {
+ double x;
+
+ x = valuesPtr->values[i];
+ if (x < 0.0) {
+ /* What do you do about negative values when using log
+ * scale values seems like a grey area. Mirror. */
+ x = -x;
+ }
+ if ((x > minLimit) && (min > x)) {
+ min = x;
+ }
+ }
+ if (min == DBL_MAX) {
+ min = minLimit;
+ }
+ return min;
+}
+
+/*ARGSUSED*/
+static void
+FreeValues(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ ElemValues *valuesPtr = (ElemValues *)(widgRec + offset);
+
+ FreeDataValues(valuesPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToValues --
+ *
+ * Given a TCL list of numeric expression representing the element
+ * values, convert into an array of double precision values. In addition,
+ * the minimum and maximum values are saved. Since elastic values are
+ * allow (values which translate to the min/max of the graph), we must
+ * try to get the non-elastic minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard TCL result. The vector is passed
+ * back via the valuesPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToValues(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* TCL list of expressions */
+ char *widgRec, /* Element record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ ElemValues *valuesPtr = (ElemValues *)(widgRec + offset);
+ Element *elemPtr = (Element *)widgRec;
+ Tcl_Obj **objv;
+ int objc;
+ int result;
+ const char *string;
+
+ valuesPtr->elemPtr = elemPtr;
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ elemPtr->flags |= MAP_ITEM;
+
+ /* Release the current data sources. */
+ FreeDataValues(valuesPtr);
+ if (objc == 0) {
+ return TCL_OK; /* Empty list of values. */
+ }
+ string = Tcl_GetString(objv[0]);
+ if ((objc == 1) && (Blt_VectorExists2(interp, string))) {
+ result = GetVectorData(interp, valuesPtr, string);
+ } else if ((objc == 2) && (Blt_Table_TableExists(interp, string))) {
+ result = GetTableData(interp, valuesPtr, string, objv[1]);
+ } else {
+ double *values;
+ int nValues;
+
+ result = ParseValues(interp, objPtr, &nValues, &values);
+ if (result != TCL_OK) {
+ return TCL_ERROR; /* Can't parse the values as numbers. */
+ }
+ FreeDataValues(valuesPtr);
+ if (nValues > 0) {
+ valuesPtr->values = values;
+ }
+ valuesPtr->nValues = nValues;
+ FindRange(valuesPtr);
+ valuesPtr->type = ELEM_SOURCE_VALUES;
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ValuesToObj --
+ *
+ * Convert the vector of floating point values into a TCL list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ValuesToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ ElemValues *valuesPtr = (ElemValues *)(widgRec + offset);
+
+ switch (valuesPtr->type) {
+ case ELEM_SOURCE_VECTOR:
+ {
+ const char *vecName;
+
+ vecName = Blt_NameOfVectorId(valuesPtr->vectorSource.vector);
+ return Tcl_NewStringObj(vecName, -1);
+ }
+ case ELEM_SOURCE_TABLE:
+ {
+ Tcl_Obj *listObjPtr;
+ const char *tableName;
+ long i;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ tableName = Blt_Table_TableName(valuesPtr->tableSource.table);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(tableName, -1));
+
+ i = Blt_Table_ColumnIndex(valuesPtr->tableSource.column);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj(i));
+ return listObjPtr;
+ }
+ case ELEM_SOURCE_VALUES:
+ {
+ Tcl_Obj *listObjPtr;
+ double *vp, *vend;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (vp = valuesPtr->values, vend = vp + valuesPtr->nValues;
+ vp < vend; vp++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(*vp));
+ }
+ return listObjPtr;
+ }
+ default:
+ abort();
+ }
+ return Tcl_NewStringObj("", 0);
+}
+
+/*ARGSUSED*/
+static void
+FreeValuePairs(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset) /* Not used. */
+{
+ Element *elemPtr = (Element *)widgRec;
+
+ FreeDataValues(&elemPtr->x);
+ FreeDataValues(&elemPtr->y);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToValuePairs --
+ *
+ * This procedure is like ObjToValues except that it interprets
+ * the list of numeric expressions as X Y coordinate pairs. The
+ * minimum and maximum for both the X and Y vectors are
+ * determined.
+ *
+ * Results:
+ * The return value is a standard TCL result. The vectors are
+ * passed back via the widget record (elemPtr).
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToValuePairs(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* TCL list of numeric expressions */
+ char *widgRec, /* Element record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Element *elemPtr = (Element *)widgRec;
+ double *values;
+ int nValues;
+ size_t newSize;
+
+ if (ParseValues(interp, objPtr, &nValues, &values) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nValues & 1) {
+ Tcl_AppendResult(interp, "odd number of data points", (char *)NULL);
+ Blt_Free(values);
+ return TCL_ERROR;
+ }
+ nValues /= 2;
+ newSize = nValues * sizeof(double);
+ FreeDataValues(&elemPtr->x); /* Release the current data sources. */
+ FreeDataValues(&elemPtr->y);
+ if (newSize > 0) {
+ double *p;
+ int i;
+
+ elemPtr->x.values = Blt_AssertMalloc(newSize);
+ elemPtr->y.values = Blt_AssertMalloc(newSize);
+ elemPtr->x.nValues = elemPtr->y.nValues = nValues;
+ for (p = values, i = 0; i < nValues; i++) {
+ elemPtr->x.values[i] = *p++;
+ elemPtr->y.values[i] = *p++;
+ }
+ Blt_Free(values);
+ FindRange(&elemPtr->x);
+ FindRange(&elemPtr->y);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ValuePairsToObj --
+ *
+ * Convert pairs of floating point values in the X and Y arrays
+ * into a TCL list.
+ *
+ * Results:
+ * The return value is a string (Tcl list).
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ValuePairsToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element information record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Element *elemPtr = (Element *)widgRec;
+ Tcl_Obj *listObjPtr;
+ int i;
+ int length;
+
+ length = NUMBEROFPOINTS(elemPtr);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = 0; i < length; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(elemPtr->x.values[i]));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(elemPtr->y.values[i]));
+ }
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToAlong --
+ *
+ * Given a TCL list of numeric expression representing the element
+ * values, convert into an array of double precision values. In
+ * addition, the minimum and maximum values are saved. Since
+ * elastic values are allow (values which translate to the
+ * min/max of the graph), we must try to get the non-elastic
+ * minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard TCL result. The vector is passed
+ * back via the valuesPtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToAlong(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representation of value. */
+ char *widgRec, /* Widget record. */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int *intPtr = (int *)(widgRec + offset);
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((string[0] == 'x') && (string[1] == '\0')) {
+ *intPtr = SEARCH_X;
+ } else if ((string[0] == 'y') && (string[1] == '\0')) {
+ *intPtr = SEARCH_Y;
+ } else if ((string[0] == 'b') && (strcmp(string, "both") == 0)) {
+ *intPtr = SEARCH_BOTH;
+ } else {
+ Tcl_AppendResult(interp, "bad along value \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AlongToObj --
+ *
+ * Convert the vector of floating point values into a TCL list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+AlongToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int along = *(int *)(widgRec + offset);
+ Tcl_Obj *objPtr;
+
+ switch (along) {
+ case SEARCH_X:
+ objPtr = Tcl_NewStringObj("x", 1);
+ break;
+ case SEARCH_Y:
+ objPtr = Tcl_NewStringObj("y", 1);
+ break;
+ case SEARCH_BOTH:
+ objPtr = Tcl_NewStringObj("both", 4);
+ break;
+ default:
+ objPtr = Tcl_NewStringObj("unknown along value", 4);
+ break;
+ }
+ return objPtr;
+}
+
+void
+Blt_FreeStylePalette(Blt_Chain stylePalette)
+{
+ Blt_ChainLink link;
+
+ /* Skip the first slot. It contains the built-in "normal" pen of
+ * the element. */
+ link = Blt_Chain_FirstLink(stylePalette);
+ if (link != NULL) {
+ Blt_ChainLink next;
+
+ for (link = Blt_Chain_NextLink(link); link != NULL; link = next) {
+ PenStyle *stylePtr;
+
+ next = Blt_Chain_NextLink(link);
+ stylePtr = Blt_Chain_GetValue(link);
+ Blt_FreePen(stylePtr->penPtr);
+ Blt_Chain_DeleteLink(stylePalette, link);
+ }
+ }
+}
+
+/*ARGSUSED*/
+static void
+FreeStyles(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Blt_Chain stylePalette = *(Blt_Chain *)(widgRec + offset);
+
+ Blt_FreeStylePalette(stylePalette);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ObjToStyles --
+ *
+ * Parse the list of style names.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToStyles(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing style list */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Blt_Chain stylePalette = *(Blt_Chain *)(widgRec + offset);
+ Blt_ChainLink link;
+ Element *elemPtr = (Element *)(widgRec);
+ PenStyle *stylePtr;
+ Tcl_Obj **objv;
+ int objc;
+ int i;
+ size_t size = (size_t)clientData;
+
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Reserve the first entry for the "normal" pen. We'll set the
+ * style later */
+ Blt_FreeStylePalette(stylePalette);
+ link = Blt_Chain_FirstLink(stylePalette);
+ if (link == NULL) {
+ link = Blt_Chain_AllocLink(size);
+ Blt_Chain_LinkAfter(stylePalette, link, NULL);
+ }
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->penPtr = elemPtr->normalPenPtr;
+ for (i = 0; i < objc; i++) {
+ link = Blt_Chain_AllocLink(size);
+ stylePtr = Blt_Chain_GetValue(link);
+ stylePtr->weight.min = (double)i;
+ stylePtr->weight.max = (double)i + 1.0;
+ stylePtr->weight.range = 1.0;
+ if (GetPenStyleFromObj(interp, elemPtr->obj.graphPtr, objv[i],
+ elemPtr->obj.classId, (PenStyle *)stylePtr) != TCL_OK) {
+ Blt_FreeStylePalette(stylePalette);
+ return TCL_ERROR;
+ }
+ Blt_Chain_LinkAfter(stylePalette, link, NULL);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * StylesToObj --
+ *
+ * Convert the style information into a Tcl_Obj.
+ *
+ * Results:
+ * The string representing the style information is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+StylesToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Blt_Chain stylePalette = *(Blt_Chain *)(widgRec + offset);
+ Blt_ChainLink link;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ link = Blt_Chain_FirstLink(stylePalette);
+ if (link != NULL) {
+ /* Skip the first style (it's the default) */
+ for (link = Blt_Chain_NextLink(link); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ PenStyle *stylePtr;
+ Tcl_Obj *subListObjPtr;
+
+ stylePtr = Blt_Chain_GetValue(link);
+ subListObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, subListObjPtr,
+ Tcl_NewStringObj(stylePtr->penPtr->name, -1));
+ Tcl_ListObjAppendElement(interp, subListObjPtr,
+ Tcl_NewDoubleObj(stylePtr->weight.min));
+ Tcl_ListObjAppendElement(interp, subListObjPtr,
+ Tcl_NewDoubleObj(stylePtr->weight.max));
+ Tcl_ListObjAppendElement(interp, listObjPtr, subListObjPtr);
+ }
+ }
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_StyleMap --
+ *
+ * Creates an array of style indices and fills it based on the weight
+ * of each data point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+PenStyle **
+Blt_StyleMap(Element *elemPtr)
+{
+ int i;
+ int nWeights; /* Number of weights to be examined.
+ * If there are more data points than
+ * weights, they will default to the
+ * normal pen. */
+
+ PenStyle **dataToStyle; /* Directory of styles. Each array
+ * element represents the style for
+ * the data point at that index */
+ Blt_ChainLink link;
+ PenStyle *stylePtr;
+ double *w; /* Weight vector */
+ int nPoints;
+
+ nPoints = NUMBEROFPOINTS(elemPtr);
+ nWeights = MIN(elemPtr->w.nValues, nPoints);
+ w = elemPtr->w.values;
+ link = Blt_Chain_FirstLink(elemPtr->stylePalette);
+ stylePtr = Blt_Chain_GetValue(link);
+
+ /*
+ * Create a style mapping array (data point index to style),
+ * initialized to the default style.
+ */
+ dataToStyle = Blt_AssertMalloc(nPoints * sizeof(PenStyle *));
+ for (i = 0; i < nPoints; i++) {
+ dataToStyle[i] = stylePtr;
+ }
+
+ for (i = 0; i < nWeights; i++) {
+ for (link = Blt_Chain_LastLink(elemPtr->stylePalette); link != NULL;
+ link = Blt_Chain_PrevLink(link)) {
+ stylePtr = Blt_Chain_GetValue(link);
+
+ if (stylePtr->weight.range > 0.0) {
+ double norm;
+
+ norm = (w[i] - stylePtr->weight.min) / stylePtr->weight.range;
+ if (((norm - 1.0) <= DBL_EPSILON) &&
+ (((1.0 - norm) - 1.0) <= DBL_EPSILON)) {
+ dataToStyle[i] = stylePtr;
+ break; /* Done: found range that matches. */
+ }
+ }
+ }
+ }
+ return dataToStyle;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetIndex --
+ *
+ * Given a string representing the index of a pair of x,y
+ * coordinates, return the numeric index.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetIndex(Tcl_Interp *interp, Element *elemPtr, Tcl_Obj *objPtr, int *indexPtr)
+{
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((*string == 'e') && (strcmp("end", string) == 0)) {
+ *indexPtr = NUMBEROFPOINTS(elemPtr) - 1;
+ } else if (Blt_ExprIntFromObj(interp, objPtr, indexPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetElement --
+ *
+ * Find the element represented the given name, returning a pointer to
+ * its data structure via elemPtrPtr.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetElement(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ Element **elemPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+ char *name;
+
+ name = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table, name);
+ if (hPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find element \"", name,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *elemPtrPtr = Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyElement --
+ *
+ * Add a new element to the graph.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DestroyElement(Element *elemPtr)
+{
+ Graph *graphPtr = elemPtr->obj.graphPtr;
+
+ Blt_DeleteBindings(graphPtr->bindTable, elemPtr);
+ Blt_Legend_RemoveElement(graphPtr, elemPtr);
+
+ Blt_FreeOptions(elemPtr->configSpecs, (char *)elemPtr,graphPtr->display, 0);
+ /*
+ * Call the element's own destructor to release the memory and
+ * resources allocated for it.
+ */
+ (*elemPtr->procsPtr->destroyProc) (graphPtr, elemPtr);
+
+ /* Remove it also from the element display list */
+ if (elemPtr->link != NULL) {
+ Blt_Chain_DeleteLink(graphPtr->elements.displayList, elemPtr->link);
+ if (!IGNORE_ELEMENT(elemPtr)) {
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ }
+ /* Remove the element for the graph's hash table of elements */
+ if (elemPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&graphPtr->elements.table, elemPtr->hashPtr);
+ }
+ if (elemPtr->obj.name != NULL) {
+ Blt_Free(elemPtr->obj.name);
+ }
+ if (elemPtr->label != NULL) {
+ Blt_Free(elemPtr->label);
+ }
+ Blt_Free(elemPtr);
+}
+
+static void
+FreeElement(DestroyData data)
+{
+ Element *elemPtr = (Element *)data;
+ DestroyElement(elemPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateElement --
+ *
+ * Add a new element to the graph.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+CreateElement(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv, ClassId classId)
+{
+ Element *elemPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if (string[0] == '-') {
+ Tcl_AppendResult(graphPtr->interp, "name of element \"", string,
+ "\" can't start with a '-'", (char *)NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&graphPtr->elements.table, string, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "element \"", string,
+ "\" already exists in \"", Tcl_GetString(objv[0]),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (classId == CID_ELEM_BAR) {
+ elemPtr = Blt_BarElement(graphPtr, string, classId);
+ } else {
+ /* Stripcharts are line graphs with some options enabled. */
+ elemPtr = Blt_LineElement(graphPtr, string, classId);
+ }
+ assert(elemPtr->configSpecs != NULL);
+ elemPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, elemPtr);
+
+ if (Blt_ConfigureComponentFromObj(interp, graphPtr->tkwin,
+ elemPtr->obj.name, "Element", elemPtr->configSpecs, objc - 4, objv + 4,
+ (char *)elemPtr, 0) != TCL_OK) {
+ DestroyElement(elemPtr);
+ return TCL_ERROR;
+ }
+ (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr);
+ elemPtr->link = Blt_Chain_Append(graphPtr->elements.displayList, elemPtr);
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ elemPtr->flags |= MAP_ITEM;
+ graphPtr->flags |= RESET_AXES;
+ Tcl_SetObjResult(interp, objv[3]);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyElements --
+ *
+ * Removes all the graph's elements. This routine is called when
+ * the graph is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory allocated for the graph's elements is freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DestroyElements(Graph *graphPtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+ Element *elemPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ elemPtr->hashPtr = NULL;
+ DestroyElement(elemPtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->elements.table);
+ Blt_DeleteHashTable(&graphPtr->elements.tagTable);
+ Blt_Chain_Destroy(graphPtr->elements.displayList);
+}
+
+void
+Blt_ConfigureElements(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr);
+ }
+}
+
+void
+Blt_MapElements(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ if (graphPtr->mode != BARS_INFRONT) {
+ Blt_ResetBarGroups(graphPtr);
+ }
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (IGNORE_ELEMENT(elemPtr)) {
+ continue;
+ }
+ if ((graphPtr->flags & MAP_ALL) || (elemPtr->flags & MAP_ITEM)) {
+ (*elemPtr->procsPtr->mapProc) (graphPtr, elemPtr);
+ elemPtr->flags &= ~MAP_ITEM;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawElements --
+ *
+ * Calls the individual element drawing routines for each
+ * element.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Elements are drawn into the drawable (pixmap) which will
+ * eventually be displayed in the graph window.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawElements(Graph *graphPtr, Drawable drawable)
+{
+ Blt_ChainLink link;
+
+ /* Draw with respect to the stacking order. */
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((elemPtr->flags & (HIDE|DELETE_PENDING)) == 0) {
+ (*elemPtr->procsPtr->drawNormalProc)(graphPtr, drawable, elemPtr);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawActiveElements --
+ *
+ * Calls the individual element drawing routines to display
+ * the active colors for each element.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Elements are drawn into the drawable (pixmap) which will
+ * eventually be displayed in the graph window.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawActiveElements(Graph *graphPtr, Drawable drawable)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((elemPtr->flags & (HIDE|ACTIVE|DELETE_PENDING)) == ACTIVE) {
+ (*elemPtr->procsPtr->drawActiveProc)(graphPtr, drawable, elemPtr);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ElementsToPostScript --
+ *
+ * Generates PostScript output for each graph element in the
+ * element display list.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ElementsToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->flags & (HIDE|DELETE_PENDING)) {
+ continue;
+ }
+ /* Comment the PostScript to indicate the start of the element */
+ Blt_Ps_Format(ps, "\n%% Element \"%s\"\n\n", elemPtr->obj.name);
+ (*elemPtr->procsPtr->printNormalProc) (graphPtr, ps, elemPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ActiveElementsToPostScript --
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ActiveElementsToPostScript( Graph *graphPtr, Blt_Ps ps)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if ((elemPtr->flags & (DELETE_PENDING|HIDE|ACTIVE)) == ACTIVE) {
+ Blt_Ps_Format(ps, "\n%% Active Element \"%s\"\n\n",
+ elemPtr->obj.name);
+ (*elemPtr->procsPtr->printActiveProc)(graphPtr, ps, elemPtr);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Marks data points of elements (given by their index) as active.
+ *
+ * Results:
+ * Returns TCL_OK if no errors occurred.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ActivateOp(
+ Graph *graphPtr, /* Graph widget */
+ Tcl_Interp *interp, /* Interpreter to report errors to */
+ int objc, /* Number of element names */
+ Tcl_Obj *const *objv) /* List of element names */
+{
+ Element *elemPtr;
+ int i;
+ int *indices;
+ int nIndices;
+
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ /* List all the currently active elements */
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ elemPtr = Blt_GetHashValue(hPtr);
+ if (elemPtr->flags & ACTIVE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(elemPtr->obj.name, -1));
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ if (Blt_GetElement(interp, graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ elemPtr->flags |= ACTIVE | ACTIVE_PENDING;
+
+ indices = NULL;
+ nIndices = -1;
+ if (objc > 4) {
+ int *activePtr;
+
+ nIndices = objc - 4;
+ activePtr = indices = Blt_AssertMalloc(sizeof(int) * nIndices);
+ for (i = 4; i < objc; i++) {
+ if (GetIndex(interp, elemPtr, objv[i], activePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ activePtr++;
+ }
+ }
+ if (elemPtr->activeIndices != NULL) {
+ Blt_Free(elemPtr->activeIndices);
+ }
+ elemPtr->nActiveIndices = nIndices;
+ elemPtr->activeIndices = indices;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+ClientData
+Blt_MakeElementTag(Graph *graphPtr, const char *tagName)
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(&graphPtr->elements.tagTable, tagName, &isNew);
+ return Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g element bind elemName sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+ char *tagName;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(tagName, -1));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->bindTable,
+ Blt_MakeElementTag(graphPtr, Tcl_GetString(objv[3])),
+ objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * Add a new element to the graph (using the default type of the
+ * graph).
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+CreateOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv,
+ ClassId classId)
+{
+ return CreateElement(graphPtr, interp, objc, objv, classId);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv)
+{
+ Element *elemPtr;
+
+ if (Blt_GetElement(interp, graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ if (Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, elemPtr->configSpecs,
+ (char *)elemPtr, objv[4], 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClosestOp --
+ *
+ * Find the element closest to the specified screen coordinates.
+ * Options:
+ * -halo Consider points only with this maximum distance
+ * from the picked coordinate.
+ * -interpolate Find closest point along element traces, not just
+ * data points.
+ * -along
+ *
+ * Results:
+ * A standard TCL result. If an element could be found within
+ * the halo distance, the interpreter result is "1", otherwise
+ * "0". If a closest element exists, the designated TCL array
+ * variable will be set with the following information:
+ *
+ * 1) the element name,
+ * 2) the index of the closest point,
+ * 3) the distance (in screen coordinates) from the picked X-Y
+ * coordinate and the closest point,
+ * 4) the X coordinate (graph coordinate) of the closest point,
+ * 5) and the Y-coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Blt_ConfigSpec closestSpecs[] = {
+ {BLT_CONFIG_PIXELS_NNEG, "-halo", (char *)NULL, (char *)NULL,
+ (char *)NULL, Blt_Offset(ClosestSearch, halo), 0},
+ {BLT_CONFIG_BOOLEAN, "-interpolate", (char *)NULL, (char *)NULL,
+ (char *)NULL, Blt_Offset(ClosestSearch, mode), 0 },
+ {BLT_CONFIG_CUSTOM, "-along", (char *)NULL, (char *)NULL,
+ (char *)NULL, Blt_Offset(ClosestSearch, along), 0, &alongOption},
+ {BLT_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL, 0, 0}
+};
+
+static int
+ClosestOp(
+ Graph *graphPtr, /* Graph widget */
+ Tcl_Interp *interp, /* Interpreter to report results to */
+ int objc, /* Number of element names */
+ Tcl_Obj *const *objv) /* List of element names */
+{
+ Element *elemPtr;
+ ClosestSearch search;
+ int i, x, y;
+ char *string;
+
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
+ Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL);
+ return TCL_ERROR;
+ }
+ for (i = 5; i < objc; i += 2) { /* Count switches-value pairs */
+ string = Tcl_GetString(objv[i]);
+ if ((string[0] != '-') ||
+ ((string[1] == '-') && (string[2] == '\0'))) {
+ break;
+ }
+ }
+ if (i > objc) {
+ i = objc;
+ }
+
+ search.mode = SEARCH_POINTS;
+ search.halo = graphPtr->halo;
+ search.index = -1;
+ search.along = SEARCH_BOTH;
+ search.x = x;
+ search.y = y;
+
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, closestSpecs, i - 5,
+ objv + 5, (char *)&search, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR; /* Error occurred processing an option. */
+ }
+ if (i < objc) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ i++; /* Skip "--" */
+ }
+ }
+ search.dist = (double)(search.halo + 1);
+
+ if (i < objc) {
+ for ( /* empty */ ; i < objc; i++) {
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ if (IGNORE_ELEMENT(elemPtr)) {
+ continue;
+ }
+ if (elemPtr->flags & (HIDE|MAP_ITEM)) {
+ continue;
+ }
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ } else {
+ Blt_ChainLink link;
+
+ /*
+ * Find the closest point from the set of displayed elements,
+ * searching the display list from back to front. That way if
+ * the points from two different elements overlay each other
+ * exactly, the last one picked will be the topmost.
+ */
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->flags & (HIDE|MAP_ITEM|DELETE_PENDING)) {
+ continue;
+ }
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ }
+ if (search.dist < (double)search.halo) {
+ Tcl_Obj *listObjPtr;
+ /*
+ * Return a list of name value pairs.
+ */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("name", -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(search.elemPtr->obj.name, -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("index", -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(search.index));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("x", -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(search.point.x));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("y", -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(search.point.y));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj("dist", -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(search.dist));
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Sets the element specifications by the given the command line
+ * arguments and calls the element specification configuration
+ * routine. If zero or one command line options are given, only
+ * information about the option(s) is returned in interp->result.
+ * If the element configuration has changed and the element is
+ * currently displayed, the axis limits are updated and
+ * recomputed.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ int nNames, nOpts;
+ Tcl_Obj *const *options;
+ int i;
+
+ /* Figure out where the option value pairs begin */
+ objc -= 3;
+ objv += 3;
+ for (i = 0; i < objc; i++) {
+ Element *elemPtr;
+ char *string;
+
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ }
+ nNames = i; /* Number of element names specified */
+ nOpts = objc - i; /* Number of options specified */
+ options = objv + nNames; /* Start of options in objv */
+
+ for (i = 0; i < nNames; i++) {
+ Element *elemPtr;
+ int flags;
+
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = BLT_CONFIG_OBJV_ONLY;
+ if (nOpts == 0) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ elemPtr->configSpecs, (char *)elemPtr, (Tcl_Obj *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ elemPtr->configSpecs, (char *)elemPtr, options[0], flags);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin,
+ elemPtr->configSpecs, nOpts, options, (char *)elemPtr, flags)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((*elemPtr->procsPtr->configProc) (graphPtr, elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Failed to configure element */
+ }
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-hide", (char *)NULL)) {
+ graphPtr->flags |= RESET_AXES;
+ elemPtr->flags |= MAP_ITEM;
+ }
+ /* If data points or axes have changed, reset the axes (may
+ * affect autoscaling) and recalculate the screen points of
+ * the element. */
+
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-*data", "-map*", "-x",
+ "-y", (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD;
+ elemPtr->flags |= MAP_ITEM;
+ }
+ /* The new label may change the size of the legend */
+ if (Blt_ConfigModified(elemPtr->configSpecs, "-label", (char *)NULL)) {
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ }
+ }
+ /* Update the pixmap if any configuration option changed */
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeactivateOp --
+ *
+ * Clears the active bit for the named elements.
+ *
+ * Results:
+ * Returns TCL_OK if no errors occurred.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeactivateOp(
+ Graph *graphPtr, /* Graph widget */
+ Tcl_Interp *interp, /* Not used. */
+ int objc, /* Number of element names */
+ Tcl_Obj *const *objv) /* List of element names */
+{
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ Element *elemPtr;
+
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ elemPtr->flags &= ~(ACTIVE | ACTIVE_PENDING);
+ if (elemPtr->activeIndices != NULL) {
+ Blt_Free(elemPtr->activeIndices);
+ elemPtr->activeIndices = NULL;
+ }
+ elemPtr->nActiveIndices = 0;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Delete the named elements from the graph.
+ *
+ * Results:
+ * TCL_ERROR is returned if any of the named elements can not be
+ * found. Otherwise TCL_OK is returned;
+ *
+ * Side Effects:
+ * If the element is currently displayed, the plotting area of
+ * the graph is redrawn. Memory and resources allocated by the
+ * elements are released.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(
+ Graph *graphPtr, /* Graph widget */
+ Tcl_Interp *interp, /* Not used. */
+ int objc, /* Number of element names */
+ Tcl_Obj *const *objv) /* List of element names */
+{
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ Element *elemPtr;
+
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ elemPtr->flags |= DELETE_PENDING;
+ Tcl_EventuallyFree(elemPtr, FreeElement);
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ * Indicates if the named element exists in the graph.
+ *
+ * Results:
+ * The return value is a standard TCL result. The interpreter
+ * result will contain "1" or "0".
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ExistsOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table, Tcl_GetString(objv[3]));
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Returns the name of the picked element (using the element
+ * bind operation). Right now, the only name accepted is
+ * "current".
+ *
+ * Results:
+ * A standard TCL result. The interpreter result will contain
+ * the name of the element.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv)
+{
+ char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if ((string[0] == 'c') && (strcmp(string, "current") == 0)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_GetCurrentItem(graphPtr->bindTable);
+ /* Report only on elements. */
+ if ((elemPtr != NULL) && ((elemPtr->flags & DELETE_PENDING) == 0) &&
+ (elemPtr->obj.classId >= CID_ELEM_BAR) &&
+ (elemPtr->obj.classId <= CID_ELEM_STRIP)) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name,-1);
+ }
+ }
+ return TCL_OK;
+}
+
+static Tcl_Obj *
+DisplayListObj(Graph *graphPtr)
+{
+ Tcl_Obj *listObjPtr;
+ Blt_ChainLink link;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ Tcl_Obj *objPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(graphPtr->interp, listObjPtr, objPtr);
+ }
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LowerOp --
+ *
+ * Lowers the named elements to the bottom of the display list.
+ *
+ * Results:
+ * A standard TCL result. The interpreter result will contain the new
+ * display list of element names.
+ *
+ * .g element lower elem ?elem...?
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+LowerOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_Chain chain;
+ Blt_ChainLink link, next;
+ int i;
+
+ /* Move the links of lowered elements out of the display list into
+ * a temporary list. */
+ chain = Blt_Chain_Create();
+ for (i = 3; i < objc; i++) {
+ Element *elemPtr;
+
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ Blt_Chain_UnlinkLink(graphPtr->elements.displayList, elemPtr->link);
+ Blt_Chain_LinkAfter(chain, elemPtr->link, NULL);
+ }
+ /* Append the links to end of the display list. */
+ for (link = Blt_Chain_FirstLink(chain); link != NULL; link = next) {
+ next = Blt_Chain_NextLink(link);
+ Blt_Chain_UnlinkLink(chain, link);
+ Blt_Chain_LinkAfter(graphPtr->elements.displayList, link, NULL);
+ }
+ Blt_Chain_Destroy(chain);
+ Tcl_SetObjResult(interp, DisplayListObj(graphPtr));
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns the names of the elements is the graph matching
+ * one of more patterns provided. If no pattern arguments
+ * are given, then all element names will be returned.
+ *
+ * Results:
+ * The return value is a standard TCL result. The interpreter
+ * result will contain a TCL list of the element names.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NamesOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Element *elemPtr;
+ Tcl_Obj *objPtr;
+
+ elemPtr = Blt_GetHashValue(hPtr);
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Element *elemPtr;
+ int i;
+
+ elemPtr = Blt_GetHashValue(hPtr);
+ for (i = 3; i < objc; i++) {
+ if (Tcl_StringMatch(elemPtr->obj.name,Tcl_GetString(objv[i]))) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ break;
+ }
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RaiseOp --
+ *
+ * Reset the element within the display list.
+ *
+ * Results:
+ * The return value is a standard TCL result. The interpreter
+ * result will contain the new display list of element names.
+ *
+ * .g element raise ?elem...?
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RaiseOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_Chain chain;
+ Blt_ChainLink link, prev;
+ int i;
+
+ /* Move the links of lowered elements out of the display list into
+ * a temporary list. */
+ chain = Blt_Chain_Create();
+ for (i = 3; i < objc; i++) {
+ Element *elemPtr;
+
+ if (Blt_GetElement(interp, graphPtr, objv[i], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ Blt_Chain_UnlinkLink(graphPtr->elements.displayList, elemPtr->link);
+ Blt_Chain_LinkAfter(chain, elemPtr->link, NULL);
+ }
+ /* Prepend the links to beginning of the display list in reverse order. */
+ for (link = Blt_Chain_LastLink(chain); link != NULL; link = prev) {
+ prev = Blt_Chain_PrevLink(link);
+ Blt_Chain_UnlinkLink(chain, link);
+ Blt_Chain_LinkBefore(graphPtr->elements.displayList, link, NULL);
+ }
+ Blt_Chain_Destroy(chain);
+ Tcl_SetObjResult(interp, DisplayListObj(graphPtr));
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ShowOp --
+ *
+ * Queries or resets the element display list.
+ *
+ * Results:
+ * The return value is a standard TCL result. The interpreter
+ * result will contain the new display list of element names.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ShowOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ if (objc == 4) {
+ Blt_Chain chain;
+ Blt_ChainLink link;
+ Tcl_Obj **elem;
+ int i, n;
+
+ if (Tcl_ListObjGetElements(interp, objv[3], &n, &elem) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Collect the named elements into a list. */
+ chain = Blt_Chain_Create();
+ for (i = 0; i < n; i++) {
+ Element *elemPtr; /* Element information record */
+
+ if (Blt_GetElement(interp, graphPtr, elem[i], &elemPtr) != TCL_OK) {
+ Blt_Chain_Destroy(chain);
+ return TCL_ERROR;
+ }
+ Blt_Chain_Append(chain, elemPtr);
+ }
+ /* Clear the links from the currently displayed elements. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ elemPtr->link = NULL;
+ }
+ Blt_Chain_Destroy(graphPtr->elements.displayList);
+ graphPtr->elements.displayList = chain;
+ /* Set links on all the displayed elements. */
+ for (link = Blt_Chain_FirstLink(chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ elemPtr->link = link;
+ }
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ Tcl_SetObjResult(interp, DisplayListObj(graphPtr));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Returns the name of the type of the element given by some
+ * element name.
+ *
+ * Results:
+ * A standard TCL result. Returns the type of the element in
+ * interp->result. If the identifier given doesn't represent an
+ * element, then an error message is left in interp->result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(
+ Graph *graphPtr, /* Graph widget */
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv) /* Element name */
+{
+ Element *elemPtr;
+ const char *string;
+
+ if (Blt_GetElement(interp, graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR; /* Can't find named element */
+ }
+ switch (elemPtr->obj.classId) {
+ case CID_ELEM_BAR: string = "bar"; break;
+ case CID_ELEM_CONTOUR: string = "contour"; break;
+ case CID_ELEM_LINE: string = "line"; break;
+ case CID_ELEM_STRIP: string = "strip"; break;
+ default: string = "???"; break;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), string, -1);
+ return TCL_OK;
+}
+
+/*
+ * Global routines:
+ */
+static Blt_OpSpec elemOps[] = {
+ {"activate", 1, ActivateOp, 3, 0, "?elemName? ?index...?",},
+ {"bind", 1, BindOp, 3, 6, "elemName sequence command",},
+ {"cget", 2, CgetOp, 5, 5, "elemName option",},
+ {"closest", 2, ClosestOp, 5, 0,
+ "x y ?option value?... ?elemName?...",},
+ {"configure", 2, ConfigureOp, 4, 0,
+ "elemName ?elemName?... ?option value?...",},
+ {"create", 2, CreateOp, 4, 0, "elemName ?option value?...",},
+ {"deactivate", 3, DeactivateOp, 3, 0, "?elemName?...",},
+ {"delete", 3, DeleteOp, 3, 0, "?elemName?...",},
+ {"exists", 1, ExistsOp, 4, 4, "elemName",},
+ {"get", 1, GetOp, 4, 4, "name",},
+ {"lower", 1, LowerOp, 3, 0, "?elemName?...",},
+ {"names", 1, NamesOp, 3, 0, "?pattern?...",},
+ {"raise", 1, RaiseOp, 3, 0, "?elemName?...",},
+ {"show", 1, ShowOp, 3, 4, "?elemList?",},
+ {"type", 1, TypeOp, 4, 4, "elemName",},
+};
+static int numElemOps = sizeof(elemOps) / sizeof(Blt_OpSpec);
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ElementOp --
+ *
+ * This procedure is invoked to process the TCL command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ElementOp(
+ Graph *graphPtr, /* Graph widget record */
+ Tcl_Interp *interp,
+ int objc, /* # arguments */
+ Tcl_Obj *const *objv, /* Argument list */
+ ClassId classId)
+{
+ void *ptr;
+ int result;
+
+ ptr = Blt_GetOpFromObj(interp, numElemOps, elemOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (ptr == CreateOp) {
+ result = CreateOp(graphPtr, interp, objc, objv, classId);
+ } else {
+ GraphElementProc *proc;
+
+ proc = ptr;
+ result = (*proc) (graphPtr, interp, objc, objv);
+ }
+ return result;
+}
diff --git a/src/bltGrElemOp.h b/src/bltGrElemOp.h
new file mode 100644
index 0000000..f0e3969
--- /dev/null
+++ b/src/bltGrElemOp.h
@@ -0,0 +1,271 @@
+
+/*
+ * bltGrElem.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_GR_ELEM_H
+#define _BLT_GR_ELEM_H
+
+#include <bltVector.h>
+#include <bltDataTable.h>
+
+#define ELEM_SOURCE_VALUES 0
+#define ELEM_SOURCE_VECTOR 1
+#define ELEM_SOURCE_TABLE 2
+
+#define SEARCH_X 0
+#define SEARCH_Y 1
+#define SEARCH_BOTH 2
+
+#define SHOW_NONE 0
+#define SHOW_X 1
+#define SHOW_Y 2
+#define SHOW_BOTH 3
+
+#define SEARCH_POINTS 0 /* Search for closest data point. */
+#define SEARCH_TRACES 1 /* Search for closest point on trace.
+ * Interpolate the connecting line segments if
+ * necessary. */
+#define SEARCH_AUTO 2 /* Automatically determine whether to search
+ * for data points or traces. Look for traces
+ * if the linewidth is > 0 and if there is
+ * more than one data point. */
+
+#define LABEL_ACTIVE (1<<9) /* Non-zero indicates that the element's entry
+ * in the legend should be drawn in its active
+ * foreground and background colors. */
+#define SCALE_SYMBOL (1<<10)
+
+#define NUMBEROFPOINTS(e) MIN((e)->x.nValues, (e)->y.nValues)
+
+#define NORMALPEN(e) ((((e)->normalPenPtr == NULL) ? \
+ (e)->builtinPenPtr : \
+ (e)->normalPenPtr))
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Weight --
+ *
+ * Designates a range of values by a minimum and maximum limit.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ double min, max, range;
+} Weight;
+
+#define SetRange(l) \
+ ((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON)
+#define SetScale(l) \
+ ((l).scale = 1.0 / (l).range)
+#define SetWeight(l, lo, hi) \
+ ((l).min = (lo), (l).max = (hi), SetRange(l))
+
+typedef struct {
+ Segment2d *segments; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ int nSegments;
+} ErrorBarSegments;
+
+/*
+ * An element has one or more vectors plus several attributes, such as line
+ * style, thickness, color, and symbol type. It has an identifier which
+ * distinguishes it among the list of all elements.
+ */
+typedef struct {
+ Weight weight; /* Weight range where this pen is valid. */
+ Pen *penPtr; /* Pen to use. */
+} PenStyle;
+
+
+typedef struct {
+ XColor *color; /* Color of error bar */
+ int lineWidth; /* Width of the error bar segments. */
+ GC gc;
+ int show; /* Flags for errorbars: none, x, y, or both */
+} ErrorBarAttributes;
+
+typedef struct {
+ /* Inputs */
+ int halo; /* Maximal screen distance a candidate point
+ * can be from the sample window coordinate */
+
+ int mode; /* Indicates whether to find the closest data
+ * point or the closest point on the trace by
+ * interpolating the line segments. Can also
+ * be SEARCH_AUTO, indicating to choose how to
+ * search.*/
+
+ int x, y; /* Screen coordinates of test point */
+
+ int along; /* Indicates to let search run along a
+ * particular axis: x, y, or both. */
+
+ /* Outputs */
+ Element *elemPtr; /* Name of the closest element */
+
+ Point2d point; /* Graph coordinates of closest point */
+
+ int index; /* Index of closest data point */
+
+ double dist; /* Distance in screen coordinates */
+
+} ClosestSearch;
+
+typedef void (ElementDrawProc) (Graph *graphPtr, Drawable drawable,
+ Element *elemPtr);
+
+typedef void (ElementToPostScriptProc) (Graph *graphPtr, Blt_Ps ps,
+ Element *elemPtr);
+
+typedef void (ElementDestroyProc) (Graph *graphPtr, Element *elemPtr);
+
+typedef int (ElementConfigProc) (Graph *graphPtr, Element *elemPtr);
+
+typedef void (ElementMapProc) (Graph *graphPtr, Element *elemPtr);
+
+typedef void (ElementExtentsProc) (Element *elemPtr, Region2d *extsPtr);
+
+typedef void (ElementClosestProc) (Graph *graphPtr, Element *elemPtr,
+ ClosestSearch *searchPtr);
+
+typedef void (ElementDrawSymbolProc) (Graph *graphPtr, Drawable drawable,
+ Element *elemPtr, int x, int y, int symbolSize);
+
+typedef void (ElementSymbolToPostScriptProc) (Graph *graphPtr,
+ Blt_Ps ps, Element *elemPtr, double x, double y, int symSize);
+
+typedef struct {
+ ElementClosestProc *closestProc;
+ ElementConfigProc *configProc;
+ ElementDestroyProc *destroyProc;
+ ElementDrawProc *drawActiveProc;
+ ElementDrawProc *drawNormalProc;
+ ElementDrawSymbolProc *drawSymbolProc;
+ ElementExtentsProc *extentsProc;
+ ElementToPostScriptProc *printActiveProc;
+ ElementToPostScriptProc *printNormalProc;
+ ElementSymbolToPostScriptProc *printSymbolProc;
+ ElementMapProc *mapProc;
+} ElementProcs;
+
+typedef struct {
+ Blt_VectorId vector;
+} VectorDataSource;
+
+typedef struct {
+ Blt_Table table; /* Data table. */
+ Blt_TableColumn column; /* Column of data used. */
+ Blt_TableNotifier notifier; /* Notifier used for column destroy
+ * event. */
+ Blt_TableTrace trace; /* Trace used for column
+ * (set/get/unset). */
+ Blt_HashEntry *hashPtr; /* Pointer to the entry of the data
+ * source in graph's hash table of
+ * datatables. One graph may use
+ * multiple columns from the same data
+ * table. */
+} TableDataSource;
+
+/*
+ * The data structure below contains information pertaining to a line vector.
+ * It consists of an array of floating point data values and for convenience,
+ * the number and minimum/maximum values.
+ */
+typedef struct {
+ int type; /* Selects the type of data populating this
+ * vector: ELEM_SOURCE_VECTOR,
+ * ELEM_SOURCE_TABLE, or ELEM_SOURCE_VALUES
+ */
+ Element *elemPtr; /* Element associated with vector. */
+ union {
+ TableDataSource tableSource;
+ VectorDataSource vectorSource;
+ };
+ double *values;
+ int nValues;
+ int arraySize;
+ double min, max;
+} ElemValues;
+
+
+struct _Element {
+ GraphObj obj; /* Must be first field in element. */
+ unsigned int flags;
+ Blt_HashEntry *hashPtr;
+
+ /* Fields specific to elements. */
+ const char *label; /* Label displayed in legend */
+ unsigned short row, col; /* Position of the entry in the
+ * legend. */
+ int legendRelief; /* Relief of label in legend. */
+ Axis2d axes; /* X-axis and Y-axis mapping the
+ * element */
+ ElemValues x, y, w; /* Contains array of floating point
+ * graph coordinate values. Also holds
+ * min/max and the number of
+ * coordinates */
+ int *activeIndices; /* Array of indices (malloc-ed) which
+ * indicate which data points are
+ * active (drawn with "active"
+ * colors). */
+ int nActiveIndices; /* Number of active data points.
+ * Special case: if nActiveIndices < 0
+ * and the active bit is set in
+ * "flags", then all data points are
+ * drawn active. */
+ ElementProcs *procsPtr;
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications. */
+ Pen *activePenPtr; /* Standard Pens */
+ Pen *normalPenPtr;
+ Pen *builtinPenPtr;
+ Blt_Chain stylePalette; /* Palette of pens. */
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+ int state;
+ Blt_ChainLink link; /* Element's link in display list. */
+};
+
+
+BLT_EXTERN double Blt_FindElemValuesMinimum(ElemValues *vecPtr, double minLimit);
+BLT_EXTERN void Blt_ResizeStatusArray(Element *elemPtr, int nPoints);
+BLT_EXTERN int Blt_GetPenStyle(Graph *graphPtr, char *name, size_t classId,
+ PenStyle *stylePtr);
+BLT_EXTERN void Blt_FreeStylePalette (Blt_Chain stylePalette);
+BLT_EXTERN PenStyle **Blt_StyleMap (Element *elemPtr);
+BLT_EXTERN void Blt_MapErrorBars(Graph *graphPtr, Element *elemPtr,
+ PenStyle **dataToStyle);
+BLT_EXTERN void Blt_FreeDataValues(ElemValues *evPtr);
+BLT_EXTERN int Blt_GetElement(Tcl_Interp *interp, Graph *graphPtr,
+ Tcl_Obj *objPtr, Element **elemPtrPtr);
+BLT_EXTERN void Blt_DestroyTableClients(Graph *graphPtr);
+
+#endif /* _BLT_GR_ELEM_H */
diff --git a/src/bltGrHairs.C b/src/bltGrHairs.C
new file mode 100644
index 0000000..86ebb57
--- /dev/null
+++ b/src/bltGrHairs.C
@@ -0,0 +1,534 @@
+
+/*
+ * bltGrHairs.c --
+ *
+ * This module implements crosshairs for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+
+typedef int (GraphCrosshairProc)(Graph *graphPtr, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const *objv);
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ * Contains the line segments positions and graphics context used
+ * to simulate crosshairs (by XORing) on the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct _Crosshairs {
+
+ XPoint hotSpot; /* Hot spot for crosshairs */
+ int visible; /* Internal state of crosshairs. If non-zero,
+ * crosshairs are displayed. */
+ int hidden; /* If non-zero, crosshairs are not displayed.
+ * This is not necessarily consistent with the
+ * internal state variable. This is true when
+ * the hot spot is off the graph. */
+ Blt_Dashes dashes; /* Dashstyle of the crosshairs. This represents
+ * an array of alternatingly drawn pixel
+ * values. If NULL, the hairs are drawn as a
+ * solid line */
+ int lineWidth; /* Width of the simulated crosshair lines */
+ XSegment segArr[2]; /* Positions of line segments representing the
+ * simulated crosshairs. */
+ XColor *colorPtr; /* Foreground color of crosshairs */
+ GC gc; /* Graphics context for crosshairs. Set to
+ * GXxor to not require redraws of graph */
+};
+
+#define DEF_HAIRS_DASHES (char *)NULL
+#define DEF_HAIRS_FOREGROUND RGB_BLACK
+#define DEF_HAIRS_LINE_WIDTH "0"
+#define DEF_HAIRS_HIDE "yes"
+#define DEF_HAIRS_POSITION (char *)NULL
+
+BLT_EXTERN Blt_CustomOption bltPointOption;
+
+static Blt_ConfigSpec configSpecs[] =
+{
+ {BLT_CONFIG_COLOR, "-color", "color", "Color", DEF_HAIRS_FOREGROUND,
+ Blt_Offset(Crosshairs, colorPtr), 0},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes", DEF_HAIRS_DASHES,
+ Blt_Offset(Crosshairs, dashes), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_HAIRS_HIDE,
+ Blt_Offset(Crosshairs, hidden), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "Linewidth",
+ DEF_HAIRS_LINE_WIDTH, Blt_Offset(Crosshairs, lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_HAIRS_POSITION, Blt_Offset(Crosshairs, hotSpot), 0, &bltPointOption},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TurnOffHairs --
+ *
+ * XOR's the existing line segments (representing the crosshairs),
+ * thereby erasing them. The internal state of the crosshairs is
+ * tracked.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshairs are erased.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+TurnOffHairs(Tk_Window tkwin, Crosshairs *chPtr)
+{
+ if (Tk_IsMapped(tkwin) && (chPtr->visible)) {
+ XDrawSegments(Tk_Display(tkwin), Tk_WindowId(tkwin), chPtr->gc,
+ chPtr->segArr, 2);
+ chPtr->visible = FALSE;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TurnOnHairs --
+ *
+ * Draws (by XORing) new line segments, creating the effect of
+ * crosshairs. The internal state of the crosshairs is tracked.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshairs are displayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+TurnOnHairs(Graph *graphPtr, Crosshairs *chPtr)
+{
+ if (Tk_IsMapped(graphPtr->tkwin) && (!chPtr->visible)) {
+ if (!PointInGraph(graphPtr, chPtr->hotSpot.x, chPtr->hotSpot.y)) {
+ return; /* Coordinates are off the graph */
+ }
+ XDrawSegments(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ chPtr->gc, chPtr->segArr, 2);
+ chPtr->visible = TRUE;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureCrosshairs --
+ *
+ * Configures attributes of the crosshairs such as line width,
+ * dashes, and position. The crosshairs are first turned off
+ * before any of the attributes changes.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ConfigureCrosshairs(Graph *graphPtr)
+{
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC newGC;
+ unsigned long int pixel;
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ /*
+ * Turn off the crosshairs temporarily. This is in case the new
+ * configuration changes the size, style, or position of the lines.
+ */
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+
+ gcValues.function = GXxor;
+
+ if (graphPtr->plotBg == NULL) {
+ /* The graph's color option may not have been set yet */
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = Blt_BackgroundBorderColor(graphPtr->plotBg)->pixel;
+ }
+ gcValues.background = pixel;
+ gcValues.foreground = (pixel ^ chPtr->colorPtr->pixel);
+
+ gcValues.line_width = LineWidth(chPtr->lineWidth);
+ gcMask = (GCForeground | GCBackground | GCFunction | GCLineWidth);
+ if (LineIsDashed(chPtr->dashes)) {
+ gcValues.line_style = LineOnOffDash;
+ gcMask |= GCLineStyle;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(chPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &chPtr->dashes);
+ }
+ if (chPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+ }
+ chPtr->gc = newGC;
+
+ /*
+ * Are the new coordinates on the graph?
+ */
+ chPtr->segArr[0].x2 = chPtr->segArr[0].x1 = chPtr->hotSpot.x;
+ chPtr->segArr[0].y1 = graphPtr->bottom;
+ chPtr->segArr[0].y2 = graphPtr->top;
+ chPtr->segArr[1].y2 = chPtr->segArr[1].y1 = chPtr->hotSpot.y;
+ chPtr->segArr[1].x1 = graphPtr->left;
+ chPtr->segArr[1].x2 = graphPtr->right;
+
+ if (!chPtr->hidden) {
+ TurnOnHairs(graphPtr, chPtr);
+ }
+}
+
+void
+Blt_EnableCrosshairs(Graph *graphPtr)
+{
+ if (!graphPtr->crosshairs->hidden) {
+ TurnOnHairs(graphPtr, graphPtr->crosshairs);
+ }
+}
+
+void
+Blt_DisableCrosshairs(Graph *graphPtr)
+{
+ if (!graphPtr->crosshairs->hidden) {
+ TurnOffHairs(graphPtr->tkwin, graphPtr->crosshairs);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UpdateCrosshairs --
+ *
+ * Update the length of the hairs (not the hot spot).
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_UpdateCrosshairs(Graph *graphPtr)
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ chPtr->segArr[0].y1 = graphPtr->bottom;
+ chPtr->segArr[0].y2 = graphPtr->top;
+ chPtr->segArr[1].x1 = graphPtr->left;
+ chPtr->segArr[1].x2 = graphPtr->right;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyCrosshairs --
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DestroyCrosshairs(Graph *graphPtr)
+{
+ if (graphPtr->crosshairs != NULL) {
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ Blt_FreeOptions(configSpecs, (char *)chPtr, graphPtr->display, 0);
+ if (chPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, chPtr->gc);
+ }
+ Blt_Free(chPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_CreateCrosshairs --
+ *
+ * Creates and initializes a new crosshair structure.
+ *
+ * Results:
+ * Returns TCL_ERROR if the crosshair structure can't be created,
+ * otherwise TCL_OK.
+ *
+ * Side Effects:
+ * Crosshair GC is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_CreateCrosshairs(Graph *graphPtr)
+{
+ Crosshairs *chPtr;
+
+ chPtr = Blt_AssertCalloc(1, sizeof(Crosshairs));
+ chPtr->hidden = TRUE;
+ chPtr->hotSpot.x = chPtr->hotSpot.y = -1;
+ graphPtr->crosshairs = chPtr;
+
+ if (Blt_ConfigureComponentFromObj(graphPtr->interp, graphPtr->tkwin,
+ "crosshairs", "Crosshairs", configSpecs, 0, (Tcl_Obj **)NULL,
+ (char *)chPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries configuration attributes of the crosshairs such as
+ * line width, dashes, and position.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv)
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, objv[3], 0);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets configuration attributes of the crosshairs
+ * such as line width, dashes, and position.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Crosshairs are reset.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, (Tcl_Obj *)NULL, 0);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)chPtr, objv[3], 0);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, configSpecs,
+ objc - 3, objv + 3, (char *)chPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureCrosshairs(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * OnOp --
+ *
+ * Maps the crosshairs.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Crosshairs are reset if necessary.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OnOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp, /* Not used. */
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv) /* Not used. */
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (chPtr->hidden) {
+ TurnOnHairs(graphPtr, chPtr);
+ chPtr->hidden = FALSE;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * OffOp --
+ *
+ * Unmaps the crosshairs.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Crosshairs are reset if necessary.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp, /* Not used. */
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv) /* Not used. */
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ if (!chPtr->hidden) {
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+ chPtr->hidden = TRUE;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ToggleOp --
+ *
+ * Toggles the state of the crosshairs.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Crosshairs are reset.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ToggleOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp, /* Not used. */
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv) /* Not used. */
+{
+ Crosshairs *chPtr = graphPtr->crosshairs;
+
+ chPtr->hidden = (chPtr->hidden == 0);
+ if (chPtr->hidden) {
+ TurnOffHairs(graphPtr->tkwin, chPtr);
+ } else {
+ TurnOnHairs(graphPtr, chPtr);
+ }
+ return TCL_OK;
+}
+
+
+static Blt_OpSpec xhairOps[] =
+{
+ {"cget", 2, CgetOp, 4, 4, "option",},
+ {"configure", 2, ConfigureOp, 3, 0, "?options...?",},
+ {"off", 2, OffOp, 3, 3, "",},
+ {"on", 2, OnOp, 3, 3, "",},
+ {"toggle", 1, ToggleOp, 3, 3, "",},
+};
+static int nXhairOps = sizeof(xhairOps) / sizeof(Blt_OpSpec);
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_CrosshairsOp --
+ *
+ * User routine to configure crosshair simulation. Crosshairs
+ * are simulated by drawing line segments parallel to both axes
+ * using the XOR drawing function. The allows the lines to be
+ * erased (by drawing them again) without redrawing the entire
+ * graph. Care must be taken to erase crosshairs before redrawing
+ * the graph and redraw them after the graph is redraw.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ * Crosshairs may be drawn in the plotting area.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_CrosshairsOp(
+ Graph *graphPtr,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphCrosshairProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nXhairOps, xhairOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (graphPtr, interp, objc, objv);
+}
diff --git a/src/bltGrLegd.C b/src/bltGrLegd.C
new file mode 100644
index 0000000..9ed3169
--- /dev/null
+++ b/src/bltGrLegd.C
@@ -0,0 +1,3047 @@
+
+/*
+ * bltGrLegd.c --
+ *
+ * This module implements the legend for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltGrElem.h"
+#include "bltPicture.h"
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/*
+ * Selection related flags:
+ *
+ * SELECT_EXPORT Export the selection to X11.
+ *
+ * SELECT_PENDING A "selection" command idle task is pending.
+ *
+ * SELECT_CLEAR Clear selection flag of entry.
+ *
+ * SELECT_SET Set selection flag of entry.
+ *
+ * SELECT_TOGGLE Toggle selection flag of entry.
+ *
+ * SELECT_MASK Mask of selection set/clear/toggle flags.
+ *
+ * SELECT_SORTED Indicates if the entries in the selection
+ * should be sorted or displayed in the order
+ * they were selected.
+ *
+ */
+
+#define SELECT_CLEAR (1<<16)
+#define SELECT_EXPORT (1<<17)
+#define SELECT_PENDING (1<<18)
+#define SELECT_SET (1<<19)
+#define SELECT_TOGGLE (SELECT_SET | SELECT_CLEAR)
+#define SELECT_MASK (SELECT_SET | SELECT_CLEAR)
+#define SELECT_SORTED (1<<20)
+
+#define RAISED (1<<21)
+
+#define SELECT_MODE_SINGLE (1<<0)
+#define SELECT_MODE_MULTIPLE (1<<1)
+
+typedef int (GraphLegendProc)(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+/*
+ * Legend --
+ *
+ * Contains information specific to how the legend will be displayed.
+ */
+struct _Legend {
+ unsigned int flags;
+ ClassId classId; /* Type: Element or Marker. */
+
+ int nEntries; /* Number of element entries in
+ * table. */
+ short int nColumns, nRows; /* Number of columns and rows in
+ * legend */
+ short int width, height; /* Dimensions of the legend */
+ short int entryWidth, entryHeight;
+
+ int site;
+ short int xReq, yReq; /* User-requested site of legend, not
+ * the final actual position. Used in
+ * conjunction with the anchor below
+ * to determine location of the
+ * legend. */
+
+ Tk_Anchor anchor; /* Anchor of legend. Used to interpret
+ * the positioning point of the legend
+ * in the graph*/
+
+ int x, y; /* Computed origin of legend. */
+
+ Graph *graphPtr;
+ Tcl_Command cmdToken; /* Token for graph's widget command. */
+ int reqColumns, reqRows;
+
+ Blt_Pad ixPad, iyPad; /* # of pixels interior padding around
+ * legend entries */
+ Blt_Pad xPad, yPad; /* # of pixels padding to exterior of
+ * legend */
+ Tk_Window tkwin; /* If non-NULL, external window to draw
+ * legend. */
+ TextStyle style;
+
+ int maxSymSize; /* Size of largest symbol to be
+ * displayed. Used to calculate size
+ * of legend */
+ XColor *fgColor;
+ Blt_Background activeBg; /* Active legend entry background
+ * color. */
+ XColor *activeFgColor;
+ int activeRelief; /* 3-D effect on active entry. */
+ int entryBW; /* Border width around each entry in
+ * legend. */
+ Blt_Background normalBg; /* 3-D effect of legend. */
+ int borderWidth; /* Width of legend 3-D border */
+ int relief; /* 3-d effect of border around the
+ * legend: TK_RELIEF_RAISED etc. */
+
+ Blt_BindTable bindTable;
+
+ int selRelief;
+ int selBW;
+
+ XColor *selInFocusFgColor; /* Text color of a selected entry. */
+ XColor *selOutFocusFgColor;
+
+ Blt_Background selInFocusBg;
+ Blt_Background selOutFocusBg;
+
+ XColor *focusColor;
+ Blt_Dashes focusDashes; /* Dash on-off value. */
+ GC focusGC; /* Graphics context for the active
+ * label. */
+
+ const char *takeFocus;
+ int focus; /* Position of the focus entry. */
+
+ int cursorX, cursorY; /* Position of the insertion cursor in
+ * the textbox window. */
+ short int cursorWidth; /* Size of the insertion cursor
+ * symbol. */
+ short int cursorHeight;
+ Element *focusPtr; /* Element that currently has the
+ * focus. If NULL, no legend entry has
+ * the focus. */
+ Element *selAnchorPtr; /* Fixed end of selection. Used to
+ * extend the selection while
+ * maintaining the other end of the
+ * selection. */
+ Element *selMarkPtr;
+ Element *selFirstPtr; /* First element selected in current
+ * pick. */
+ Element *selLastPtr; /* Last element selected in current
+ * pick. */
+ int exportSelection;
+ int active;
+ int cursorOn; /* Indicates if the cursor is
+ * displayed. */
+ int onTime, offTime; /* Time in milliseconds to wait before
+ * changing the cursor from off-to-on
+ * and on-to-off. Setting offTime to 0
+ * makes the * cursor steady. */
+ Tcl_TimerToken timerToken; /* Handle for a timer event called
+ * periodically to blink the cursor. */
+ const char *selectCmd; /* TCL script that's invoked whenever
+ * the selection changes. */
+ int selectMode; /* Mode of selection: single or
+ * multiple. */
+ Blt_HashTable selectTable; /* Table of selected elements. Used to
+ * quickly determine whether an element
+ * is selected. */
+ Blt_Chain selected; /* List of selected elements. */
+
+ const char *title;
+ unsigned int titleWidth, titleHeight;
+ TextStyle titleStyle; /* Legend title attributes */
+};
+
+#define padLeft xPad.side1
+#define padRight xPad.side2
+#define padTop yPad.side1
+#define padBottom yPad.side2
+#define PADDING(x) ((x).side1 + (x).side2)
+#define LABEL_PAD 2
+
+#define DEF_LEGEND_ACTIVEBACKGROUND RGB_SKYBLUE4
+#define DEF_LEGEND_ACTIVEBORDERWIDTH "2"
+#define DEF_LEGEND_ACTIVEFOREGROUND RGB_WHITE
+#define DEF_LEGEND_ACTIVERELIEF "flat"
+#define DEF_LEGEND_ANCHOR "n"
+#define DEF_LEGEND_BACKGROUND (char *)NULL
+#define DEF_LEGEND_BORDERWIDTH STD_BORDERWIDTH
+#define DEF_LEGEND_COLUMNS "0"
+#define DEF_LEGEND_EXPORTSELECTION "no"
+#define DEF_LEGEND_FONT "{Sans Serif} 8"
+#define DEF_LEGEND_FOREGROUND STD_NORMAL_FOREGROUND
+#define DEF_LEGEND_HIDE "no"
+#define DEF_LEGEND_IPADX "1"
+#define DEF_LEGEND_IPADY "1"
+#define DEF_LEGEND_PADX "1"
+#define DEF_LEGEND_PADY "1"
+#define DEF_LEGEND_POSITION "rightmargin"
+#define DEF_LEGEND_RAISED "no"
+#define DEF_LEGEND_RELIEF "flat"
+#define DEF_LEGEND_ROWS "0"
+#define DEF_LEGEND_SELECTBACKGROUND RGB_SKYBLUE4
+#define DEF_LEGEND_SELECT_BG_MONO STD_SELECT_BG_MONO
+#define DEF_LEGEND_SELECTBORDERWIDTH "1"
+#define DEF_LEGEND_SELECTMODE "multiple"
+#define DEF_LEGEND_SELECT_FG_MONO STD_SELECT_FG_MONO
+#define DEF_LEGEND_SELECTFOREGROUND RGB_WHITE /*STD_SELECT_FOREGROUND*/
+#define DEF_LEGEND_SELECTRELIEF "flat"
+#define DEF_LEGEND_FOCUSDASHES "dot"
+#define DEF_LEGEND_FOCUSEDIT "no"
+#define DEF_LEGEND_FOCUSFOREGROUND STD_ACTIVE_FOREGROUND
+#define DEF_LEGEND_FOCUS_FG_MONO STD_ACTIVE_FG_MONO
+#define DEF_LEGEND_TAKEFOCUS "1"
+#define DEF_LEGEND_TITLE (char *)NULL
+#define DEF_LEGEND_TITLECOLOR STD_NORMAL_FOREGROUND
+#define DEF_LEGEND_TITLEFONT "{Sans Serif} 8 bold"
+
+static Blt_OptionParseProc ObjToPosition;
+static Blt_OptionPrintProc PositionToObj;
+static Blt_CustomOption legendPositionOption =
+{
+ ObjToPosition, PositionToObj, NULL, (ClientData)0
+};
+
+static Blt_OptionParseProc ObjToSelectmode;
+static Blt_OptionPrintProc SelectmodeToObj;
+static Blt_CustomOption selectmodeOption = {
+ ObjToSelectmode, SelectmodeToObj, NULL, NULL,
+};
+
+static Blt_ConfigSpec configSpecs[] =
+{
+ {BLT_CONFIG_BACKGROUND, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_LEGEND_ACTIVEBACKGROUND,
+ Blt_Offset(Legend, activeBg), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-activeborderwidth", "activeBorderWidth",
+ "BorderWidth", DEF_LEGEND_BORDERWIDTH,
+ Blt_Offset(Legend, entryBW), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_COLOR, "-activeforeground", "activeForeground",
+ "ActiveForeground", DEF_LEGEND_ACTIVEFOREGROUND,
+ Blt_Offset(Legend, activeFgColor), 0},
+ {BLT_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
+ DEF_LEGEND_ACTIVERELIEF, Blt_Offset(Legend, activeRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_LEGEND_ANCHOR,
+ Blt_Offset(Legend, anchor), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_LEGEND_BACKGROUND, Blt_Offset(Legend, normalBg),BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_LEGEND_BORDERWIDTH, Blt_Offset(Legend, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0,0},
+ {BLT_CONFIG_INT_NNEG, "-columns", "columns", "columns",
+ DEF_LEGEND_COLUMNS, Blt_Offset(Legend, reqColumns),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-exportselection", "exportSelection",
+ "ExportSelection", DEF_LEGEND_EXPORTSELECTION,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)SELECT_EXPORT},
+ {BLT_CONFIG_DASHES, "-focusdashes", "focusDashes", "FocusDashes",
+ DEF_LEGEND_FOCUSDASHES, Blt_Offset(Legend, focusDashes),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_LEGEND_FOCUSFOREGROUND, Blt_Offset(Legend, focusColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_COLOR, "-focusforeground", "focusForeground", "FocusForeground",
+ DEF_LEGEND_FOCUS_FG_MONO, Blt_Offset(Legend, focusColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_FONT, "-font", "font", "Font", DEF_LEGEND_FONT,
+ Blt_Offset(Legend, style.font), 0},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_LEGEND_FOREGROUND, Blt_Offset(Legend, fgColor), 0},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_LEGEND_HIDE,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_PAD, "-ipadx", "iPadX", "Pad", DEF_LEGEND_IPADX,
+ Blt_Offset(Legend, ixPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-ipady", "iPadY", "Pad", DEF_LEGEND_IPADY,
+ Blt_Offset(Legend, iyPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-nofocusselectbackground",
+ "noFocusSelectBackground", "NoFocusSelectBackground",
+ DEF_LEGEND_SELECTBACKGROUND, Blt_Offset(Legend, selOutFocusBg), 0},
+ {BLT_CONFIG_COLOR, "-nofocusselectforeground", "noFocusSelectForeground",
+ "NoFocusSelectForeground", DEF_LEGEND_SELECTFOREGROUND,
+ Blt_Offset(Legend, selOutFocusFgColor), 0},
+ {BLT_CONFIG_PAD, "-padx", "padX", "Pad", DEF_LEGEND_PADX,
+ Blt_Offset(Legend, xPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-pady", "padY", "Pad", DEF_LEGEND_PADY,
+ Blt_Offset(Legend, yPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-position", "position", "Position",
+ DEF_LEGEND_POSITION, 0, BLT_CONFIG_DONT_SET_DEFAULT,
+ &legendPositionOption},
+ {BLT_CONFIG_BITMASK, "-raised", "raised", "Raised", DEF_LEGEND_RAISED,
+ Blt_Offset(Legend, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)RAISED},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_LEGEND_RELIEF,
+ Blt_Offset(Legend, relief), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_INT_NNEG, "-rows", "rows", "rows", DEF_LEGEND_ROWS,
+ Blt_Offset(Legend, reqRows),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-selectbackground", "selectBackground",
+ "Background", DEF_LEGEND_SELECTBACKGROUND,
+ Blt_Offset(Legend, selInFocusBg), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-selectborderwidth", "selectBorderWidth",
+ "BorderWidth", DEF_LEGEND_SELECTBORDERWIDTH,
+ Blt_Offset(Legend, selBW), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-selectcommand", "selectCommand", "SelectCommand",
+ (char *)NULL, Blt_Offset(Legend, selectCmd), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_LEGEND_SELECT_FG_MONO, Blt_Offset(Legend, selInFocusFgColor),
+ BLT_CONFIG_MONO_ONLY},
+ {BLT_CONFIG_COLOR, "-selectforeground", "selectForeground", "Foreground",
+ DEF_LEGEND_SELECTFOREGROUND, Blt_Offset(Legend, selInFocusFgColor),
+ BLT_CONFIG_COLOR_ONLY},
+ {BLT_CONFIG_CUSTOM, "-selectmode", "selectMode", "SelectMode",
+ DEF_LEGEND_SELECTMODE, Blt_Offset(Legend, selectMode),
+ BLT_CONFIG_DONT_SET_DEFAULT, &selectmodeOption},
+ {BLT_CONFIG_RELIEF, "-selectrelief", "selectRelief", "Relief",
+ DEF_LEGEND_SELECTRELIEF, Blt_Offset(Legend, selRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_LEGEND_TAKEFOCUS, Blt_Offset(Legend, takeFocus),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-title", "title", "Title", DEF_LEGEND_TITLE,
+ Blt_Offset(Legend, title), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-titlecolor", "titleColor", "Foreground",
+ DEF_LEGEND_TITLECOLOR, Blt_Offset(Legend, titleStyle.color), 0},
+ {BLT_CONFIG_FONT, "-titlefont", "titleFont", "Font",
+ DEF_LEGEND_TITLEFONT, Blt_Offset(Legend, titleStyle.font), 0},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tcl_IdleProc DisplayLegend;
+static Blt_BindPickProc PickEntryProc;
+static Tk_EventProc LegendEventProc;
+static Tcl_TimerProc BlinkCursorProc;
+static Tk_LostSelProc LostSelectionProc;
+static Tk_SelectionProc SelectionProc;
+
+BLT_EXTERN Tcl_ObjCmdProc Blt_GraphInstCmdProc;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Legend_EventuallyRedraw --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at the next
+ * idle point. This request is made only if the window is displayed and
+ * no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Legend_EventuallyRedraw(Graph *graphPtr)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if ((legendPtr->tkwin != NULL) && !(legendPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayLegend, legendPtr);
+ legendPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectCmdProc --
+ *
+ * Invoked at the next idle point whenever the current selection changes.
+ * Executes some application-specific code in the -selectcommand option.
+ * This provides a way for applications to handle selection changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TCL code gets executed for some application-specific task.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+SelectCmdProc(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+
+ Tcl_Preserve(legendPtr);
+ legendPtr->flags &= ~SELECT_PENDING;
+ if (legendPtr->selectCmd != NULL) {
+ Tcl_Interp *interp;
+
+ interp = legendPtr->graphPtr->interp;
+ if (Tcl_GlobalEval(interp, legendPtr->selectCmd) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ Tcl_Release(legendPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * EventuallyInvokeSelectCmd --
+ *
+ * Queues a request to execute the -selectcommand code associated with
+ * the widget at the next idle point. Invoked whenever the selection
+ * changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TCL code gets executed for some application-specific task.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+EventuallyInvokeSelectCmd(Legend *legendPtr)
+{
+ if ((legendPtr->flags & SELECT_PENDING) == 0) {
+ legendPtr->flags |= SELECT_PENDING;
+ Tcl_DoWhenIdle(SelectCmdProc, legendPtr);
+ }
+}
+
+static void
+ClearSelection(Legend *legendPtr)
+{
+ Blt_DeleteHashTable(&legendPtr->selectTable);
+ Blt_InitHashTable(&legendPtr->selectTable, BLT_ONE_WORD_KEYS);
+ Blt_Chain_Reset(legendPtr->selected);
+ Blt_Legend_EventuallyRedraw(legendPtr->graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LostSelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is grabbed away
+ * from a Text widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The existing selection is unhighlighted, and the window is marked as
+ * not containing a selection.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+LostSelectionProc(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+
+ if (legendPtr->flags & SELECT_EXPORT) {
+ ClearSelection(legendPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LegendEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various events on
+ * graphs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get cleaned up.
+ * When it gets exposed, the graph is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+LegendEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail == NotifyInferior) {
+ return;
+ }
+ if (eventPtr->type == FocusIn) {
+ legendPtr->flags |= FOCUS;
+ } else {
+ legendPtr->flags &= ~FOCUS;
+ }
+ Tcl_DeleteTimerHandler(legendPtr->timerToken);
+ if ((legendPtr->active) && (legendPtr->flags & FOCUS)) {
+ legendPtr->cursorOn = TRUE;
+ if (legendPtr->offTime != 0) {
+ legendPtr->timerToken = Tcl_CreateTimerHandler(
+ legendPtr->onTime, BlinkCursorProc, graphPtr);
+ }
+ } else {
+ legendPtr->cursorOn = FALSE;
+ legendPtr->timerToken = (Tcl_TimerToken)NULL;
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else if (eventPtr->type == DestroyNotify) {
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ Blt_DeleteWindowInstanceData(legendPtr->tkwin);
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp,
+ legendPtr->cmdToken);
+ legendPtr->cmdToken = NULL;
+ }
+ legendPtr->tkwin = graphPtr->tkwin;
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ legendPtr->site = LEGEND_RIGHT;
+ legendPtr->flags |= HIDE;
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_MoveBindingTable(legendPtr->bindTable, graphPtr->tkwin);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ } else if (eventPtr->type == ConfigureNotify) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+}
+
+static int
+CreateLegendWindow(Tcl_Interp *interp, Legend *legendPtr, const char *pathName)
+{
+ Graph *graphPtr;
+ Tk_Window tkwin;
+
+ graphPtr = legendPtr->graphPtr;
+ tkwin = Tk_CreateWindowFromPath(interp, graphPtr->tkwin, pathName, NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_SetWindowInstanceData(tkwin, legendPtr);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, graphPtr);
+ /* Move the legend's binding table to the new window. */
+ Blt_MoveBindingTable(legendPtr->bindTable, tkwin);
+ if (legendPtr->tkwin != graphPtr->tkwin) {
+ Tk_DestroyWindow(legendPtr->tkwin);
+ }
+ /* Create a command by the same name as the legend window so that Legend
+ * bindings can use %W interchangably. */
+ legendPtr->cmdToken = Tcl_CreateObjCommand(interp, pathName,
+ Blt_GraphInstCmdProc, graphPtr, NULL);
+ legendPtr->tkwin = tkwin;
+ legendPtr->site = LEGEND_WINDOW;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPosition --
+ *
+ * Convert the string representation of a legend XY position into window
+ * coordinates. The form of the string must be "@x,y" or none.
+ *
+ * Results:
+ * The return value is a standard TCL result. The symbol type is written
+ * into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToPosition(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* New legend position string */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Graph *graphPtr;
+ Legend *legendPtr = (Legend *)widgRec;
+ char c;
+ int length;
+ const char *string;
+
+ graphPtr = legendPtr->graphPtr;
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+ if (c == '\0') {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 'l') && (strncmp(string, "leftmargin", length) == 0)) {
+ legendPtr->site = LEGEND_LEFT;
+ } else if ((c == 'r') && (strncmp(string, "rightmargin", length) == 0)) {
+ legendPtr->site = LEGEND_RIGHT;
+ } else if ((c == 't') && (strncmp(string, "topmargin", length) == 0)) {
+ legendPtr->site = LEGEND_TOP;
+ } else if ((c == 'b') && (strncmp(string, "bottommargin", length) == 0)) {
+ legendPtr->site = LEGEND_BOTTOM;
+ } else if ((c == 'p') && (strncmp(string, "plotarea", length) == 0)) {
+ legendPtr->site = LEGEND_PLOT;
+ } else if (c == '@') {
+ char *comma;
+ long x, y;
+ int result;
+
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ Tcl_AppendResult(interp, "bad screen position \"", string,
+ "\": should be @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ x = y = 0;
+ *comma = '\0';
+ result = ((Tcl_ExprLong(interp, string + 1, &x) == TCL_OK) &&
+ (Tcl_ExprLong(interp, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ return TCL_ERROR;
+ }
+ legendPtr->xReq = x;
+ legendPtr->yReq = y;
+ legendPtr->site = LEGEND_XY;
+ } else if (c == '.') {
+ if (CreateLegendWindow(interp, legendPtr, string) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else {
+ Tcl_AppendResult(interp, "bad position \"", string, "\": should be \
+\"leftmargin\", \"rightmargin\", \"topmargin\", \"bottommargin\", \
+\"plotarea\", windowName or @x,y", (char *)NULL);
+ return TCL_ERROR;
+ }
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PositionToObj --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PositionToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Legend *legendPtr = (Legend *)widgRec;
+ Tcl_Obj *objPtr;
+
+ switch (legendPtr->site) {
+ case LEGEND_LEFT:
+ objPtr = Tcl_NewStringObj("leftmargin", -1);
+ break;
+
+ case LEGEND_RIGHT:
+ objPtr = Tcl_NewStringObj("rightmargin", -1);
+ break;
+
+ case LEGEND_TOP:
+ objPtr = Tcl_NewStringObj("topmargin", -1);
+ break;
+
+ case LEGEND_BOTTOM:
+ objPtr = Tcl_NewStringObj("bottommargin", -1);
+ break;
+
+ case LEGEND_PLOT:
+ objPtr = Tcl_NewStringObj("plotarea", -1);
+ break;
+
+ case LEGEND_WINDOW:
+ objPtr = Tcl_NewStringObj(Tk_PathName(legendPtr->tkwin), -1);
+ break;
+
+ case LEGEND_XY:
+ {
+ char string[200];
+
+ sprintf_s(string, 200, "@%d,%d", legendPtr->xReq, legendPtr->yReq);
+ objPtr = Tcl_NewStringObj(string, -1);
+ }
+ default:
+ objPtr = Tcl_NewStringObj("unknown legend position", -1);
+ }
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToSelectmode --
+ *
+ * Convert the string reprsenting a select mode, to its numeric form.
+ *
+ * Results:
+ * If the string is successfully converted, TCL_OK is returned.
+ * Otherwise, TCL_ERROR is returned and an error message is left
+ * in interpreter's result field.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToSelectmode(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* Tcl_Obj representing the new value. */
+ char *widgRec,
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ char *string;
+ char c;
+ int *modePtr = (int *)(widgRec + offset);
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ if ((c == 's') && (strcmp(string, "single") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else if ((c == 'm') && (strcmp(string, "multiple") == 0)) {
+ *modePtr = SELECT_MODE_MULTIPLE;
+ } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
+ *modePtr = SELECT_MODE_SINGLE;
+ } else {
+ Tcl_AppendResult(interp, "bad select mode \"", string,
+ "\": should be \"single\" or \"multiple\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectmodeToObj --
+ *
+ * Results:
+ * The string representation of the select mode is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+SelectmodeToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec,
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ int mode = *(int *)(widgRec + offset);
+
+ switch (mode) {
+ case SELECT_MODE_SINGLE:
+ return Tcl_NewStringObj("single", -1);
+ case SELECT_MODE_MULTIPLE:
+ return Tcl_NewStringObj("multiple", -1);
+ default:
+ return Tcl_NewStringObj("unknown scroll mode", -1);
+ }
+}
+
+
+static void
+SetLegendOrigin(Legend *legendPtr)
+{
+ Graph *graphPtr;
+ int x, y, w, h;
+
+ graphPtr = legendPtr->graphPtr;
+ x = y = w = h = 0; /* Suppress compiler warning. */
+ switch (legendPtr->site) {
+ case LEGEND_RIGHT:
+ w = graphPtr->rightMargin.width - graphPtr->rightMargin.axesOffset;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->right + graphPtr->rightMargin.axesOffset;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_LEFT:
+ w = graphPtr->leftMargin.width - graphPtr->leftMargin.axesOffset;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->inset;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_TOP:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->topMargin.height - graphPtr->topMargin.axesOffset;
+ if (graphPtr->title != NULL) {
+ h -= graphPtr->titleHeight;
+ }
+ x = graphPtr->left;
+ y = graphPtr->inset;
+ if (graphPtr->title != NULL) {
+ y += graphPtr->titleHeight;
+ }
+ break;
+
+ case LEGEND_BOTTOM:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->bottomMargin.height - graphPtr->bottomMargin.axesOffset;
+ x = graphPtr->left;
+ y = graphPtr->bottom + graphPtr->bottomMargin.axesOffset;
+ break;
+
+ case LEGEND_PLOT:
+ w = graphPtr->right - graphPtr->left;
+ h = graphPtr->bottom - graphPtr->top;
+ x = graphPtr->left;
+ y = graphPtr->top;
+ break;
+
+ case LEGEND_XY:
+ w = legendPtr->width;
+ h = legendPtr->height;
+ x = legendPtr->xReq;
+ y = legendPtr->yReq;
+ if (x < 0) {
+ x += graphPtr->width;
+ }
+ if (y < 0) {
+ y += graphPtr->height;
+ }
+ break;
+
+ case LEGEND_WINDOW:
+ legendPtr->anchor = TK_ANCHOR_NW;
+ legendPtr->x = legendPtr->y = 0;
+ return;
+ }
+
+ switch (legendPtr->anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ if (w > legendPtr->width) {
+ x += (w - legendPtr->width) / 2;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height) / 2;
+ }
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ if (w > legendPtr->width) {
+ x += w - legendPtr->width;
+ }
+ if (h > legendPtr->height) {
+ y += (h - legendPtr->height);
+ }
+ break;
+ }
+ legendPtr->x = x + legendPtr->padLeft;
+ legendPtr->y = y + legendPtr->padTop;
+}
+
+static int
+EntryIsSelected(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ return (hPtr != NULL);
+}
+
+static void
+SelectElement(Legend *legendPtr, Element *elemPtr)
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_CreateHashEntry(&legendPtr->selectTable, (char *)elemPtr,&isNew);
+ if (isNew) {
+ Blt_ChainLink link;
+
+ link = Blt_Chain_Append(legendPtr->selected, elemPtr);
+ Blt_SetHashValue(hPtr, link);
+ }
+}
+
+static void
+DeselectElement(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ if (hPtr != NULL) {
+ Blt_ChainLink link;
+
+ link = Blt_GetHashValue(hPtr);
+ Blt_Chain_DeleteLink(legendPtr->selected, link);
+ Blt_DeleteHashEntry(&legendPtr->selectTable, hPtr);
+ }
+}
+
+static void
+SelectEntry(Legend *legendPtr, Element *elemPtr)
+{
+ Blt_HashEntry *hPtr;
+
+ switch (legendPtr->flags & SELECT_MASK) {
+ case SELECT_CLEAR:
+ DeselectElement(legendPtr, elemPtr);
+ break;
+
+ case SELECT_SET:
+ SelectElement(legendPtr, elemPtr);
+ break;
+
+ case SELECT_TOGGLE:
+ hPtr = Blt_FindHashEntry(&legendPtr->selectTable, (char *)elemPtr);
+ if (hPtr != NULL) {
+ DeselectElement(legendPtr, elemPtr);
+ } else {
+ SelectElement(legendPtr, elemPtr);
+ }
+ break;
+ }
+}
+
+#ifdef notdef
+static Element *
+PointerToElement(Legend *legendPtr, int x, int y)
+{
+ Graph *graphPtr = legendPtr->graphPtr;
+ int w, h;
+ int n;
+
+ w = legendPtr->width;
+ h = legendPtr->height;
+
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ w -= 2 * legendPtr->borderWidth + PADDING(legendPtr->xPad);
+ h -= 2 * legendPtr->borderWidth + PADDING(legendPtr->yPad);
+
+ if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) {
+ return NULL;
+ }
+
+ /* It's in the bounding box, so compute the index. */
+ {
+ int row, column;
+
+ row = y / legendPtr->entryHeight;
+ column = x / legendPtr->entryWidth;
+ n = (column * legendPtr->nRows) + row;
+ }
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink link;
+ int count;
+
+ count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if (count > n) {
+ return NULL;
+ } else if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ return NULL;
+}
+#endif
+
+/*ARGSUSED*/
+static ClientData
+PickEntryProc(ClientData clientData, int x, int y, ClientData *contextPtr)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+ int w, h;
+
+ legendPtr = graphPtr->legend;
+ w = legendPtr->width;
+ h = legendPtr->height;
+
+ if (legendPtr->titleHeight > 0) {
+ y -= legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ x -= legendPtr->x + legendPtr->borderWidth;
+ y -= legendPtr->y + legendPtr->borderWidth;
+ w -= 2 * legendPtr->borderWidth + PADDING(legendPtr->xPad);
+ h -= 2 * legendPtr->borderWidth + PADDING(legendPtr->yPad);
+
+ if ((x >= 0) && (x < w) && (y >= 0) && (y < h)) {
+ int row, column;
+ int n;
+
+ /*
+ * It's in the bounding box, so compute the index.
+ */
+ row = y / legendPtr->entryHeight;
+ column = x / legendPtr->entryWidth;
+ n = (column * legendPtr->nRows) + row;
+ if (n < legendPtr->nEntries) {
+ Blt_ChainLink link;
+ int count;
+
+ /* Legend entries are stored in bottom-to-top. */
+ count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ if (count == n) {
+ return elemPtr;
+ }
+ count++;
+ }
+ }
+ if (link != NULL) {
+ return Blt_Chain_GetValue(link);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_MapLegend --
+ *
+ * Calculates the dimensions (width and height) needed for the legend.
+ * Also determines the number of rows and columns necessary to list all
+ * the valid element labels.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The following fields of the legend are calculated and set.
+ *
+ * nEntries - number of valid labels of elements in the
+ * display list.
+ * nRows - number of rows of entries
+ * nColumns - number of columns of entries
+ * entryHeight - height of each entry
+ * entryWidth - width of each entry
+ * height - width of legend (includes borders and padding)
+ * width - height of legend (includes borders and padding)
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_MapLegend(
+ Graph *graphPtr,
+ int plotWidth, /* Maximum width available in window
+ * to draw the legend. Will calculate
+ * # of columns from this. */
+ int plotHeight) /* Maximum height available in window
+ * to draw the legend. Will calculate
+ * # of rows from this. */
+{
+ Legend *legendPtr = graphPtr->legend;
+ Blt_ChainLink link;
+ int nRows, nColumns, nEntries;
+ int lw, lh;
+ int maxWidth, maxHeight;
+ int symbolWidth;
+ Blt_FontMetrics fontMetrics;
+
+ /* Initialize legend values to default (no legend displayed) */
+ legendPtr->entryWidth = legendPtr->entryHeight = 0;
+ legendPtr->nRows = legendPtr->nColumns = legendPtr->nEntries = 0;
+ legendPtr->height = legendPtr->width = 0;
+
+ if (legendPtr->site == LEGEND_WINDOW) {
+ if (Tk_Width(legendPtr->tkwin) > 1) {
+ plotWidth = Tk_Width(legendPtr->tkwin);
+ }
+ if (Tk_Height(legendPtr->tkwin) > 1) {
+ plotHeight = Tk_Height(legendPtr->tkwin);
+ }
+ }
+ Blt_Ts_GetExtents(&legendPtr->titleStyle, legendPtr->title,
+ &legendPtr->titleWidth, &legendPtr->titleHeight);
+ /*
+ * Count the number of legend entries and determine the widest and tallest
+ * label. The number of entries would normally be the number of elements,
+ * but elements can have no legend entry (-label "").
+ */
+ nEntries = 0;
+ maxWidth = maxHeight = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ unsigned int w, h;
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Element has no legend entry. */
+ }
+ Blt_Ts_GetExtents(&legendPtr->style, elemPtr->label, &w, &h);
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ if (maxHeight < h) {
+ maxHeight = h;
+ }
+ nEntries++;
+ }
+ if (nEntries == 0) {
+ return; /* No visible legend entries. */
+ }
+
+
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolWidth = 2 * fontMetrics.ascent;
+
+ maxWidth += 2 * legendPtr->entryBW + PADDING(legendPtr->ixPad) +
+ + symbolWidth + 3 * LABEL_PAD;
+
+ maxHeight += 2 * legendPtr->entryBW + PADDING(legendPtr->iyPad);
+
+ maxWidth |= 0x01;
+ maxHeight |= 0x01;
+
+ lw = plotWidth - 2 * legendPtr->borderWidth - PADDING(legendPtr->xPad);
+ lh = plotHeight - 2 * legendPtr->borderWidth - PADDING(legendPtr->yPad);
+
+ /*
+ * The number of rows and columns is computed as one of the following:
+ *
+ * both options set User defined.
+ * -rows Compute columns from rows.
+ * -columns Compute rows from columns.
+ * neither set Compute rows and columns from
+ * size of plot.
+ */
+ if (legendPtr->reqRows > 0) {
+ nRows = MIN(legendPtr->reqRows, nEntries);
+ if (legendPtr->reqColumns > 0) {
+ nColumns = MIN(legendPtr->reqColumns, nEntries);
+ } else {
+ nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */
+ }
+ } else if (legendPtr->reqColumns > 0) { /* Only -columns. */
+ nColumns = MIN(legendPtr->reqColumns, nEntries);
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ } else {
+ /* Compute # of rows and columns from the legend size. */
+ nRows = lh / maxHeight;
+ nColumns = lw / maxWidth;
+ if (nRows < 1) {
+ nRows = nEntries;
+ }
+ if (nColumns < 1) {
+ nColumns = nEntries;
+ }
+ if (nRows > nEntries) {
+ nRows = nEntries;
+ }
+ switch (legendPtr->site) {
+ case LEGEND_TOP:
+ case LEGEND_BOTTOM:
+ nRows = ((nEntries - 1) / nColumns) + 1;
+ break;
+ case LEGEND_LEFT:
+ case LEGEND_RIGHT:
+ default:
+ nColumns = ((nEntries - 1) / nRows) + 1;
+ break;
+ }
+ }
+ if (nColumns < 1) {
+ nColumns = 1;
+ }
+ if (nRows < 1) {
+ nRows = 1;
+ }
+
+ lh = (nRows * maxHeight);
+ if (legendPtr->titleHeight > 0) {
+ lh += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ lw = nColumns * maxWidth;
+ if (lw < legendPtr->titleWidth) {
+ lw = legendPtr->titleWidth;
+ }
+ legendPtr->width = lw + 2 * legendPtr->borderWidth +
+ PADDING(legendPtr->xPad);
+ legendPtr->height = lh + 2 * legendPtr->borderWidth +
+ PADDING(legendPtr->yPad);
+ legendPtr->nRows = nRows;
+ legendPtr->nColumns = nColumns;
+ legendPtr->nEntries = nEntries;
+ legendPtr->entryHeight = maxHeight;
+ legendPtr->entryWidth = maxWidth;
+
+ {
+ int row, col, count;
+
+ row = col = count = 0;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ count++;
+ elemPtr->row = row;
+ elemPtr->col = col;
+ row++;
+ if ((count % nRows) == 0) {
+ col++;
+ row = 0;
+ }
+ }
+ }
+ if ((legendPtr->site == LEGEND_WINDOW) &&
+ ((Tk_ReqWidth(legendPtr->tkwin) != legendPtr->width) ||
+ (Tk_ReqHeight(legendPtr->tkwin) != legendPtr->height))) {
+ Tk_GeometryRequest(legendPtr->tkwin,legendPtr->width,legendPtr->height);
+ }
+}
+
+void
+Blt_DrawLegend(Graph *graphPtr, Drawable drawable)
+{
+ Blt_Background bg;
+ Blt_ChainLink link;
+ Blt_FontMetrics fontMetrics;
+ Legend *legendPtr = graphPtr->legend;
+ Pixmap pixmap;
+ Tk_Window tkwin;
+ int count;
+ int symbolSize, xMid, yMid;
+ int x, y, w, h;
+ int xLabel, yStart, xSymbol, ySymbol;
+
+ if ((legendPtr->flags & HIDE) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+
+ SetLegendOrigin(legendPtr);
+ graphPtr = legendPtr->graphPtr;
+ tkwin = legendPtr->tkwin;
+ if (legendPtr->site == LEGEND_WINDOW) {
+ w = Tk_Width(tkwin);
+ h = Tk_Height(tkwin);
+ } else {
+ w = legendPtr->width;
+ h = legendPtr->height;
+ }
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_WindowId(tkwin), w, h,
+ Tk_Depth(tkwin));
+
+ if (legendPtr->normalBg != NULL) {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, legendPtr->normalBg, 0, 0,
+ w, h, 0, TK_RELIEF_FLAT);
+ } else if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ /*
+ * Legend background is transparent and is positioned over the the
+ * plot area. Either copy the part of the background from the backing
+ * store pixmap or (if no backing store exists) just fill it with the
+ * background color of the plot.
+ */
+ if (graphPtr->cache != None) {
+ XCopyArea(graphPtr->display, graphPtr->cache, pixmap,
+ graphPtr->drawGC, legendPtr->x, legendPtr->y, w, h, 0, 0);
+ } else {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->plotBg, 0, 0,
+ w, h, TK_RELIEF_FLAT, 0);
+ }
+ } else {
+ int xOrigin, yOrigin;
+ /*
+ * The legend is located in one of the margins or the external window.
+ */
+ Blt_GetBackgroundOrigin(graphPtr->normalBg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(graphPtr->tkwin, graphPtr->normalBg,
+ xOrigin - legendPtr->x,yOrigin - legendPtr->y);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->normalBg, 0, 0,
+ w, h, 0, TK_RELIEF_FLAT);
+ Blt_SetBackgroundOrigin(tkwin, graphPtr->normalBg, xOrigin, yOrigin);
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+
+ symbolSize = fontMetrics.ascent;
+ xMid = symbolSize + 1 + legendPtr->entryBW;
+ yMid = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ xLabel = 2 * symbolSize + legendPtr->entryBW +
+ legendPtr->ixPad.side1 + 2 * LABEL_PAD;
+ ySymbol = yMid + legendPtr->iyPad.side1;
+ xSymbol = xMid + LABEL_PAD;
+
+ x = legendPtr->padLeft + legendPtr->borderWidth;
+ y = legendPtr->padTop + legendPtr->borderWidth;
+ Blt_DrawText(tkwin, pixmap, legendPtr->title, &legendPtr->titleStyle, x, y);
+ if (legendPtr->titleHeight > 0) {
+ y += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+ count = 0;
+ yStart = y;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ int isSelected;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this entry */
+ }
+ isSelected = EntryIsSelected(legendPtr, elemPtr);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ int xOrigin, yOrigin;
+
+ Blt_GetBackgroundOrigin(legendPtr->activeBg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(tkwin, legendPtr->activeBg,
+ xOrigin - legendPtr->x, yOrigin - legendPtr->y);
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->activeFgColor);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, legendPtr->activeBg,
+ x, y, legendPtr->entryWidth, legendPtr->entryHeight,
+ legendPtr->entryBW, legendPtr->activeRelief);
+ Blt_SetBackgroundOrigin(tkwin, legendPtr->activeBg,
+ xOrigin, yOrigin);
+ } else if (isSelected) {
+ int xOrigin, yOrigin;
+ Blt_Background bg;
+ XColor *fg;
+
+ fg = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusFgColor : legendPtr->selOutFocusFgColor;
+ bg = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusBg : legendPtr->selOutFocusBg;
+ Blt_GetBackgroundOrigin(bg, &xOrigin, &yOrigin);
+ Blt_SetBackgroundOrigin(tkwin, bg, xOrigin - legendPtr->x,
+ yOrigin - legendPtr->y);
+ Blt_Ts_SetForeground(legendPtr->style, fg);
+ Blt_FillBackgroundRectangle(tkwin, pixmap, bg, x, y,
+ legendPtr->entryWidth, legendPtr->entryHeight,
+ legendPtr->selBW, legendPtr->selRelief);
+ Blt_SetBackgroundOrigin(tkwin, bg, xOrigin, yOrigin);
+ } else {
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->fgColor);
+ if (elemPtr->legendRelief != TK_RELIEF_FLAT) {
+ Blt_FillBackgroundRectangle(tkwin, pixmap, graphPtr->normalBg,
+ x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ elemPtr->legendRelief);
+ }
+ }
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr,
+ x + xSymbol, y + ySymbol, symbolSize);
+ Blt_DrawText(tkwin, pixmap, elemPtr->label, &legendPtr->style,
+ x + xLabel,
+ y + legendPtr->entryBW + legendPtr->iyPad.side1);
+ count++;
+ if (legendPtr->focusPtr == elemPtr) { /* Focus outline */
+ if (isSelected) {
+ XColor *color;
+
+ color = (legendPtr->flags & FOCUS) ?
+ legendPtr->selInFocusFgColor :
+ legendPtr->selOutFocusFgColor;
+ XSetForeground(graphPtr->display, legendPtr->focusGC,
+ color->pixel);
+ }
+ XDrawRectangle(graphPtr->display, pixmap, legendPtr->focusGC,
+ x + 1, y + 1, legendPtr->entryWidth - 3,
+ legendPtr->entryHeight - 3);
+ if (isSelected) {
+ XSetForeground(graphPtr->display, legendPtr->focusGC,
+ legendPtr->focusColor->pixel);
+ }
+ }
+ /* Check when to move to the next column */
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->entryHeight;
+ } else {
+ x += legendPtr->entryWidth;
+ y = yStart;
+ }
+ }
+ /*
+ * Draw the border and/or background of the legend.
+ */
+ bg = legendPtr->normalBg;
+ if (bg == NULL) {
+ bg = graphPtr->normalBg;
+ }
+ /* Disable crosshairs before redisplaying to the screen */
+ if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ Blt_DisableCrosshairs(graphPtr);
+ }
+ Blt_DrawBackgroundRectangle(tkwin, pixmap, bg, 0, 0, w, h,
+ legendPtr->borderWidth, legendPtr->relief);
+ XCopyArea(graphPtr->display, pixmap, drawable, graphPtr->drawGC, 0, 0, w, h,
+ legendPtr->x, legendPtr->y);
+ if (legendPtr->site & LEGEND_PLOTAREA_MASK) {
+ Blt_EnableCrosshairs(graphPtr);
+ }
+ Tk_FreePixmap(graphPtr->display, pixmap);
+ graphPtr->flags &= ~DRAW_LEGEND;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LegendToPostScript --
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_LegendToPostScript(Graph *graphPtr, Blt_Ps ps)
+{
+ Legend *legendPtr = graphPtr->legend;
+ double x, y, yStart;
+ int xLabel, xSymbol, ySymbol;
+ int count;
+ Blt_ChainLink link;
+ int symbolSize, xMid, yMid;
+ int width, height;
+ Blt_FontMetrics fontMetrics;
+
+ if ((legendPtr->flags & HIDE) || (legendPtr->nEntries == 0)) {
+ return;
+ }
+ SetLegendOrigin(legendPtr);
+
+ x = legendPtr->x, y = legendPtr->y;
+ width = legendPtr->width - PADDING(legendPtr->xPad);
+ height = legendPtr->height - PADDING(legendPtr->yPad);
+
+ Blt_Ps_Append(ps, "% Legend\n");
+ graphPtr = legendPtr->graphPtr;
+ if (graphPtr->pageSetup->flags & PS_DECORATIONS) {
+ if (legendPtr->normalBg != NULL) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(legendPtr->normalBg);
+ Blt_Ps_Fill3DRectangle(ps, border, x, y, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+ } else {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(graphPtr->normalBg);
+ Blt_Ps_Draw3DRectangle(ps, border, x, y, width, height,
+ legendPtr->borderWidth, legendPtr->relief);
+ }
+ } else {
+ Blt_Ps_SetClearBackground(ps);
+ Blt_Ps_XFillRectangle(ps, x, y, width, height);
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ symbolSize = fontMetrics.ascent;
+ xMid = symbolSize + 1 + legendPtr->entryBW;
+ yMid = (symbolSize / 2) + 1 + legendPtr->entryBW;
+ xLabel = 2 * symbolSize + legendPtr->entryBW + legendPtr->ixPad.side1 + 5;
+ xSymbol = xMid + legendPtr->ixPad.side1;
+ ySymbol = yMid + legendPtr->iyPad.side1;
+
+ x += legendPtr->borderWidth;
+ y += legendPtr->borderWidth;
+ Blt_Ps_DrawText(ps, legendPtr->title, &legendPtr->titleStyle, x, y);
+ if (legendPtr->titleHeight > 0) {
+ y += legendPtr->titleHeight + legendPtr->yPad.side1;
+ }
+
+ count = 0;
+ yStart = y;
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue; /* Skip this label */
+ }
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(legendPtr->activeBg);
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->activeFgColor);
+ Blt_Ps_Fill3DRectangle(ps, border, x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ legendPtr->activeRelief);
+ } else {
+ Blt_Ts_SetForeground(legendPtr->style, legendPtr->fgColor);
+ if (elemPtr->legendRelief != TK_RELIEF_FLAT) {
+ Tk_3DBorder border;
+
+ border = Blt_BackgroundBorder(graphPtr->normalBg);
+ Blt_Ps_Draw3DRectangle(ps, border, x, y, legendPtr->entryWidth,
+ legendPtr->entryHeight, legendPtr->entryBW,
+ elemPtr->legendRelief);
+ }
+ }
+ (*elemPtr->procsPtr->printSymbolProc) (graphPtr, ps, elemPtr,
+ x + xSymbol, y + ySymbol, symbolSize);
+ Blt_Ps_DrawText(ps, elemPtr->label, &legendPtr->style,
+ x + xLabel, y + legendPtr->entryBW + legendPtr->iyPad.side1);
+ count++;
+ if ((count % legendPtr->nRows) > 0) {
+ y += legendPtr->entryHeight;
+ } else {
+ x += legendPtr->entryWidth;
+ y = yStart;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DisplayLegend --
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DisplayLegend(ClientData clientData)
+{
+ Legend *legendPtr = clientData;
+ Graph *graphPtr;
+
+ legendPtr->flags &= ~REDRAW_PENDING;
+ if (legendPtr->tkwin == NULL) {
+ return; /* Window has been destroyed. */
+ }
+ graphPtr = legendPtr->graphPtr;
+ if (legendPtr->site == LEGEND_WINDOW) {
+ int w, h;
+
+ w = Tk_Width(legendPtr->tkwin);
+ h = Tk_Height(legendPtr->tkwin);
+ if ((w != legendPtr->width) || (h != legendPtr->height)) {
+ Blt_MapLegend(graphPtr, w, h);
+ }
+ }
+ if (Tk_IsMapped(legendPtr->tkwin)) {
+ Blt_DrawLegend(graphPtr, Tk_WindowId(legendPtr->tkwin));
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ConfigureLegend --
+ *
+ * Routine to configure the legend.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ConfigureLegend(Graph *graphPtr)
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ /* GC for active label. Dashed outline. */
+ gcMask = GCForeground | GCLineStyle;
+ gcValues.foreground = legendPtr->focusColor->pixel;
+ gcValues.line_style = (LineIsDashed(legendPtr->focusDashes))
+ ? LineOnOffDash : LineSolid;
+ newGC = Blt_GetPrivateGC(legendPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(legendPtr->focusDashes)) {
+ legendPtr->focusDashes.offset = 2;
+ Blt_SetDashes(graphPtr->display, newGC, &legendPtr->focusDashes);
+ }
+ if (legendPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, legendPtr->focusGC);
+ }
+ legendPtr->focusGC = newGC;
+
+ /*
+ * Update the layout of the graph (and redraw the elements) if any of
+ * the following legend options (all of which affect the size of the
+ * legend) have changed.
+ *
+ * -activeborderwidth, -borderwidth
+ * -border
+ * -font
+ * -hide
+ * -ipadx, -ipady, -padx, -pady
+ * -rows
+ *
+ * If the position of the legend changed to/from the default
+ * position, also indicate that a new layout is needed.
+ *
+ */
+ if (legendPtr->site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ } else if (Blt_ConfigModified(configSpecs, "-*border*", "-*pad?",
+ "-hide", "-font", "-rows", (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD;
+ graphPtr->flags |= (REDRAW_WORLD | CACHE_DIRTY);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyLegend --
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resources associated with the legend are freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DestroyLegend(Graph *graphPtr)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (graphPtr->legend == NULL) {
+ return;
+ }
+
+ Blt_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
+ Blt_Ts_FreeStyle(graphPtr->display, &legendPtr->style);
+ Blt_Ts_FreeStyle(graphPtr->display, &legendPtr->titleStyle);
+ Blt_DestroyBindingTable(legendPtr->bindTable);
+
+ if (legendPtr->focusGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, legendPtr->focusGC);
+ }
+ if (legendPtr->timerToken != NULL) {
+ Tcl_DeleteTimerHandler(legendPtr->timerToken);
+ }
+ if (legendPtr->tkwin != NULL) {
+ Tk_DeleteSelHandler(legendPtr->tkwin, XA_PRIMARY, XA_STRING);
+ }
+ if (legendPtr->site == LEGEND_WINDOW) {
+ Tk_Window tkwin;
+
+ /* The graph may be in the process of being torn down */
+ if (legendPtr->cmdToken != NULL) {
+ Tcl_DeleteCommandFromToken(graphPtr->interp, legendPtr->cmdToken);
+ }
+ if (legendPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayLegend, legendPtr);
+ legendPtr->flags &= ~REDRAW_PENDING;
+ }
+ tkwin = legendPtr->tkwin;
+ legendPtr->tkwin = NULL;
+ if (tkwin != NULL) {
+ Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ LegendEventProc, graphPtr);
+ Blt_DeleteWindowInstanceData(tkwin);
+ Tk_DestroyWindow(tkwin);
+ }
+ }
+ Blt_Free(legendPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_CreateLegend --
+ *
+ * Creates and initializes a legend structure with default settings
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_CreateLegend(Graph *graphPtr)
+{
+ Legend *legendPtr;
+
+ legendPtr = Blt_AssertCalloc(1, sizeof(Legend));
+ graphPtr->legend = legendPtr;
+ legendPtr->graphPtr = graphPtr;
+ legendPtr->tkwin = graphPtr->tkwin;
+ legendPtr->xReq = legendPtr->yReq = -SHRT_MAX;
+ legendPtr->relief = TK_RELIEF_SUNKEN;
+ legendPtr->activeRelief = TK_RELIEF_FLAT;
+ legendPtr->entryBW = 2;
+ legendPtr->borderWidth = 2;
+ legendPtr->ixPad.side1 = legendPtr->ixPad.side2 = 1;
+ legendPtr->iyPad.side1 = legendPtr->iyPad.side2 = 1;
+ legendPtr->xPad.side1 = legendPtr->xPad.side2 = 1;
+ legendPtr->yPad.side1 = legendPtr->yPad.side2 = 1;
+ legendPtr->anchor = TK_ANCHOR_N;
+ legendPtr->site = LEGEND_RIGHT;
+ legendPtr->selectMode = SELECT_MODE_MULTIPLE;
+ Blt_Ts_InitStyle(legendPtr->style);
+ Blt_Ts_InitStyle(legendPtr->titleStyle);
+ legendPtr->style.justify = TK_JUSTIFY_LEFT;
+ legendPtr->style.anchor = TK_ANCHOR_NW;
+ legendPtr->titleStyle.justify = TK_JUSTIFY_LEFT;
+ legendPtr->titleStyle.anchor = TK_ANCHOR_NW;
+ legendPtr->bindTable = Blt_CreateBindingTable(graphPtr->interp,
+ graphPtr->tkwin, graphPtr, PickEntryProc, Blt_GraphTags);
+
+ Blt_InitHashTable(&legendPtr->selectTable, BLT_ONE_WORD_KEYS);
+ legendPtr->selected = Blt_Chain_Create();
+ Tk_CreateSelHandler(legendPtr->tkwin, XA_PRIMARY, XA_STRING,
+ SelectionProc, legendPtr, XA_STRING);
+ legendPtr->selRelief = TK_RELIEF_FLAT;
+ legendPtr->selBW = 1;
+ legendPtr->onTime = 600;
+ legendPtr->offTime = 300;
+ if (Blt_ConfigureComponentFromObj(graphPtr->interp, graphPtr->tkwin,
+ "legend", "Legend", configSpecs, 0, (Tcl_Obj **)NULL,
+ (char *)legendPtr, 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureLegend(graphPtr);
+ return TCL_OK;
+}
+
+static Element *
+GetNextRow(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col;
+ row = focusPtr->row + 1;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetNextColumn(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col + 1;
+ row = focusPtr->row;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr; /* Don't go beyond legend boundaries. */
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetPreviousRow(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col;
+ row = focusPtr->row - 1;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetPreviousColumn(Graph *graphPtr, Element *focusPtr)
+{
+ Blt_ChainLink link;
+ int row, col;
+
+ col = focusPtr->col - 1;
+ row = focusPtr->row;
+ for (link = focusPtr->link; link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label == NULL) {
+ continue;
+ }
+ if ((elemPtr->col == col) && (elemPtr->row == row)) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetFirstElement(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+static Element *
+GetLastElement(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->label != NULL) {
+ return elemPtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetElementFromObj --
+ *
+ * Parse an index into an entry and return either its value or an error.
+ *
+ * Results:
+ * A standard TCL result. If all went well, then *indexPtr is filled in
+ * with the character index (into entryPtr) corresponding to string. The
+ * index value is guaranteed to lie between 0 and the number of characters
+ * in the string, inclusive. If an error occurs then an error message is
+ * left in the interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetElementFromObj(Graph *graphPtr, Tcl_Obj *objPtr, Element **elemPtrPtr)
+{
+ Element *elemPtr;
+ Legend *legendPtr;
+ Tcl_Interp *interp;
+ char c;
+ const char *string;
+ int last;
+ int index;
+
+ legendPtr = graphPtr->legend;
+ interp = graphPtr->interp;
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ elemPtr = NULL;
+
+ last = Blt_Chain_GetLength(graphPtr->elements.displayList) - 1;
+ if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
+ elemPtr = legendPtr->selAnchorPtr;
+ } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
+ elemPtr = (Element *)Blt_GetCurrentItem(legendPtr->bindTable);
+ } else if ((c == 'f') && (strcmp(string, "first") == 0)) {
+ elemPtr = GetFirstElement(graphPtr);
+ } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
+ elemPtr = legendPtr->focusPtr;
+ } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
+ elemPtr = GetLastElement(graphPtr);
+ } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ elemPtr = GetLastElement(graphPtr);
+ } else if ((c == 'n') && (strcmp(string, "next.row") == 0)) {
+ elemPtr = GetNextRow(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'n') && (strcmp(string, "next.column") == 0)) {
+ elemPtr = GetNextColumn(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'p') && (strcmp(string, "previous.row") == 0)) {
+ elemPtr = GetPreviousRow(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 'p') && (strcmp(string, "previous.column") == 0)) {
+ elemPtr = GetPreviousColumn(graphPtr, legendPtr->focusPtr);
+ } else if ((c == 's') && (strcmp(string, "sel.first") == 0)) {
+ elemPtr = legendPtr->selFirstPtr;
+ } else if ((c == 's') && (strcmp(string, "sel.last") == 0)) {
+ elemPtr = legendPtr->selLastPtr;
+ } else if (c == '@') {
+ int x, y;
+
+ if (Blt_GetXY(interp, legendPtr->tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ elemPtr = (Element *)PickEntryProc(graphPtr, x, y, NULL);
+ } else {
+ if (Blt_GetElement(interp, graphPtr, objPtr, &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr->link == NULL) {
+ Tcl_AppendResult(interp, "bad legend index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (elemPtr->label == NULL) {
+ elemPtr = NULL;
+ }
+ }
+ *elemPtrPtr = elemPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectRange --
+ *
+ * Sets the selection flag for a range of nodes. The range is determined
+ * by two pointers which designate the first/last nodes of the range.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectRange(Legend *legendPtr, Element *fromPtr, Element *toPtr)
+{
+
+ if (Blt_Chain_IsBefore(fromPtr->link, toPtr->link)) {
+ Blt_ChainLink link;
+
+ for (link = fromPtr->link; link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ SelectEntry(legendPtr, elemPtr);
+ if (link == toPtr->link) {
+ break;
+ }
+ }
+ } else {
+ Blt_ChainLink link;
+
+ for (link = fromPtr->link; link != NULL;
+ link = Blt_Chain_PrevLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ SelectEntry(legendPtr, elemPtr);
+ if (link == toPtr->link) {
+ break;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+#ifdef notdef
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectText --
+ *
+ * Modify the selection by moving its un-anchored end. This could make
+ * the selection either larger or smaller.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectText(Legend *legendPtr, Element *elemPtr)
+{
+ Element *firstPtr, *lastPtr;
+ Graph *graphPtr = legendPtr->graphPtr;
+
+ /* Grab the selection if we don't own it already. */
+ if ((legendPtr->flags&SELECT_EXPORT) && (legendPtr->selFirstPtr == NULL)) {
+ Tk_OwnSelection(legendPtr->tkwin, XA_PRIMARY, LostSelectionProc,
+ legendPtr);
+ }
+ /* If the anchor hasn't been set, assume the beginning of the legend. */
+ if (legendPtr->selAnchorPtr == NULL) {
+ legendPtr->selAnchorPtr = GetFirstElement(graphPtr);
+ }
+ if (legendPtr->selAnchorPtr != elemPtr) {
+ firstPtr = legendPtr->selAnchorPtr;
+ lastPtr = elemPtr;
+ } else {
+ firstPtr = elemPtr;
+ lastPtr = legendPtr->selAnchorPtr;
+ }
+ if ((legendPtr->selFirstPtr != firstPtr) ||
+ (legendPtr->selLastPtr != lastPtr)) {
+ legendPtr->selFirstPtr = firstPtr;
+ legendPtr->selLastPtr = lastPtr;
+ SelectRange(legendPtr, firstPtr, lastPtr);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ return TCL_OK;
+}
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ActivateOp --
+ *
+ * Activates a particular label in the legend.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ActivateOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ unsigned int active, redraw;
+ const char *string;
+ int i;
+
+ string = Tcl_GetString(objv[2]);
+ active = (string[0] == 'a') ? LABEL_ACTIVE : 0;
+ redraw = FALSE;
+ for (i = 3; i < objc; i++) {
+ Blt_ChainLink link;
+ const char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (Tcl_StringMatch(elemPtr->obj.name, pattern)) {
+ fprintf(stderr, "legend %s(%s) %s is currently %d\n",
+ string, pattern, elemPtr->obj.name,
+ (elemPtr->flags & LABEL_ACTIVE));
+ if (active) {
+ if ((elemPtr->flags & LABEL_ACTIVE) == 0) {
+ elemPtr->flags |= LABEL_ACTIVE;
+ redraw = TRUE;
+ }
+ } else {
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ elemPtr->flags &= ~LABEL_ACTIVE;
+ redraw = TRUE;
+ }
+ }
+ fprintf(stderr, "legend %s(%s) %s is now %d\n",
+ string, pattern, elemPtr->obj.name,
+ (elemPtr->flags & LABEL_ACTIVE));
+ }
+ }
+ }
+ if ((redraw) && ((legendPtr->flags & HIDE) == 0)) {
+ /*
+ * See if how much we need to draw. If the graph is already scheduled
+ * for a redraw, just make sure the right flags are set. Otherwise
+ * redraw only the legend: it's either in an external window or it's
+ * the only thing that need updating.
+ */
+ if ((legendPtr->site != LEGEND_WINDOW) &&
+ (graphPtr->flags & REDRAW_PENDING)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ graphPtr->flags |= REDRAW_WORLD; /* Redraw entire graph. */
+ } else {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ }
+ {
+ Blt_ChainLink link;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ /* List active elements in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->flags & LABEL_ACTIVE) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .t bind index sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ const char *tagName;
+ Tcl_Obj *objPtr;
+
+ tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr);
+ objPtr = Tcl_NewStringObj(tagName, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->legend->bindTable,
+ Blt_MakeElementTag(graphPtr, Tcl_GetString(objv[3])), objc - 4,
+ objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr->legend, objv[3], 0);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets options for the legend.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int flags = BLT_CONFIG_OBJV_ONLY;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, (Tcl_Obj *)NULL, flags);
+ } else if (objc == 4) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)legendPtr, objv[3], flags);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, configSpecs,
+ objc - 3, objv + 3, (char *)legendPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_ConfigureLegend(graphPtr);
+ return TCL_OK;
+}
+
+
+/*ARGSUSED*/
+static int
+CurselectionOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (legendPtr->flags & SELECT_SORTED) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(legendPtr->selected); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+ Tcl_Obj *objPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ } else {
+ Blt_ChainLink link;
+
+ /* List of selected entries is in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+
+ if (EntryIsSelected(legendPtr, elemPtr)) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_NewStringObj(elemPtr->obj.name, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+FocusOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (objc == 4) {
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((elemPtr != NULL) && (elemPtr != legendPtr->focusPtr)) {
+ /* Changing focus can only affect the visible entries. The entry
+ * layout stays the same. */
+ legendPtr->focusPtr = elemPtr;
+ }
+ Blt_SetFocusItem(legendPtr->bindTable, legendPtr->focusPtr,
+ CID_LEGEND_ENTRY);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ if (legendPtr->focusPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ legendPtr->focusPtr->obj.name, -1);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument can be
+ * either a screen position "@x,y" or the name of an element.
+ *
+ * I don't know how useful it is to test with the name of an element.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ if (((legendPtr->flags & HIDE) == 0) && (legendPtr->nEntries > 0)) {
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name,-1);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IconOp --
+ *
+ * Find the legend entry from the given argument. The argument
+ * can be either a screen position "@x,y" or the name of an
+ * element.
+ *
+ * I don't know how useful it is to test with the name of an
+ * element.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ * .g legend icon elemName image
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+IconOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_Picture picture;
+ Element *elemPtr;
+ Legend *legendPtr = graphPtr->legend;
+ Pixmap pixmap;
+ Blt_FontMetrics fontMetrics;
+ Tk_PhotoHandle photo;
+ const char *imageName;
+ int isPicture;
+ int w, h, x, y, s;
+
+ if (GetElementFromObj(graphPtr, objv[3], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (elemPtr == NULL) {
+ return TCL_OK; /* Unknown index. */
+ }
+ imageName = Tcl_GetString(objv[4]);
+ photo = Tk_FindPhoto(interp, imageName);
+ if (photo != NULL) {
+ isPicture = FALSE;
+ } else if (Blt_GetPicture(interp, imageName, &picture) == TCL_OK) {
+ isPicture = TRUE;
+ } else {
+ return TCL_ERROR;
+ }
+ Blt_GetFontMetrics(legendPtr->style.font, &fontMetrics);
+ s = fontMetrics.ascent;
+ h = s + PADDING(legendPtr->iyPad) + 1;
+ w = s + s + 1 + PADDING(legendPtr->ixPad);
+ x = (w / 2);
+ y = (h / 2);
+
+ pixmap = Tk_GetPixmap(graphPtr->display, Tk_RootWindow(graphPtr->tkwin),
+ w, h, Tk_Depth(graphPtr->tkwin));
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, pixmap, graphPtr->plotBg,
+ 0, 0, w, h, TK_RELIEF_FLAT, 0);
+ (*elemPtr->procsPtr->drawSymbolProc) (graphPtr, pixmap, elemPtr, x, y, s);
+ picture = Blt_DrawableToPicture(graphPtr->tkwin, pixmap, 0, 0, w, h, 1.0);
+ Tk_FreePixmap(graphPtr->display, pixmap);
+ if (picture == NULL) {
+ Tcl_AppendResult(interp, "can't get picture of symbol.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Make the background transparent. */
+ {
+ int y;
+ Blt_Pixel bg;
+ Blt_Pixel *destRowPtr;
+ XColor *colorPtr;
+
+ colorPtr = Blt_BackgroundBorderColor(graphPtr->plotBg);
+ bg.Red = colorPtr->red >> 8;
+ bg.Green = colorPtr->green >> 8;
+ bg.Blue = colorPtr->blue >> 8;
+ bg.Alpha = 0xFF;
+
+ destRowPtr = Blt_PictureBits(picture);
+ for (y = 0; y < h; y++) {
+ Blt_Pixel *dp, *dend;
+
+ for (dp = destRowPtr, dend = dp + w; dp < dend; dp++) {
+ if (dp->u32 == bg.u32) {
+ dp->Alpha = 0x0;
+ }
+ }
+ destRowPtr += Blt_PictureStride(picture);
+ }
+ }
+ Blt_ClassifyPicture(picture);
+ if (isPicture) {
+ Blt_ResetPicture(interp, imageName, picture);
+ } else {
+ Blt_PictureToPhoto(picture, photo);
+ Blt_FreePicture(picture);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionAnchorOp --
+ *
+ * Sets the selection anchor to the element given by a index. The
+ * selection anchor is the end of the selection that is fixed while
+ * dragging out a selection with the mouse. The index "anchor" may be
+ * used to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionAnchorOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry
+ * is selected. */
+ legendPtr->selAnchorPtr = elemPtr;
+ legendPtr->selMarkPtr = NULL;
+ if (elemPtr != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name, -1);
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionClearallOp
+ *
+ * Clears the entire selection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionClearallOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+
+ ClearSelection(legendPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionIncludesOp
+ *
+ * Returns 1 if the element indicated by index is currently
+ * selected, 0 if it isn't.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionIncludesOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+ int bool;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bool = EntryIsSelected(legendPtr, elemPtr);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionMarkOp --
+ *
+ * Sets the selection mark to the element given by a index. The
+ * selection anchor is the end of the selection that is movable while
+ * dragging out a selection with the mouse. The index "mark" may be used
+ * to refer to the anchor element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionMarkOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *elemPtr;
+
+ if (GetElementFromObj(graphPtr, objv[4], &elemPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (legendPtr->selAnchorPtr == NULL) {
+ Tcl_AppendResult(interp, "selection anchor must be set first",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (legendPtr->selMarkPtr != elemPtr) {
+ Blt_ChainLink link, next;
+
+ /* Deselect entry from the list all the way back to the anchor. */
+ for (link = Blt_Chain_LastLink(legendPtr->selected); link != NULL;
+ link = next) {
+ Element *selectPtr;
+
+ next = Blt_Chain_PrevLink(link);
+ selectPtr = Blt_Chain_GetValue(link);
+ if (selectPtr == legendPtr->selAnchorPtr) {
+ break;
+ }
+ DeselectElement(legendPtr, selectPtr);
+ }
+ legendPtr->flags &= ~SELECT_MASK;
+ legendPtr->flags |= SELECT_SET;
+ SelectRange(legendPtr, legendPtr->selAnchorPtr, elemPtr);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->obj.name, -1);
+ legendPtr->selMarkPtr = elemPtr;
+
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionPresentOp
+ *
+ * Returns 1 if there is a selection and 0 if it isn't.
+ *
+ * Results:
+ * A standard TCL result. interp->result will contain a boolean string
+ * indicating if there is a selection.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionPresentOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ int bool;
+
+ bool = (Blt_Chain_GetLength(legendPtr->selected) > 0);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), bool);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionSetOp
+ *
+ * Selects, deselects, or toggles all of the elements in the range
+ * between first and last, inclusive, without affecting the selection
+ * state of elements outside that range.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ * .g legend selection set first last
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SelectionSetOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Legend *legendPtr = graphPtr->legend;
+ Element *firstPtr, *lastPtr;
+ const char *string;
+
+ legendPtr->flags &= ~SELECT_MASK;
+ string = Tcl_GetString(objv[3]);
+ switch (string[0]) {
+ case 's':
+ legendPtr->flags |= SELECT_SET;
+ break;
+ case 'c':
+ legendPtr->flags |= SELECT_CLEAR;
+ break;
+ case 't':
+ legendPtr->flags |= SELECT_TOGGLE;
+ break;
+ }
+ if (GetElementFromObj(graphPtr, objv[4], &firstPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((firstPtr->flags & HIDE) && ((legendPtr->flags & SELECT_CLEAR)==0)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[4]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ lastPtr = firstPtr;
+ if (objc > 5) {
+ if (GetElementFromObj(graphPtr, objv[5], &lastPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((lastPtr->flags & HIDE) &&
+ ((legendPtr->flags & SELECT_CLEAR) == 0)) {
+ Tcl_AppendResult(interp, "can't select hidden node \"",
+ Tcl_GetString(objv[5]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (firstPtr == lastPtr) {
+ SelectEntry(legendPtr, firstPtr);
+ } else {
+ SelectRange(legendPtr, firstPtr, lastPtr);
+ }
+ /* Set both the anchor and the mark. Indicates that a single entry is
+ * selected. */
+ if (legendPtr->selAnchorPtr == NULL) {
+ legendPtr->selAnchorPtr = firstPtr;
+ }
+ if (legendPtr->flags & SELECT_EXPORT) {
+ Tk_OwnSelection(legendPtr->tkwin, XA_PRIMARY, LostSelectionProc,
+ legendPtr);
+ }
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ if (legendPtr->selectCmd != NULL) {
+ EventuallyInvokeSelectCmd(legendPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionOp --
+ *
+ * This procedure handles the individual options for text selections.
+ * The selected text is designated by start and end indices into the text
+ * pool. The selected segment has both a anchored and unanchored ends.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The selection changes.
+ *
+ * .g legend selection anchor
+ *---------------------------------------------------------------------------
+ */
+static Blt_OpSpec selectionOps[] =
+{
+ {"anchor", 1, SelectionAnchorOp, 5, 5, "elem",},
+ {"clear", 5, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+ {"clearall", 6, SelectionClearallOp, 4, 4, "",},
+ {"includes", 1, SelectionIncludesOp, 5, 5, "elem",},
+ {"mark", 1, SelectionMarkOp, 5, 5, "elem",},
+ {"present", 1, SelectionPresentOp, 4, 4, "",},
+ {"set", 1, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+ {"toggle", 1, SelectionSetOp, 5, 6, "firstElem ?lastElem?",},
+};
+static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
+
+static int
+SelectionOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ GraphLegendProc *proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG3,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LegendOp --
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Legend is possibly redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Blt_OpSpec legendOps[] =
+{
+ {"activate", 1, ActivateOp, 3, 0, "?pattern?...",},
+ {"bind", 1, BindOp, 3, 6, "elem sequence command",},
+ {"cget", 2, CgetOp, 4, 4, "option",},
+ {"configure", 2, ConfigureOp, 3, 0, "?option value?...",},
+ {"curselection", 2, CurselectionOp, 3, 3, "",},
+ {"deactivate", 1, ActivateOp, 3, 0, "?pattern?...",},
+ {"focus", 1, FocusOp, 4, 4, "elem",},
+ {"get", 1, GetOp, 4, 4, "elem",},
+ {"icon", 1, IconOp, 5, 5, "elem image",},
+ {"selection", 1, SelectionOp, 3, 0, "args"},
+};
+static int nLegendOps = sizeof(legendOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_LegendOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphLegendProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nLegendOps, legendOps, BLT_OP_ARG2,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (graphPtr, interp, objc, objv);
+}
+
+int
+Blt_Legend_Site(Graph *graphPtr)
+{
+ return graphPtr->legend->site;
+}
+
+int
+Blt_Legend_Width(Graph *graphPtr)
+{
+ return graphPtr->legend->width;
+}
+
+int
+Blt_Legend_Height(Graph *graphPtr)
+{
+ return graphPtr->legend->height;
+}
+
+int
+Blt_Legend_IsHidden(Graph *graphPtr)
+{
+ return (graphPtr->legend->flags & HIDE);
+}
+
+int
+Blt_Legend_IsRaised(Graph *graphPtr)
+{
+ return (graphPtr->legend->flags & RAISED);
+}
+
+int
+Blt_Legend_X(Graph *graphPtr)
+{
+ return graphPtr->legend->x;
+}
+
+int
+Blt_Legend_Y(Graph *graphPtr)
+{
+ return graphPtr->legend->y;
+}
+
+void
+Blt_Legend_RemoveElement(Graph *graphPtr, Element *elemPtr)
+{
+ Blt_DeleteBindings(graphPtr->legend->bindTable, elemPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SelectionProc --
+ *
+ * This procedure is called back by Tk when the selection is requested by
+ * someone. It returns part or all of the selection in a buffer provided
+ * by the caller.
+ *
+ * Results:
+ * The return value is the number of non-NULL bytes stored at buffer.
+ * Buffer is filled (or partially filled) with a NUL-terminated string
+ * containing part or all of the selection, as given by offset and
+ * maxBytes.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SelectionProc(
+ ClientData clientData, /* Information about the widget. */
+ int offset, /* Offset within selection of first
+ * character to be returned. */
+ char *buffer, /* Location in which to place
+ * selection. */
+ int maxBytes) /* Maximum number of bytes to place at
+ * buffer, not including terminating
+ * NULL character. */
+{
+ Legend *legendPtr = clientData;
+ int nBytes;
+ Tcl_DString dString;
+
+ if ((legendPtr->flags & SELECT_EXPORT) == 0) {
+ return -1;
+ }
+ /* Retrieve the names of the selected entries. */
+ Tcl_DStringInit(&dString);
+ if (legendPtr->flags & SELECT_SORTED) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(legendPtr->selected);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ Tcl_DStringAppend(&dString, elemPtr->obj.name, -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ } else {
+ Blt_ChainLink link;
+ Graph *graphPtr;
+
+ graphPtr = legendPtr->graphPtr;
+ /* List of selected entries is in stacking order. */
+ for (link = Blt_Chain_FirstLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Element *elemPtr;
+
+ elemPtr = Blt_Chain_GetValue(link);
+ if (EntryIsSelected(legendPtr, elemPtr)) {
+ Tcl_DStringAppend(&dString, elemPtr->obj.name, -1);
+ Tcl_DStringAppend(&dString, "\n", -1);
+ }
+ }
+ }
+ nBytes = Tcl_DStringLength(&dString) - offset;
+ strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes);
+ Tcl_DStringFree(&dString);
+ buffer[maxBytes] = '\0';
+ return MIN(nBytes, maxBytes);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BlinkCursorProc --
+ *
+ * This procedure is called as a timer handler to blink the insertion
+ * cursor off and on.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor gets turned on or off, redisplay gets invoked, and this
+ * procedure reschedules itself.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+BlinkCursorProc(ClientData clientData)
+{
+ Graph *graphPtr = clientData;
+ Legend *legendPtr;
+
+ legendPtr = graphPtr->legend;
+ if (!(legendPtr->flags & FOCUS) || (legendPtr->offTime == 0)) {
+ return;
+ }
+ if (legendPtr->active) {
+ int time;
+
+ legendPtr->cursorOn ^= 1;
+ time = (legendPtr->cursorOn) ? legendPtr->onTime : legendPtr->offTime;
+ legendPtr->timerToken = Tcl_CreateTimerHandler(time, BlinkCursorProc,
+ graphPtr);
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+}
diff --git a/src/bltGrLegd.h b/src/bltGrLegd.h
new file mode 100644
index 0000000..0ba1cb3
--- /dev/null
+++ b/src/bltGrLegd.h
@@ -0,0 +1,60 @@
+/*
+ * bltGrLegd.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_GR_LEGEND_H
+#define _BLT_GR_LEGEND_H
+
+#define LEGEND_RIGHT (1<<0) /* Right margin */
+#define LEGEND_LEFT (1<<1) /* Left margin */
+#define LEGEND_BOTTOM (1<<2) /* Bottom margin */
+#define LEGEND_TOP (1<<3) /* Top margin, below the graph title. */
+#define LEGEND_PLOT (1<<4) /* Plot area */
+#define LEGEND_XY (1<<5) /* Screen coordinates in the plotting
+ * area. */
+#define LEGEND_WINDOW (1<<6) /* External window. */
+#define LEGEND_MARGIN_MASK \
+ (LEGEND_RIGHT | LEGEND_LEFT | LEGEND_BOTTOM | LEGEND_TOP)
+#define LEGEND_PLOTAREA_MASK (LEGEND_PLOT | LEGEND_XY)
+
+BLT_EXTERN int Blt_CreateLegend(Graph *graphPtr);
+BLT_EXTERN void Blt_DestroyLegend(Graph *graphPtr);
+BLT_EXTERN void Blt_DrawLegend(Graph *graphPtr, Drawable drawable);
+BLT_EXTERN void Blt_MapLegend(Graph *graphPtr, int width, int height);
+BLT_EXTERN int Blt_LegendOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+BLT_EXTERN int Blt_Legend_Site(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_Width(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_Height(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_IsHidden(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_IsRaised(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_X(Graph *graphPtr);
+BLT_EXTERN int Blt_Legend_Y(Graph *graphPtr);
+BLT_EXTERN void Blt_Legend_RemoveElement(Graph *graphPtr, Element *elemPtr);
+BLT_EXTERN void Blt_Legend_EventuallyRedraw(Graph *graphPtr);
+
+#endif /* BLT_GR_LEGEND_H */
diff --git a/src/bltGrMarker.C b/src/bltGrMarker.C
new file mode 100644
index 0000000..c9546f6
--- /dev/null
+++ b/src/bltGrMarker.C
@@ -0,0 +1,5196 @@
+
+/*
+ * bltGrMarker.c --
+ *
+ * This module implements markers for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltImage.h"
+#include "bltPicture.h"
+#include "bltPainter.h"
+#include "bltChain.h"
+#include "bltGrElem.h"
+#include "bltBitmap.h"
+
+typedef int (GraphMarkerProc)(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+#define GETBITMAP(b) \
+ (((b)->destBitmap == None) ? (b)->srcBitmap : (b)->destBitmap)
+
+#define MAX_OUTLINE_POINTS 12
+
+#define IMAGE_PHOTO (1<<7)
+
+/* Map graph coordinates to normalized coordinates [0..1] */
+#define NORMALIZE(A,x) (((x) - (A)->axisRange.min) * (A)->axisRange.scale)
+
+#define DEF_MARKER_ANCHOR "center"
+#define DEF_MARKER_BACKGROUND RGB_WHITE
+#define DEF_MARKER_BITMAP (char *)NULL
+#define DEF_MARKER_CAP_STYLE "butt"
+#define DEF_MARKER_COORDS (char *)NULL
+#define DEF_MARKER_DASHES (char *)NULL
+#define DEF_MARKER_DASH_OFFSET "0"
+#define DEF_MARKER_ELEMENT (char *)NULL
+#define DEF_MARKER_FOREGROUND RGB_BLACK
+#define DEF_MARKER_FILL_COLOR RGB_RED
+#define DEF_MARKER_FONT STD_FONT
+#define DEF_MARKER_GAP_COLOR RGB_PINK
+#define DEF_MARKER_HEIGHT "0"
+#define DEF_MARKER_HIDE "no"
+#define DEF_MARKER_JOIN_STYLE "miter"
+#define DEF_MARKER_JUSTIFY "left"
+#define DEF_MARKER_LINE_WIDTH "1"
+#define DEF_MARKER_MAP_X "x"
+#define DEF_MARKER_MAP_Y "y"
+#define DEF_MARKER_NAME (char *)NULL
+#define DEF_MARKER_OUTLINE_COLOR RGB_BLACK
+#define DEF_MARKER_PAD "4"
+#define DEF_MARKER_ANGLE "0.0"
+#define DEF_MARKER_SCALE "1.0"
+#define DEF_MARKER_STATE "normal"
+#define DEF_MARKER_STIPPLE (char *)NULL
+#define DEF_MARKER_TEXT (char *)NULL
+#define DEF_MARKER_UNDER "no"
+#define DEF_MARKER_WIDTH "0"
+#define DEF_MARKER_WINDOW (char *)NULL
+#define DEF_MARKER_XOR "no"
+#define DEF_MARKER_X_OFFSET "0"
+#define DEF_MARKER_Y_OFFSET "0"
+#define DEF_MARKER_FILTER "box"
+
+#define DEF_TEXT_TAGS "Text all"
+#define DEF_IMAGE_TAGS "Image all"
+#define DEF_BITMAP_TAGS "Bitmap all"
+#define DEF_WINDOW_TAGS "Window all"
+#define DEF_POLYGON_TAGS "Polygon all"
+#define DEF_LINE_TAGS "Line all"
+
+static Blt_OptionParseProc ObjToCoordsProc;
+static Blt_OptionPrintProc CoordsToObjProc;
+static Blt_OptionFreeProc FreeCoordsProc;
+static Blt_CustomOption coordsOption =
+{
+ ObjToCoordsProc, CoordsToObjProc, FreeCoordsProc, (ClientData)0
+};
+static Blt_OptionFreeProc FreeColorPairProc;
+static Blt_OptionParseProc ObjToColorPairProc;
+static Blt_OptionPrintProc ColorPairToObjProc;
+static Blt_CustomOption colorPairOption =
+{
+ ObjToColorPairProc, ColorPairToObjProc, FreeColorPairProc, (ClientData)0
+};
+
+static Blt_OptionParseProc ObjToPictImageProc;
+static Blt_OptionPrintProc PictImageToObjProc;
+static Blt_OptionFreeProc FreePictImageProc;
+static Blt_CustomOption pictImageOption =
+{
+ ObjToPictImageProc, PictImageToObjProc, FreePictImageProc, (ClientData)0
+};
+
+BLT_EXTERN Blt_CustomOption bltXAxisOption;
+BLT_EXTERN Blt_CustomOption bltYAxisOption;
+BLT_EXTERN Blt_CustomOption bltFilterOption;
+
+typedef Marker *(MarkerCreateProc)(void);
+typedef void (MarkerDrawProc)(Marker *markerPtr, Drawable drawable);
+typedef void (MarkerFreeProc)(Marker *markerPtr);
+typedef int (MarkerConfigProc)(Marker *markerPtr);
+typedef void (MarkerMapProc)(Marker *markerPtr);
+typedef void (MarkerPostscriptProc)(Marker *markerPtr, Blt_Ps ps);
+typedef int (MarkerPointProc)(Marker *markerPtr, Point2d *samplePtr);
+typedef int (MarkerRegionProc)(Marker *markerPtr, Region2d *extsPtr,
+ int enclosed);
+
+static Tcl_FreeProc FreeMarker;
+
+typedef struct {
+ Blt_ConfigSpec *configSpecs; /* Marker configuration
+ * specifications */
+ MarkerConfigProc *configProc;
+ MarkerDrawProc *drawProc;
+ MarkerFreeProc *freeProc;
+ MarkerMapProc *mapProc;
+ MarkerPointProc *pointProc;
+ MarkerRegionProc *regionProc;
+ MarkerPostscriptProc *postscriptProc;
+
+} MarkerClass;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Marker --
+ *
+ * Structure defining the generic marker. In C++ parlance this would be
+ * the base class from which all markers are derived.
+ *
+ * This structure corresponds with the specific types of markers. Don't
+ * change this structure without changing the individual marker
+ * structures of each type below.
+ *
+ * --------------------------------------------------------------------------
+ */
+struct _Marker {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+ Point2d *worldPts; /* Coordinate array to position
+ * marker */
+ int nWorldPts; /* Number of points in above array */
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+};
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BitmapMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+
+ Point2d *worldPts; /* Coordinate array to position
+ * marker. */
+ int nWorldPts; /* # of points in above array. */
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+
+ /* Fields specific to bitmap markers. */
+
+ Pixmap srcBitmap; /* Original bitmap. May be further
+ * scaled or rotated. */
+ float reqAngle; /* Requested rotation of the bitmap */
+ float angle; /* Normalized rotation (0..360
+ * degrees) */
+ Tk_Anchor anchor; /* If only one X-Y coordinate is given,
+ * indicates how to translate the given
+ * marker position. Otherwise, if there
+ * are two X-Y coordinates, then this
+ * value is ignored. */
+ Point2d anchorPt; /* Translated anchor point. */
+
+ XColor *outlineColor; /* Foreground color */
+ XColor *fillColor; /* Background color */
+
+ GC gc; /* Private graphic context */
+ GC fillGC; /* Shared graphic context */
+ Pixmap destBitmap; /* Bitmap to be drawn. */
+ int destWidth, destHeight; /* Dimensions of the final bitmap */
+
+ Point2d outline[MAX_OUTLINE_POINTS];/* Polygon representing the background
+ * of the bitmap. */
+ int nOutlinePts;
+} BitmapMarker;
+
+static Blt_ConfigSpec bitmapConfigSpecs[] =
+{
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR,
+ Blt_Offset(BitmapMarker, anchor), 0},
+ {BLT_CONFIG_COLOR, "-background", "background", "Background",
+ DEF_MARKER_BACKGROUND, Blt_Offset(BitmapMarker, fillColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_BITMAP_TAGS,
+ Blt_Offset(BitmapMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap", DEF_MARKER_BITMAP,
+ Blt_Offset(BitmapMarker, srcBitmap), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(BitmapMarker, worldPts), BLT_CONFIG_NULL_OK,
+ &coordsOption},
+ {BLT_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT,
+ Blt_Offset(BitmapMarker, elemName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-fill", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FOREGROUND, Blt_Offset(BitmapMarker, outlineColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(BitmapMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(BitmapMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(BitmapMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(BitmapMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_FLOAT, "-rotate", "rotate", "Rotate", DEF_MARKER_ANGLE,
+ Blt_Offset(BitmapMarker, reqAngle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(BitmapMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(BitmapMarker, drawUnder), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(BitmapMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(BitmapMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigureBitmapProc;
+static MarkerCreateProc CreateBitmapProc;
+static MarkerDrawProc DrawBitmapProc;
+static MarkerFreeProc FreeBitmapProc;
+static MarkerMapProc MapBitmapProc;
+static MarkerPointProc PointInBitmapProc;
+static MarkerPostscriptProc BitmapToPostscriptProc;
+static MarkerRegionProc RegionInBitmapProc;
+
+static MarkerClass bitmapMarkerClass = {
+ bitmapConfigSpecs,
+ ConfigureBitmapProc,
+ DrawBitmapProc,
+ FreeBitmapProc,
+ MapBitmapProc,
+ PointInBitmapProc,
+ RegionInBitmapProc,
+ BitmapToPostscriptProc,
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ImageMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+
+ Point2d *worldPts; /* Coordinate array to position
+ * marker. */
+
+ int nWorldPts; /* # of points in above array. */
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+
+ Tk_Image tkImage; /* Tk image to be displayed. */
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+ Point2d anchorPt; /* Translated anchor point. */
+ int width, height; /* Dimensions of the possibly scaled
+ * image. */
+ Blt_Painter painter;
+ Blt_Picture picture;
+ Blt_ResampleFilter filter;
+ int pictX, pictY; /* */
+ Blt_Picture scaled; /* Pixmap containing the scaled image */
+ GC gc;
+
+} ImageMarker;
+
+static Blt_ConfigSpec imageConfigSpecs[] =
+{
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR,
+ Blt_Offset(ImageMarker, anchor), 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_IMAGE_TAGS,
+ Blt_Offset(ImageMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(ImageMarker, worldPts), BLT_CONFIG_NULL_OK, &coordsOption},
+ {BLT_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT,
+ Blt_Offset(ImageMarker, elemName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(ImageMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-image", "image", "Image", (char *)NULL,
+ Blt_Offset(ImageMarker, picture), BLT_CONFIG_NULL_OK, &pictImageOption},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(ImageMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(ImageMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(ImageMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-resamplefilter", "resampleFilter", "ResampleFilter",
+ DEF_MARKER_FILTER, Blt_Offset(ImageMarker, filter),
+ BLT_CONFIG_NULL_OK | BLT_CONFIG_DONT_SET_DEFAULT, &bltFilterOption},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(ImageMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(ImageMarker, drawUnder), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(ImageMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(ImageMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigureImageProc;
+static MarkerCreateProc CreateImageProc;
+static MarkerDrawProc DrawImageProc;
+static MarkerFreeProc FreeImageProc;
+static MarkerMapProc MapImageProc;
+static MarkerPointProc PointInImageProc;
+static MarkerPostscriptProc ImageToPostscriptProc;
+static MarkerRegionProc RegionInImageProc;
+
+static MarkerClass imageMarkerClass = {
+ imageConfigSpecs,
+ ConfigureImageProc,
+ DrawImageProc,
+ FreeImageProc,
+ MapImageProc,
+ PointInImageProc,
+ RegionInImageProc,
+ ImageToPostscriptProc,
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LineMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+
+ Point2d *worldPts; /* Coordinate array to position
+ * marker. */
+
+ int nWorldPts; /* Number of points in above array */
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+
+ XColor *fillColor;
+ XColor *outlineColor; /* Foreground and background colors */
+
+ int lineWidth; /* Line width. */
+ int capStyle; /* Cap style. */
+ int joinStyle; /* Join style.*/
+ Blt_Dashes dashes; /* Dash list values (max 11) */
+
+ GC gc; /* Private graphic context */
+
+ Segment2d *segments; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising the
+ * mapped line. The segments may not
+ * necessarily be connected after
+ * clipping. */
+ int nSegments; /* # segments in the above array. */
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is currently drawn. */
+} LineMarker;
+
+static Blt_ConfigSpec lineConfigSpecs[] =
+{
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_LINE_TAGS,
+ Blt_Offset(LineMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CAP_STYLE, "-cap", "cap", "Cap", DEF_MARKER_CAP_STYLE,
+ Blt_Offset(LineMarker, capStyle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(LineMarker, worldPts), BLT_CONFIG_NULL_OK, &coordsOption},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes", DEF_MARKER_DASHES,
+ Blt_Offset(LineMarker, dashes), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_NNEG, "-dashoffset", "dashOffset", "DashOffset",
+ DEF_MARKER_DASH_OFFSET, Blt_Offset(LineMarker, dashes.offset),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT,
+ Blt_Offset(LineMarker, elemName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-fill", "fill", "Fill", (char *)NULL,
+ Blt_Offset(LineMarker, fillColor), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_JOIN_STYLE, "-join", "join", "Join", DEF_MARKER_JOIN_STYLE,
+ Blt_Offset(LineMarker, joinStyle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Blt_Offset(LineMarker, lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(LineMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(LineMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(LineMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(LineMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_COLOR, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Blt_Offset(LineMarker, outlineColor),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(LineMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(LineMarker, drawUnder), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(LineMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-xor", "xor", "Xor", DEF_MARKER_XOR,
+ Blt_Offset(LineMarker, xor), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(LineMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigureLineProc;
+static MarkerCreateProc CreateLineProc;
+static MarkerDrawProc DrawLineProc;
+static MarkerFreeProc FreeLineProc;
+static MarkerMapProc MapLineProc;
+static MarkerPointProc PointInLineProc;
+static MarkerPostscriptProc LineToPostscriptProc;
+static MarkerRegionProc RegionInLineProc;
+
+static MarkerClass lineMarkerClass = {
+ lineConfigSpecs,
+ ConfigureLineProc,
+ DrawLineProc,
+ FreeLineProc,
+ MapLineProc,
+ PointInLineProc,
+ RegionInLineProc,
+ LineToPostscriptProc,
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PolygonMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+
+ Point2d *worldPts; /* Coordinate array to position
+ * marker. */
+
+ int nWorldPts; /* Number of points in above array */
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+
+ Point2d *screenPts; /* Array of points representing the
+ * polygon in screen coordinates. It's
+ * not used for drawing, but to generate
+ * the outlinePts and fillPts arrays
+ * that are the coordinates of the
+ * possibly clipped outline and filled
+ * polygon. */
+
+ ColorPair outline;
+ ColorPair fill;
+
+ Pixmap stipple; /* Stipple pattern to fill the
+ * polygon. */
+ int lineWidth; /* Width of polygon outline. */
+ int capStyle;
+ int joinStyle;
+ Blt_Dashes dashes; /* List of dash values. Indicates how
+ * to draw the dashed line. If no dash
+ * values are provided, or the first
+ * value is zero, then the line is drawn
+ * solid. */
+
+ GC outlineGC; /* Graphics context to draw the outline
+ * of the polygon. */
+ GC fillGC; /* Graphics context to draw the filled
+ * polygon. */
+
+ Point2d *fillPts; /* Malloc'ed array of points used to
+ * draw the filled polygon. These points
+ * may form a degenerate polygon after
+ * clipping. */
+ int nFillPts; /* # points in the above array. */
+ Segment2d *outlinePts; /* Malloc'ed array of points.
+ * Represents individual line segments
+ * (2 points per segment) comprising the
+ * outline of the polygon. The segments
+ * may not necessarily be closed or
+ * connected after clipping. */
+ int nOutlinePts; /* # points in the above array. */
+ int xor;
+ int xorState; /* State of the XOR drawing. Indicates
+ * if the marker is visible. We have to
+ * drawn it again to erase it. */
+} PolygonMarker;
+
+static Blt_ConfigSpec polygonConfigSpecs[] =
+{
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_POLYGON_TAGS,
+ Blt_Offset(PolygonMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CAP_STYLE, "-cap", "cap", "Cap", DEF_MARKER_CAP_STYLE,
+ Blt_Offset(PolygonMarker, capStyle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(PolygonMarker, worldPts), BLT_CONFIG_NULL_OK, &coordsOption},
+ {BLT_CONFIG_DASHES, "-dashes", "dashes", "Dashes", DEF_MARKER_DASHES,
+ Blt_Offset(PolygonMarker, dashes), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT,
+ Blt_Offset(PolygonMarker, elemName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_MARKER_FILL_COLOR,
+ Blt_Offset(PolygonMarker, fill), BLT_CONFIG_NULL_OK, &colorPairOption},
+ {BLT_CONFIG_JOIN_STYLE, "-join", "join", "Join", DEF_MARKER_JOIN_STYLE,
+ Blt_Offset(PolygonMarker, joinStyle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-linewidth", "lineWidth", "LineWidth",
+ DEF_MARKER_LINE_WIDTH, Blt_Offset(PolygonMarker, lineWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(PolygonMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(PolygonMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(PolygonMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(PolygonMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_MARKER_OUTLINE_COLOR, Blt_Offset(PolygonMarker, outline),
+ BLT_CONFIG_NULL_OK, &colorPairOption},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(PolygonMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMAP, "-stipple", "stipple", "Stipple", DEF_MARKER_STIPPLE,
+ Blt_Offset(PolygonMarker, stipple), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(PolygonMarker, drawUnder),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(PolygonMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-xor", "xor", "Xor", DEF_MARKER_XOR,
+ Blt_Offset(PolygonMarker, xor), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(PolygonMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigurePolygonProc;
+static MarkerCreateProc CreatePolygonProc;
+static MarkerDrawProc DrawPolygonProc;
+static MarkerFreeProc FreePolygonProc;
+static MarkerMapProc MapPolygonProc;
+static MarkerPointProc PointInPolygonProc;
+static MarkerPostscriptProc PolygonToPostscriptProc;
+static MarkerRegionProc RegionInPolygonProc;
+
+static MarkerClass polygonMarkerClass = {
+ polygonConfigSpecs,
+ ConfigurePolygonProc,
+ DrawPolygonProc,
+ FreePolygonProc,
+ MapPolygonProc,
+ PointInPolygonProc,
+ RegionInPolygonProc,
+ PolygonToPostscriptProc,
+};
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TextMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+ MarkerClass *classPtr;
+ Blt_HashEntry *hashPtr;
+ Blt_ChainLink link;
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+ Point2d *worldPts; /* Coordinate array to position
+ * marker. */
+ int nWorldPts; /* # of points in above array */
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+ int state;
+
+ /* Fields specific to text markers. */
+#ifdef notdef
+ const char *textVarName; /* Name of variable (malloc'ed) or
+ * NULL. If non-NULL, graph displays the
+ * contents of this variable. */
+#endif
+ const char *string; /* Text string to be display. The
+ * string make contain newlines. */
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+ Point2d anchorPt; /* Translated anchor point. */
+ int width, height; /* Dimension of bounding box. */
+ TextStyle style; /* Text attributes (font, fg, anchor,
+ * etc) */
+ Point2d outline[5];
+ XColor *fillColor;
+ GC fillGC;
+} TextMarker;
+
+
+static Blt_ConfigSpec textConfigSpecs[] =
+{
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR,
+ Blt_Offset(TextMarker, anchor), 0},
+ {BLT_CONFIG_COLOR, "-background", "background", "MarkerBackground",
+ (char *)NULL, Blt_Offset(TextMarker, fillColor), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", "Background", (char *)NULL, 0, 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_TEXT_TAGS,
+ Blt_Offset(TextMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(TextMarker, worldPts), BLT_CONFIG_NULL_OK,
+ &coordsOption},
+ {BLT_CONFIG_STRING, "-element", "element", "Element",
+ DEF_MARKER_ELEMENT, Blt_Offset(TextMarker, elemName),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", "Foreground", (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-fill", "background", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_FONT, "-font", "font", "Font", DEF_MARKER_FONT,
+ Blt_Offset(TextMarker, style.font), 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_MARKER_FOREGROUND, Blt_Offset(TextMarker, style.color), 0},
+ {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_MARKER_JUSTIFY, Blt_Offset(TextMarker, style.justify),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(TextMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(TextMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(TextMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(TextMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_PAD, "-padx", "padX", "PadX", DEF_MARKER_PAD,
+ Blt_Offset(TextMarker, style.xPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-pady", "padY", "PadY", DEF_MARKER_PAD,
+ Blt_Offset(TextMarker, style.yPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_FLOAT, "-rotate", "rotate", "Rotate", DEF_MARKER_ANGLE,
+ Blt_Offset(TextMarker, style.angle), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(TextMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-text", "text", "Text", DEF_MARKER_TEXT,
+ Blt_Offset(TextMarker, string), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(TextMarker, drawUnder), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(TextMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(TextMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigureTextProc;
+static MarkerCreateProc CreateTextProc;
+static MarkerDrawProc DrawTextProc;
+static MarkerFreeProc FreeTextProc;
+static MarkerMapProc MapTextProc;
+static MarkerPointProc PointInTextProc;
+static MarkerPostscriptProc TextToPostscriptProc;
+static MarkerRegionProc RegionInTextProc;
+
+static MarkerClass textMarkerClass = {
+ textConfigSpecs,
+ ConfigureTextProc,
+ DrawTextProc,
+ FreeTextProc,
+ MapTextProc,
+ PointInTextProc,
+ RegionInTextProc,
+ TextToPostscriptProc,
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * WindowMarker --
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ GraphObj obj; /* Must be first field in marker. */
+
+ MarkerClass *classPtr;
+
+ Blt_HashEntry *hashPtr;
+
+ Blt_ChainLink link;
+
+ const char *elemName; /* Element associated with marker. Let's
+ * you link a marker to an element. The
+ * marker is drawn only if the element
+ * is also visible. */
+ Axis2d axes;
+
+ Point2d *worldPts; /* Coordinate array to position
+ * marker */
+
+ int nWorldPts; /* # of points in above array */
+
+ int drawUnder; /* If non-zero, draw the marker
+ * underneath any elements. This can be
+ * a performance penalty because the
+ * graph must be redraw entirely each
+ * time the marker is redrawn. */
+
+ int clipped; /* Indicates if the marker is totally
+ * clipped by the plotting area. */
+
+ unsigned int flags;
+
+
+ int xOffset, yOffset; /* Pixel offset from graph position */
+
+ int state;
+
+ /* Fields specific to window markers. */
+
+ const char *childName; /* Name of child widget. */
+ Tk_Window child; /* Window to display. */
+ int reqWidth, reqHeight; /* If non-zero, this overrides the size
+ * requested by the child widget. */
+
+ Tk_Anchor anchor; /* Indicates how to translate the given
+ * marker position. */
+
+ Point2d anchorPt; /* Translated anchor point. */
+ int width, height; /* Current size of the child window. */
+
+} WindowMarker;
+
+static Blt_ConfigSpec windowConfigSpecs[] =
+{
+ {BLT_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR,
+ Blt_Offset(WindowMarker, anchor), 0},
+ {BLT_CONFIG_LIST, "-bindtags", "bindTags", "BindTags", DEF_WINDOW_TAGS,
+ Blt_Offset(WindowMarker, obj.tags), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS,
+ Blt_Offset(WindowMarker, worldPts), BLT_CONFIG_NULL_OK,
+ &coordsOption},
+ {BLT_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT,
+ Blt_Offset(WindowMarker, elemName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_POS, "-height", "height", "Height", DEF_MARKER_HEIGHT,
+ Blt_Offset(WindowMarker, reqHeight), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-hide", "hide", "Hide", DEF_MARKER_HIDE,
+ Blt_Offset(WindowMarker, flags), BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)HIDE},
+ {BLT_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X,
+ Blt_Offset(WindowMarker, axes.x), 0, &bltXAxisOption},
+ {BLT_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y,
+ Blt_Offset(WindowMarker, axes.y), 0, &bltYAxisOption},
+ {BLT_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME,
+ Blt_Offset(WindowMarker, obj.name), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STATE, "-state", "state", "State", DEF_MARKER_STATE,
+ Blt_Offset(WindowMarker, state), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER,
+ Blt_Offset(WindowMarker, drawUnder), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_POS, "-width", "width", "Width", DEF_MARKER_WIDTH,
+ Blt_Offset(WindowMarker, reqWidth), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-window", "window", "Window", DEF_MARKER_WINDOW,
+ Blt_Offset(WindowMarker, childName), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET,
+ Blt_Offset(WindowMarker, xOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET,
+ Blt_Offset(WindowMarker, yOffset), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static MarkerConfigProc ConfigureWindowProc;
+static MarkerCreateProc CreateWindowProc;
+static MarkerDrawProc DrawWindowProc;
+static MarkerFreeProc FreeWindowProc;
+static MarkerMapProc MapWindowProc;
+static MarkerPointProc PointInWindowProc;
+static MarkerPostscriptProc WindowToPostscriptProc;
+static MarkerRegionProc RegionInWindowProc;
+
+static MarkerClass windowMarkerClass = {
+ windowConfigSpecs,
+ ConfigureWindowProc,
+ DrawWindowProc,
+ FreeWindowProc,
+ MapWindowProc,
+ PointInWindowProc,
+ RegionInWindowProc,
+ WindowToPostscriptProc,
+};
+
+static Tk_ImageChangedProc ImageChangedProc;
+
+
+
+#ifdef notdef
+static MarkerClass rectangleMarkerClass = {
+ rectangleConfigSpecs,
+ ConfigureRectangleProc,
+ DrawRectangleProc,
+ FreeRectangleProc,
+ MapRectangleProc,
+ PointInRectangleProc,
+ RegionInRectangleProc,
+ RectangleToPostscriptProc,
+};
+
+static MarkerClass ovalMarkerClass = {
+ ovalConfigSpecs,
+ ConfigureOvalProc,
+ DrawOvalProc,
+ FreeOvalProc,
+ MapOvalProc,
+ PointInOvalProc,
+ RegionInOvalProc,
+ OvalToPostscriptProc,
+};
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BoxesDontOverlap --
+ *
+ * Tests if the bounding box of a marker overlaps the plotting area in
+ * any way. If so, the marker will be drawn. Just do a min/max test on
+ * the extents of both boxes.
+ *
+ * Note: It's assumed that the extents of the bounding box lie
+ * within the area. So for a 10x10 rectangle, bottom and
+ * left would be 9.
+ *
+ * Results:
+ * Returns 0 is the marker is visible in the plotting area, and 1
+ * otherwise (marker is clipped).
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+BoxesDontOverlap(Graph *graphPtr, Region2d *extsPtr)
+{
+ assert(extsPtr->right >= extsPtr->left);
+ assert(extsPtr->bottom >= extsPtr->top);
+ assert(graphPtr->right >= graphPtr->left);
+ assert(graphPtr->bottom >= graphPtr->top);
+
+ return (((double)graphPtr->right < extsPtr->left) ||
+ ((double)graphPtr->bottom < extsPtr->top) ||
+ (extsPtr->right < (double)graphPtr->left) ||
+ (extsPtr->bottom < (double)graphPtr->top));
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetCoordinate --
+ *
+ * Convert the expression string into a floating point value. The * only
+ * reason we use this routine instead of Blt_ExprDouble is to * handle
+ * "elastic" bounds. That is, convert the strings "-Inf", * "Inf" into
+ * -(DBL_MAX) and DBL_MAX respectively.
+ *
+ * Results:
+ * The return value is a standard TCL result. The value of the
+ * expression is passed back via valuePtr.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetCoordinate(
+ Tcl_Interp *interp, /* Interpreter to return results */
+ Tcl_Obj *objPtr, /* Numeric expression string to
+ * parse */
+ double *valuePtr) /* Real-valued result of expression */
+{
+ char c;
+ const char *expr;
+
+ expr = Tcl_GetString(objPtr);
+ c = expr[0];
+ if ((c == 'I') && (strcmp(expr, "Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) {
+ *valuePtr = -DBL_MAX; /* Elastic lower bound */
+ } else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) {
+ *valuePtr = DBL_MAX; /* Elastic upper bound */
+ } else if (Blt_ExprDoubleFromObj(interp, objPtr, valuePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PrintCoordinate --
+ *
+ * Convert the floating point value into its string representation. The
+ * only reason this routine is used in instead of sprintf, is to handle
+ * the "elastic" bounds. That is, convert the values DBL_MAX and
+ * -(DBL_MAX) into "+Inf" and "-Inf" respectively.
+ *
+ * Results:
+ * The return value is a standard TCL result. The string of the *
+ * expression is passed back via string.
+ *
+ * -------------------------------------------------------------------------- */
+static Tcl_Obj *
+PrintCoordinate(double x)
+{
+ if (x == DBL_MAX) {
+ return Tcl_NewStringObj("+Inf", -1);
+ } else if (x == -DBL_MAX) {
+ return Tcl_NewStringObj("-Inf", -1);
+ } else {
+ return Tcl_NewDoubleObj(x);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ParseCoordinates --
+ *
+ * The TCL coordinate list is converted to their floating point
+ * values. It will then replace the current marker coordinates.
+ *
+ * Since different marker types require different number of coordinates
+ * this must be checked here.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side effects:
+ * If the marker coordinates are reset, the graph is eventually redrawn
+ * with at the new marker coordinates.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ParseCoordinates(
+ Tcl_Interp *interp,
+ Marker *markerPtr,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ int nWorldPts;
+ int minArgs, maxArgs;
+ Point2d *worldPts;
+ int i;
+
+ if (objc == 0) {
+ return TCL_OK;
+ }
+ if (objc & 1) {
+ Tcl_AppendResult(interp, "odd number of marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ switch (markerPtr->obj.classId) {
+ case CID_MARKER_LINE:
+ minArgs = 4, maxArgs = 0;
+ break;
+ case CID_MARKER_POLYGON:
+ minArgs = 6, maxArgs = 0;
+ break;
+ case CID_MARKER_WINDOW:
+ case CID_MARKER_TEXT:
+ minArgs = 2, maxArgs = 2;
+ break;
+ case CID_MARKER_IMAGE:
+ case CID_MARKER_BITMAP:
+ minArgs = 2, maxArgs = 4;
+ break;
+ default:
+ Tcl_AppendResult(interp, "unknown marker type", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if (objc < minArgs) {
+ Tcl_AppendResult(interp, "too few marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((maxArgs > 0) && (objc > maxArgs)) {
+ Tcl_AppendResult(interp, "too many marker coordinates specified",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ nWorldPts = objc / 2;
+ worldPts = Blt_Malloc(nWorldPts * sizeof(Point2d));
+ if (worldPts == NULL) {
+ Tcl_AppendResult(interp, "can't allocate new coordinate array",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ {
+ Point2d *pp;
+
+ pp = worldPts;
+ for (i = 0; i < objc; i += 2) {
+ double x, y;
+
+ if ((GetCoordinate(interp, objv[i], &x) != TCL_OK) ||
+ (GetCoordinate(interp, objv[i + 1], &y) != TCL_OK)) {
+ Blt_Free(worldPts);
+ return TCL_ERROR;
+ }
+ pp->x = x, pp->y = y, pp++;
+ }
+ }
+ /* Don't free the old coordinate array until we've parsed the new
+ * coordinates without errors. */
+ if (markerPtr->worldPts != NULL) {
+ Blt_Free(markerPtr->worldPts);
+ }
+ markerPtr->worldPts = worldPts;
+ markerPtr->nWorldPts = nWorldPts;
+ markerPtr->flags |= MAP_ITEM;
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static void
+FreeCoordsProc(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ Point2d **pointsPtr = (Point2d **)(widgRec + offset);
+
+ if (*pointsPtr != NULL) {
+ Blt_Free(*pointsPtr);
+ *pointsPtr = NULL;
+ }
+ markerPtr->nWorldPts = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToCoordsProc --
+ *
+ * Given a TCL list of numeric expression representing the element
+ * values, convert into an array of floating point values. In addition,
+ * the minimum and maximum values are saved. Since elastic values are
+ * allow (values which translate to the min/max of the graph), we must
+ * try to get the non-elastic minimum and maximum.
+ *
+ * Results:
+ * The return value is a standard TCL result. The vector is
+ * passed back via the vecPtr.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToCoordsProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to return results */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* TCL list of numeric expressions */
+ char *widgRec, /* Marker record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 0) {
+ return TCL_OK;
+ }
+ return ParseCoordinates(interp, markerPtr, objc, objv);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CoordsToObjProc --
+ *
+ * Convert the vector of floating point values into a TCL list.
+ *
+ * Results:
+ * The string representation of the vector is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+CoordsToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Marker record */
+ int offset, /* Not used. */
+ int flags) /* Not used. */
+{
+ Marker *markerPtr = (Marker *)widgRec;
+ Tcl_Obj *listObjPtr;
+ Point2d *pp, *pend;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (pp = markerPtr->worldPts, pend = pp + markerPtr->nWorldPts; pp < pend;
+ pp++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, PrintCoordinate(pp->x));
+ Tcl_ListObjAppendElement(interp, listObjPtr, PrintCoordinate(pp->y));
+ }
+ return listObjPtr;
+}
+
+/*LINTLIBRARY*/
+static int
+GetColorPair(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ Tcl_Obj *fgObjPtr, Tcl_Obj *bgObjPtr,
+ ColorPair *pairPtr,
+ int allowDefault)
+{
+ XColor *fgColor, *bgColor;
+ const char *string;
+
+ fgColor = bgColor = NULL;
+ if (fgObjPtr != NULL) {
+ int length;
+
+ string = Tcl_GetStringFromObj(fgObjPtr, &length);
+ if (string[0] == '\0') {
+ fgColor = NULL;
+ } else if ((allowDefault) && (string[0] == 'd') &&
+ (strncmp(string, "defcolor", length) == 0)) {
+ fgColor = COLOR_DEFAULT;
+ } else {
+ fgColor = Tk_AllocColorFromObj(interp, tkwin, fgObjPtr);
+ if (fgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ if (bgObjPtr != NULL) {
+ int length;
+
+ string = Tcl_GetStringFromObj(bgObjPtr, &length);
+ if (string[0] == '\0') {
+ bgColor = NULL;
+ } else if ((allowDefault) && (string[0] == 'd') &&
+ (strncmp(string, "defcolor", length) == 0)) {
+ bgColor = COLOR_DEFAULT;
+ } else {
+ bgColor = Tk_AllocColorFromObj(interp, tkwin, bgObjPtr);
+ if (bgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ if (pairPtr->fgColor != NULL) {
+ Tk_FreeColor(pairPtr->fgColor);
+ }
+ if (pairPtr->bgColor != NULL) {
+ Tk_FreeColor(pairPtr->bgColor);
+ }
+ pairPtr->fgColor = fgColor;
+ pairPtr->bgColor = bgColor;
+ return TCL_OK;
+}
+
+void
+Blt_FreeColorPair(ColorPair *pairPtr)
+{
+ if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->bgColor);
+ }
+ if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->fgColor);
+ }
+ pairPtr->bgColor = pairPtr->fgColor = NULL;
+}
+
+static void
+FreeColorPairProc(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+
+ Blt_FreeColorPair(pairPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToColorPairProc --
+ *
+ * Convert the color names into pair of XColor pointers.
+ *
+ * Results:
+ * A standard TCL result. The color pointer is written into the
+ * widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToColorPairProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to return results */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing color */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ long longValue = (long)clientData;
+ int bool;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc > 2) {
+ Tcl_AppendResult(interp, "too many names in colors list",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (objc == 0) {
+ Blt_FreeColorPair(pairPtr);
+ return TCL_OK;
+ }
+ bool = (int)longValue;
+ if (objc == 1) {
+ if (GetColorPair(interp, tkwin, objv[0], NULL, pairPtr, bool)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ if (GetColorPair(interp, tkwin, objv[0], objv[1], pairPtr, bool)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ * Convert the color option value into a string.
+ *
+ * Results:
+ * The static string representing the color option is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static const char *
+NameOfColor(XColor *colorPtr)
+{
+ if (colorPtr == NULL) {
+ return "";
+ } else if (colorPtr == COLOR_DEFAULT) {
+ return "defcolor";
+ } else {
+ return Tk_NameOfColor(colorPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ColorPairToObjProc --
+ *
+ * Convert the color pairs into color names.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ColorPairToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Element information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(NameOfColor(pairPtr->fgColor), -1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(NameOfColor(pairPtr->bgColor), -1));
+ return listObjPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ImageChangedProc
+ *
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ImageChangedProc(
+ ClientData clientData,
+ int x, int y, int w, int h, /* Not used. */
+ int imageWidth, int imageHeight) /* Not used. */
+{
+ Graph *graphPtr;
+ ImageMarker *imPtr = clientData;
+ int isPhoto;
+
+ graphPtr = imPtr->obj.graphPtr;
+ if ((imPtr->picture != NULL) && (imPtr->flags & IMAGE_PHOTO)) {
+ Blt_FreePicture(imPtr->picture);
+ }
+ imPtr->picture = NULL;
+ imPtr->flags &= ~IMAGE_PHOTO;
+ if (Blt_Image_IsDeleted(imPtr->tkImage)) {
+ Tk_FreeImage(imPtr->tkImage);
+ imPtr->tkImage = NULL;
+ return;
+ }
+ imPtr->picture = Blt_GetPictureFromImage(graphPtr->interp, imPtr->tkImage,
+ &isPhoto);
+ if (isPhoto) {
+ imPtr->flags |= IMAGE_PHOTO;
+ }
+ graphPtr->flags |= CACHE_DIRTY;
+ imPtr->flags |= MAP_ITEM;
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*ARGSUSED*/
+static void
+FreePictImageProc(
+ ClientData clientData,
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ ImageMarker *imPtr = (ImageMarker *)widgRec;
+
+ if ((imPtr->picture != NULL) && (imPtr->flags & IMAGE_PHOTO)) {
+ Blt_FreePicture(imPtr->picture);
+ }
+ imPtr->picture = NULL;
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ }
+ imPtr->tkImage = NULL;
+ imPtr->flags &= ~IMAGE_PHOTO;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPictImageProc --
+ *
+ * Given an image name, get the Tk image associated with it.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToPictImageProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to return results */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representation of value. */
+ char *widgRec, /* Widget record. */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ Blt_Picture *picturePtr = (Blt_Picture *)(widgRec + offset);
+ Graph *graphPtr;
+ ImageMarker *imPtr = (ImageMarker *)widgRec;
+ Tk_Image tkImage;
+ const char *name;
+ int isPhoto;
+
+ name = Tcl_GetString(objPtr);
+ tkImage = Tk_GetImage(interp, tkwin, name, ImageChangedProc, imPtr);
+ if (tkImage == NULL) {
+ return TCL_ERROR;
+ }
+ if (imPtr->tkImage != NULL) {
+ Tk_FreeImage(imPtr->tkImage);
+ }
+ imPtr->flags &= ~IMAGE_PHOTO;
+ if (*picturePtr != NULL) {
+ Blt_FreePicture(*picturePtr);
+ }
+ *picturePtr = NULL;
+ imPtr->tkImage = tkImage;
+ graphPtr = imPtr->obj.graphPtr;
+ *picturePtr = Blt_GetPictureFromImage(graphPtr->interp, tkImage, &isPhoto);
+ if (isPhoto) {
+ imPtr->flags |= IMAGE_PHOTO;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PictImageToObjProc --
+ *
+ * Convert the image name into a string Tcl_Obj.
+ *
+ * Results:
+ * The string representation of the image is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PictImageToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp,
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ ImageMarker *imPtr = (ImageMarker *)(widgRec);
+
+ if (imPtr->tkImage == NULL) {
+ return Tcl_NewStringObj("", -1);
+ }
+ return Tcl_NewStringObj(Blt_Image_Name(imPtr->tkImage), -1);
+}
+
+static INLINE int
+IsElementHidden(Marker *markerPtr)
+{
+ Blt_HashEntry *hPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ /* Look up the named element and see if it's hidden */
+ hPtr = Blt_FindHashEntry(&graphPtr->elements.table, markerPtr->elemName);
+ if (hPtr != NULL) {
+ Element *elemPtr;
+
+ elemPtr = Blt_GetHashValue(hPtr);
+ if ((elemPtr->link == NULL) || (elemPtr->flags & HIDE)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * HMap --
+ *
+ * Maps the given graph coordinate value to its axis, returning a window
+ * position. This is a slight variation on the normal Blt_HMap routine.
+ * It treats -Inf as the minimum axis value and Inf as the maximum.
+ *
+ * Results:
+ * Returns a floating point number representing the window coordinate
+ * position on the given axis.
+ *
+ * --------------------------------------------------------------------------
+ */
+static double
+HMap(Axis *axisPtr, double x)
+{
+ if (x == DBL_MAX) {
+ x = 1.0;
+ } else if (x == -DBL_MAX) {
+ x = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (x > 0.0) {
+ x = log10(x);
+ } else if (x < 0.0) {
+ x = 0.0;
+ }
+ }
+ x = NORMALIZE(axisPtr, x);
+ }
+ if (axisPtr->descending) {
+ x = 1.0 - x;
+ }
+ /* Horizontal transformation */
+ return (x * axisPtr->screenRange + axisPtr->screenMin);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VMap --
+ *
+ * Map the given graph coordinate value to its axis, returning a window
+ * position. This is a slight variation on the normal Blt_VMap routine.
+ * It treats -Inf as the minimum axis value and Inf as the maximum.
+ *
+ * Results:
+ * Returns a double precision number representing the window coordinate
+ * position on the given axis.
+ *
+ *---------------------------------------------------------------------------
+ */
+static double
+VMap(Axis *axisPtr, double y)
+{
+ if (y == DBL_MAX) {
+ y = 1.0;
+ } else if (y == -DBL_MAX) {
+ y = 0.0;
+ } else {
+ if (axisPtr->logScale) {
+ if (y > 0.0) {
+ y = log10(y);
+ } else if (y < 0.0) {
+ y = 0.0;
+ }
+ }
+ y = NORMALIZE(axisPtr, y);
+ }
+ if (axisPtr->descending) {
+ y = 1.0 - y;
+ }
+ /* Vertical transformation. */
+ return (((1.0 - y) * axisPtr->screenRange) + axisPtr->screenMin);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapPoint --
+ *
+ * Maps the given graph x,y coordinate values to a window position.
+ *
+ * Results:
+ * Returns a XPoint structure containing the window coordinates of the
+ * given graph x,y coordinate.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Point2d
+MapPoint(
+ Point2d *pointPtr, /* Graph X-Y coordinate. */
+ Axis2d *axesPtr) /* Specifies which axes to use */
+{
+ Point2d result;
+ Graph *graphPtr = axesPtr->y->obj.graphPtr;
+
+ if (graphPtr->inverted) {
+ result.x = HMap(axesPtr->y, pointPtr->y);
+ result.y = VMap(axesPtr->x, pointPtr->x);
+ } else {
+ result.x = HMap(axesPtr->x, pointPtr->x);
+ result.y = VMap(axesPtr->y, pointPtr->y);
+ }
+ return result; /* Result is screen coordinate. */
+}
+
+static Marker *
+CreateMarker(
+ Graph *graphPtr,
+ const char *name,
+ ClassId classId)
+{
+ Marker *markerPtr;
+
+ /* Create the new marker based upon the given type */
+ switch (classId) {
+ case CID_MARKER_BITMAP:
+ markerPtr = CreateBitmapProc(); /* bitmap */
+ break;
+ case CID_MARKER_LINE:
+ markerPtr = CreateLineProc(); /* line */
+ break;
+ case CID_MARKER_IMAGE:
+ markerPtr = CreateImageProc(); /* image */
+ break;
+ case CID_MARKER_TEXT:
+ markerPtr = CreateTextProc(); /* text */
+ break;
+ case CID_MARKER_POLYGON:
+ markerPtr = CreatePolygonProc(); /* polygon */
+ break;
+ case CID_MARKER_WINDOW:
+ markerPtr = CreateWindowProc(); /* window */
+ break;
+ default:
+ return NULL;
+ }
+ markerPtr->obj.graphPtr = graphPtr;
+ markerPtr->drawUnder = FALSE;
+ markerPtr->flags |= MAP_ITEM;
+ markerPtr->obj.name = Blt_AssertStrdup(name);
+ Blt_GraphSetObjectClass(&markerPtr->obj, classId);
+ return markerPtr;
+}
+
+
+static void
+DestroyMarker(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (markerPtr->drawUnder) {
+ /* If the marker to be deleted is currently displayed below the
+ * elements, then backing store needs to be repaired. */
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ /*
+ * Call the marker's type-specific deallocation routine. We do it first
+ * while all the marker fields are still valid.
+ */
+ (*markerPtr->classPtr->freeProc)(markerPtr);
+
+ /* Dump any bindings that might be registered for the marker. */
+ Blt_DeleteBindings(graphPtr->bindTable, markerPtr);
+
+ /* Release all the X resources associated with the marker. */
+ Blt_FreeOptions(markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ graphPtr->display, 0);
+
+ if (markerPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&graphPtr->markers.table,
+ markerPtr->hashPtr);
+ }
+ if (markerPtr->link != NULL) {
+ Blt_Chain_DeleteLink(graphPtr->markers.displayList, markerPtr->link);
+ }
+ if (markerPtr->obj.name != NULL) {
+ Blt_Free(markerPtr->obj.name);
+ }
+ Blt_Free(markerPtr);
+}
+
+static void
+FreeMarker(DestroyData dataPtr)
+{
+ Marker *markerPtr = (Marker *)dataPtr;
+ DestroyMarker(markerPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureBitmapProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a bitmap
+ * marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as bitmap pixmap, colors, rotation,
+ * etc. get set for markerPtr; old resources get freed, if there were
+ * any. The marker is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+ConfigureBitmapProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ if (bmPtr->srcBitmap == None) {
+ return TCL_OK;
+ }
+ bmPtr->angle = FMOD(bmPtr->reqAngle, 360.0);
+ if (bmPtr->angle < 0.0) {
+ bmPtr->angle += 360.0;
+ }
+ gcMask = 0;
+
+ if (bmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = bmPtr->outlineColor->pixel;
+ }
+
+ if (bmPtr->fillColor != NULL) {
+ /* Opaque bitmap: both foreground and background (fill) colors
+ * are used. */
+ gcValues.background = bmPtr->fillColor->pixel;
+ gcMask |= GCBackground;
+ } else {
+ /* Transparent bitmap: set the clip mask to the current bitmap. */
+ gcValues.clip_mask = bmPtr->srcBitmap;
+ gcMask |= GCClipMask;
+ }
+
+ /*
+ * This is technically a shared GC, but we're going to set/change the clip
+ * origin anyways before we draw the bitmap. This relies on the fact that
+ * no other client will be allocated this GC with the GCClipMask set to
+ * this particular bitmap.
+ */
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ bmPtr->gc = newGC;
+
+ /* Create the background GC containing the fill color. */
+
+ if (bmPtr->fillColor != NULL) {
+ gcValues.foreground = bmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ bmPtr->fillGC = newGC;
+ }
+
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+#ifdef notdef
+static void
+PrintPolyPoint(char *mesg, Point2d *points, int nPoints)
+{
+ int i;
+
+ fprintf(stderr, "%s:\t\tpoint[0]=%g,%g\n", mesg, points[0].x, points[0].y);
+ for (i = 1; i < nPoints; i++) {
+ fprintf(stderr, "\t\tpoint[%d]=%g,%g\n", i, points[i].x, points[i].y);
+ }
+}
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapBitmapProc --
+ *
+ * This procedure gets called each time the layout of the graph changes.
+ * The x, y window coordinates of the bitmap marker are saved in the
+ * marker structure.
+ *
+ * Additionly, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in the
+ * private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was set, the
+ * GC stipple origins are changed to calculated window coordinates.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapBitmapProc(Marker *markerPtr)
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Region2d extents;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ Point2d anchorPt;
+ Point2d corner1, corner2;
+ int destWidth, destHeight;
+ int srcWidth, srcHeight;
+ int i;
+
+ if (bmPtr->srcBitmap == None) {
+ return;
+ }
+ if (bmPtr->destBitmap != None) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ bmPtr->destBitmap = None;
+ }
+ /*
+ * Collect the coordinates. The number of coordinates will determine the
+ * calculations to be made.
+ *
+ * x1 y1 A single pair of X-Y coordinates. They represent
+ * the anchor position of the bitmap.
+ *
+ * x1 y1 x2 y2 Two pairs of X-Y coordinates. They represent
+ * two opposite corners of a bounding rectangle. The
+ * bitmap is possibly rotated and scaled to fit into
+ * this box.
+ *
+ */
+ Tk_SizeOfBitmap(graphPtr->display, bmPtr->srcBitmap, &srcWidth,
+ &srcHeight);
+ corner1 = MapPoint(markerPtr->worldPts, &markerPtr->axes);
+ if (markerPtr->nWorldPts > 1) {
+ double hold;
+
+ corner2 = MapPoint(markerPtr->worldPts + 1, &markerPtr->axes);
+ /* Flip the corners if necessary */
+ if (corner1.x > corner2.x) {
+ hold = corner1.x, corner1.x = corner2.x, corner2.x = hold;
+ }
+ if (corner1.y > corner2.y) {
+ hold = corner1.y, corner1.y = corner2.y, corner2.y = hold;
+ }
+ } else {
+ corner2.x = corner1.x + srcWidth - 1;
+ corner2.y = corner1.y + srcHeight - 1;
+ }
+ destWidth = (int)(corner2.x - corner1.x) + 1;
+ destHeight = (int)(corner2.y - corner1.y) + 1;
+
+ if (markerPtr->nWorldPts == 1) {
+ anchorPt = Blt_AnchorPoint(corner1.x, corner1.y, (double)destWidth,
+ (double)destHeight, bmPtr->anchor);
+ } else {
+ anchorPt = corner1;
+ }
+ anchorPt.x += markerPtr->xOffset;
+ anchorPt.y += markerPtr->yOffset;
+
+ /* Check if the bitmap sits at least partially in the plot area. */
+ extents.left = anchorPt.x;
+ extents.top = anchorPt.y;
+ extents.right = anchorPt.x + destWidth - 1;
+ extents.bottom = anchorPt.y + destHeight - 1;
+ markerPtr->clipped = BoxesDontOverlap(graphPtr, &extents);
+ if (markerPtr->clipped) {
+ return; /* Bitmap is offscreen. Don't generate
+ * rotated or scaled bitmaps. */
+ }
+
+ /*
+ * Scale the bitmap if necessary. It's a little tricky because we only
+ * want to scale what's visible on the screen, not the entire bitmap.
+ */
+ if ((bmPtr->angle != 0.0f) || (destWidth != srcWidth) ||
+ (destHeight != srcHeight)) {
+ int regionX, regionY, regionWidth, regionHeight;
+ double left, right, top, bottom;
+
+ /* Ignore parts of the bitmap outside of the plot area. */
+ left = MAX(graphPtr->left, extents.left);
+ right = MIN(graphPtr->right, extents.right);
+ top = MAX(graphPtr->top, extents.top);
+ bottom = MIN(graphPtr->bottom, extents.bottom);
+
+ /* Determine the portion of the scaled bitmap to display. */
+ regionX = regionY = 0;
+ if (graphPtr->left > extents.left) {
+ regionX = (int)(graphPtr->left - extents.left);
+ }
+ if (graphPtr->top > extents.top) {
+ regionY = (int)(graphPtr->top - extents.top);
+ }
+ regionWidth = (int)(right - left) + 1;
+ regionHeight = (int)(bottom - top) + 1;
+
+ anchorPt.x = left;
+ anchorPt.y = top;
+ bmPtr->destBitmap = Blt_ScaleRotateBitmapArea(graphPtr->tkwin,
+ bmPtr->srcBitmap, srcWidth, srcHeight, regionX, regionY,
+ regionWidth, regionHeight, destWidth, destHeight, bmPtr->angle);
+ bmPtr->destWidth = regionWidth;
+ bmPtr->destHeight = regionHeight;
+ } else {
+ bmPtr->destWidth = srcWidth;
+ bmPtr->destHeight = srcHeight;
+ bmPtr->destBitmap = None;
+ }
+ bmPtr->anchorPt = anchorPt;
+ {
+ double xScale, yScale;
+ double tx, ty;
+ double rotWidth, rotHeight;
+ Point2d polygon[5];
+ int n;
+
+ /*
+ * Compute a polygon to represent the background area of the bitmap.
+ * This is needed for backgrounds of arbitrarily rotated bitmaps. We
+ * also use it to print a background in PostScript.
+ */
+ Blt_GetBoundingBox(srcWidth, srcHeight, bmPtr->angle, &rotWidth,
+ &rotHeight, polygon);
+ xScale = (double)destWidth / rotWidth;
+ yScale = (double)destHeight / rotHeight;
+
+ /*
+ * Adjust each point of the polygon. Both scale it to the new size and
+ * translate it to the actual screen position of the bitmap.
+ */
+ tx = extents.left + destWidth * 0.5;
+ ty = extents.top + destHeight * 0.5;
+ for (i = 0; i < 4; i++) {
+ polygon[i].x = (polygon[i].x * xScale) + tx;
+ polygon[i].y = (polygon[i].y * yScale) + ty;
+ }
+ Blt_GraphExtents(graphPtr, &extents);
+ n = Blt_PolyRectClip(&extents, polygon, 4, bmPtr->outline);
+ assert(n <= MAX_OUTLINE_POINTS);
+ if (n < 3) {
+ memcpy(&bmPtr->outline, polygon, sizeof(Point2d) * 4);
+ bmPtr->nOutlinePts = 4;
+ } else {
+ bmPtr->nOutlinePts = n;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PointInBitmapProc --
+ *
+ * Indicates if the given point is over the bitmap marker. The area of
+ * the bitmap is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the bitmap marker, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+PointInBitmapProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (bmPtr->srcBitmap == None) {
+ return 0;
+ }
+ if (bmPtr->angle != 0.0f) {
+ Point2d points[MAX_OUTLINE_POINTS];
+ int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap and see
+ * if the point is inside of it.
+ */
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPt.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPt.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, bmPtr->nOutlinePts);
+ }
+ return ((samplePtr->x >= bmPtr->anchorPt.x) &&
+ (samplePtr->x < (bmPtr->anchorPt.x + bmPtr->destWidth)) &&
+ (samplePtr->y >= bmPtr->anchorPt.y) &&
+ (samplePtr->y < (bmPtr->anchorPt.y + bmPtr->destHeight)));
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInBitmapProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInBitmapProc(Marker *markerPtr, Region2d *extsPtr, int enclosed)
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+
+ if (markerPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (bmPtr->angle != 0.0f) {
+ Point2d points[MAX_OUTLINE_POINTS];
+ int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap and see
+ * if the point is inside of it.
+ */
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ points[i].x = bmPtr->outline[i].x + bmPtr->anchorPt.x;
+ points[i].y = bmPtr->outline[i].y + bmPtr->anchorPt.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, bmPtr->nOutlinePts,
+ enclosed);
+ }
+ if (enclosed) {
+ return ((bmPtr->anchorPt.x >= extsPtr->left) &&
+ (bmPtr->anchorPt.y >= extsPtr->top) &&
+ ((bmPtr->anchorPt.x + bmPtr->destWidth) <= extsPtr->right) &&
+ ((bmPtr->anchorPt.y + bmPtr->destHeight) <= extsPtr->bottom));
+ }
+ return !((bmPtr->anchorPt.x >= extsPtr->right) ||
+ (bmPtr->anchorPt.y >= extsPtr->bottom) ||
+ ((bmPtr->anchorPt.x + bmPtr->destWidth) <= extsPtr->left) ||
+ ((bmPtr->anchorPt.y + bmPtr->destHeight) <= extsPtr->top));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawBitmapProc --
+ *
+ * Draws the bitmap marker that have a transparent of filled background.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current mode.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawBitmapProc(Marker *markerPtr, Drawable drawable)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ double rangle;
+ Pixmap bitmap;
+
+ bitmap = GETBITMAP(bmPtr);
+ if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) {
+ return;
+ }
+ rangle = FMOD(bmPtr->angle, 90.0);
+ if ((bmPtr->fillColor == NULL) || (rangle != 0.0)) {
+
+ /*
+ * If the bitmap is rotated and a filled background is required, then
+ * a filled polygon is drawn before the bitmap.
+ */
+ if (bmPtr->fillColor != NULL) {
+ int i;
+ XPoint polygon[MAX_OUTLINE_POINTS];
+
+ for (i = 0; i < bmPtr->nOutlinePts; i++) {
+ polygon[i].x = (short int)bmPtr->outline[i].x;
+ polygon[i].y = (short int)bmPtr->outline[i].y;
+ }
+ XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC,
+ polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin);
+ }
+ XSetClipMask(graphPtr->display, bmPtr->gc, bitmap);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPt.x,
+ (int)bmPtr->anchorPt.y);
+ } else {
+ XSetClipMask(graphPtr->display, bmPtr->gc, None);
+ XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable, bmPtr->gc, 0, 0,
+ bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPt.x,
+ (int)bmPtr->anchorPt.y, 1);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BitmapToPostscriptProc --
+ *
+ * Generates PostScript to print a bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+BitmapToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Pixmap bitmap;
+
+ bitmap = GETBITMAP(bmPtr);
+ if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) {
+ return; /* No bitmap to display. */
+ }
+ if (bmPtr->fillColor != NULL) {
+ Blt_Ps_XSetBackground(ps, bmPtr->fillColor);
+ Blt_Ps_XFillPolygon(ps, bmPtr->outline, 4);
+ }
+ Blt_Ps_XSetForeground(ps, bmPtr->outlineColor);
+
+ Blt_Ps_Format(ps,
+ " gsave\n %g %g translate\n %d %d scale\n",
+ bmPtr->anchorPt.x, bmPtr->anchorPt.y + bmPtr->destHeight,
+ bmPtr->destWidth, -bmPtr->destHeight);
+ Blt_Ps_Format(ps, " %d %d true [%d 0 0 %d 0 %d] {",
+ bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth,
+ -bmPtr->destHeight, bmPtr->destHeight);
+ Blt_Ps_XSetBitmapData(ps, graphPtr->display, bitmap,
+ bmPtr->destWidth, bmPtr->destHeight);
+ Blt_Ps_VarAppend(ps,
+ " } imagemask\n",
+ "grestore\n", (char *)NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeBitmapProc --
+ *
+ * Releases the memory and attributes of the bitmap marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Bitmap attributes (GCs, colors, bitmap, etc) get destroyed. Memory is
+ * released, X resources are freed, and the graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeBitmapProc(Marker *markerPtr)
+{
+ BitmapMarker *bmPtr = (BitmapMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (bmPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->gc);
+ }
+ if (bmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, bmPtr->fillGC);
+ }
+ if (bmPtr->destBitmap != None) {
+ Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateBitmapProc --
+ *
+ * Allocate memory and initialize methods for the new bitmap marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the bitmap marker structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Marker *
+CreateBitmapProc(void)
+{
+ BitmapMarker *bmPtr;
+
+ bmPtr = Blt_AssertCalloc(1, sizeof(BitmapMarker));
+ bmPtr->classPtr = &bitmapMarkerClass;
+ return (Marker *)bmPtr;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureImageProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a image
+ * marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as image pixmap, colors, rotation,
+ * etc. get set for markerPtr; old resources get freed, if there were
+ * any. The marker is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureImageProc(Marker *markerPtr)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ Blt_Painter painter;
+ GC newGC;
+
+ newGC = Tk_GetGC(graphPtr->tkwin, 0L, (XGCValues *)NULL);
+ if (imPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, imPtr->gc);
+ }
+ imPtr->gc = newGC;
+
+ painter = Blt_GetPainter(graphPtr->tkwin, 1.0);
+ if (imPtr->painter != NULL) {
+ Blt_FreePainter(painter);
+ }
+ imPtr->painter = painter;
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapImageProc --
+ *
+ * This procedure gets called each time the layout of the graph changes.
+ * The x, y window coordinates of the image marker are saved in the
+ * marker structure.
+ *
+ * In addition, if no background color was specified, the
+ * GCTileStipXOrigin and GCTileStipYOrigin attributes will not set in the
+ * private GC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window coordinates are saved and if no background color was set, the
+ * GC stipple origins are changed to calculated window coordinates.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapImageProc(Marker *markerPtr)
+{
+ Region2d extents;
+ Graph *graphPtr;
+ ImageMarker *imPtr;
+ Point2d anchorPt;
+ Point2d c1, c2;
+ int newWidth, newHeight;
+ int srcWidth, srcHeight;
+ int x, y, w, h;
+ int left, right, top, bottom;
+
+ imPtr = (ImageMarker *)markerPtr;
+ if (imPtr->picture == NULL) {
+ return;
+ }
+ if (imPtr->scaled != NULL) {
+ Blt_FreePicture(imPtr->scaled);
+ imPtr->scaled = NULL;
+ }
+ graphPtr = markerPtr->obj.graphPtr;
+ c1 = MapPoint(markerPtr->worldPts, &markerPtr->axes);
+
+ imPtr->width = srcWidth = Blt_PictureWidth(imPtr->picture);
+ imPtr->height = srcHeight = Blt_PictureHeight(imPtr->picture);
+
+ if ((srcWidth == 0) || (srcHeight == 0)) {
+ markerPtr->clipped = TRUE;
+ return; /* Empty image. */
+ }
+ if (markerPtr->nWorldPts > 1) {
+ double hold;
+
+ c2 = MapPoint(markerPtr->worldPts + 1, &markerPtr->axes);
+ /* Flip the corners if necessary */
+ if (c1.x > c2.x) {
+ hold = c1.x, c1.x = c2.x, c2.x = hold;
+ }
+ if (c1.y > c2.y) {
+ hold = c1.y, c1.y = c2.y, c2.y = hold;
+ }
+ } else {
+ c2.x = c1.x + srcWidth - 1;
+ c2.y = c1.y + srcHeight - 1;
+ }
+ newWidth = (int)(c2.x - c1.x) + 1;
+ newHeight = (int)(c2.y - c1.y) + 1;
+
+ if (markerPtr->nWorldPts == 1) {
+ anchorPt = Blt_AnchorPoint(c1.x, c1.y, (double)newWidth,
+ (double)newHeight, imPtr->anchor);
+ } else {
+ anchorPt = c1;
+ }
+ anchorPt.x += markerPtr->xOffset;
+ anchorPt.y += markerPtr->yOffset;
+
+ /* Check if the image sits at least partially in the plot area. */
+ extents.left = anchorPt.x;
+ extents.top = anchorPt.y;
+ extents.right = anchorPt.x + newWidth - 1;
+ extents.bottom = anchorPt.y + newHeight - 1;
+
+ markerPtr->clipped = BoxesDontOverlap(graphPtr, &extents);
+ if (markerPtr->clipped) {
+ return; /* Image is offscreen. Don't generate
+ * rotated or scaled images. */
+ }
+
+ /* Determine the extents of the subimage inside of the destination
+ * image. */
+ left = MAX((int)extents.left, graphPtr->left);
+ top = MAX((int)extents.top, graphPtr->top);
+ right = MIN((int)extents.right, graphPtr->right);
+ bottom = MIN((int)extents.bottom, graphPtr->bottom);
+
+ /* Reset image location and coordinates to that of the region */
+ anchorPt.x = left;
+ anchorPt.y = top;
+
+ x = y = 0;
+ if (graphPtr->left > (int)extents.left) {
+ x = graphPtr->left - (int)extents.left;
+ }
+ if (graphPtr->top > (int)extents.top) {
+ y = graphPtr->top - (int)extents.top;
+ }
+ w = (int)(right - left + 1);
+ h = (int)(bottom - top + 1);
+
+ if (markerPtr->nWorldPts > 1) {
+ Blt_Picture scaled;
+
+ scaled = Blt_ScalePictureArea(imPtr->picture, x, y, w, h,
+ newWidth, newHeight);
+ imPtr->scaled = scaled;
+ imPtr->pictX = 0;
+ imPtr->pictY = 0;
+ } else {
+ imPtr->pictX = x;
+ imPtr->pictY = y;
+ }
+ imPtr->width = newWidth;
+ imPtr->height = newHeight;
+ imPtr->anchorPt = anchorPt;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PointInWindowProc --
+ *
+ * Indicates if the given point is over the window marker. The area of
+ * the window is the rectangle.
+ *
+ * Results:
+ * Returns 1 is the point is over the window marker, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+PointInImageProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ double left, right, top, bottom;
+
+ left = imPtr->anchorPt.x;
+ right = imPtr->anchorPt.x + imPtr->width;
+ top = imPtr->anchorPt.y;
+ bottom = imPtr->anchorPt.y + imPtr->height;
+
+ return ((samplePtr->x >= left) && (samplePtr->x < right) &&
+ (samplePtr->y >= top) && (samplePtr->y < bottom));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInImageProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInImageProc(Marker *markerPtr, Region2d *regPtr, int enclosed)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+
+ if (markerPtr->nWorldPts > 0) {
+ double left, right, top, bottom;
+
+ left = imPtr->anchorPt.x;
+ right = imPtr->anchorPt.x + imPtr->width;
+ top = imPtr->anchorPt.y;
+ bottom = imPtr->anchorPt.y + imPtr->height;
+ if (enclosed) {
+ return ((left >= regPtr->left) && (top >= regPtr->top) &&
+ (right <= regPtr->right) && (bottom <= regPtr->bottom));
+ }
+ return !((left >= regPtr->right) || (top >= regPtr->bottom) ||
+ (right <= regPtr->left) || (bottom <= regPtr->top));
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawImageProc --
+ *
+ * This procedure is invoked to draw a image marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * GC stipple origins are changed to current window coordinates.
+ * Commands are output to X to draw the marker in its current mode.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawImageProc(Marker *markerPtr, Drawable drawable)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Blt_Picture picture;
+
+ picture = (imPtr->scaled != NULL) ? imPtr->scaled : imPtr->picture;
+ if (picture != NULL) {
+ Blt_PaintPictureWithBlend(imPtr->painter, drawable, picture,
+ imPtr->pictX, imPtr->pictY, imPtr->width, imPtr->height,
+ (int)imPtr->anchorPt.x, (int)imPtr->anchorPt.y, 0, 0.4);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ImageToPostscriptProc --
+ *
+ * This procedure is invoked to print a image marker.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ImageToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Blt_Picture picture;
+
+ picture = (imPtr->scaled != NULL) ? imPtr->scaled : imPtr->picture;
+ if (picture != NULL) {
+ Blt_Ps_DrawPicture(ps, picture, imPtr->anchorPt.x, imPtr->anchorPt.y);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeImageProc --
+ *
+ * Destroys the structure containing the attributes of the image marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Image attributes (GCs, colors, image, etc) get destroyed. Memory is
+ * released, X resources are freed, and the graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeImageProc(Marker *markerPtr)
+{
+ ImageMarker *imPtr = (ImageMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (imPtr->painter != NULL) {
+ Blt_FreePainter(imPtr->painter);
+ }
+ if (imPtr->scaled != NULL) {
+ Blt_FreePicture(imPtr->scaled);
+ }
+ if (imPtr->gc != NULL) {
+ Tk_FreeGC(graphPtr->display, imPtr->gc);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateImageProc --
+ *
+ * Allocate memory and initialize methods for the new image marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the image marker structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Marker *
+CreateImageProc(void)
+{
+ ImageMarker *imPtr;
+
+ imPtr = Blt_AssertCalloc(1, sizeof(ImageMarker));
+ imPtr->classPtr = &imageMarkerClass;
+ return (Marker *)imPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureTextProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a text marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font, etc. get
+ * set for markerPtr; old resources get freed, if there were any. The
+ * marker is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureTextProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ tmPtr->style.angle = (float)FMOD(tmPtr->style.angle, 360.0);
+ if (tmPtr->style.angle < 0.0f) {
+ tmPtr->style.angle += 360.0f;
+ }
+ newGC = NULL;
+ if (tmPtr->fillColor != NULL) {
+ gcMask = GCForeground;
+ gcValues.foreground = tmPtr->fillColor->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (tmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, tmPtr->fillGC);
+ }
+ tmPtr->fillGC = newGC;
+
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapTextProc --
+ *
+ * Calculate the layout position for a text marker. Positional information
+ * is saved in the marker. If the text is rotated, a bitmap containing the
+ * text is created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If no background color has been specified, the GC stipple origins are
+ * changed to current window coordinates. For both rotated and
+ * non-rotated text, if any old bitmap is leftover, it is freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapTextProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Region2d extents;
+ Point2d anchorPt;
+ int i;
+ unsigned int w, h;
+ double rw, rh;
+
+ tmPtr->width = tmPtr->height = 0;
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ Blt_Ts_GetExtents(&tmPtr->style, tmPtr->string, &w, &h);
+ Blt_GetBoundingBox(w, h, tmPtr->style.angle, &rw, &rh, tmPtr->outline);
+ tmPtr->width = ROUND(rw);
+ tmPtr->height = ROUND(rh);
+ for (i = 0; i < 4; i++) {
+ tmPtr->outline[i].x += ROUND(rw * 0.5);
+ tmPtr->outline[i].y += ROUND(rh * 0.5);
+ }
+ tmPtr->outline[4].x = tmPtr->outline[0].x;
+ tmPtr->outline[4].y = tmPtr->outline[0].y;
+ anchorPt = MapPoint(markerPtr->worldPts, &markerPtr->axes);
+ anchorPt = Blt_AnchorPoint(anchorPt.x, anchorPt.y, (double)(tmPtr->width),
+ (double)(tmPtr->height), tmPtr->anchor);
+ anchorPt.x += markerPtr->xOffset;
+ anchorPt.y += markerPtr->yOffset;
+ /*
+ * Determine the bounding box of the text and test to see if it is at
+ * least partially contained within the plotting area.
+ */
+ extents.left = anchorPt.x;
+ extents.top = anchorPt.y;
+ extents.right = anchorPt.x + tmPtr->width - 1;
+ extents.bottom = anchorPt.y + tmPtr->height - 1;
+ markerPtr->clipped = BoxesDontOverlap(graphPtr, &extents);
+ tmPtr->anchorPt = anchorPt;
+
+}
+
+static int
+PointInTextProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return 0;
+ }
+ if (tmPtr->style.angle != 0.0f) {
+ Point2d points[5];
+ int i;
+
+ /*
+ * Figure out the bounding polygon (isolateral) for the text and see
+ * if the point is inside of it.
+ */
+ for (i = 0; i < 5; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPt.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPt.y;
+ }
+ return Blt_PointInPolygon(samplePtr, points, 5);
+ }
+ return ((samplePtr->x >= tmPtr->anchorPt.x) &&
+ (samplePtr->x < (tmPtr->anchorPt.x + tmPtr->width)) &&
+ (samplePtr->y >= tmPtr->anchorPt.y) &&
+ (samplePtr->y < (tmPtr->anchorPt.y + tmPtr->height)));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInTextProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInTextProc(Marker *markerPtr, Region2d *extsPtr, int enclosed)
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (markerPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (tmPtr->style.angle != 0.0f) {
+ Point2d points[5];
+ int i;
+
+ /*
+ * Generate the bounding polygon (isolateral) for the bitmap and see
+ * if the point is inside of it.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPt.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPt.y;
+ }
+ return Blt_RegionInPolygon(extsPtr, points, 4, enclosed);
+ }
+ if (enclosed) {
+ return ((tmPtr->anchorPt.x >= extsPtr->left) &&
+ (tmPtr->anchorPt.y >= extsPtr->top) &&
+ ((tmPtr->anchorPt.x + tmPtr->width) <= extsPtr->right) &&
+ ((tmPtr->anchorPt.y + tmPtr->height) <= extsPtr->bottom));
+ }
+ return !((tmPtr->anchorPt.x >= extsPtr->right) ||
+ (tmPtr->anchorPt.y >= extsPtr->bottom) ||
+ ((tmPtr->anchorPt.x + tmPtr->width) <= extsPtr->left) ||
+ ((tmPtr->anchorPt.y + tmPtr->height) <= extsPtr->top));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawTextProc --
+ *
+ * Draws the text marker on the graph.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to draw the marker in its current mode.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawTextProc(Marker *markerPtr, Drawable drawable)
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ XPoint points[4];
+ int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by filling a bounding
+ * polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = (short int)(tmPtr->outline[i].x + tmPtr->anchorPt.x);
+ points[i].y = (short int)(tmPtr->outline[i].y + tmPtr->anchorPt.y);
+ }
+ XFillPolygon(graphPtr->display, drawable, tmPtr->fillGC, points, 4,
+ Convex, CoordModeOrigin);
+ }
+ if (tmPtr->style.color != NULL) {
+ Blt_Ts_DrawText(graphPtr->tkwin, drawable, tmPtr->string, -1,
+ &tmPtr->style, (int)tmPtr->anchorPt.x, (int)tmPtr->anchorPt.y);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TextToPostscriptProc --
+ *
+ * Outputs PostScript commands to draw a text marker at a given x,y
+ * coordinate, rotation, anchor, and font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript font and color settings are changed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+TextToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+
+ if (tmPtr->string == NULL) {
+ return;
+ }
+ if (tmPtr->fillGC != NULL) {
+ Point2d points[4];
+ int i;
+
+ /*
+ * Simulate the rotated background of the bitmap by filling a bounding
+ * polygon with the background color.
+ */
+ for (i = 0; i < 4; i++) {
+ points[i].x = tmPtr->outline[i].x + tmPtr->anchorPt.x;
+ points[i].y = tmPtr->outline[i].y + tmPtr->anchorPt.y;
+ }
+ Blt_Ps_XSetBackground(ps, tmPtr->fillColor);
+ Blt_Ps_XFillPolygon(ps, points, 4);
+ }
+ Blt_Ps_DrawText(ps, tmPtr->string, &tmPtr->style, tmPtr->anchorPt.x,
+ tmPtr->anchorPt.y);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeTextProc --
+ *
+ * Destroys the structure containing the attributes of the text marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Text attributes (GCs, colors, stipple, font, etc) get destroyed.
+ * Memory is released, X resources are freed, and the graph is redrawn.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeTextProc(Marker *markerPtr)
+{
+ TextMarker *tmPtr = (TextMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ Blt_Ts_FreeStyle(graphPtr->display, &tmPtr->style);
+}
+
+/*
+ *---------------------------------------------------------------------------
+
+ * CreateTextProc --
+ *
+ * Allocate memory and initialize methods for the new text marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the text marker structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Marker *
+CreateTextProc(void)
+{
+ TextMarker *tmPtr;
+
+ tmPtr = Blt_AssertCalloc(1, sizeof(TextMarker));
+ tmPtr->classPtr = &textMarkerClass;
+ Blt_Ts_InitStyle(tmPtr->style);
+ tmPtr->style.anchor = TK_ANCHOR_NW;
+ tmPtr->style.padLeft = tmPtr->style.padRight = 4;
+ tmPtr->style.padTop = tmPtr->style.padBottom = 4;
+ return (Marker *)tmPtr;
+}
+
+static Tk_EventProc ChildEventProc;
+static Tk_GeomRequestProc ChildGeometryProc;
+static Tk_GeomLostSlaveProc ChildCustodyProc;
+static Tk_GeomMgr winMarkerMgrInfo =
+{
+ (char *)"graph", /* Name of geometry manager used by
+ * winfo */
+ ChildGeometryProc, /* Procedure to for new geometry
+ * requests. */
+ ChildCustodyProc, /* Procedure when window is taken
+ * away. */
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureWindowProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a window
+ * marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as window pathname, placement,
+ * etc. get set for markerPtr; old resources get freed, if there were
+ * any. The marker is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureWindowProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Tk_Window tkwin;
+
+ if (wmPtr->childName == NULL) {
+ return TCL_OK;
+ }
+ tkwin = Tk_NameToWindow(graphPtr->interp, wmPtr->childName,
+ graphPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_Parent(tkwin) != graphPtr->tkwin) {
+ Tcl_AppendResult(graphPtr->interp, "\"", wmPtr->childName,
+ "\" is not a child of \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (tkwin != wmPtr->child) {
+ if (wmPtr->child != NULL) {
+ Tk_DeleteEventHandler(wmPtr->child, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->child, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_UnmapWindow(wmPtr->child);
+ }
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildEventProc,
+ wmPtr);
+ Tk_ManageGeometry(tkwin, &winMarkerMgrInfo, wmPtr);
+ }
+ wmPtr->child = tkwin;
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapWindowProc --
+ *
+ * Calculate the layout position for a window marker. Positional
+ * information is saved in the marker.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapWindowProc(Marker *markerPtr)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ Point2d anchorPt;
+ Region2d extents;
+ int width, height;
+
+ if (wmPtr->child == (Tk_Window)NULL) {
+ return;
+ }
+ anchorPt = MapPoint(markerPtr->worldPts, &markerPtr->axes);
+
+ width = Tk_ReqWidth(wmPtr->child);
+ height = Tk_ReqHeight(wmPtr->child);
+ if (wmPtr->reqWidth > 0) {
+ width = wmPtr->reqWidth;
+ }
+ if (wmPtr->reqHeight > 0) {
+ height = wmPtr->reqHeight;
+ }
+ wmPtr->anchorPt = Blt_AnchorPoint(anchorPt.x, anchorPt.y, (double)width,
+ (double)height, wmPtr->anchor);
+ wmPtr->anchorPt.x += markerPtr->xOffset;
+ wmPtr->anchorPt.y += markerPtr->yOffset;
+ wmPtr->width = width;
+ wmPtr->height = height;
+
+ /*
+ * Determine the bounding box of the window and test to see if it is at
+ * least partially contained within the plotting area.
+ */
+ extents.left = wmPtr->anchorPt.x;
+ extents.top = wmPtr->anchorPt.y;
+ extents.right = wmPtr->anchorPt.x + wmPtr->width - 1;
+ extents.bottom = wmPtr->anchorPt.y + wmPtr->height - 1;
+ markerPtr->clipped = BoxesDontOverlap(graphPtr, &extents);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PointInWindowProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+PointInWindowProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ return ((samplePtr->x >= wmPtr->anchorPt.x) &&
+ (samplePtr->x < (wmPtr->anchorPt.x + wmPtr->width)) &&
+ (samplePtr->y >= wmPtr->anchorPt.y) &&
+ (samplePtr->y < (wmPtr->anchorPt.y + wmPtr->height)));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInWindowProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInWindowProc(Marker *markerPtr, Region2d *extsPtr, int enclosed)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (markerPtr->nWorldPts < 1) {
+ return FALSE;
+ }
+ if (enclosed) {
+ return ((wmPtr->anchorPt.x >= extsPtr->left) &&
+ (wmPtr->anchorPt.y >= extsPtr->top) &&
+ ((wmPtr->anchorPt.x + wmPtr->width) <= extsPtr->right) &&
+ ((wmPtr->anchorPt.y + wmPtr->height) <= extsPtr->bottom));
+ }
+ return !((wmPtr->anchorPt.x >= extsPtr->right) ||
+ (wmPtr->anchorPt.y >= extsPtr->bottom) ||
+ ((wmPtr->anchorPt.x + wmPtr->width) <= extsPtr->left) ||
+ ((wmPtr->anchorPt.y + wmPtr->height) <= extsPtr->top));
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawWindowProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+DrawWindowProc(Marker *markerPtr, Drawable drawable)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->child == NULL) {
+ return;
+ }
+ if ((wmPtr->height != Tk_Height(wmPtr->child)) ||
+ (wmPtr->width != Tk_Width(wmPtr->child)) ||
+ ((int)wmPtr->anchorPt.x != Tk_X(wmPtr->child)) ||
+ ((int)wmPtr->anchorPt.y != Tk_Y(wmPtr->child))) {
+ Tk_MoveResizeWindow(wmPtr->child, (int)wmPtr->anchorPt.x,
+ (int)wmPtr->anchorPt.y, wmPtr->width, wmPtr->height);
+ }
+ if (!Tk_IsMapped(wmPtr->child)) {
+ Tk_MapWindow(wmPtr->child);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * WindowToPostscriptProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+WindowToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->child == NULL) {
+ return;
+ }
+ if (Tk_IsMapped(wmPtr->child)) {
+ Blt_Ps_XDrawWindow(ps, wmPtr->child, wmPtr->anchorPt.x,
+ wmPtr->anchorPt.y);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeWindowProc --
+ *
+ * Destroys the structure containing the attributes of the window *
+ * marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window is destroyed and removed from the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeWindowProc(Marker *markerPtr)
+{
+ WindowMarker *wmPtr = (WindowMarker *)markerPtr;
+
+ if (wmPtr->child != NULL) {
+ Tk_DeleteEventHandler(wmPtr->child, StructureNotifyMask,
+ ChildEventProc, wmPtr);
+ Tk_ManageGeometry(wmPtr->child, (Tk_GeomMgr *) 0, (ClientData)0);
+ Tk_DestroyWindow(wmPtr->child);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateWindowProc --
+ *
+ * Allocate memory and initialize methods for the new window marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the window marker structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Marker *
+CreateWindowProc(void)
+{
+ WindowMarker *wmPtr;
+
+ wmPtr = Blt_AssertCalloc(1, sizeof(WindowMarker));
+ wmPtr->classPtr = &windowMarkerClass;
+ return (Marker *)wmPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ChildEventProc --
+ *
+ * This procedure is invoked whenever StructureNotify events occur for a
+ * window that's managed as part of a graph window marker. This
+ * procedure's only purpose is to clean up when windows are deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is disassociated from the window item when it is
+ * deleted.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ChildEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ wmPtr->child = NULL;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ChildGeometryProc --
+ *
+ * This procedure is invoked whenever a window that's associated with a
+ * window item changes its requested dimensions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size and location on the window of the window may change,
+ * depending on the options specified for the window item.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+ChildGeometryProc(ClientData clientData, Tk_Window tkwin)
+{
+ WindowMarker *wmPtr = clientData;
+
+ if (wmPtr->reqWidth == 0) {
+ wmPtr->width = Tk_ReqWidth(tkwin);
+ }
+ if (wmPtr->reqHeight == 0) {
+ wmPtr->height = Tk_ReqHeight(tkwin);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ChildCustodyProc --
+ *
+ * This procedure is invoked when an embedded window has been stolen by
+ * another geometry manager. The information and memory associated with
+ * the widget is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for the graph to be redrawn without the embedded widget at
+ * the next idle point.
+ *
+ *---------------------------------------------------------------------------
+ */
+ /* ARGSUSED */
+static void
+ChildCustodyProc(ClientData clientData, Tk_Window tkwin)
+{
+ Marker *markerPtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = markerPtr->obj.graphPtr;
+ markerPtr->flags |= DELETE_PENDING;
+ Tcl_EventuallyFree(markerPtr, FreeMarker);
+ /*
+ * Not really needed. We should get an Expose event when the child window
+ * is unmapped.
+ */
+ Blt_EventuallyRedrawGraph(graphPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapLineProc --
+ *
+ * Calculate the layout position for a line marker. Positional
+ * information is saved in the marker. The line positions are stored in
+ * an array of points (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapLineProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ Point2d *srcPtr, *pend;
+ Segment2d *segments, *segPtr;
+ Point2d p, q;
+ Region2d extents;
+
+ lmPtr->nSegments = 0;
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+ if (markerPtr->nWorldPts < 2) {
+ return; /* Too few points */
+ }
+ Blt_GraphExtents(graphPtr, &extents);
+
+ /*
+ * Allow twice the number of world coordinates. The line will represented
+ * as series of line segments, not one continous polyline. This is
+ * because clipping against the plot area may chop the line into several
+ * disconnected segments.
+ */
+ segments = Blt_AssertMalloc(markerPtr->nWorldPts * sizeof(Segment2d));
+ srcPtr = markerPtr->worldPts;
+ p = MapPoint(srcPtr, &markerPtr->axes);
+ p.x += markerPtr->xOffset;
+ p.y += markerPtr->yOffset;
+
+ segPtr = segments;
+ for (srcPtr++, pend = markerPtr->worldPts + markerPtr->nWorldPts;
+ srcPtr < pend; srcPtr++) {
+ Point2d next;
+
+ next = MapPoint(srcPtr, &markerPtr->axes);
+ next.x += markerPtr->xOffset;
+ next.y += markerPtr->yOffset;
+ q = next;
+ if (Blt_LineRectClip(&extents, &p, &q)) {
+ segPtr->p = p;
+ segPtr->q = q;
+ segPtr++;
+ }
+ p = next;
+ }
+ lmPtr->nSegments = segPtr - segments;
+ lmPtr->segments = segments;
+ markerPtr->clipped = (lmPtr->nSegments == 0);
+}
+
+static int
+PointInLineProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ return Blt_PointInSegments(samplePtr, lmPtr->segments, lmPtr->nSegments,
+ (double)markerPtr->obj.graphPtr->halo);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInLineProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInLineProc(Marker *markerPtr, Region2d *extsPtr, int enclosed)
+{
+ if (markerPtr->nWorldPts < 2) {
+ return FALSE;
+ }
+ if (enclosed) {
+ Point2d *pp, *pend;
+
+ for (pp = markerPtr->worldPts, pend = pp + markerPtr->nWorldPts;
+ pp < pend; pp++) {
+ Point2d p;
+
+ p = MapPoint(pp, &markerPtr->axes);
+ if ((p.x < extsPtr->left) && (p.x > extsPtr->right) &&
+ (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) {
+ return FALSE;
+ }
+ }
+ return TRUE; /* All points inside bounding box. */
+ } else {
+ int count;
+ Point2d *pp, *pend;
+
+ count = 0;
+ for (pp = markerPtr->worldPts, pend = pp + (markerPtr->nWorldPts - 1);
+ pp < pend; pp++) {
+ Point2d p, q;
+
+ p = MapPoint(pp, &markerPtr->axes);
+ q = MapPoint(pp + 1, &markerPtr->axes);
+ if (Blt_LineRectClip(extsPtr, &p, &q)) {
+ count++;
+ }
+ }
+ return (count > 0); /* At least 1 segment passes through
+ * region. */
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawLineProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawLineProc(Marker *markerPtr, Drawable drawable)
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ Blt_Draw2DSegments(graphPtr->display, drawable, lmPtr->gc,
+ lmPtr->segments, lmPtr->nSegments);
+ if (lmPtr->xor) { /* Toggle the drawing state */
+ lmPtr->xorState = (lmPtr->xorState == 0);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureLineProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a line marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as line width, colors, dashes,
+ * etc. get set for markerPtr; old resources get freed, if there were
+ * any. The marker is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLineProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle);
+ if (lmPtr->outlineColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = lmPtr->outlineColor->pixel;
+ }
+ if (lmPtr->fillColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = lmPtr->fillColor->pixel;
+ }
+ gcValues.cap_style = lmPtr->capStyle;
+ gcValues.join_style = lmPtr->joinStyle;
+ gcValues.line_width = LineWidth(lmPtr->lineWidth);
+ gcValues.line_style = LineSolid;
+ if (LineIsDashed(lmPtr->dashes)) {
+ gcValues.line_style =
+ (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash;
+ }
+ if (lmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = Blt_BackgroundBorderColor(graphPtr->plotBg)->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawLineProc(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (LineIsDashed(lmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &lmPtr->dashes);
+ }
+ lmPtr->gc = newGC;
+ if (lmPtr->xor) {
+ if (drawable != None) {
+ MapLineProc(markerPtr);
+ DrawLineProc(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LineToPostscriptProc --
+ *
+ * Prints postscript commands to display the connect line. Dashed lines
+ * need to be handled specially, especially if a background color is
+ * designated.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript output commands are saved in the interpreter
+ * (infoPtr->interp) result field.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+LineToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+
+ if (lmPtr->nSegments > 0) {
+ Blt_Ps_XSetLineAttributes(ps, lmPtr->outlineColor,
+ lmPtr->lineWidth, &lmPtr->dashes, lmPtr->capStyle,
+ lmPtr->joinStyle);
+ if ((LineIsDashed(lmPtr->dashes)) && (lmPtr->fillColor != NULL)) {
+ Blt_Ps_Append(ps, "/DashesProc {\n gsave\n ");
+ Blt_Ps_XSetBackground(ps, lmPtr->fillColor);
+ Blt_Ps_Append(ps, " ");
+ Blt_Ps_XSetDashes(ps, (Blt_Dashes *)NULL);
+ Blt_Ps_VarAppend(ps,
+ "stroke\n",
+ " grestore\n",
+ "} def\n", (char *)NULL);
+ } else {
+ Blt_Ps_Append(ps, "/DashesProc {} def\n");
+ }
+ Blt_Ps_Draw2DSegments(ps, lmPtr->segments, lmPtr->nSegments);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeLineProc --
+ *
+ * Destroys the structure and attributes of a line marker.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Line attributes (GCs, colors, stipple, etc) get released. Memory is
+ * deallocated, X resources are freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreeLineProc(Marker *markerPtr)
+{
+ LineMarker *lmPtr = (LineMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (lmPtr->gc != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lmPtr->gc);
+ }
+ if (lmPtr->segments != NULL) {
+ Blt_Free(lmPtr->segments);
+ }
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateLineProc --
+ *
+ * Allocate memory and initialize methods for a new line marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line marker structure.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Marker *
+CreateLineProc(void)
+{
+ LineMarker *lmPtr;
+
+ lmPtr = Blt_AssertCalloc(1, sizeof(LineMarker));
+ lmPtr->classPtr = &lineMarkerClass;
+ lmPtr->xor = FALSE;
+ lmPtr->capStyle = CapButt;
+ lmPtr->joinStyle = JoinMiter;
+ return (Marker *)lmPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapPolygonProc --
+ *
+ * Calculate the layout position for a polygon marker. Positional
+ * information is saved in the polygon in an array of points (malloc'ed).
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MapPolygonProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ Point2d *screenPts;
+ Region2d extents;
+ int nScreenPts;
+
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ pmPtr->outlinePts = NULL;
+ pmPtr->nOutlinePts = 0;
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ pmPtr->fillPts = NULL;
+ pmPtr->nFillPts = 0;
+ }
+ if (pmPtr->screenPts != NULL) {
+ Blt_Free(pmPtr->screenPts);
+ pmPtr->screenPts = NULL;
+ }
+ if (markerPtr->nWorldPts < 3) {
+ return; /* Too few points */
+ }
+
+ /*
+ * Allocate and fill a temporary array to hold the screen coordinates of
+ * the polygon.
+ */
+ nScreenPts = markerPtr->nWorldPts + 1;
+ screenPts = Blt_AssertMalloc((nScreenPts + 1) * sizeof(Point2d));
+ {
+ Point2d *sp, *dp, *send;
+
+ dp = screenPts;
+ for (sp = markerPtr->worldPts, send = sp + markerPtr->nWorldPts;
+ sp < send; sp++) {
+ *dp = MapPoint(sp, &markerPtr->axes);
+ dp->x += markerPtr->xOffset;
+ dp->y += markerPtr->yOffset;
+ dp++;
+ }
+ *dp = screenPts[0];
+ }
+ Blt_GraphExtents(graphPtr, &extents);
+ markerPtr->clipped = TRUE;
+ if (pmPtr->fill.fgColor != NULL) { /* Polygon fill required. */
+ Point2d *fillPts;
+ int n;
+
+ fillPts = Blt_AssertMalloc(sizeof(Point2d) * nScreenPts * 3);
+ n = Blt_PolyRectClip(&extents, screenPts, markerPtr->nWorldPts,fillPts);
+ if (n < 3) {
+ Blt_Free(fillPts);
+ } else {
+ pmPtr->nFillPts = n;
+ pmPtr->fillPts = fillPts;
+ markerPtr->clipped = FALSE;
+ }
+ }
+ if ((pmPtr->outline.fgColor != NULL) && (pmPtr->lineWidth > 0)) {
+ Segment2d *outlinePts;
+ Segment2d *segPtr;
+ Point2d *sp, *send;
+
+ /*
+ * Generate line segments representing the polygon outline. The
+ * resulting outline may or may not be closed from viewport clipping.
+ */
+ outlinePts = Blt_Malloc(nScreenPts * sizeof(Segment2d));
+ if (outlinePts == NULL) {
+ return; /* Can't allocate point array */
+ }
+ /*
+ * Note that this assumes that the point array contains an extra point
+ * that closes the polygon.
+ */
+ segPtr = outlinePts;
+ for (sp = screenPts, send = sp + (nScreenPts - 1); sp < send; sp++) {
+ segPtr->p = sp[0];
+ segPtr->q = sp[1];
+ if (Blt_LineRectClip(&extents, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ }
+ }
+ pmPtr->nOutlinePts = segPtr - outlinePts;
+ pmPtr->outlinePts = outlinePts;
+ if (pmPtr->nOutlinePts > 0) {
+ markerPtr->clipped = FALSE;
+ }
+ }
+ pmPtr->screenPts = screenPts;
+}
+
+static int
+PointInPolygonProc(Marker *markerPtr, Point2d *samplePtr)
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if ((markerPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+ return Blt_PointInPolygon(samplePtr, pmPtr->screenPts,
+ markerPtr->nWorldPts + 1);
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RegionInPolygonProc --
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+RegionInPolygonProc(Marker *markerPtr, Region2d *extsPtr, int enclosed)
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if ((markerPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) {
+ return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts,
+ markerPtr->nWorldPts, enclosed);
+ }
+ return FALSE;
+}
+
+static void
+DrawPolygonProc(Marker *markerPtr, Drawable drawable)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ /* Draw polygon fill region */
+ if ((pmPtr->nFillPts > 0) && (pmPtr->fill.fgColor != NULL)) {
+ XPoint *dp, *points;
+ Point2d *sp, *send;
+
+ points = Blt_Malloc(pmPtr->nFillPts * sizeof(XPoint));
+ if (points == NULL) {
+ return;
+ }
+ dp = points;
+ for (sp = pmPtr->fillPts, send = sp + pmPtr->nFillPts; sp < send;
+ sp++) {
+ dp->x = (short int)sp->x;
+ dp->y = (short int)sp->y;
+ dp++;
+ }
+
+ XFillPolygon(graphPtr->display, drawable, pmPtr->fillGC, points,
+ pmPtr->nFillPts, Complex, CoordModeOrigin);
+ Blt_Free(points);
+ }
+ /* and then the outline */
+ if ((pmPtr->nOutlinePts > 0) && (pmPtr->lineWidth > 0) &&
+ (pmPtr->outline.fgColor != NULL)) {
+ Blt_Draw2DSegments(graphPtr->display, drawable, pmPtr->outlineGC,
+ pmPtr->outlinePts, pmPtr->nOutlinePts);
+ }
+}
+
+
+static void
+PolygonToPostscriptProc(Marker *markerPtr, Blt_Ps ps)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+
+ if (pmPtr->fill.fgColor != NULL) {
+
+ /*
+ * Options: fg bg
+ * Draw outline only.
+ * x Draw solid or stipple.
+ * x x Draw solid or stipple.
+ */
+
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_Ps_Polyline(ps, pmPtr->fillPts, pmPtr->nFillPts);
+
+ /* If the background fill color was specified, draw the polygon in a
+ * solid fashion with that color. */
+ if (pmPtr->fill.bgColor != NULL) {
+ /* Draw the solid background as the background layer of the opaque
+ * stipple */
+ Blt_Ps_XSetBackground(ps, pmPtr->fill.bgColor);
+ /* Retain the path. We'll need it for the foreground layer. */
+ Blt_Ps_Append(ps, "gsave fill grestore\n");
+ }
+ Blt_Ps_XSetForeground(ps, pmPtr->fill.fgColor);
+ if (pmPtr->stipple != None) {
+ /* Draw the stipple in the foreground color. */
+ Blt_Ps_XSetStipple(ps, graphPtr->display, pmPtr->stipple);
+ } else {
+ Blt_Ps_Append(ps, "fill\n");
+ }
+ }
+
+ /* Draw the outline in the foreground color. */
+ if ((pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) {
+
+ /* Set up the line attributes. */
+ Blt_Ps_XSetLineAttributes(ps, pmPtr->outline.fgColor,
+ pmPtr->lineWidth, &pmPtr->dashes, pmPtr->capStyle,
+ pmPtr->joinStyle);
+
+ /*
+ * Define on-the-fly a PostScript macro "DashesProc" that will be
+ * executed for each call to the Polygon drawing routine. If the line
+ * isn't dashed, simply make this an empty definition.
+ */
+ if ((pmPtr->outline.bgColor != NULL) && (LineIsDashed(pmPtr->dashes))) {
+ Blt_Ps_Append(ps, "/DashesProc {\ngsave\n ");
+ Blt_Ps_XSetBackground(ps, pmPtr->outline.bgColor);
+ Blt_Ps_Append(ps, " ");
+ Blt_Ps_XSetDashes(ps, (Blt_Dashes *)NULL);
+ Blt_Ps_Append(ps, "stroke\n grestore\n} def\n");
+ } else {
+ Blt_Ps_Append(ps, "/DashesProc {} def\n");
+ }
+ Blt_Ps_Draw2DSegments(ps, pmPtr->outlinePts, pmPtr->nOutlinePts);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigurePolygonProc --
+ *
+ * This procedure is called to process an objv/objc list, plus the Tk
+ * option database, in order to configure (or reconfigure) a polygon
+ * marker.
+ *
+ * Results:
+ * A standard TCL result. If TCL_ERROR is returned, then interp->result
+ * contains an error message.
+ *
+ * Side effects:
+ * Configuration information, such as polygon color, dashes, fillstyle,
+ * etc. get set for markerPtr; old resources get freed, if there were
+ * any. The marker is eventually redisplayed.
+ *
+ * --------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePolygonProc(Marker *markerPtr)
+{
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Drawable drawable;
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ gcMask = (GCLineWidth | GCLineStyle);
+ if (pmPtr->outline.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->outline.fgColor->pixel;
+ }
+ if (pmPtr->outline.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->outline.bgColor->pixel;
+ }
+ gcMask |= (GCCapStyle | GCJoinStyle);
+ gcValues.cap_style = pmPtr->capStyle;
+ gcValues.join_style = pmPtr->joinStyle;
+ gcValues.line_style = LineSolid;
+ gcValues.dash_offset = 0;
+ gcValues.line_width = LineWidth(pmPtr->lineWidth);
+ if (LineIsDashed(pmPtr->dashes)) {
+ gcValues.line_style = (pmPtr->outline.bgColor == NULL)
+ ? LineOnOffDash : LineDoubleDash;
+ }
+ if (pmPtr->xor) {
+ unsigned long pixel;
+ gcValues.function = GXxor;
+
+ gcMask |= GCFunction;
+ if (graphPtr->plotBg == NULL) {
+ /* The graph's color option may not have been set yet */
+ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin));
+ } else {
+ pixel = Blt_BackgroundBorderColor(graphPtr->plotBg)->pixel;
+ }
+ if (gcMask & GCBackground) {
+ gcValues.background ^= pixel;
+ }
+ gcValues.foreground ^= pixel;
+ if (drawable != None) {
+ DrawPolygonProc(markerPtr, drawable);
+ }
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (LineIsDashed(pmPtr->dashes)) {
+ Blt_SetDashes(graphPtr->display, newGC, &pmPtr->dashes);
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ pmPtr->outlineGC = newGC;
+
+ gcMask = 0;
+ if (pmPtr->fill.fgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = pmPtr->fill.fgColor->pixel;
+ }
+ if (pmPtr->fill.bgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = pmPtr->fill.bgColor->pixel;
+ }
+ if (pmPtr->stipple != None) {
+ gcValues.stipple = pmPtr->stipple;
+ gcValues.fill_style = (pmPtr->fill.bgColor != NULL)
+ ? FillOpaqueStippled : FillStippled;
+ gcMask |= (GCStipple | GCFillStyle);
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ pmPtr->fillGC = newGC;
+
+ if ((gcMask == 0) && !(graphPtr->flags & RESET_AXES) && (pmPtr->xor)) {
+ if (drawable != None) {
+ MapPolygonProc(markerPtr);
+ DrawPolygonProc(markerPtr, drawable);
+ }
+ return TCL_OK;
+ }
+ markerPtr->flags |= MAP_ITEM;
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ graphPtr->flags |= RESET_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreePolygonProc --
+ *
+ * Release memory and resources allocated for the polygon element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the polygon element is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FreePolygonProc(Marker *markerPtr)
+{
+ PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
+ Graph *graphPtr = markerPtr->obj.graphPtr;
+
+ if (pmPtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, pmPtr->fillGC);
+ }
+ if (pmPtr->outlineGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC);
+ }
+ if (pmPtr->fillPts != NULL) {
+ Blt_Free(pmPtr->fillPts);
+ }
+ if (pmPtr->outlinePts != NULL) {
+ Blt_Free(pmPtr->outlinePts);
+ }
+ if (pmPtr->screenPts != NULL) {
+ Blt_Free(pmPtr->screenPts);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreatePolygonProc --
+ *
+ * Allocate memory and initialize methods for the new polygon marker.
+ *
+ * Results:
+ * The pointer to the newly allocated marker structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon marker structure.
+ *
+ * --------------------------------------------------------------------------
+ */
+static Marker *
+CreatePolygonProc(void)
+{
+ PolygonMarker *pmPtr;
+
+ pmPtr = Blt_AssertCalloc(1, sizeof(PolygonMarker));
+ pmPtr->classPtr = &polygonMarkerClass;
+ pmPtr->capStyle = CapButt;
+ pmPtr->joinStyle = JoinMiter;
+ return (Marker *)pmPtr;
+}
+
+static int
+GetMarkerFromObj(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ Marker **markerPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, string);
+ if (hPtr != NULL) {
+ *markerPtrPtr = Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find marker \"", string,
+ "\" in \"", Tk_PathName(graphPtr->tkwin), (char *)NULL);
+ }
+ return TCL_ERROR;
+}
+
+
+static int
+RenameMarker(Graph *graphPtr, Marker *markerPtr, const char *oldName,
+ const char *newName)
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ /* Rename the marker only if no marker already exists by that name */
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, newName, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(graphPtr->interp, "can't rename marker: \"", newName,
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr->obj.name = Blt_AssertStrdup(newName);
+ markerPtr->hashPtr = hPtr;
+ Blt_SetHashValue(hPtr, (char *)markerPtr);
+
+ /* Delete the old hash entry */
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, oldName);
+ Blt_DeleteHashEntry(&graphPtr->markers.table, hPtr);
+ if (oldName != NULL) {
+ Blt_Free(oldName);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Returns a list of marker identifiers in interp->result;
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NamesOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (objc == 3) {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(markerPtr->obj.name, -1));
+ }
+ } else {
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+ int i;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ for (i = 3; i < objc; i++) {
+ const char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ if (Tcl_StringMatch(markerPtr->obj.name, pattern)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(markerPtr->obj.name, -1));
+ break;
+ }
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BindOp --
+ *
+ * .g element bind elemName sequence command
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BindOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ Blt_HashEntry *hp;
+ Blt_HashSearch iter;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (hp = Blt_FirstHashEntry(&graphPtr->markers.tagTable, &iter);
+ hp != NULL; hp = Blt_NextHashEntry(&iter)) {
+ const char *tag;
+ Tcl_Obj *objPtr;
+
+ tag = Blt_GetHashKey(&graphPtr->markers.tagTable, hp);
+ objPtr = Tcl_NewStringObj(tag, -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+ }
+ return Blt_ConfigureBindingsFromObj(interp, graphPtr->bindTable,
+ Blt_MakeMarkerTag(graphPtr, Tcl_GetString(objv[3])),
+ objc - 4, objv + 4);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+CgetOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Marker *markerPtr;
+
+ if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ConfigureValueFromObj(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr, objv[4], 0)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Marker *markerPtr;
+ Tcl_Obj *const *options;
+ const char *oldName;
+ const char *string;
+ int flags = BLT_CONFIG_OBJV_ONLY;
+ int nNames, nOpts;
+ int i;
+ int under;
+
+ markerPtr = NULL; /* Suppress compiler warning. */
+
+ /* Figure out where the option value pairs begin */
+ objc -= 3;
+ objv += 3;
+ for (i = 0; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ if (GetMarkerFromObj(interp, graphPtr, objv[i], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* # of element names specified */
+ nOpts = objc - i; /* # of options specified */
+ options = objv + nNames; /* Start of options in objv */
+
+ for (i = 0; i < nNames; i++) {
+ GetMarkerFromObj(interp, graphPtr, objv[i], &markerPtr);
+ if (nOpts == 0) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ (Tcl_Obj *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, (char *)markerPtr,
+ options[0], flags);
+ }
+ /* Save the old marker name. */
+ oldName = markerPtr->obj.name;
+ under = markerPtr->drawUnder;
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin,
+ markerPtr->classPtr->configSpecs, nOpts, options,
+ (char *)markerPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (oldName != markerPtr->obj.name) {
+ if (RenameMarker(graphPtr, markerPtr, oldName, markerPtr->obj.name)
+ != TCL_OK) {
+ markerPtr->obj.name = oldName;
+ return TCL_ERROR;
+ }
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+
+ return TCL_ERROR;
+ }
+ if (markerPtr->drawUnder != under) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * This procedure creates and initializes a new marker.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing the new
+ * element. If an error occurred, then the return value is NULL and an
+ * error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated, etc.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+CreateOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Marker *markerPtr;
+ Blt_HashEntry *hPtr;
+ int isNew;
+ ClassId classId;
+ int i;
+ const char *name;
+ char ident[200];
+ const char *string;
+ char c;
+
+ string = Tcl_GetString(objv[3]);
+ c = string[0];
+ /* Create the new marker based upon the given type */
+ if ((c == 't') && (strcmp(string, "text") == 0)) {
+ classId = CID_MARKER_TEXT;
+ } else if ((c == 'l') && (strcmp(string, "line") == 0)) {
+ classId = CID_MARKER_LINE;
+ } else if ((c == 'p') && (strcmp(string, "polygon") == 0)) {
+ classId = CID_MARKER_POLYGON;
+ } else if ((c == 'i') && (strcmp(string, "image") == 0)) {
+ classId = CID_MARKER_IMAGE;
+ } else if ((c == 'b') && (strcmp(string, "bitmap") == 0)) {
+ classId = CID_MARKER_BITMAP;
+ } else if ((c == 'w') && (strcmp(string, "window") == 0)) {
+ classId = CID_MARKER_WINDOW;
+ } else {
+ Tcl_AppendResult(interp, "unknown marker type \"", string,
+ "\": should be \"text\", \"line\", \"polygon\", \"bitmap\", \"image\", or \
+\"window\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Scan for "-name" option. We need it for the component name */
+ name = NULL;
+ for (i = 4; i < objc; i += 2) {
+ int length;
+
+ string = Tcl_GetStringFromObj(objv[i], &length);
+ if ((length > 1) && (strncmp(string, "-name", length) == 0)) {
+ name = Tcl_GetString(objv[i + 1]);
+ break;
+ }
+ }
+ /* If no name was given for the marker, make up one. */
+ if (name == NULL) {
+ sprintf_s(ident, 200, "marker%d", graphPtr->nextMarkerId++);
+ name = ident;
+ } else if (name[0] == '-') {
+ Tcl_AppendResult(interp, "name of marker \"", name,
+ "\" can't start with a '-'", (char *)NULL);
+ return TCL_ERROR;
+ }
+ markerPtr = CreateMarker(graphPtr, name, classId);
+ if (Blt_ConfigureComponentFromObj(interp, graphPtr->tkwin, name,
+ markerPtr->obj.className, markerPtr->classPtr->configSpecs,
+ objc - 4, objv + 4, (char *)markerPtr, 0) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) {
+ DestroyMarker(markerPtr);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, name, &isNew);
+ if (!isNew) {
+ Marker *oldPtr;
+ /*
+ * Marker by the same name already exists. Delete the old marker and
+ * it's list entry. But save the hash entry.
+ */
+ oldPtr = Blt_GetHashValue(hPtr);
+ oldPtr->hashPtr = NULL;
+ DestroyMarker(oldPtr);
+ }
+ Blt_SetHashValue(hPtr, markerPtr);
+ markerPtr->hashPtr = hPtr;
+ /* Unlike elements, new markers are drawn on top of old markers. */
+ markerPtr->link = Blt_Chain_Prepend(graphPtr->markers.displayList,markerPtr);
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the marker given by markerId.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ Marker *markerPtr;
+
+ if (GetMarkerFromObj(NULL, graphPtr, objv[i], &markerPtr) == TCL_OK) {
+ markerPtr->flags |= DELETE_PENDING;
+ Tcl_EventuallyFree(markerPtr, FreeMarker);
+ }
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetOp --
+ *
+ * Find the legend entry from the given argument. The argument can be
+ * either a screen position "@x,y" or the name of an element.
+ *
+ * I don't know how useful it is to test with the name of an element.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new legend attributes.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Marker *markerPtr;
+ const char *string;
+
+ string = Tcl_GetString(objv[3]);
+ if ((string[0] == 'c') && (strcmp(string, "current") == 0)) {
+ markerPtr = (Marker *)Blt_GetCurrentItem(graphPtr->bindTable);
+ if (markerPtr == NULL) {
+ return TCL_OK; /* Report only on markers. */
+
+ }
+ if ((markerPtr->obj.classId >= CID_MARKER_BITMAP) &&
+ (markerPtr->obj.classId <= CID_MARKER_WINDOW)) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ markerPtr->obj.name, -1);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RelinkOp --
+ *
+ * Reorders the marker (given by the first name) before/after the another
+ * marker (given by the second name) in the marker display list. If no
+ * second name is given, the marker is placed at the beginning/end of the
+ * list.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side Effects:
+ * Graph will be redrawn to reflect the new display list.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RelinkOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_ChainLink link, place;
+ Marker *markerPtr;
+ const char *string;
+
+ /* Find the marker to be raised or lowered. */
+ if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Right now it's assumed that all markers are always in the display
+ list. */
+ link = markerPtr->link;
+ Blt_Chain_UnlinkLink(graphPtr->markers.displayList, markerPtr->link);
+
+ place = NULL;
+ if (objc == 5) {
+ if (GetMarkerFromObj(interp, graphPtr, objv[4], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ place = markerPtr->link;
+ }
+
+ /* Link the marker at its new position. */
+ string = Tcl_GetString(objv[2]);
+ if (string[0] == 'l') {
+ Blt_Chain_LinkAfter(graphPtr->markers.displayList, link, place);
+ } else {
+ Blt_Chain_LinkBefore(graphPtr->markers.displayList, link, place);
+ }
+ if (markerPtr->drawUnder) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FindOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FindOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_ChainLink link;
+ Region2d extents;
+ const char *string;
+ int enclosed;
+ int left, right, top, bottom;
+ int mode;
+
+#define FIND_ENCLOSED (1<<0)
+#define FIND_OVERLAPPING (1<<1)
+ string = Tcl_GetString(objv[3]);
+ if (strcmp(string, "enclosed") == 0) {
+ mode = FIND_ENCLOSED;
+ } else if (strcmp(string, "overlapping") == 0) {
+ mode = FIND_OVERLAPPING;
+ } else {
+ Tcl_AppendResult(interp, "bad search type \"", string,
+ ": should be \"enclosed\", or \"overlapping\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if ((Tcl_GetIntFromObj(interp, objv[4], &left) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[5], &top) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[6], &right) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[7], &bottom) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (left < right) {
+ extents.left = (double)left;
+ extents.right = (double)right;
+ } else {
+ extents.left = (double)right;
+ extents.right = (double)left;
+ }
+ if (top < bottom) {
+ extents.top = (double)top;
+ extents.bottom = (double)bottom;
+ } else {
+ extents.top = (double)bottom;
+ extents.bottom = (double)top;
+ }
+ enclosed = (mode == FIND_ENCLOSED);
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ if (markerPtr->flags & (HIDE|DELETE_PENDING)) {
+ continue;
+ }
+ if ((markerPtr->elemName != NULL) && (IsElementHidden(markerPtr))) {
+ continue;
+ }
+ if ((*markerPtr->classPtr->regionProc)(markerPtr, &extents, enclosed)) {
+ Tcl_Obj *objPtr;
+
+ objPtr = Tcl_GetObjResult(interp);
+ Tcl_SetStringObj(objPtr, markerPtr->obj.name, -1);
+ return TCL_OK;
+ }
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), "", -1);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ExistsOp --
+ *
+ * Returns if marker by a given ID currently exists.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ExistsOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, Tcl_GetString(objv[3]));
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Returns a symbolic name for the type of the marker whose ID is given.
+ *
+ * Results:
+ * A standard TCL result. interp->result will contain the symbolic type
+ * of the marker.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Marker *markerPtr;
+ const char *type;
+
+ if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (markerPtr->obj.classId) {
+ case CID_MARKER_BITMAP: type = "bitmap"; break;
+ case CID_MARKER_IMAGE: type = "image"; break;
+ case CID_MARKER_LINE: type = "line"; break;
+ case CID_MARKER_POLYGON: type = "polygon"; break;
+ case CID_MARKER_TEXT: type = "text"; break;
+ case CID_MARKER_WINDOW: type = "window"; break;
+ default: type = "???"; break;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), type, -1);
+ return TCL_OK;
+}
+
+/* Public routines */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_MarkerOp --
+ *
+ * This procedure is invoked to process the TCL command that corresponds
+ * to a widget managed by this module. See the user documentation for
+ * details on what it does.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Blt_OpSpec markerOps[] =
+{
+ {"bind", 1, BindOp, 3, 6, "marker sequence command",},
+ {"cget", 2, CgetOp, 5, 5, "marker option",},
+ {"configure", 2, ConfigureOp, 4, 0,"marker ?marker?... ?option value?...",},
+ {"create", 2, CreateOp, 4, 0, "type ?option value?...",},
+ {"delete", 1, DeleteOp, 3, 0, "?marker?...",},
+ {"exists", 1, ExistsOp, 4, 4, "marker",},
+ {"find", 1, FindOp, 8, 8, "enclosed|overlapping x1 y1 x2 y2",},
+ {"get", 1, GetOp, 4, 4, "name",},
+ {"lower", 1, RelinkOp, 4, 5, "marker ?afterMarker?",},
+ {"names", 1, NamesOp, 3, 0, "?pattern?...",},
+ {"raise", 1, RelinkOp, 4, 5, "marker ?beforeMarker?",},
+ {"type", 1, TypeOp, 4, 4, "marker",},
+};
+static int nMarkerOps = sizeof(markerOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+int
+Blt_MarkerOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphMarkerProc *proc;
+ int result;
+
+ proc = Blt_GetOpFromObj(interp, nMarkerOps, markerOps, BLT_OP_ARG2,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ result = (*proc) (graphPtr, interp, objc, objv);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_MarkersToPostScript --
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_MarkersToPostScript(Graph *graphPtr, Blt_Ps ps, int under)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ if ((markerPtr->classPtr->postscriptProc == NULL) ||
+ (markerPtr->nWorldPts == 0)) {
+ continue;
+ }
+ if (markerPtr->drawUnder != under) {
+ continue;
+ }
+ if (markerPtr->flags & (HIDE|DELETE_PENDING)) {
+ continue;
+ }
+ if ((markerPtr->elemName != NULL) && (IsElementHidden(markerPtr))) {
+ continue;
+ }
+ Blt_Ps_VarAppend(ps, "\n% Marker \"", markerPtr->obj.name,
+ "\" is a ", markerPtr->obj.className, ".\n", (char *)NULL);
+ (*markerPtr->classPtr->postscriptProc) (markerPtr, ps);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawMarkers --
+ *
+ * Calls the individual drawing routines (based on marker type) for each
+ * marker in the display list.
+ *
+ * A marker will not be drawn if
+ *
+ * 1) An element linked to the marker (indicated by elemName) is
+ * currently hidden.
+ *
+ * 2) No coordinates have been specified for the marker.
+ *
+ * 3) The marker is requesting to be drawn at a different level
+ * (above/below the elements) from the current mode.
+ *
+ * 4) The marker is configured as hidden (-hide option).
+ *
+ * 5) The marker isn't visible in the current viewport (i.e. clipped).
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Markers are drawn into the drawable (pixmap) which will eventually
+ * be displayed in the graph window.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawMarkers(Graph *graphPtr, Drawable drawable, int under)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_LastLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+
+ if ((markerPtr->nWorldPts == 0) ||
+ (markerPtr->drawUnder != under) ||
+ (markerPtr->clipped) ||
+ (markerPtr->flags & (DELETE_PENDING|HIDE))) {
+ continue;
+ }
+ if ((markerPtr->elemName != NULL) && (IsElementHidden(markerPtr))) {
+ continue;
+ }
+ (*markerPtr->classPtr->drawProc) (markerPtr, drawable);
+ }
+}
+
+void
+Blt_ConfigureMarkers(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ (*markerPtr->classPtr->configProc) (markerPtr);
+ }
+}
+
+void
+Blt_MapMarkers(Graph *graphPtr)
+{
+ Blt_ChainLink link;
+
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ if (markerPtr->nWorldPts == 0) {
+ continue;
+ }
+ if (markerPtr->flags & (HIDE|DELETE_PENDING)) {
+ continue;
+ }
+ if ((graphPtr->flags & MAP_ALL) || (markerPtr->flags & MAP_ITEM)) {
+ (*markerPtr->classPtr->mapProc) (markerPtr);
+ markerPtr->flags &= ~MAP_ITEM;
+ }
+ }
+}
+
+void
+Blt_DestroyMarkers(Graph *graphPtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.table, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_GetHashValue(hPtr);
+ /*
+ * Dereferencing the pointer to the hash table prevents the hash table
+ * entry from being automatically deleted.
+ */
+ markerPtr->hashPtr = NULL;
+ DestroyMarker(markerPtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->markers.table);
+ Blt_DeleteHashTable(&graphPtr->markers.tagTable);
+ Blt_Chain_Destroy(graphPtr->markers.displayList);
+}
+
+Marker *
+Blt_NearestMarker(
+ Graph *graphPtr,
+ int x, int y, /* Screen coordinates */
+ int under)
+{
+ Blt_ChainLink link;
+ Point2d point;
+
+ point.x = (double)x;
+ point.y = (double)y;
+ for (link = Blt_Chain_FirstLink(graphPtr->markers.displayList);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Marker *markerPtr;
+
+ markerPtr = Blt_Chain_GetValue(link);
+ if ((markerPtr->nWorldPts == 0) ||
+ (markerPtr->flags & (HIDE|DELETE_PENDING|MAP_ITEM))) {
+ continue; /* Don't consider markers that are
+ * pending to be mapped. Even if the
+ * marker has already been mapped, the
+ * coordinates could be invalid now.
+ * Better to pick no marker than the
+ * wrong marker. */
+
+ }
+ if ((markerPtr->elemName != NULL) && (IsElementHidden(markerPtr))) {
+ continue;
+ }
+ if ((markerPtr->drawUnder == under) &&
+ (markerPtr->state == STATE_NORMAL)) {
+ if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
+ return markerPtr;
+ }
+ }
+ }
+ return NULL;
+}
+
+ClientData
+Blt_MakeMarkerTag(Graph *graphPtr, const char *tagName)
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ assert(tagName != NULL);
+ hPtr = Blt_CreateHashEntry(&graphPtr->markers.tagTable, tagName, &isNew);
+ return Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr);
+}
+
+
+#ifdef notdef
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureArrows --
+ *
+ * If arrowheads have been requested for a line, this procedure makes
+ * arrangements for the arrowheads.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side effects:
+ * Information in linePtr is set up for one or two arrowheads. the
+ * firstArrowPtr and lastArrowPtr polygons are allocated and initialized,
+ * if need be, and the end points of the line are adjusted so that a
+ * thick line doesn't stick out past the arrowheads.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+ConfigureArrows(canvas, linePtr)
+ Tk_Canvas canvas; /* Canvas in which arrows will be
+ * displayed (interp and tkwin fields
+ * are needed). */
+ LineItem *linePtr; /* Item to configure for arrows. */
+{
+ double *poly, *coordPtr;
+ double dx, dy, length, sinTheta, cosTheta, temp;
+ double fracHeight; /* Line width as fraction of arrowhead
+ * width. */
+ double backup; /* Distance to backup end points so
+ * the line ends in the middle of the
+ * arrowhead. */
+ double vertX, vertY; /* Position of arrowhead vertex. */
+ double shapeA, shapeB, shapeC; /* Adjusted coordinates (see
+ * explanation below). */
+ double width;
+ Tk_State state = linePtr->header.state;
+
+ if (linePtr->numPoints <2) {
+ return TCL_OK;
+ }
+
+ if(state == TK_STATE_NULL) {
+ state = ((TkCanvas *)canvas)->canvas_state;
+ }
+
+ width = linePtr->outline.width;
+ if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) {
+ if (linePtr->outline.activeWidth>width) {
+ width = linePtr->outline.activeWidth;
+ }
+ } else if (state==TK_STATE_DISABLED) {
+ if (linePtr->outline.disabledWidth>0) {
+ width = linePtr->outline.disabledWidth;
+ }
+ }
+
+ /*
+ * The code below makes a tiny increase in the shape parameters for the
+ * line. This is a bit of a hack, but it seems to result in displays that
+ * more closely approximate the specified parameters. Without the
+ * adjustment, the arrows come out smaller than expected.
+ */
+
+ shapeA = linePtr->arrowShapeA + 0.001;
+ shapeB = linePtr->arrowShapeB + 0.001;
+ shapeC = linePtr->arrowShapeC + width/2.0 + 0.001;
+
+ /*
+ * If there's an arrowhead on the first point of the line, compute its
+ * polygon and adjust the first point of the line so that the line doesn't
+ * stick out past the leading edge of the arrowhead.
+ */
+
+ fracHeight = (width/2.0)/shapeC;
+ backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;
+ if (linePtr->arrow != ARROWS_LAST) {
+ poly = linePtr->firstArrowPtr;
+ if (poly == NULL) {
+ poly = (double *) ckalloc((unsigned)
+ (2*PTS_IN_ARROW*sizeof(double)));
+ poly[0] = poly[10] = linePtr->coordPtr[0];
+ poly[1] = poly[11] = linePtr->coordPtr[1];
+ linePtr->firstArrowPtr = poly;
+ }
+ dx = poly[0] - linePtr->coordPtr[2];
+ dy = poly[1] - linePtr->coordPtr[3];
+ length = hypot(dx, dy);
+ if (length == 0) {
+ sinTheta = cosTheta = 0.0;
+ } else {
+ sinTheta = dy/length;
+ cosTheta = dx/length;
+ }
+ vertX = poly[0] - shapeA*cosTheta;
+ vertY = poly[1] - shapeA*sinTheta;
+ temp = shapeC*sinTheta;
+ poly[2] = poly[0] - shapeB*cosTheta + temp;
+ poly[8] = poly[2] - 2*temp;
+ temp = shapeC*cosTheta;
+ poly[3] = poly[1] - shapeB*sinTheta - temp;
+ poly[9] = poly[3] + 2*temp;
+ poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
+ poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
+ poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
+ poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
+
+ /*
+ * Polygon done. Now move the first point towards the second so that
+ * the corners at the end of the line are inside the arrowhead.
+ */
+
+ linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
+ linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
+ }
+
+ /*
+ * Similar arrowhead calculation for the last point of the line.
+ */
+
+ if (linePtr->arrow != ARROWS_FIRST) {
+ coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
+ poly = linePtr->lastArrowPtr;
+ if (poly == NULL) {
+ poly = (double *) ckalloc((unsigned)
+ (2*PTS_IN_ARROW*sizeof(double)));
+ poly[0] = poly[10] = coordPtr[2];
+ poly[1] = poly[11] = coordPtr[3];
+ linePtr->lastArrowPtr = poly;
+ }
+ dx = poly[0] - coordPtr[0];
+ dy = poly[1] - coordPtr[1];
+ length = hypot(dx, dy);
+ if (length == 0) {
+ sinTheta = cosTheta = 0.0;
+ } else {
+ sinTheta = dy/length;
+ cosTheta = dx/length;
+ }
+ vertX = poly[0] - shapeA*cosTheta;
+ vertY = poly[1] - shapeA*sinTheta;
+ temp = shapeC*sinTheta;
+ poly[2] = poly[0] - shapeB*cosTheta + temp;
+ poly[8] = poly[2] - 2*temp;
+ temp = shapeC*cosTheta;
+ poly[3] = poly[1] - shapeB*sinTheta - temp;
+ poly[9] = poly[3] + 2*temp;
+ poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
+ poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
+ poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
+ poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
+ coordPtr[2] = poly[0] - backup*cosTheta;
+ coordPtr[3] = poly[1] - backup*sinTheta;
+ }
+
+ return TCL_OK;
+}
+#endif
diff --git a/src/bltGrMisc.C b/src/bltGrMisc.C
new file mode 100644
index 0000000..101273e
--- /dev/null
+++ b/src/bltGrMisc.C
@@ -0,0 +1,2024 @@
+
+
+/*
+ * bltGrMisc.c --
+ *
+ * This module implements miscellaneous routines for the BLT graph widget.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+#include <bltAlloc.h>
+#include <bltOp.h>
+#include <stdarg.h>
+
+static Blt_OptionParseProc ObjToPoint;
+static Blt_OptionPrintProc PointToObj;
+Blt_CustomOption bltPointOption =
+{
+ ObjToPoint, PointToObj, NULL, (ClientData)0
+};
+
+static Blt_OptionParseProc ObjToLimitsProc;
+static Blt_OptionPrintProc LimitsToObjProc;
+Blt_CustomOption bltLimitsOption =
+{
+ ObjToLimitsProc, LimitsToObjProc, NULL, (ClientData)0
+};
+
+
+/*
+ *---------------------------------------------------------------------------
+ * Custom option parse and print procedures
+ *---------------------------------------------------------------------------
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetXY --
+ *
+ * Converts a string in the form "@x,y" into an XPoint structure of the x
+ * and y coordinates.
+ *
+ * Results:
+ * A standard TCL result. If the string represents a valid position
+ * *pointPtr* will contain the converted x and y coordinates and TCL_OK
+ * is returned. Otherwise, TCL_ERROR is returned and interp->result will
+ * contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetXY(Tcl_Interp *interp, Tk_Window tkwin, const char *string,
+ int *xPtr, int *yPtr)
+{
+ char *comma;
+ int result;
+ int x, y;
+
+ if ((string == NULL) || (*string == '\0')) {
+ *xPtr = *yPtr = -SHRT_MAX;
+ return TCL_OK;
+ }
+ if (*string != '@') {
+ goto badFormat;
+ }
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ goto badFormat;
+ }
+ *comma = '\0';
+ result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
+ (Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *xPtr = x, *yPtr = y;
+ return TCL_OK;
+
+ badFormat:
+ Tcl_AppendResult(interp, "bad position \"", string,
+ "\": should be \"@x,y\"", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPoint --
+ *
+ * Convert the string representation of a legend XY position into window
+ * coordinates. The form of the string must be "@x,y" or none.
+ *
+ * Results:
+ * A standard TCL result. The symbol type is written into the
+ * widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToPoint(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* New legend position string */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+ int x, y;
+
+ if (Blt_GetXY(interp, tkwin, Tcl_GetString(objPtr), &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ pointPtr->x = x, pointPtr->y = y;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PointToObj --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PointToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+ Tcl_Obj *objPtr;
+
+ if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
+ char string[200];
+
+ sprintf_s(string, 200, "@%d,%d", pointPtr->x, pointPtr->y);
+ objPtr = Tcl_NewStringObj(string, -1);
+ } else {
+ objPtr = Tcl_NewStringObj("", -1);
+ }
+ return objPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToLimitsProc --
+ *
+ * Converts the list of elements into zero or more pixel values which
+ * determine the range of pixel values possible. An element can be in any
+ * form accepted by Tk_GetPixels. The list has a different meaning based
+ * upon the number of elements.
+ *
+ * # of elements:
+ *
+ * 0 - the limits are reset to the defaults.
+ * 1 - the minimum and maximum values are set to this
+ * value, freezing the range at a single value.
+ * 2 - first element is the minimum, the second is the
+ * maximum.
+ * 3 - first element is the minimum, the second is the
+ * maximum, and the third is the nominal value.
+ *
+ * Any element may be the empty string which indicates the default.
+ *
+ * Results:
+ * The return value is a standard TCL result. The min and max fields
+ * of the range are set.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToLimitsProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tk_Window tkwin, /* Widget of paneset */
+ Tcl_Obj *objPtr, /* New width list */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ Blt_Limits *limitsPtr = (Blt_Limits *)(widgRec + offset);
+
+ if (Blt_GetLimitsFromObj(interp, tkwin, objPtr, limitsPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LimitsToObjProc --
+ *
+ * Convert the limits of the pixel values allowed into a list.
+ *
+ * Results:
+ * The string representation of the limits is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+LimitsToObjProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Row/column structure record */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ Blt_Limits *limitsPtr = (Blt_Limits *)(widgRec + offset);
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (limitsPtr->flags & LIMITS_MIN_SET) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(limitsPtr->min));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ if (limitsPtr->flags & LIMITS_MAX_SET) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(limitsPtr->max));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ if (limitsPtr->flags & LIMITS_NOM_SET) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(limitsPtr->nom));
+ } else {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("", -1));
+ }
+ return listObjPtr;
+}
+
+int
+Blt_PointInSegments(
+ Point2d *samplePtr,
+ Segment2d *segments,
+ int nSegments,
+ double halo)
+{
+ Segment2d *sp, *send;
+ double minDist;
+
+ minDist = DBL_MAX;
+ for (sp = segments, send = sp + nSegments; sp < send; sp++) {
+ double dist;
+ double left, right, top, bottom;
+ Point2d p, t;
+
+ t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y,
+ &sp->p, &sp->q);
+ if (sp->p.x > sp->q.x) {
+ right = sp->p.x, left = sp->q.x;
+ } else {
+ right = sp->q.x, left = sp->p.x;
+ }
+ if (sp->p.y > sp->q.y) {
+ bottom = sp->p.y, top = sp->q.y;
+ } else {
+ bottom = sp->q.y, top = sp->p.y;
+ }
+ p.x = BOUND(t.x, left, right);
+ p.y = BOUND(t.y, top, bottom);
+ dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
+ if (dist < minDist) {
+ minDist = dist;
+ }
+ }
+ return (minDist < halo);
+}
+
+int
+Blt_PointInPolygon(
+ Point2d *s, /* Sample point. */
+ Point2d *points, /* Points representing the polygon. */
+ int nPoints) /* # of points in above array. */
+{
+ Point2d *p, *q, *qend;
+ int count;
+
+ count = 0;
+ for (p = points, q = p + 1, qend = p + nPoints; q < qend; p++, q++) {
+ if (((p->y <= s->y) && (s->y < q->y)) ||
+ ((q->y <= s->y) && (s->y < p->y))) {
+ double b;
+
+ b = (q->x - p->x) * (s->y - p->y) / (q->y - p->y) + p->x;
+ if (s->x < b) {
+ count++; /* Count the number of intersections. */
+ }
+ }
+ }
+ return (count & 0x01);
+}
+
+int
+Blt_RegionInPolygon(
+ Region2d *regionPtr,
+ Point2d *points,
+ int nPoints,
+ int enclosed)
+{
+ Point2d *pp, *pend;
+
+ if (enclosed) {
+ /*
+ * All points of the polygon must be inside the rectangle.
+ */
+ for (pp = points, pend = pp + nPoints; pp < pend; pp++) {
+ if ((pp->x < regionPtr->left) || (pp->x > regionPtr->right) ||
+ (pp->y < regionPtr->top) || (pp->y > regionPtr->bottom)) {
+ return FALSE; /* One point is exterior. */
+ }
+ }
+ return TRUE;
+ } else {
+ Point2d r;
+ /*
+ * If any segment of the polygon clips the bounding region, the
+ * polygon overlaps the rectangle.
+ */
+ points[nPoints] = points[0];
+ for (pp = points, pend = pp + nPoints; pp < pend; pp++) {
+ Point2d p, q;
+
+ p = *pp;
+ q = *(pp + 1);
+ if (Blt_LineRectClip(regionPtr, &p, &q)) {
+ return TRUE;
+ }
+ }
+ /*
+ * Otherwise the polygon and rectangle are either disjoint or
+ * enclosed. Check if one corner of the rectangle is inside the
+ * polygon.
+ */
+ r.x = regionPtr->left;
+ r.y = regionPtr->top;
+ return Blt_PointInPolygon(&r, points, nPoints);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GraphExtents --
+ *
+ * Generates a bounding box representing the plotting area of the
+ * graph. This data structure is used to clip the points and line
+ * segments of the line element.
+ *
+ * The clip region is the plotting area plus such arbitrary extra space.
+ * The reason we clip with a bounding box larger than the plot area is so
+ * that symbols will be drawn even if their center point isn't in the
+ * plotting area.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The bounding box is filled with the dimensions of the plotting area.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_GraphExtents(Graph *graphPtr, Region2d *regionPtr)
+{
+ regionPtr->left = (double)(graphPtr->hOffset - graphPtr->xPad.side1);
+ regionPtr->top = (double)(graphPtr->vOffset - graphPtr->yPad.side1);
+ regionPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange +
+ graphPtr->xPad.side2);
+ regionPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange +
+ graphPtr->yPad.side2);
+}
+
+static int
+ClipTest (double ds, double dr, double *t1, double *t2)
+{
+ double t;
+
+ if (ds < 0.0) {
+ t = dr / ds;
+ if (t > *t2) {
+ return FALSE;
+ }
+ if (t > *t1) {
+ *t1 = t;
+ }
+ } else if (ds > 0.0) {
+ t = dr / ds;
+ if (t < *t1) {
+ return FALSE;
+ }
+ if (t < *t2) {
+ *t2 = t;
+ }
+ } else {
+ /* d = 0, so line is parallel to this clipping edge */
+ if (dr < 0.0) { /* Line is outside clipping edge */
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_LineRectClip --
+ *
+ * Clips the given line segment to a rectangular region. The coordinates
+ * of the clipped line segment are returned. The original coordinates
+ * are overwritten.
+ *
+ * Reference:
+ * Liang, Y-D., and B. Barsky, A new concept and method for
+ * Line Clipping, ACM, TOG,3(1), 1984, pp.1-22.
+ *
+ * Results:
+ * Returns if line segment is visible within the region. The coordinates
+ * of the original line segment are overwritten by the clipped
+ * coordinates.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_LineRectClip(
+ Region2d *regionPtr, /* Rectangular region to clip. */
+ Point2d *p, Point2d *q) /* (in/out) Coordinates of original and
+ * clipped line segment. */
+{
+ double t1, t2;
+ double dx, dy;
+
+ t1 = 0.0, t2 = 1.0;
+ dx = q->x - p->x;
+ if ((ClipTest (-dx, p->x - regionPtr->left, &t1, &t2)) &&
+ (ClipTest (dx, regionPtr->right - p->x, &t1, &t2))) {
+ dy = q->y - p->y;
+ if ((ClipTest (-dy, p->y - regionPtr->top, &t1, &t2)) &&
+ (ClipTest (dy, regionPtr->bottom - p->y, &t1, &t2))) {
+ if (t2 < 1.0) {
+ q->x = p->x + t2 * dx;
+ q->y = p->y + t2 * dy;
+ }
+ if (t1 > 0.0) {
+ p->x += t1 * dx;
+ p->y += t1 * dy;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_PolyRectClip --
+ *
+ * Clips the given polygon to a rectangular region. The resulting
+ * polygon is returned. Note that the resulting polyon may be complex,
+ * connected by zero width/height segments. The drawing routine (such as
+ * XFillPolygon) will not draw a connecting segment.
+ *
+ * Reference:
+ * Liang Y. D. and Brian A. Barsky, "Analysis and Algorithm for
+ * Polygon Clipping", Communications of ACM, Vol. 26,
+ * p.868-877, 1983
+ *
+ * Results:
+ * Returns the number of points in the clipped polygon. The points of the
+ * clipped polygon are stored in *outputPts*.
+ *
+ *---------------------------------------------------------------------------
+ */
+#define EPSILON FLT_EPSILON
+#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++
+#define LastVertex(vx, vy) r->x=(vx), r->y=(vy), count++
+
+int
+Blt_PolyRectClip(
+ Region2d *regionPtr, /* Rectangular region clipping the polygon. */
+ Point2d *points, /* Points of polygon to be clipped. */
+ int nPoints, /* # of points in polygon. */
+ Point2d *clipPts) /* (out) Points of clipped polygon. */
+{
+ Point2d *p; /* First vertex of input polygon edge. */
+ Point2d *pend;
+ Point2d *q; /* Last vertex of input polygon edge. */
+ Point2d *r;
+ int count;
+
+ r = clipPts;
+ count = 0; /* Counts # of vertices in output polygon. */
+
+ points[nPoints] = points[0];
+ for (p = points, q = p + 1, pend = p + nPoints; p < pend; p++, q++) {
+ double dx, dy;
+ double tin1, tin2, tinx, tiny;
+ double xin, yin, xout, yout;
+
+ dx = q->x - p->x; /* X-direction */
+ dy = q->y - p->y; /* Y-direction */
+
+ if (FABS(dx) < EPSILON) {
+ dx = (p->x > regionPtr->left) ? -EPSILON : EPSILON ;
+ }
+ if (FABS(dy) < EPSILON) {
+ dy = (p->y > regionPtr->top) ? -EPSILON : EPSILON ;
+ }
+
+ if (dx > 0.0) { /* Left */
+ xin = regionPtr->left;
+ xout = regionPtr->right + 1.0;
+ } else { /* Right */
+ xin = regionPtr->right + 1.0;
+ xout = regionPtr->left;
+ }
+ if (dy > 0.0) { /* Top */
+ yin = regionPtr->top;
+ yout = regionPtr->bottom + 1.0;
+ } else { /* Bottom */
+ yin = regionPtr->bottom + 1.0;
+ yout = regionPtr->top;
+ }
+
+ tinx = (xin - p->x) / dx;
+ tiny = (yin - p->y) / dy;
+
+ if (tinx < tiny) { /* Hits x first */
+ tin1 = tinx;
+ tin2 = tiny;
+ } else { /* Hits y first */
+ tin1 = tiny;
+ tin2 = tinx;
+ }
+
+ if (tin1 <= 1.0) {
+ if (tin1 > 0.0) {
+ AddVertex(xin, yin);
+ }
+ if (tin2 <= 1.0) {
+ double toutx, touty, tout1;
+
+ toutx = (xout - p->x) / dx;
+ touty = (yout - p->y) / dy;
+ tout1 = MIN(toutx, touty);
+
+ if ((tin2 > 0.0) || (tout1 > 0.0)) {
+ if (tin2 <= tout1) {
+ if (tin2 > 0.0) {
+ if (tinx > tiny) {
+ AddVertex(xin, p->y + tinx * dy);
+ } else {
+ AddVertex(p->x + tiny * dx, yin);
+ }
+ }
+ if (tout1 < 1.0) {
+ if (toutx < touty) {
+ AddVertex(xout, p->y + toutx * dy);
+ } else {
+ AddVertex(p->x + touty * dx, yout);
+ }
+ } else {
+ AddVertex(q->x, q->y);
+ }
+ } else {
+ if (tinx > tiny) {
+ AddVertex(xin, yout);
+ } else {
+ AddVertex(xout, yin);
+ }
+
+ }
+ }
+ }
+ }
+ }
+ if (count > 0) {
+ LastVertex(clipPts[0].x, clipPts[0].y);
+ }
+ return count;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetProjection --
+ *
+ * Computes the projection of a point on a line. The line (given by two
+ * points), is assumed the be infinite.
+ *
+ * Compute the slope (angle) of the line and rotate it 90 degrees. Using
+ * the slope-intercept method (we know the second line from the sample
+ * test point and the computed slope), then find the intersection of both
+ * lines. This will be the projection of the sample point on the first
+ * line.
+ *
+ * Results:
+ * Returns the coordinates of the projection on the line.
+ *
+ *---------------------------------------------------------------------------
+ */
+Point2d
+Blt_GetProjection(
+ int x, int y, /* Screen coordinates of the sample point. */
+ Point2d *p, Point2d *q) /* Line segment to project point onto */
+{
+ double dx, dy;
+ Point2d t;
+
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+
+ /* Test for horizontal and vertical lines */
+ if (FABS(dx) < DBL_EPSILON) {
+ t.x = p->x, t.y = (double)y;
+ } else if (FABS(dy) < DBL_EPSILON) {
+ t.x = (double)x, t.y = p->y;
+ } else {
+ double m1, m2; /* Slope of both lines */
+ double b1, b2; /* y-intercepts */
+ double midX, midY; /* Midpoint of line segment. */
+ double ax, ay, bx, by;
+
+ /* Compute the slope and intercept of PQ. */
+ m1 = (dy / dx);
+ b1 = p->y - (p->x * m1);
+
+ /*
+ * Compute the slope and intercept of a second line segment: one that
+ * intersects through sample X-Y coordinate with a slope perpendicular
+ * to original line.
+ */
+
+ /* Find midpoint of PQ. */
+ midX = (p->x + q->x) * 0.5;
+ midY = (p->y + q->y) * 0.5;
+
+ /* Rotate the line 90 degrees */
+ ax = midX - (0.5 * dy);
+ ay = midY - (0.5 * -dx);
+ bx = midX + (0.5 * dy);
+ by = midY + (0.5 * -dx);
+
+ m2 = (ay - by) / (ax - bx);
+ b2 = y - (x * m2);
+
+ /*
+ * Given the equations of two lines which contain the same point,
+ *
+ * y = m1 * x + b1
+ * y = m2 * x + b2
+ *
+ * solve for the intersection.
+ *
+ * x = (b2 - b1) / (m1 - m2)
+ * y = m1 * x + b1
+ *
+ */
+
+ t.x = (b2 - b1) / (m1 - m2);
+ t.y = m1 * t.x + b1;
+ }
+ return t;
+}
+
+typedef struct {
+ double hue, sat, val;
+} HSV;
+
+#define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
+ (c)->green = (int)((g) * 65535.0), \
+ (c)->blue = (int)((b) * 65535.0))
+
+#ifdef notdef
+void
+Blt_XColorToHSV(XColor *colorPtr, HSV *hsvPtr)
+{
+ unsigned short max, min;
+ double range;
+ unsigned short *colorValues;
+
+ /* Find the minimum and maximum RGB intensities */
+ colorValues = (unsigned short *)&colorPtr->red;
+ max = MAX3(colorValues[0], colorValues[1], colorValues[2]);
+ min = MIN3(colorValues[0], colorValues[1], colorValues[2]);
+
+ hsvPtr->val = (double)max / 65535.0;
+ hsvPtr->hue = hsvPtr->sat = 0.0;
+
+ range = (double)(max - min);
+ if (max != min) {
+ hsvPtr->sat = range / (double)max;
+ }
+ if (hsvPtr->sat > 0.0) {
+ double red, green, blue;
+
+ /* Normalize the RGB values */
+ red = (double)(max - colorPtr->red) / range;
+ green = (double)(max - colorPtr->green) / range;
+ blue = (double)(max - colorPtr->blue) / range;
+
+ if (colorPtr->red == max) {
+ hsvPtr->hue = (blue - green);
+ } else if (colorPtr->green == max) {
+ hsvPtr->hue = 2 + (red - blue);
+ } else if (colorPtr->blue == max) {
+ hsvPtr->hue = 4 + (green - red);
+ }
+ hsvPtr->hue *= 60.0;
+ } else {
+ hsvPtr->sat = 0.5;
+ }
+ if (hsvPtr->hue < 0.0) {
+ hsvPtr->hue += 360.0;
+ }
+}
+
+void
+Blt_HSVToXColor(HSV *hsvPtr, XColor *colorPtr)
+{
+ double hue, p, q, t;
+ double frac;
+ int quadrant;
+
+ if (hsvPtr->val < 0.0) {
+ hsvPtr->val = 0.0;
+ } else if (hsvPtr->val > 1.0) {
+ hsvPtr->val = 1.0;
+ }
+ if (hsvPtr->sat == 0.0) {
+ SetColor(colorPtr, hsvPtr->val, hsvPtr->val, hsvPtr->val);
+ return;
+ }
+ hue = FMOD(hsvPtr->hue, 360.0) / 60.0;
+ quadrant = (int)floor(hue);
+ frac = hsvPtr->hue - quadrant;
+ p = hsvPtr->val * (1 - (hsvPtr->sat));
+ q = hsvPtr->val * (1 - (hsvPtr->sat * frac));
+ t = hsvPtr->val * (1 - (hsvPtr->sat * (1 - frac)));
+
+ switch (quadrant) {
+ case 0:
+ SetColor(colorPtr, hsvPtr->val, t, p);
+ break;
+ case 1:
+ SetColor(colorPtr, q, hsvPtr->val, p);
+ break;
+ case 2:
+ SetColor(colorPtr, p, hsvPtr->val, t);
+ break;
+ case 3:
+ SetColor(colorPtr, p, q, hsvPtr->val);
+ break;
+ case 4:
+ SetColor(colorPtr, t, p, hsvPtr->val);
+ break;
+ case 5:
+ SetColor(colorPtr, hsvPtr->val, p, q);
+ break;
+ }
+}
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_AdjustViewport --
+ *
+ * Adjusts the offsets of the viewport according to the scroll mode.
+ * This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ * "canvas" The viewport scrolls within the range of world
+ * coordinates. This way the viewport always displays
+ * a full page of the world. If the world is smaller
+ * than the viewport, then (bizarrely) the world and
+ * viewport are inverted so that the world moves up
+ * and down within the viewport.
+ *
+ * "listbox" The viewport can scroll beyond the range of world
+ * coordinates. Every entry can be displayed at the
+ * top of the viewport. This also means that the
+ * scrollbar thumb weirdly shrinks as the last entry
+ * is scrolled upward.
+ *
+ * Results:
+ * The corrected offset is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_AdjustViewport(int offset, int worldSize, int windowSize, int scrollUnits,
+ int scrollMode)
+{
+ switch (scrollMode) {
+ case BLT_SCROLL_MODE_CANVAS:
+
+ /*
+ * Canvas-style scrolling allows the world to be scrolled within the
+ * window.
+ */
+ if (worldSize < windowSize) {
+ if ((worldSize - offset) > windowSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset > 0) {
+ offset = 0;
+ }
+ } else {
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ }
+ break;
+
+ case BLT_SCROLL_MODE_LISTBOX:
+ if (offset < 0) {
+ offset = 0;
+ }
+ if (offset >= worldSize) {
+ offset = worldSize - scrollUnits;
+ }
+ break;
+
+ case BLT_SCROLL_MODE_HIERBOX:
+
+ /*
+ * Hierbox-style scrolling allows the world to be scrolled within the
+ * window.
+ */
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ break;
+ }
+ return offset;
+}
+
+int
+Blt_GetScrollInfoFromObj(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
+ int *offsetPtr, int worldSize, int windowSize,
+ int scrollUnits, int scrollMode)
+{
+ char c;
+ const char *string;
+ int length;
+ int offset;
+
+ offset = *offsetPtr;
+ string = Tcl_GetStringFromObj(objv[0], &length);
+ c = string[0];
+ if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
+ double fract;
+ int count;
+
+ if (objc != 3) {
+ return TCL_ERROR;
+ }
+ /* Scroll number unit/page */
+ if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetStringFromObj(objv[2], &length);
+ c = string[0];
+ if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
+ fract = (double)count *scrollUnits;
+ } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count * windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"",
+ Tcl_GetString(objv[2]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += (int)fract;
+ } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
+ double fract;
+
+ if (objc != 2) {
+ return TCL_ERROR;
+ }
+ /* moveto fraction */
+ if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = (int)(worldSize * fract);
+ } else {
+ double fract;
+ int count;
+
+ /* Treat like "scroll units" */
+ if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count *scrollUnits;
+ offset += (int)fract;
+ }
+ *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+ scrollMode);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_UpdateScrollbar --
+ *
+ * Invoke a TCL command to the scrollbar, defining the new position and
+ * length of the scroll. See the Tk documentation for further information
+ * on the scrollbar. It is assumed the scrollbar command prefix is
+ * valid.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Scrollbar is commanded to change position and/or size.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_UpdateScrollbar(
+ Tcl_Interp *interp,
+ Tcl_Obj *scrollCmdObjPtr, /* Scrollbar command prefix. May be
+ * several words */
+ int first, int last, int width)
+{
+ Tcl_Obj *cmdObjPtr;
+ double firstFract, lastFract;
+
+ firstFract = 0.0, lastFract = 1.0;
+ if (width > 0) {
+ firstFract = (double)first / (double)width;
+ lastFract = (double)last / (double)width;
+ }
+ cmdObjPtr = Tcl_DuplicateObj(scrollCmdObjPtr);
+ Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(firstFract));
+ Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(lastFract));
+ Tcl_IncrRefCount(cmdObjPtr);
+ if (Tcl_EvalObjEx(interp, cmdObjPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DecrRefCount(cmdObjPtr);
+
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGCFromDrawable --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget. This
+ * is needed because the certain GC parameters (like dashes) can not be
+ * set via XCreateGC, therefore there is no way for Tk's hashing
+ * mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGCFromDrawable(
+ Display *display,
+ Drawable drawable,
+ unsigned long gcMask,
+ XGCValues *valuePtr)
+{
+ GC newGC;
+
+#ifdef WIN32
+ newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
+#else
+ newGC = XCreateGC(display, drawable, gcMask, valuePtr);
+#endif
+ return newGC;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGC --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget. This
+ * is needed because the certain GC parameters (like dashes) can not be
+ * set via XCreateGC, therefore there is no way for Tk's hashing
+ * mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGC(
+ Tk_Window tkwin,
+ unsigned long gcMask,
+ XGCValues *valuePtr)
+{
+ GC gc;
+ Pixmap pixmap;
+ Drawable drawable;
+ Display *display;
+
+ pixmap = None;
+ drawable = Tk_WindowId(tkwin);
+ display = Tk_Display(tkwin);
+ if (drawable == None) {
+ Drawable root;
+ int depth;
+
+ root = Tk_RootWindow(tkwin);
+ depth = Tk_Depth(tkwin);
+
+ if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
+ drawable = root;
+ } else {
+ pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
+ drawable = pixmap;
+ Blt_SetDrawableAttribs(display, drawable, 1, 1, depth,
+ Tk_Colormap(tkwin), Tk_Visual(tkwin));
+ }
+ }
+ gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
+ if (pixmap != None) {
+ Tk_FreePixmap(display, pixmap);
+ }
+ return gc;
+}
+
+void
+Blt_FreePrivateGC(Display *display, GC gc)
+{
+ Tk_FreeXId(display, (XID) XGContextFromGC(gc));
+ XFreeGC(display, gc);
+}
+
+#ifndef WIN32
+void
+Blt_SetDashes(Display *display, GC gc, Blt_Dashes *dashesPtr)
+{
+ XSetDashes(display, gc, dashesPtr->offset, (const char *)dashesPtr->values,
+ (int)strlen((char *)dashesPtr->values));
+}
+#endif
+
+void
+Blt_ScreenDPI(Tk_Window tkwin, unsigned int *xPtr, unsigned int *yPtr)
+{
+ Screen *screen;
+
+#define MM_INCH 25.4
+ screen = Tk_Screen(tkwin);
+ *xPtr = (unsigned int)((WidthOfScreen(screen) * MM_INCH) /
+ WidthMMOfScreen(screen));
+ *yPtr = (unsigned int)((HeightOfScreen(screen) * MM_INCH) /
+ HeightMMOfScreen(screen));
+}
+
+void
+Blt_Draw2DSegments(
+ Display *display,
+ Drawable drawable,
+ GC gc,
+ Segment2d *segments,
+ int nSegments)
+{
+ XSegment *dp, *xsegments;
+ Segment2d *sp, *send;
+
+ xsegments = Blt_Malloc(nSegments * sizeof(XSegment));
+ if (xsegments == NULL) {
+ return;
+ }
+ dp = xsegments;
+ for (sp = segments, send = sp + nSegments; sp < send; sp++) {
+ dp->x1 = (short int)sp->p.x;
+ dp->y1 = (short int)sp->p.y;
+ dp->x2 = (short int)sp->q.x;
+ dp->y2 = (short int)sp->q.y;
+ dp++;
+ }
+ XDrawSegments(display, drawable, gc, xsegments, nSegments);
+ Blt_Free(xsegments);
+}
+
+void
+Blt_DrawArrowOld(Display *display, Drawable drawable, GC gc, int x, int y, int w,
+ int h, int borderWidth, int orientation)
+{
+ XPoint arrow[4];
+ int s2, s;
+ int ax, ay;
+
+#define ARROW_IPAD 1
+ w -= 2 * (ARROW_IPAD + borderWidth);
+ h -= 2 * (ARROW_IPAD + borderWidth);
+ x += ARROW_IPAD + borderWidth;
+ y += ARROW_IPAD + borderWidth;
+
+ w |= 0x01;
+ h |= 0x01;
+ s = MIN(w, h);
+ s2 = s / 2;
+ ax = x + w / 2;
+ ay = y + h / 2;
+
+ switch (orientation) {
+ case ARROW_UP:
+ ay -= s2/2 + 1;
+ arrow[2].x = arrow[0].x = ax;
+ arrow[2].y = arrow[0].y = ay;
+ arrow[0].x = ax + s2 + 1;
+ arrow[1].x = ax - s2;
+ arrow[0].y = arrow[1].y = ay + s2 + 1;
+ fprintf(stderr, "up arrow %d,%d %d,%d %d,%d\n",
+ arrow[0].x, arrow[0].y,
+ arrow[1].x, arrow[1].y,
+ arrow[2].x, arrow[2].y);
+ break;
+ case ARROW_DOWN:
+ ay -= s2/2;
+ arrow[3].x = arrow[0].x = ax;
+ arrow[3].y = arrow[0].y = ay + s2 + 1;
+ arrow[1].x = ax + s2 + 1;
+ arrow[2].x = ax - s2;
+ arrow[2].y = arrow[1].y = ay;
+ fprintf(stderr, "down arrow %d,%d %d,%d %d,%d\n",
+ arrow[0].x, arrow[0].y,
+ arrow[1].x, arrow[1].y,
+ arrow[2].x, arrow[2].y);
+ break;
+ case ARROW_LEFT:
+ ax -= s2 / 2;
+ arrow[3].x = arrow[0].x = ax;
+ arrow[3].y = arrow[0].y = ay;
+ arrow[1].y = ay - s2;
+ arrow[2].y = ay + s2 + 1;
+ arrow[2].x = arrow[1].x = ax + s2 + 1;
+ break;
+ case ARROW_RIGHT:
+ ax -= s2 / 2;
+ arrow[3].x = arrow[0].x = ax + s2 + 1;
+ arrow[3].y = arrow[0].y = ay;
+ arrow[1].y = ay - s2;
+ arrow[2].y = ay + s2;
+ arrow[2].x = arrow[1].x = ax;
+ break;
+ }
+ XFillPolygon(display, drawable, gc, arrow, 3, Convex, CoordModeOrigin);
+}
+
+void
+Blt_DrawArrow(Display *display, Drawable drawable, XColor *color, int x, int y,
+ int w, int h, int borderWidth, int orientation)
+{
+ int s;
+ int s2;
+ int ax, ay;
+ int dx, dy;
+ GC gc;
+
+#define ARROW_IPAD 1
+ w -= 2 * (ARROW_IPAD + borderWidth);
+ h -= 2 * (ARROW_IPAD + borderWidth);
+ x += ARROW_IPAD + borderWidth;
+ y += ARROW_IPAD + borderWidth;
+
+ s = MIN(w, h);
+ s2 = (s / 2) + 1;
+ ax = x + w / 2;
+ ay = y + h / 2;
+
+ gc = Tk_GCForColor(color, drawable);
+ switch (orientation) {
+ case ARROW_UP:
+ ay -= s2 / 2;
+ for (dx = 0; dx < s2; dx++, ay++) {
+ XDrawLine(display, drawable, gc, ax - dx, ay, ax + dx, ay);
+ }
+ break;
+ case ARROW_DOWN:
+ ay += s2 / 2;
+ for (dx = 0; dx < s2; dx++, ay--) {
+ XDrawLine(display, drawable, gc, ax - dx, ay, ax + dx, ay);
+ }
+ break;
+ case ARROW_LEFT:
+ ax -= s2 / 2;
+ for (dy = 0; dy < s2; dy++, ax++) {
+ XDrawLine(display, drawable, gc, ax, ay - dy, ax, ay + dy);
+ }
+ break;
+ case ARROW_RIGHT:
+ ax += s2 / 2;
+ for (dy = 0; dy < s2; dy++, ax--) {
+ XDrawLine(display, drawable, gc, ax, ay - dy, ax, ay + dy);
+ }
+ break;
+ }
+}
+
+long
+Blt_MaxRequestSize(Display *display, size_t elemSize)
+{
+ static long maxSizeBytes = 0L;
+
+ if (maxSizeBytes == 0L) {
+ long size;
+#ifndef WIN32
+ size = XExtendedMaxRequestSize(display);
+ if (size == 0) {
+ size = XMaxRequestSize(display);
+ }
+#else
+ size = XMaxRequestSize(display);
+#endif
+ size -= (4 * elemSize);
+ /* maxSizeBytes = (size * 4); */
+ maxSizeBytes = size;
+ }
+ return (maxSizeBytes / elemSize);
+}
+
+void
+Blt_GetLineExtents(size_t nPoints, Point2d *points, Region2d *r)
+{
+ Point2d *p, *pend;
+ r->top = r->left = DBL_MAX;
+ r->bottom = r->right = -DBL_MAX;
+ for (p = points, pend = p + nPoints; p < pend; p++) {
+ if (r->top > p->y) {
+ r->top = p->y;
+ }
+ if (r->bottom < p->y) {
+ r->bottom = p->y;
+ }
+ if (r->left > p->x) {
+ r->left = p->x;
+ }
+ if (r->right < p->x) {
+ r->right = p->x;
+ }
+ }
+}
+
+#undef Blt_Fill3DRectangle
+void
+Blt_Fill3DRectangle(
+ Tk_Window tkwin, /* Window for which border was allocated. */
+ Drawable drawable, /* X window or pixmap in which to draw. */
+ Tk_3DBorder border, /* Token for border to draw. */
+ int x, int y,
+ int width, int height, /* Outside area of rectangular region. */
+ int borderWidth, /* Desired width for border, in pixels. Border
+ * will be *inside* region. */
+ int relief) /* Indicates 3D effect: TK_RELIEF_FLAT,
+ * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+#ifndef notdef
+ if ((borderWidth > 1) && (width > 2) && (height > 2) &&
+ ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
+ GC lightGC, darkGC;
+ int x2, y2;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+#define TK_3D_LIGHT2_GC TK_3D_DARK_GC+1
+#define TK_3D_DARK2_GC TK_3D_DARK_GC+2
+ if (relief == TK_RELIEF_RAISED) {
+ lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+ darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+#ifdef notdef
+ darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+ } else {
+
+ lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+#ifdef notdef
+ lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+ darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+ }
+ XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
+ x++, y++, width -= 2, height -= 2, borderWidth--;
+ }
+#endif
+ Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width, height,
+ borderWidth, relief);
+}
+
+
+#undef Blt_Draw3DRectangle
+void
+Blt_Draw3DRectangle(
+ Tk_Window tkwin, /* Window for which border was allocated. */
+ Drawable drawable, /* X window or pixmap in which to draw. */
+ Tk_3DBorder border, /* Token for border to draw. */
+ int x, int y,
+ int width, int height, /* Outside area of rectangular region. */
+ int borderWidth, /* Desired width for border, in pixels. Border
+ * will be *inside* region. */
+ int relief) /* Indicates 3D effect: TK_RELIEF_FLAT,
+ * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+#ifndef notdef
+ if ((borderWidth > 1) && (width > 2) && (height > 2) &&
+ ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
+ GC lightGC, darkGC;
+ int x2, y2;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+ if (relief == TK_RELIEF_RAISED) {
+ lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+ darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+#ifdef notdef
+ darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+ } else {
+ lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+#ifdef notdef
+ lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
+#endif
+ darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+ }
+ XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
+ x++, y++, width -= 2, height -= 2, borderWidth--;
+ }
+#endif
+ Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
+ borderWidth, relief);
+}
+
+#ifdef notdef
+typedef struct {
+ Screen *screen;
+ Visual *visual;
+ Colormap colormap;
+ Tk_Uid nameUid;
+} BorderKey;
+
+typedef struct {
+ Screen *screen; /* Screen on which the border will be used. */
+ Visual *visual; /* Visual for all windows and pixmaps using
+ * the border. */
+ int depth; /* Number of bits per pixel of drawables where
+ * the border will be used. */
+ Colormap colormap; /* Colormap out of which pixels are
+ * allocated. */
+ int refCount; /* Number of active uses of this color (each
+ * active use corresponds to a call to
+ * Blt_Get3DBorder). If this count is 0, then
+ * this structure is no longer valid and it
+ * isn't present in borderTable: it is being
+ * kept around only because there are objects
+ * referring to it. The structure is freed
+ * when refCount is 0. */
+
+ XColor *bgColorPtr; /* Color of face. */
+ XColor *shadows[4];
+
+ Pixmap darkStipple; /* Stipple pattern to use for drawing shadows
+ * areas. Used for displays with <= 64 colors
+ * or where colormap has filled up. */
+ Pixmap lightStipple; /* Stipple pattern to use for drawing shadows
+ * areas. Used for displays with <= 64 colors
+ * or where colormap has filled up. */
+ GC bgGC; /* Used (if necessary) to draw areas in the
+ * background color. */
+ GC lightGC, darkGC;
+ Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in order to
+ * delete structure). */
+
+ struct _Blt_3DBorder *nextPtr;
+} Border, *Blt_3DBorder;
+
+
+void
+Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
+ height, borderWidth, relief)
+ Tk_Window tkwin; /* Window for which border was allocated. */
+ Drawable drawable; /* X window or pixmap in which to draw. */
+ Blt_3DBorder *borderPtr; /* Border to draw. */
+ int x, y, width, height; /* Outside area of rectangular region. */
+ int borderWidth; /* Desired width for border, in
+ * pixels. Border will be *inside* region. */
+ int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
+ * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+ if ((width > (2 * borderWidth)) && (height > (2 * borderWidth))) {
+ int x2, y2;
+ int i;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ XSetForeground(borderPtr->lightGC, borderPtr->shadows[0]);
+ XSetForeground(borderPtr->darkGC, borderPtr->shadows[3]);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
+ x, y, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
+ x, y, x, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
+ x2, y, x2, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
+ x2, y2, x, y2);
+ XSetForeground(borderPtr->lightGC, borderPtr->shadows[1]);
+ XSetForeground(borderPtr->darkGC, borderPtr->shadows[2]);
+ for (i = 1; i < (borderWidth - 1); i++) {
+
+ /*
+ * +----------
+ * |+-------
+ * ||+-----
+ * |||
+ * |||
+ * ||
+ * |
+ */
+ x++, y++, x2--, y2--;
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
+ x, y, x2, y);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
+ x, y, x, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
+ x2, y, x2, y2);
+ XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
+ x2, y2, x, y2);
+ }
+ }
+}
+
+void
+Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width, height, borderWidth,
+ relief)
+ Tk_Window tkwin; /* Window for which border was allocated. */
+ Drawable drawable; /* X window or pixmap in which to draw. */
+ Tk_3DBorder border; /* Token for border to draw. */
+ int x, y, width, height; /* Outside area of rectangular region. */
+ int borderWidth; /* Desired width for border, in
+ * pixels. Border will be *inside* region. */
+ int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
+ * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
+{
+ Blt_3DBorder *borderPtr;
+
+ XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC, x, y, width,
+ height);
+ if ((borderWidth > 0) && (relief != BLT_RELIEF_FLAT)) {
+ Blt_Draw3DRectangle(tkwin, drawable, borderPtr, x, y, width, height,
+ borderWidth, relief);
+ }
+}
+
+
+void
+FreeBorder(display, borderPtr)
+ Display *display;
+ Border *borderPtr;
+{
+ int i;
+
+ if (borderPtr->bgColorPtr != NULL) {
+ Tk_FreeColor(display, borderPtr->bgColorPtr);
+ }
+ for (i = 0; i < 4; i++) {
+ Tk_FreeColor(display, borderPtr->shadows[i]);
+ }
+ if (borderPtr->darkGC != NULL) {
+ Blt_FreePrivateGC(display, borderPtr->darkGC);
+ }
+ if (borderPtr->lightGC != NULL) {
+ Blt_FreePrivateGC(tkwin, borderPtr->lightGC);
+ }
+ if (borderPtr->bgGC != NULL) {
+ Blt_FreePrivateGC(tkwin, borderPtr->bgGC);
+ }
+ Blt_Free(borderPtr);
+}
+
+void
+Blt_Free3DBorder(display, border)
+ Display *display;
+ Blt_3DBorder border;
+{
+ Border *borderPtr = (Border *)border;
+
+ borderPtr->refCount--;
+ if (borderPtr->refCount >= 0) {
+ /* Search for the border in the bucket. Start at the head. */
+ headPtr = Blt_GetHashValue(borderPtr->hashPtr);
+ lastPtr = NULL;
+ while ((headPtr != borderPtr) && (headPtr != NULL)) {
+ lastPtr = headPtr;
+ headPtr = headPtr->next;
+ }
+ if (headPtr == NULL) {
+ return; /* This can't happen. It means that we could
+ * not find the border. */
+ }
+ if (lastPtr != NULL) {
+ lastPtr->next = borderPtr->next;
+ } else {
+ Tcl_DeleteHashEntry(borderPtr->hashPtr);
+ }
+ FreeBorder(display, borderPtr);
+ }
+}
+
+Blt_3DBorder *
+Blt_Get3DBorder(Tcl_Interp *interp, Tk_Window tkwin, const char *borderName)
+{
+ Blt_3DBorder *borderPtr, *lastBorderPtr;
+ Blt_HashEntry *hPtr;
+ XColor *bgColorPtr;
+ char **argv;
+ const char *colorName;
+ int argc;
+ int isNew;
+
+ lastBorderPtr = NULL;
+ hPtr = Tcl_CreateHashEntry(&dataPtr->borderTable, borderName, &isNew);
+ if (!isNew) {
+ borderPtr = lastBorderPtr = Blt_GetHashValue(hPtr);
+ while (borderPtr != NULL) {
+ if ((Tk_Screen(tkwin) == borderPtr->screen) &&
+ (Tk_Colormap(tkwin) == borderPtr->colormap)) {
+ borderPtr->refCount++;
+ return borderPtr;
+ }
+ borderPtr = borderPtr->nextPtr;
+ }
+ }
+ /* Create a new border. */
+ argv = NULL;
+ bgColorPtr = NULL;
+
+ if (Tcl_SplitList(interp, borderName, &argc, &argv) != TCL_OK) {
+ goto error;
+ }
+ colorName = borderName;
+ bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
+ if (bgColorPtr == NULL) {
+ goto error;
+ }
+
+ /* Create a new border */
+ borderPtr = Blt_AssertCalloc(1, sizeof(Blt_3DBorder));
+ borderPtr->screen = Tk_Screen(tkwin);
+ borderPtr->visual = Tk_Visual(tkwin);
+ borderPtr->depth = Tk_Depth(tkwin);
+ borderPtr->colormap = Tk_Colormap(tkwin);
+ borderPtr->refCount = 1;
+ borderPtr->bgColorPtr = bgColorPtr;
+ borderPtr->darkGC = Blt_GetPrivateGC(tkwin, 0, NULL);
+ borderPtr->lightGC = Blt_GetPrivateGC(tkwin, 0, NULL);
+ borderPtr->hashPtr = lastBorderPtr->hashPtr;
+ lastBorderPtr->nextPtr = lastBorderPtr;
+ {
+ HSV hsv;
+ XColor color;
+ double sat, sat0, diff, step, hstep;
+ int count;
+
+ /* Convert the face (background) color to HSV */
+ Blt_XColorToHSV(borderPtr->bgColorPtr, &hsv);
+
+ /* Using the color as the baseline intensity, pick a set of colors
+ * around the intensity. */
+#define UFLOOR(x,u) (floor((x)*(u))/(u))
+ diff = hsv.sat - UFLOOR(hsv.sat, 0.2);
+ sat = 0.1 + (diff - 0.1);
+ sat0 = hsv.sat;
+ count = 0;
+ for (sat = 0.1 + (diff - 0.1); sat <= 1.0; sat += 0.2) {
+ if (FABS(sat0 - sat) >= 0.1) {
+ hsv.sat = sat;
+ Blt_HSVToXColor(&hsv, &color);
+ borderPtr->shadows[count] = Tk_GetColorByValue(tkwin, &color);
+ count++;
+ }
+ }
+ }
+ Blt_SetHashValue(hPtr, borderPtr);
+ if (argv != NULL) {
+ Blt_Free(argv);
+ }
+ return TCL_OK;
+
+ error:
+ if (argv != NULL) {
+ Blt_Free(argv);
+ }
+ if (bgColorPtr != NULL) {
+ Tk_FreeColor(bgColorPtr);
+ }
+ if (isNew) {
+ Blt_DeleteHashEntry(&borderTable, hPtr);
+ }
+ return NULL;
+}
+
+
+#endif
+
+typedef struct {
+ float x, y, z;
+} Vector3f;
+
+typedef struct {
+ float x, y, z, w;
+} Vector4f;
+
+typedef Vector4f Quaternion;
+
+typedef float Matrix3x3f[3][3];
+
+typedef struct _ArcBall {
+ Vector3f click;
+ Vector3f drag;
+ float xScale;
+ float yScale;
+} ArcBall;
+
+/*
+ * Arcball sphere constants:
+ * Diameter is 2.0f
+ * Radius is 1.0f
+ * Radius squared is 1.0f
+ */
+
+/* assuming IEEE-754 (float), which i believe has max precision of 7 bits */
+#define EPSILON FLT_EPSILON
+static INLINE float
+LengthVector3f(Vector3f *a)
+{
+ return sqrtf((a->x * a->x) + (a->y * a->y) + (a->z * a->z));
+}
+
+static INLINE float DotProductVector3f(Vector3f *a, Vector3f *b)
+{
+ return (a->x * b->x) + (a->y * b->y) + (a->z * b->z);
+}
+
+/**
+ * Calculate the cross product of two 3D vectors: c = a x b.
+ * "c" must not refer to the same memory location as "a" or "b".
+ */
+static INLINE void
+CrossProductVector3f(Vector3f *a, Vector3f *b, Vector3f *c)
+{
+ c->x = (a->y * b->z) - (b->y * a->z);
+ c->y = (a->z * b->x) - (b->z * a->x);
+ c->z = (a->x * b->y) - (b->x * a->y);
+}
+
+static void
+PointOnSphere (ArcBall *arcPtr, float x, float y, Vector3f *outPtr)
+{
+ float sx, sy;
+ float d;
+
+ /* Adjust point coords and scale down to range of [-1 ... 1] */
+ sx = (x * arcPtr->xScale) - 1.0f;
+ sy = 1.0f - (y * arcPtr->yScale);
+
+ /* Compute the square of the length of the vector to the point from the
+ * center. */
+ d = (sx * sx) + (sy * sy);
+
+ /* If the point is mapped outside of the sphere ...
+ * (length > radius squared)
+ */
+ if (d > 1.0f) {
+ float scale;
+
+ /* Compute a normalizing factor (radius / sqrt(length)) */
+ scale = 1.0f / sqrt (d);
+
+ /* Return the "normalized" vector, a point on the sphere */
+ outPtr->x = sx * scale;
+ outPtr->y = sy * scale;
+ outPtr->z = 0.0f;
+ } else { /* else it's on the inside */
+ /* Return a vector to a point mapped inside the sphere
+ * sqrt(radius squared - length) */
+ outPtr->x = sx;
+ outPtr->y = sy;
+ outPtr->z = sqrtf(1.0f - d);
+ }
+}
+
+static void
+SetArcBallBounds(ArcBall *arcPtr, float w, float h)
+{
+ if (w <= 1.0f ) {
+ w = 2.0f;
+ }
+ if (h <= 1.0f ) {
+ h = 2.0f;
+ }
+ /* Set adjustment factor for width/height */
+ arcPtr->xScale = 1.0f / ((w - 1.0f) * 0.5f);
+ arcPtr->yScale = 1.0f / ((h - 1.0f) * 0.5f);
+}
+
+static ArcBall *
+CreateArcBall (float w, float h)
+{
+ ArcBall *arcPtr;
+
+ arcPtr = Blt_AssertCalloc(1, sizeof(ArcBall));
+ SetArcBallBounds (arcPtr, w, h);
+ return arcPtr;
+}
+
+static void
+DestroyArcBall(ArcBall *arcPtr)
+{
+ if (arcPtr != NULL) {
+ Blt_Free(arcPtr);
+ }
+}
+
+/* Mouse down: Supply mouse position in x and y */
+static void
+ArcBallOnClick(ArcBall *arcPtr, float x, float y)
+{
+ PointOnSphere (arcPtr, x, y, &arcPtr->click);
+}
+
+/* Mouse drag, calculate rotation: Supply mouse position in x and y */
+static void
+ArcBallOnDrag(ArcBall *arcPtr, float x, float y, Quaternion *outPtr)
+{
+ /* Map the point to the sphere */
+ PointOnSphere(arcPtr, x, y, &arcPtr->drag);
+
+ /* Return the quaternion equivalent to the rotation */
+ if (outPtr != NULL) {
+ Vector3f perp;
+
+ /* Compute the vector perpendicular to the begin and end vectors */
+ CrossProductVector3f(&arcPtr->drag, &arcPtr->click, &perp);
+
+ /* Compute the length of the perpendicular vector */
+ if (LengthVector3f(&perp) > FLT_EPSILON) {
+ /* If its non-zero, we're ok, so return the perpendicular
+ * vector as the transform after all
+ */
+ outPtr->x = perp.x;
+ outPtr->y = perp.y;
+ outPtr->z = perp.z;
+ /* In the quaternion values, w is cosine (theta / 2),
+ * where theta is rotation angle
+ */
+ outPtr->w = DotProductVector3f(&arcPtr->click, &arcPtr->drag);
+ } else {
+ /* If it is zero, the begin and end vectors coincide,
+ * so return an identity transform
+ */
+ outPtr->x = outPtr->y = outPtr->z = outPtr->z = 0.0f;
+ }
+ }
+}
+
+
+static INLINE void
+SetRotationMatrix(const Quaternion* q, Matrix3x3f mat)
+{
+ float n, s;
+ float xs, ys, zs;
+ float wx, wy, wz;
+ float xx, xy, xz;
+ float yy, yz, zz;
+
+ assert(mat && q);
+
+ n = (q->x * q->x) + (q->y * q->y) + (q->z * q->z) + (q->w * q->w);
+
+ s = (n > 0.0f) ? (2.0f / n) : 0.0f;
+
+ xs = q->x * s;
+ ys = q->y * s;
+ zs = q->z * s;
+ wx = q->w * xs;
+ wy = q->w * ys;
+ wz = q->w * zs;
+ xx = q->x * xs;
+ xy = q->x * ys;
+ xz = q->x * zs;
+ yy = q->y * ys;
+ yz = q->y * zs;
+ zz = q->z * zs;
+
+ mat[0][0] = 1.0f - (yy + zz);
+ mat[0][1] = xy - wz;
+ mat[0][2] = xz + wy;
+ mat[1][0] = xy + wz;
+ mat[1][1] = 1.0f - (xx + zz);
+ mat[1][2] = yz - wx;
+ mat[2][0] = xz - wy;
+ mat[2][1] = yz + wx;
+ mat[2][2] = 1.0f - (xx + yy);
+}
+
+/* Return quaternion product qL * qR. Note: order is important!
+ * To combine rotations, use the product Mul(Second, First),
+ * which gives the effect of rotating by First then Second. */
+static INLINE void
+CombineRotations(Quaternion *a, Quaternion *b, Quaternion *result)
+{
+ result->w = (a->w * b->w) - (a->x * b->x) - (a->y * b->y) - (a->z * b->z);
+ result->x = (a->w * b->x) + (a->x * b->w) + (a->y * b->z) - (a->z * b->y);
+ result->y = (a->w * b->y) + (a->y * b->w) + (a->z * b->x) - (a->x * b->z);
+ result->z = (a->w * b->z) + (a->z * b->w) + (a->x * b->y) - (a->y * b->x);
+}
+
+
+static int
+GetQuaternionFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Quaternion *q)
+{
+ Tcl_Obj **objv;
+ int objc;
+ double x, y, z, w;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc != 4) {
+ Tcl_AppendResult(interp, "wrong number of elements in quaternion \"",
+ Tcl_GetString(objPtr), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Tcl_GetDoubleFromObj(interp, objv[0], &x) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[1], &y) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[2], &z) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[3], &w) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ q->x = x, q->y = y, q->z = z, q->w = w;
+ return TCL_OK;
+}
+
+static int
+ArcBallCombineOp(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+ Quaternion q1, q2, r;
+
+ if (GetQuaternionFromObj(interp, objv[2], &q1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (GetQuaternionFromObj(interp, objv[3], &q2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ CombineRotations(&q2, &q1, &r);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(r.x));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(r.y));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(r.w));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(r.z));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+static int
+ArcBallRotateOp(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ ArcBall *arcPtr;
+ Tcl_Obj *listObjPtr;
+ Quaternion q;
+ double x1, y1, x2, y2;
+ int w, h;
+
+ if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((Tcl_GetDoubleFromObj(interp, objv[4], &x1) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[5], &y1) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[6], &x2) != TCL_OK) ||
+ (Tcl_GetDoubleFromObj(interp, objv[7], &y2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ arcPtr = CreateArcBall((float)w, (float)h);
+ ArcBallOnClick(arcPtr, x1, y1);
+ ArcBallOnDrag(arcPtr, x2, y2, &q);
+ DestroyArcBall(arcPtr);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(q.x));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(q.y));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(q.w));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(q.z));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+static int
+ArcBallMatrixOp(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Matrix3x3f rot;
+ Tcl_Obj *listObjPtr;
+ Quaternion q;
+
+ if (GetQuaternionFromObj(interp, objv[2], &q) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ SetRotationMatrix(&q, rot);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[0][0]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[0][1]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[0][2]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[1][0]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[1][1]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[1][2]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[2][0]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[2][1]));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(rot[2][2]));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+static Blt_OpSpec arcBallOps[] =
+{
+ {"combine", 1, ArcBallCombineOp, 4, 4, "quat1 quat2",},
+ {"matrix", 1, ArcBallMatrixOp, 3, 3, "quat",},
+ {"rotate", 1, ArcBallRotateOp, 8, 8, "w h x1 y1 x2 y2",},
+};
+static int nArcBallOps = sizeof(arcBallOps) / sizeof(Blt_OpSpec);
+
+static int
+ArcBallCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ Tcl_ObjCmdProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nArcBallOps, arcBallOps, BLT_OP_ARG1,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (clientData, interp, objc, objv);
+}
+
+#undef Tk_GetPixmap
+Pixmap
+Blt_GetPixmap(Display *display, Drawable drawable, int w, int h, int depth,
+ int lineNum, const char *fileName)
+{
+ if (w <= 0) {
+ fprintf(stderr, "line %d of %s: width is %d\n",
+ lineNum, fileName, w);
+ abort();
+ }
+ if (h <= 0) {
+ fprintf(stderr, "line %d of %s: height is %d\n",
+ lineNum, fileName, h);
+ abort();
+ }
+ return Tk_GetPixmap(display, drawable, w, h, depth);
+}
diff --git a/src/bltGrPSOutput.C b/src/bltGrPSOutput.C
new file mode 100644
index 0000000..44cfdb8
--- /dev/null
+++ b/src/bltGrPSOutput.C
@@ -0,0 +1,1697 @@
+
+/*
+ * bltPs.c --
+ *
+ * This module implements general PostScript conversion routines.
+ *
+ * Copyright 1991-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "bltInt.h"
+#include <stdarg.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include "tkIntBorder.h"
+#include "bltDBuffer.h"
+#include "bltPicture.h"
+#include "bltPsInt.h"
+#include "tkDisplay.h"
+#include "tkFont.h"
+
+#define PS_MAXPATH 1500 /* Maximum number of components in a
+ * PostScript (level 1) path. */
+
+#define PICA_MM 2.83464566929
+#define PICA_INCH 72.0
+#define PICA_CM 28.3464566929
+
+static Tcl_Interp *psInterp = NULL;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_GetPica --
+ *
+ * Given a string, returns the number of pica corresponding to that
+ * string.
+ *
+ * Results:
+ * The return value is a standard TCL return result. If TCL_OK is
+ * returned, then everything went well and the pixel distance is stored
+ * at *doublePtr; otherwise TCL_ERROR is returned and an error message is
+ * left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Ps_GetPicaFromObj(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ Tcl_Obj *objPtr, /* String describing a number of
+ * pixels. */
+ int *picaPtr) /* Place to store converted result. */
+{
+ char *p;
+ double pica;
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ pica = strtod((char *)string, &p);
+ if (p == string) {
+ goto error;
+ }
+ if (pica < 0.0) {
+ goto error;
+ }
+ while ((*p != '\0') && isspace(UCHAR(*p))) {
+ p++;
+ }
+ switch (*p) {
+ case '\0': break;
+ case 'c': pica *= PICA_CM; p++; break;
+ case 'i': pica *= PICA_INCH; p++; break;
+ case 'm': pica *= PICA_MM; p++; break;
+ case 'p': p++; break;
+ default: goto error;
+ }
+ while ((*p != '\0') && isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p == '\0') {
+ *picaPtr = ROUND(pica);
+ return TCL_OK;
+ }
+ error:
+ Tcl_AppendResult(interp, "bad screen distance \"", string, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+}
+
+int
+Blt_Ps_GetPadFromObj(
+ Tcl_Interp *interp, /* Interpreter to send results back
+ * to */
+ Tcl_Obj *objPtr, /* Pixel value string */
+ Blt_Pad *padPtr)
+{
+ int side1, side2;
+ int objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((objc < 1) || (objc > 2)) {
+ Tcl_AppendResult(interp, "wrong # elements in padding list",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_Ps_GetPicaFromObj(interp, objv[0], &side1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ side2 = side1;
+ if ((objc > 1) &&
+ (Blt_Ps_GetPicaFromObj(interp, objv[1], &side2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ /* Don't update the pad structure until we know both values are okay. */
+ padPtr->side1 = side1;
+ padPtr->side2 = side2;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_ComputeBoundingBox --
+ *
+ * Computes the bounding box for the PostScript plot. First get the size
+ * of the plot (by default, it's the size of graph's X window). If the
+ * plot plus the page border is bigger than the designated paper size, or
+ * if the "-maxpect" option is turned on, scale the plot to the page.
+ *
+ * Note: All coordinates/sizes are in screen coordinates, not PostScript
+ * coordinates. This includes the computed bounding box and paper
+ * size. They will be scaled to printer points later.
+ *
+ * Results:
+ * Returns the height of the paper in screen coordinates.
+ *
+ * Side Effects:
+ * The graph dimensions (width and height) are changed to the requested
+ * PostScript plot size.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Ps_ComputeBoundingBox(PageSetup *setupPtr, int width, int height)
+{
+ int paperWidth, paperHeight;
+ int x, y, hSize, vSize, hBorder, vBorder;
+ float hScale, vScale, scale;
+
+ x = setupPtr->padLeft;
+ y = setupPtr->padTop;
+
+ hBorder = PADDING(setupPtr->xPad);
+ vBorder = PADDING(setupPtr->yPad);
+
+ if (setupPtr->flags & PS_LANDSCAPE) {
+ hSize = height;
+ vSize = width;
+ } else {
+ hSize = width;
+ vSize = height;
+ }
+ /*
+ * If the paper size wasn't specified, set it to the graph size plus the
+ * paper border.
+ */
+ paperWidth = (setupPtr->reqPaperWidth > 0) ? setupPtr->reqPaperWidth :
+ hSize + hBorder;
+ paperHeight = (setupPtr->reqPaperHeight > 0) ? setupPtr->reqPaperHeight :
+ vSize + vBorder;
+
+ /*
+ * Scale the plot size (the graph itself doesn't change size) if it's
+ * bigger than the paper or if -maxpect was set.
+ */
+ hScale = vScale = 1.0f;
+ if ((setupPtr->flags & PS_MAXPECT) || ((hSize + hBorder) > paperWidth)) {
+ hScale = (float)(paperWidth - hBorder) / (float)hSize;
+ }
+ if ((setupPtr->flags & PS_MAXPECT) || ((vSize + vBorder) > paperHeight)) {
+ vScale = (float)(paperHeight - vBorder) / (float)vSize;
+ }
+ scale = MIN(hScale, vScale);
+ if (scale != 1.0f) {
+ hSize = (int)((hSize * scale) + 0.5f);
+ vSize = (int)((vSize * scale) + 0.5f);
+ }
+ setupPtr->scale = scale;
+ if (setupPtr->flags & PS_CENTER) {
+ if (paperWidth > hSize) {
+ x = (paperWidth - hSize) / 2;
+ }
+ if (paperHeight > vSize) {
+ y = (paperHeight - vSize) / 2;
+ }
+ }
+ setupPtr->left = x;
+ setupPtr->bottom = y;
+ setupPtr->right = x + hSize - 1;
+ setupPtr->top = y + vSize - 1;
+ setupPtr->paperHeight = paperHeight;
+ setupPtr->paperWidth = paperWidth;
+ return paperHeight;
+}
+
+PostScript *
+Blt_Ps_Create(Tcl_Interp *interp, PageSetup *setupPtr)
+{
+ PostScript *psPtr;
+
+ psPtr = Blt_AssertMalloc(sizeof(PostScript));
+ psPtr->setupPtr = setupPtr;
+ psPtr->interp = interp;
+ Tcl_DStringInit(&psPtr->dString);
+ return psPtr;
+}
+
+void
+Blt_Ps_SetPrinting(PostScript *psPtr, int state)
+{
+ psInterp = ((state) && (psPtr != NULL)) ? psPtr->interp : NULL;
+}
+
+int
+Blt_Ps_IsPrinting(void)
+{
+ return (psInterp != NULL);
+}
+
+void
+Blt_Ps_Free(PostScript *psPtr)
+{
+ Tcl_DStringFree(&psPtr->dString);
+ Blt_Free(psPtr);
+}
+
+const char *
+Blt_Ps_GetValue(PostScript *psPtr, int *lengthPtr)
+{
+ *lengthPtr = strlen(Tcl_DStringValue(&psPtr->dString));
+ return Tcl_DStringValue(&psPtr->dString);
+}
+
+void
+Blt_Ps_SetInterp(PostScript *psPtr, Tcl_Interp *interp)
+{
+ Tcl_DStringResult(interp, &psPtr->dString);
+}
+
+char *
+Blt_Ps_GetScratchBuffer(PostScript *psPtr)
+{
+ return psPtr->scratchArr;
+}
+
+Tcl_Interp *
+Blt_Ps_GetInterp(PostScript *psPtr)
+{
+ return psPtr->interp;
+}
+
+Tcl_DString *
+Blt_Ps_GetDString(PostScript *psPtr)
+{
+ return &psPtr->dString;
+}
+
+int
+Blt_Ps_SaveFile(Tcl_Interp *interp, PostScript *psPtr, const char *fileName)
+{
+ Tcl_Channel channel;
+ int nWritten, nBytes;
+ char *bytes;
+
+ channel = Tcl_OpenFileChannel(interp, fileName, "w", 0660);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ nBytes = Tcl_DStringLength(&psPtr->dString);
+ bytes = Tcl_DStringValue(&psPtr->dString);
+ nWritten = Tcl_Write(channel, bytes, nBytes);
+ Tcl_Close(interp, channel);
+ if (nWritten != nBytes) {
+ Tcl_AppendResult(interp, "short file \"", fileName, (char *)NULL);
+ Tcl_AppendResult(interp, "\" : wrote ", Blt_Itoa(nWritten), " of ",
+ (char *)NULL);
+ Tcl_AppendResult(interp, Blt_Itoa(nBytes), " bytes.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+void
+Blt_Ps_VarAppend
+TCL_VARARGS_DEF(PostScript *, arg1)
+{
+ va_list argList;
+ PostScript *psPtr;
+ const char *string;
+
+ psPtr = TCL_VARARGS_START(PostScript, arg1, argList);
+ for (;;) {
+ string = va_arg(argList, char *);
+ if (string == NULL) {
+ break;
+ }
+ Tcl_DStringAppend(&psPtr->dString, string, -1);
+ }
+}
+
+void
+Blt_Ps_AppendBytes(PostScript *psPtr, const char *bytes, int length)
+{
+ Tcl_DStringAppend(&psPtr->dString, bytes, length);
+}
+
+void
+Blt_Ps_Append(PostScript *psPtr, const char *string)
+{
+ Tcl_DStringAppend(&psPtr->dString, string, -1);
+}
+
+void
+Blt_Ps_Format
+TCL_VARARGS_DEF(PostScript *, arg1)
+{
+ va_list argList;
+ PostScript *psPtr;
+ char *fmt;
+
+ psPtr = TCL_VARARGS_START(PostScript, arg1, argList);
+ fmt = va_arg(argList, char *);
+ vsnprintf(psPtr->scratchArr, POSTSCRIPT_BUFSIZ, fmt, argList);
+ va_end(argList);
+ Tcl_DStringAppend(&psPtr->dString, psPtr->scratchArr, -1);
+}
+
+int
+Blt_Ps_IncludeFile(Tcl_Interp *interp, Blt_Ps ps, const char *fileName)
+{
+ Tcl_Channel channel;
+ Tcl_DString dString;
+ char *buf;
+ char *libDir;
+ int nBytes;
+
+ buf = Blt_Ps_GetScratchBuffer(ps);
+
+ /*
+ * Read in a standard prolog file from file and append it to the
+ * PostScript output stored in the Tcl_DString in psPtr.
+ */
+
+ libDir = (char *)Tcl_GetVar(interp, "blt_library", TCL_GLOBAL_ONLY);
+ if (libDir == NULL) {
+ Tcl_AppendResult(interp, "couldn't find BLT script library:",
+ "global variable \"blt_library\" doesn't exist", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, libDir, -1);
+ Tcl_DStringAppend(&dString, "/", -1);
+ Tcl_DStringAppend(&dString, fileName, -1);
+ fileName = Tcl_DStringValue(&dString);
+ Blt_Ps_VarAppend(ps, "\n% including file \"", fileName, "\"\n\n",
+ (char *)NULL);
+ channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
+ if (channel == NULL) {
+ Tcl_AppendResult(interp, "couldn't open prologue file \"", fileName,
+ "\": ", Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ for(;;) {
+ nBytes = Tcl_Read(channel, buf, POSTSCRIPT_BUFSIZ);
+ if (nBytes < 0) {
+ Tcl_AppendResult(interp, "error reading prologue file \"",
+ fileName, "\": ", Tcl_PosixError(interp),
+ (char *)NULL);
+ Tcl_Close(interp, channel);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ if (nBytes == 0) {
+ break;
+ }
+ buf[nBytes] = '\0';
+ Blt_Ps_Append(ps, buf);
+ }
+ Tcl_DStringFree(&dString);
+ Tcl_Close(interp, channel);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * XColorToPostScript --
+ *
+ * Convert the a XColor (from its RGB values) to a PostScript command.
+ * If a Tk color map variable exists, it will be consulted for a
+ * PostScript translation based upon the color name.
+ *
+ * Maps an X color intensity (0 to 2^16-1) to a floating point value
+ * [0..1]. Many versions of Tk don't properly handle the the lower 8
+ * bits of the color intensity, so we can only consider the upper 8 bits.
+ *
+ * Results:
+ * The string representing the color mode is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+XColorToPostScript(Blt_Ps ps, XColor *colorPtr)
+{
+ /*
+ * Shift off the lower byte before dividing because some versions of Tk
+ * don't fill the lower byte correctly.
+ */
+ Blt_Ps_Format(ps, "%g %g %g",
+ ((double)(colorPtr->red >> 8) / 255.0),
+ ((double)(colorPtr->green >> 8) / 255.0),
+ ((double)(colorPtr->blue >> 8) / 255.0));
+}
+
+void
+Blt_Ps_XSetBackground(PostScript *psPtr, XColor *colorPtr)
+{
+ /* If the color name exists in TCL array variable, use that translation */
+ if ((psPtr->setupPtr != NULL) && (psPtr->setupPtr->colorVarName != NULL)) {
+ const char *psColor;
+
+ psColor = Tcl_GetVar2(psPtr->interp, psPtr->setupPtr->colorVarName,
+ Tk_NameOfColor(colorPtr), 0);
+ if (psColor != NULL) {
+ Blt_Ps_VarAppend(psPtr, " ", psColor, "\n", (char *)NULL);
+ return;
+ }
+ }
+ XColorToPostScript(psPtr, colorPtr);
+ Blt_Ps_Append(psPtr, " setrgbcolor\n");
+ if (psPtr->setupPtr->flags & PS_GREYSCALE) {
+ Blt_Ps_Append(psPtr, " currentgray setgray\n");
+ }
+}
+
+void
+Blt_Ps_XSetForeground(PostScript *psPtr, XColor *colorPtr)
+{
+ /* If the color name exists in TCL array variable, use that translation */
+ if ((psPtr->setupPtr != NULL) && (psPtr->setupPtr->colorVarName != NULL)) {
+ const char *psColor;
+
+ psColor = Tcl_GetVar2(psPtr->interp, psPtr->setupPtr->colorVarName,
+ Tk_NameOfColor(colorPtr), 0);
+ if (psColor != NULL) {
+ Blt_Ps_VarAppend(psPtr, " ", psColor, "\n", (char *)NULL);
+ return;
+ }
+ }
+ XColorToPostScript(psPtr, colorPtr);
+ Blt_Ps_Append(psPtr, " setrgbcolor\n");
+ if (psPtr->setupPtr->flags & PS_GREYSCALE) {
+ Blt_Ps_Append(psPtr, " currentgray setgray\n");
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ReverseBits --
+ *
+ * Convert a byte from a X image into PostScript image order. This
+ * requires not only the nybbles to be reversed but also their bit
+ * values.
+ *
+ * Results:
+ * The converted byte is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+INLINE static unsigned char
+ReverseBits(unsigned char byte)
+{
+ byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
+ byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
+ byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
+ return byte;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ByteToHex --
+ *
+ * Convert a byte to its ASCII hexidecimal equivalent.
+ *
+ * Results:
+ * The converted 2 ASCII character string is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+INLINE static void
+ByteToHex(unsigned char byte, char *string)
+{
+ static char hexDigits[] = "0123456789ABCDEF";
+
+ string[0] = hexDigits[byte >> 4];
+ string[1] = hexDigits[byte & 0x0F];
+}
+
+#ifdef WIN32
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_XSetBitmapData --
+ *
+ * Output a PostScript image string of the given bitmap image. It is
+ * assumed the image is one bit deep and a zero value indicates an
+ * off-pixel. To convert to PostScript, the bits need to be reversed
+ * from the X11 image order.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript image string is appended.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_XSetBitmapData(
+ PostScript *psPtr,
+ Display *display,
+ Pixmap bitmap,
+ int width, int height)
+{
+ unsigned char byte;
+ int x, y, bitPos;
+ unsigned long pixel;
+ int byteCount;
+ char string[10];
+ unsigned char *srcBits, *srcPtr;
+ int bytesPerRow;
+
+ srcBits = Blt_GetBitmapData(display, bitmap, width, height, &bytesPerRow);
+ if (srcBits == NULL) {
+ OutputDebugString("Can't get bitmap data");
+ return;
+ }
+ Blt_Ps_Append(psPtr, "\t<");
+ byteCount = bitPos = 0; /* Suppress compiler warning */
+ for (y = height - 1; y >= 0; y--) {
+ srcPtr = srcBits + (bytesPerRow * y);
+ byte = 0;
+ for (x = 0; x < width; x++) {
+ bitPos = x % 8;
+ pixel = (*srcPtr & (0x80 >> bitPos));
+ if (pixel) {
+ byte |= (unsigned char)(1 << bitPos);
+ }
+ if (bitPos == 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ byteCount++;
+ srcPtr++;
+ byte = 0;
+ if (byteCount >= 30) {
+ string[2] = '\n';
+ string[3] = '\t';
+ string[4] = '\0';
+ byteCount = 0;
+ }
+ Blt_Ps_Append(psPtr, string);
+ }
+ } /* x */
+ if (bitPos != 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ Blt_Ps_Append(psPtr, string);
+ byteCount++;
+ }
+ } /* y */
+ Blt_Free(srcBits);
+ Blt_Ps_Append(psPtr, ">\n");
+}
+
+#else
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_XSetBitmapData --
+ *
+ * Output a PostScript image string of the given bitmap image. It is
+ * assumed the image is one bit deep and a zero value indicates an
+ * off-pixel. To convert to PostScript, the bits need to be reversed
+ * from the X11 image order.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript image string is appended to interp->result.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_XSetBitmapData(Blt_Ps ps, Display *display, Pixmap bitmap, int w, int h)
+{
+ XImage *imagePtr;
+ int byteCount;
+ int y, bitPos;
+
+ imagePtr = XGetImage(display, bitmap, 0, 0, w, h, 1, ZPixmap);
+ Blt_Ps_Append(ps, "\t<");
+ byteCount = bitPos = 0; /* Suppress compiler warning */
+ for (y = 0; y < h; y++) {
+ unsigned char byte;
+ char string[10];
+ int x;
+
+ byte = 0;
+ for (x = 0; x < w; x++) {
+ unsigned long pixel;
+
+ pixel = XGetPixel(imagePtr, x, y);
+ bitPos = x % 8;
+ byte |= (unsigned char)(pixel << bitPos);
+ if (bitPos == 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ byteCount++;
+ byte = 0;
+ if (byteCount >= 30) {
+ string[2] = '\n';
+ string[3] = '\t';
+ string[4] = '\0';
+ byteCount = 0;
+ }
+ Blt_Ps_Append(ps, string);
+ }
+ } /* x */
+ if (bitPos != 7) {
+ byte = ReverseBits(byte);
+ ByteToHex(byte, string);
+ string[2] = '\0';
+ Blt_Ps_Append(ps, string);
+ byteCount++;
+ }
+ } /* y */
+ Blt_Ps_Append(ps, ">\n");
+ XDestroyImage(imagePtr);
+}
+
+#endif /* WIN32 */
+
+typedef struct {
+ const char *alias;
+ const char *fontName;
+} FamilyMap;
+
+static FamilyMap familyMap[] =
+{
+ { "Arial", "Helvetica" },
+ { "AvantGarde", "AvantGarde" },
+ { "Bookman", "Bookman" },
+ { "Courier New", "Courier" },
+ { "Courier", "Courier" },
+ { "Geneva", "Helvetica" },
+ { "Helvetica", "Helvetica" },
+ { "Mathematica1", "Helvetica" },
+ { "Monaco", "Courier" },
+ { "New Century Schoolbook", "NewCenturySchlbk" },
+ { "New York", "Times" },
+ { "Nimbus Roman No9 L" "Times" },
+ { "Nimbus Sans L Condensed","Helvetica" },
+ { "Nimbus Sans L", "Helvetica" },
+ { "Palatino", "Palatino" },
+ { "Standard Symbols L", "Symbol" },
+ { "Swiss 721", "Helvetica" },
+ { "Symbol", "Symbol" },
+ { "Times New Roman", "Times" },
+ { "Times Roman", "Times" },
+ { "Times", "Times" },
+ { "ZapfChancery", "ZapfChancery" },
+ { "ZapfDingbats", "ZapfDingbats" }
+};
+
+static int nFamilyNames = (sizeof(familyMap) / sizeof(FamilyMap));
+
+static const char *
+FamilyToPsFamily(const char *family)
+{
+ FamilyMap *fp, *fend;
+
+ if (strncasecmp(family, "itc ", 4) == 0) {
+ family += 4;
+ }
+ for (fp = familyMap, fend = fp + nFamilyNames; fp < fend; fp++) {
+ if (strcasecmp(fp->alias, family) == 0) {
+ return fp->fontName;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Routines to convert X drawing functions to PostScript commands.
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_SetClearBackground(Blt_Ps ps)
+{
+ Blt_Ps_Append(ps, "1 1 1 setrgbcolor\n");
+}
+
+void
+Blt_Ps_XSetCapStyle(Blt_Ps ps, int capStyle)
+{
+ /*
+ * X11:not last = 0, butt = 1, round = 2, projecting = 3
+ * PS: butt = 0, round = 1, projecting = 2
+ */
+ if (capStyle > 0) {
+ capStyle--;
+ }
+ Blt_Ps_Format(ps, "%d setlinecap\n", capStyle);
+}
+
+void
+Blt_Ps_XSetJoinStyle(Blt_Ps ps, int joinStyle)
+{
+ /*
+ * miter = 0, round = 1, bevel = 2
+ */
+ Blt_Ps_Format(ps, "%d setlinejoin\n", joinStyle);
+}
+
+void
+Blt_Ps_XSetLineWidth(Blt_Ps ps, int lineWidth)
+{
+ if (lineWidth < 1) {
+ lineWidth = 1;
+ }
+ Blt_Ps_Format(ps, "%d setlinewidth\n", lineWidth);
+}
+
+void
+Blt_Ps_XSetDashes(Blt_Ps ps, Blt_Dashes *dashesPtr)
+{
+
+ Blt_Ps_Append(ps, "[ ");
+ if (dashesPtr != NULL) {
+ unsigned char *vp;
+
+ for (vp = dashesPtr->values; *vp != 0; vp++) {
+ Blt_Ps_Format(ps, " %d", *vp);
+ }
+ }
+ Blt_Ps_Append(ps, "] 0 setdash\n");
+}
+
+void
+Blt_Ps_XSetLineAttributes(
+ Blt_Ps ps,
+ XColor *colorPtr,
+ int lineWidth,
+ Blt_Dashes *dashesPtr,
+ int capStyle,
+ int joinStyle)
+{
+ Blt_Ps_XSetJoinStyle(ps, joinStyle);
+ Blt_Ps_XSetCapStyle(ps, capStyle);
+ Blt_Ps_XSetForeground(ps, colorPtr);
+ Blt_Ps_XSetLineWidth(ps, lineWidth);
+ Blt_Ps_XSetDashes(ps, dashesPtr);
+ Blt_Ps_Append(ps, "/DashesProc {} def\n");
+}
+
+void
+Blt_Ps_Rectangle(Blt_Ps ps, int x, int y, int width, int height)
+{
+ Blt_Ps_Append(ps, "newpath\n");
+ Blt_Ps_Format(ps, " %d %d moveto\n", x, y);
+ Blt_Ps_Format(ps, " %d %d rlineto\n", width, 0);
+ Blt_Ps_Format(ps, " %d %d rlineto\n", 0, height);
+ Blt_Ps_Format(ps, " %d %d rlineto\n", -width, 0);
+ Blt_Ps_Append(ps, "closepath\n");
+}
+
+void
+Blt_Ps_XFillRectangle(Blt_Ps ps, double x, double y, int width, int height)
+{
+ Blt_Ps_Rectangle(ps, (int)x, (int)y, width, height);
+ Blt_Ps_Append(ps, "fill\n");
+}
+
+void
+Blt_Ps_PolylineFromXPoints(Blt_Ps ps, XPoint *points, int n)
+{
+ XPoint *pp, *pend;
+
+ pp = points;
+ Blt_Ps_Append(ps, "newpath\n");
+ Blt_Ps_Format(ps, " %d %d moveto\n", pp->x, pp->y);
+ pp++;
+ for (pend = points + n; pp < pend; pp++) {
+ Blt_Ps_Format(ps, " %d %d lineto\n", pp->x, pp->y);
+ }
+}
+
+void
+Blt_Ps_Polyline(Blt_Ps ps, Point2d *screenPts, int nScreenPts)
+{
+ Point2d *pp, *pend;
+
+ pp = screenPts;
+ Blt_Ps_Append(ps, "newpath\n");
+ Blt_Ps_Format(ps, " %g %g moveto\n", pp->x, pp->y);
+ for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) {
+ Blt_Ps_Format(ps, " %g %g lineto\n", pp->x, pp->y);
+ }
+}
+
+void
+Blt_Ps_Polygon(Blt_Ps ps, Point2d *screenPts, int nScreenPts)
+{
+ Point2d *pp, *pend;
+
+ pp = screenPts;
+ Blt_Ps_Append(ps, "newpath\n");
+ Blt_Ps_Format(ps, " %g %g moveto\n", pp->x, pp->y);
+ for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) {
+ Blt_Ps_Format(ps, " %g %g lineto\n", pp->x, pp->y);
+ }
+ Blt_Ps_Format(ps, " %g %g lineto\n", screenPts[0].x, screenPts[0].y);
+ Blt_Ps_Append(ps, "closepath\n");
+}
+
+void
+Blt_Ps_XFillPolygon(Blt_Ps ps, Point2d *screenPts, int nScreenPts)
+{
+ Blt_Ps_Polygon(ps, screenPts, nScreenPts);
+ Blt_Ps_Append(ps, "fill\n");
+}
+
+void
+Blt_Ps_XDrawSegments(Blt_Ps ps, XSegment *segments, int nSegments)
+{
+ XSegment *sp, *send;
+
+ for (sp = segments, send = sp + nSegments; sp < send; sp++) {
+ Blt_Ps_Format(ps, "%d %d moveto %d %d lineto\n", sp->x1, sp->y1,
+ sp->x2, sp->y2);
+ Blt_Ps_Append(ps, "DashesProc stroke\n");
+ }
+}
+
+
+void
+Blt_Ps_XFillRectangles(Blt_Ps ps, XRectangle *rectangles, int nRectangles)
+{
+ XRectangle *rp, *rend;
+
+ for (rp = rectangles, rend = rp + nRectangles; rp < rend; rp++) {
+ Blt_Ps_XFillRectangle(ps, (double)rp->x, (double)rp->y,
+ (int)rp->width, (int)rp->height);
+ }
+}
+
+#ifndef TK_RELIEF_SOLID
+#define TK_RELIEF_SOLID -1 /* Set the an impossible value. */
+#endif /* TK_RELIEF_SOLID */
+
+void
+Blt_Ps_Draw3DRectangle(
+ Blt_Ps ps,
+ Tk_3DBorder border, /* Token for border to draw. */
+ double x, double y, /* Coordinates of rectangle */
+ int width, int height, /* Region to be drawn. */
+ int borderWidth, /* Desired width for border, in pixels. */
+ int relief) /* Should be either TK_RELIEF_RAISED or
+ * TK_RELIEF_SUNKEN; indicates position of
+ * interior of window relative to exterior. */
+{
+ Point2d points[7];
+ TkBorder *borderPtr = (TkBorder *) border;
+ XColor *lightPtr, *darkPtr;
+ XColor *topPtr, *bottomPtr;
+ XColor light, dark;
+ int twiceWidth = (borderWidth * 2);
+
+ if ((width < twiceWidth) || (height < twiceWidth)) {
+ return;
+ }
+ if ((relief == TK_RELIEF_SOLID) ||
+ (borderPtr->lightColor == NULL) || (borderPtr->darkColor == NULL)) {
+ if (relief == TK_RELIEF_SOLID) {
+ dark.red = dark.blue = dark.green = 0x00;
+ light.red = light.blue = light.green = 0x00;
+ relief = TK_RELIEF_SUNKEN;
+ } else {
+ light = *borderPtr->bgColor;
+ dark.red = dark.blue = dark.green = 0xFF;
+ }
+ lightPtr = &light;
+ darkPtr = &dark;
+ } else {
+ lightPtr = borderPtr->lightColor;
+ darkPtr = borderPtr->darkColor;
+ }
+
+
+ /* Handle grooves and ridges with recursive calls. */
+
+ if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
+ int halfWidth, insideOffset;
+
+ halfWidth = borderWidth / 2;
+ insideOffset = borderWidth - halfWidth;
+ Blt_Ps_Draw3DRectangle(ps, border, (double)x, (double)y,
+ width, height, halfWidth,
+ (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+ Blt_Ps_Draw3DRectangle(ps, border,
+ (double)(x + insideOffset), (double)(y + insideOffset),
+ width - insideOffset * 2, height - insideOffset * 2, halfWidth,
+ (relief == TK_RELIEF_GROOVE) ? TK_RELIEF_RAISED : TK_RELIEF_SUNKEN);
+ return;
+ }
+ if (relief == TK_RELIEF_RAISED) {
+ topPtr = lightPtr;
+ bottomPtr = darkPtr;
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ topPtr = darkPtr;
+ bottomPtr = lightPtr;
+ } else {
+ topPtr = bottomPtr = borderPtr->bgColor;
+ }
+ Blt_Ps_XSetBackground(ps, bottomPtr);
+ Blt_Ps_XFillRectangle(ps, x, y + height - borderWidth, width, borderWidth);
+ Blt_Ps_XFillRectangle(ps, x + width - borderWidth, y, borderWidth, height);
+ points[0].x = points[1].x = points[6].x = x;
+ points[0].y = points[6].y = y + height;
+ points[1].y = points[2].y = y;
+ points[2].x = x + width;
+ points[3].x = x + width - borderWidth;
+ points[3].y = points[4].y = y + borderWidth;
+ points[4].x = points[5].x = x + borderWidth;
+ points[5].y = y + height - borderWidth;
+ if (relief != TK_RELIEF_FLAT) {
+ Blt_Ps_XSetBackground(ps, topPtr);
+ }
+ Blt_Ps_XFillPolygon(ps, points, 7);
+}
+
+void
+Blt_Ps_Fill3DRectangle(
+ Blt_Ps ps,
+ Tk_3DBorder border, /* Token for border to draw. */
+ double x, double y, /* Coordinates of top-left of border area */
+ int width, int height, /* Dimension of border to be drawn. */
+ int borderWidth, /* Desired width for border, in pixels. */
+ int relief) /* Should be either TK_RELIEF_RAISED or
+ * TK_RELIEF_SUNKEN; indicates position of
+ * interior of window relative to exterior. */
+{
+ TkBorder *borderPtr = (TkBorder *) border;
+
+ Blt_Ps_XSetBackground(ps, borderPtr->bgColor);
+ Blt_Ps_XFillRectangle(ps, x, y, width, height);
+ Blt_Ps_Draw3DRectangle(ps, border, x, y, width, height, borderWidth,
+ relief);
+}
+
+void
+Blt_Ps_XSetStipple(Blt_Ps ps, Display *display, Pixmap bitmap)
+{
+ int width, height;
+
+ Tk_SizeOfBitmap(display, bitmap, &width, &height);
+ Blt_Ps_Format(ps,
+ "gsave\n"
+ " clip\n"
+ " %d %d\n",
+ width, height);
+ Blt_Ps_XSetBitmapData(ps, display, bitmap, width, height);
+ Blt_Ps_VarAppend(ps,
+ " StippleFill\n"
+ "grestore\n", (char *)NULL);
+}
+
+static void
+Base85Encode(Blt_DBuffer dBuffer, Tcl_DString *resultPtr)
+{
+ char *dp;
+ int count;
+ int length, nBytes, oldLength;
+ int n;
+ unsigned char *sp, *send;
+
+ oldLength = Tcl_DStringLength(resultPtr);
+ nBytes = Blt_DBuffer_Length(dBuffer);
+
+ /*
+ * Compute worst-case length of buffer needed for encoding.
+ * That is 5 characters per 4 bytes. There are newlines every
+ * 65 characters. The actual size can be smaller, depending upon
+ * the number of 0 tuples ('z' bytes).
+ */
+ length = oldLength + nBytes;
+ length += ((nBytes + 3)/4) * 5; /* 5 characters per 4 bytes. */
+ length += (nBytes+64) / 65; /* # of newlines. */
+ Tcl_DStringSetLength(resultPtr, length);
+
+
+ n = count = 0;
+ dp = Tcl_DStringValue(resultPtr) + oldLength;
+ for (sp = Blt_DBuffer_Bytes(dBuffer), send = sp + nBytes; sp < send;
+ sp += 4) {
+ unsigned int tuple;
+
+ tuple = 0;
+#ifdef WORDS_BIGENDIAN
+ tuple |= (sp[3] << 24);
+ tuple |= (sp[2] << 16);
+ tuple |= (sp[1] << 8);
+ tuple |= sp[0];
+#else
+ tuple |= (sp[0] << 24);
+ tuple |= (sp[1] << 16);
+ tuple |= (sp[2] << 8);
+ tuple |= sp[3];
+#endif
+ if (tuple == 0) {
+ *dp++ = 'z';
+ count++;
+ n++;
+ } else {
+ dp[4] = '!' + (tuple % 85);
+ tuple /= 85;
+ dp[3] = '!' + (tuple % 85);
+ tuple /= 85;
+ dp[2] = '!' + (tuple % 85);
+ tuple /= 85;
+ dp[1] = '!' + (tuple % 85);
+ tuple /= 85;
+ dp[0] = '!' + (tuple % 85);
+ dp += 5;
+ n += 5;
+ count += 5;
+ }
+ if (count > 64) {
+ *dp++ = '\n';
+ n++;
+ count = 0;
+ }
+ }
+
+ {
+ unsigned int tuple;
+
+ /* Handle remaining bytes (0-3). */
+ nBytes = (nBytes & 0x3);
+ sp -= nBytes;
+ tuple = 0;
+ switch (nBytes) {
+#ifdef WORDS_BIGENDIAN
+ case 3:
+ tuple |= (sp[2] << 8);
+ case 2:
+ tuple |= (sp[1] << 16);
+ case 1:
+ tuple |= (sp[0] << 24);
+#else
+ case 3:
+ tuple |= (sp[2] << 24);
+ case 2:
+ tuple |= (sp[1] << 16);
+ case 1:
+ tuple |= (sp[0] << 8);
+#endif
+ default:
+ break;
+ }
+ if (nBytes > 0) {
+ tuple /= 85;
+ if (nBytes > 2) {
+ dp[3] = '!' + (tuple % 85);
+ n++;
+ }
+ tuple /= 85;
+ if (nBytes > 1) {
+ dp[2] = '!' + (tuple % 85);
+ n++;
+ }
+ tuple /= 85;
+ dp[1] = '!' + (tuple % 85);
+ tuple /= 85;
+ dp[0] = '!' + (tuple % 85);
+ *dp++ = '\n';
+ n += 3;
+ }
+ Tcl_DStringSetLength(resultPtr, n);
+ }
+}
+
+static void
+AsciiHexEncode(Blt_DBuffer dBuffer, Tcl_DString *resultPtr)
+{
+ static char hexDigits[] = "0123456789ABCDEF";
+ int length, oldLength;
+ int nBytes;
+
+ /*
+ * Compute the length of the buffer needed for the encoding. That is 2
+ * characters per byte. There are newlines every 64 characters.
+ */
+ length = oldLength = Tcl_DStringLength(resultPtr);
+ nBytes = Blt_DBuffer_Length(dBuffer) * 2; /* Two characters per byte */
+ length += nBytes;
+ length += (nBytes+63)/64; /* Number of newlines required. */
+ Tcl_DStringSetLength(resultPtr, length);
+ {
+ unsigned char *sp, *send;
+ char *bytes, *dp;
+ int count, n;
+
+ count = n = 0;
+ dp = bytes = Tcl_DStringValue(resultPtr) + oldLength;
+ for (sp = Blt_DBuffer_Bytes(dBuffer),
+ send = sp + Blt_DBuffer_Length(dBuffer);
+ sp < send; sp++) {
+ dp[0] = hexDigits[*sp >> 4];
+ dp[1] = hexDigits[*sp & 0x0F];
+ dp += 2;
+ n += 2;
+ count++;
+ if ((count & 0x1F) == 0) {
+ *dp++ = '\n';
+ n++;
+ }
+ }
+ *dp++ = '\0';
+#ifdef notdef
+ Tcl_DStringSetLength(resultPtr, n + oldLength);
+#endif
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_DrawPicture --
+ *
+ * Translates a picture into 3 component RGB PostScript output. Uses PS
+ * Language Level 2 operator "colorimage".
+ *
+ * Results:
+ * The dynamic string will contain the PostScript output.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_DrawPicture(PostScript *psPtr, Blt_Picture picture, double x, double y)
+{
+ PageSetup *setupPtr = psPtr->setupPtr;
+ int w, h;
+ Blt_DBuffer dBuffer;
+
+ w = Blt_PictureWidth(picture);
+ h = Blt_PictureHeight(picture);
+ Blt_Ps_Format(psPtr,
+ "gsave\n"
+ "/DeviceRGB setcolorspace\n"
+ "%g %g translate\n"
+ "%d %d scale\n", x, y, w, h);
+ if ((setupPtr->flags & PS_GREYSCALE) || (setupPtr->level == 1)) {
+ int strSize;
+
+ strSize = (setupPtr->flags & PS_GREYSCALE) ? w : w * 3;
+ Blt_Ps_Format(psPtr,
+ "/picstr %d string def\n"
+ "%d %d 8\n"
+ "[%d 0 0 %d 0 %d]\n"
+ "{\n"
+ " currentfile picstr readhexstring pop\n"
+ "}\n", strSize, w, h, w, -h, h);
+ if (setupPtr->flags & PS_GREYSCALE) {
+ Blt_Picture greyscale;
+
+ Blt_Ps_Append(psPtr, "image\n");
+ greyscale = Blt_GreyscalePicture(picture);
+ dBuffer = Blt_PictureToDBuffer(picture, 1);
+ Blt_FreePicture(greyscale);
+ } else {
+ Blt_Ps_Append(psPtr, "false 3 colorimage\n");
+ dBuffer = Blt_PictureToDBuffer(picture, 3);
+ }
+ AsciiHexEncode(dBuffer, &psPtr->dString);
+ Blt_DBuffer_Free(dBuffer);
+ } else {
+ Blt_Ps_Format(psPtr,
+ "<<\n"
+ "/ImageType 1\n"
+ "/Width %d\n"
+ "/Height %d\n"
+ "/BitsPerComponent 8\n"
+ "/Decode [0 1 0 1 0 1]\n"
+ "/ImageMatrix [%d 0 0 %d 0 %d]\n"
+ "/Interpolate true\n"
+ "/DataSource currentfile /ASCII85Decode filter\n"
+ ">>\n"
+ "image\n", w, h, w, -h, h);
+ dBuffer = Blt_PictureToDBuffer(picture, 3);
+ Base85Encode(dBuffer, &psPtr->dString);
+ Blt_DBuffer_Free(dBuffer);
+ }
+ Blt_Ps_Append(psPtr, "\ngrestore\n\n");
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_XDrawWindow --
+ *
+ * Converts a Tk window to PostScript. If the window could not be
+ * "snapped", then a grey rectangle is drawn in its place.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_XDrawWindow(Blt_Ps ps, Tk_Window tkwin, double x, double y)
+{
+ Blt_Picture picture;
+
+ picture = Blt_DrawableToPicture(tkwin, Tk_WindowId(tkwin), 0, 0,
+ Tk_Width(tkwin), Tk_Height(tkwin), GAMMA);
+ if (picture == NULL) {
+ /* Can't grab window image so paint the window area grey */
+ Blt_Ps_VarAppend(ps, "% Can't grab window \"", Tk_PathName(tkwin),
+ "\"\n", (char *)NULL);
+ Blt_Ps_Append(ps, "0.5 0.5 0.5 setrgbcolor\n");
+ Blt_Ps_XFillRectangle(ps, x, y, Tk_Width(tkwin), Tk_Height(tkwin));
+ return;
+ }
+ Blt_Ps_DrawPicture(ps, picture, x, y);
+ Blt_FreePicture(picture);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_DrawPhoto --
+ *
+ * Output a PostScript image string of the given photo image. The photo
+ * is first converted into a picture and then translated into PostScript.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The PostScript output representing the photo is appended to the
+ * psPtr's dynamic string.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_DrawPhoto(Blt_Ps ps, Tk_PhotoHandle photo, double x, double y)
+{
+ Blt_Picture picture;
+
+ picture = Blt_PhotoToPicture(photo);
+ Blt_Ps_DrawPicture(ps, picture, x, y);
+ Blt_FreePicture(picture);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_XSetFont --
+ *
+ * Map the Tk font to a PostScript font and point size.
+ *
+ * If a TCL array variable was specified, each element should be indexed
+ * by the X11 font name and contain a list of 1-2 elements; the
+ * PostScript font name and the desired point size. The point size may
+ * be omitted and the X font point size will be used.
+ *
+ * Otherwise, if the foundry is "Adobe", we try to do a plausible mapping
+ * looking at the full name of the font and building a string in the form
+ * of "Family-TypeFace".
+ *
+ * Returns:
+ * None.
+ *
+ * Side Effects:
+ * PostScript commands are output to change the type and the point size
+ * of the current font.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+Blt_Ps_XSetFont(PostScript *psPtr, Blt_Font font)
+{
+ Tcl_Interp *interp = psPtr->interp;
+ const char *family;
+
+ /* Use the font variable information if it exists. */
+ if ((psPtr->setupPtr != NULL) && (psPtr->setupPtr->fontVarName != NULL)) {
+ char *value;
+
+ value = (char *)Tcl_GetVar2(interp, psPtr->setupPtr->fontVarName,
+ Blt_NameOfFont(font), 0);
+ if (value != NULL) {
+ const char **argv = NULL;
+ int argc;
+ int newSize;
+ double pointSize;
+ const char *fontName;
+
+ if (Tcl_SplitList(NULL, value, &argc, &argv) != TCL_OK) {
+ return;
+ }
+ fontName = argv[0];
+ if ((argc != 2) ||
+ (Tcl_GetInt(interp, argv[1], &newSize) != TCL_OK)) {
+ Blt_Free(argv);
+ return;
+ }
+ pointSize = (double)newSize;
+ Blt_Ps_Format(psPtr, "%g /%s SetFont\n", pointSize,
+ fontName);
+ Blt_Free(argv);
+ return;
+ }
+ /*FallThru*/
+ }
+
+ /*
+ * Check to see if it's a PostScript font. Tk_PostScriptFontName silently
+ * generates a bogus PostScript font name, so we have to check to see if
+ * this is really a PostScript font first before we call it.
+ */
+ family = FamilyToPsFamily(Blt_FamilyOfFont(font));
+ if (family != NULL) {
+ Tcl_DString dString;
+ double pointSize;
+
+ Tcl_DStringInit(&dString);
+ pointSize = (double)Blt_PostscriptFontName(font, &dString);
+ Blt_Ps_Format(psPtr, "%g /%s SetFont\n", pointSize,
+ Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return;
+ }
+ Blt_Ps_Append(psPtr, "12.0 /Helvetica-Bold SetFont\n");
+}
+
+static void
+TextLayoutToPostScript(Blt_Ps ps, int x, int y, TextLayout *textPtr)
+{
+ char *bp, *dst;
+ int count; /* Counts the # of bytes written to the
+ * intermediate scratch buffer. */
+ const char *src, *end;
+ TextFragment *fragPtr;
+ int i;
+ unsigned char c;
+#if HAVE_UTF
+ Tcl_UniChar ch;
+#endif
+ int limit;
+
+ limit = POSTSCRIPT_BUFSIZ - 4; /* High water mark for scratch buffer. */
+ fragPtr = textPtr->fragments;
+ for (i = 0; i < textPtr->nFrags; i++, fragPtr++) {
+ if (fragPtr->count < 1) {
+ continue;
+ }
+ Blt_Ps_Append(ps, "(");
+ count = 0;
+ dst = Blt_Ps_GetScratchBuffer(ps);
+ src = fragPtr->text;
+ end = fragPtr->text + fragPtr->count;
+ while (src < end) {
+ if (count > limit) {
+ /* Don't let the scatch buffer overflow */
+ dst = Blt_Ps_GetScratchBuffer(ps);
+ dst[count] = '\0';
+ Blt_Ps_Append(ps, dst);
+ count = 0;
+ }
+#if HAVE_UTF
+ /*
+ * INTL: For now we just treat the characters as binary data and
+ * display the lower byte. Eventually this should be revised to
+ * handle international postscript fonts.
+ */
+ src += Tcl_UtfToUniChar(src, &ch);
+ c = (unsigned char)(ch & 0xff);
+#else
+ c = *src++;
+#endif
+
+ if ((c == '\\') || (c == '(') || (c == ')')) {
+ /*
+ * If special PostScript characters characters "\", "(", and
+ * ")" are contained in the text string, prepend backslashes
+ * to them.
+ */
+ *dst++ = '\\';
+ *dst++ = c;
+ count += 2;
+ } else if ((c < ' ') || (c > '~')) {
+ /* Convert non-printable characters into octal. */
+ sprintf_s(dst, 5, "\\%03o", c);
+ dst += 4;
+ count += 4;
+ } else {
+ *dst++ = c;
+ count++;
+ }
+ }
+ bp = Blt_Ps_GetScratchBuffer(ps);
+ bp[count] = '\0';
+ Blt_Ps_Append(ps, bp);
+ Blt_Ps_Format(ps, ") %d %d %d DrawAdjText\n",
+ fragPtr->width, x + fragPtr->x, y + fragPtr->y);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ps_DrawText --
+ *
+ * Output PostScript commands to print a text string. The string may be
+ * rotated at any arbitrary angle, and placed according the anchor type
+ * given. The anchor indicates how to interpret the window coordinates as
+ * an anchor for the text bounding box.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Text string is drawn using the given font and GC on the graph window
+ * at the given coordinates, anchor, and rotation
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ps_DrawText(
+ Blt_Ps ps,
+ const char *string, /* String to convert to PostScript */
+ TextStyle *tsPtr, /* Text attribute information */
+ double x, double y) /* Window coordinates where to print text */
+{
+ TextLayout *textPtr;
+ Point2d t;
+
+ if ((string == NULL) || (*string == '\0')) { /* Empty string, do nothing */
+ return;
+ }
+ textPtr = Blt_Ts_CreateLayout(string, -1, tsPtr);
+ {
+ float angle;
+ double rw, rh;
+
+ angle = FMOD(tsPtr->angle, (double)360.0);
+ Blt_GetBoundingBox(textPtr->width, textPtr->height, angle, &rw, &rh,
+ (Point2d *)NULL);
+ /*
+ * Find the center of the bounding box
+ */
+ t = Blt_AnchorPoint(x, y, rw, rh, tsPtr->anchor);
+ t.x += rw * 0.5;
+ t.y += rh * 0.5;
+ }
+
+ /* Initialize text (sets translation and rotation) */
+ Blt_Ps_Format(ps, "%d %d %g %g %g BeginText\n", textPtr->width,
+ textPtr->height, tsPtr->angle, t.x, t.y);
+
+ Blt_Ps_XSetFont(ps, tsPtr->font);
+
+ Blt_Ps_XSetForeground(ps, tsPtr->color);
+ TextLayoutToPostScript(ps, 0, 0, textPtr);
+ Blt_Free(textPtr);
+ Blt_Ps_Append(ps, "EndText\n");
+}
+
+void
+Blt_Ps_XDrawLines(Blt_Ps ps, XPoint *points, int n)
+{
+ int nLeft;
+
+ if (n <= 0) {
+ return;
+ }
+ for (nLeft = n; nLeft > 0; nLeft -= PS_MAXPATH) {
+ int length;
+
+ length = MIN(PS_MAXPATH, nLeft);
+ Blt_Ps_PolylineFromXPoints(ps, points, length);
+ Blt_Ps_Append(ps, "DashesProc stroke\n");
+ points += length;
+ }
+}
+
+void
+Blt_Ps_DrawPolyline(Blt_Ps ps, Point2d *points, int nPoints)
+{
+ int nLeft;
+
+ if (nPoints <= 0) {
+ return;
+ }
+ for (nLeft = nPoints; nLeft > 0; nLeft -= PS_MAXPATH) {
+ int length;
+
+ length = MIN(PS_MAXPATH, nLeft);
+ Blt_Ps_Polyline(ps, points, length);
+ Blt_Ps_Append(ps, "DashesProc stroke\n");
+ points += length;
+ }
+}
+
+void
+Blt_Ps_DrawBitmap(
+ Blt_Ps ps,
+ Display *display,
+ Pixmap bitmap, /* Bitmap to be converted to PostScript */
+ double xScale, double yScale)
+{
+ int width, height;
+ double sw, sh;
+
+ Tk_SizeOfBitmap(display, bitmap, &width, &height);
+ sw = (double)width * xScale;
+ sh = (double)height * yScale;
+ Blt_Ps_Append(ps, " gsave\n");
+ Blt_Ps_Format(ps, " %g %g translate\n", sw * -0.5, sh * 0.5);
+ Blt_Ps_Format(ps, " %g %g scale\n", sw, -sh);
+ Blt_Ps_Format(ps, " %d %d true [%d 0 0 %d 0 %d] {",
+ width, height, width, -height, height);
+ Blt_Ps_XSetBitmapData(ps, display, bitmap, width, height);
+ Blt_Ps_Append(ps, " } imagemask\n grestore\n");
+}
+
+void
+Blt_Ps_Draw2DSegments(Blt_Ps ps, Segment2d *segments, int nSegments)
+{
+ Segment2d *sp, *send;
+
+ Blt_Ps_Append(ps, "newpath\n");
+ for (sp = segments, send = sp + nSegments; sp < send; sp++) {
+ Blt_Ps_Format(ps, " %g %g moveto %g %g lineto\n",
+ sp->p.x, sp->p.y, sp->q.x, sp->q.y);
+ Blt_Ps_Append(ps, "DashesProc stroke\n");
+ }
+}
+
+void
+Blt_Ps_FontName(const char *family, int flags, Tcl_DString *resultPtr)
+{
+ const char *familyName, *weightName, *slantName;
+ int len;
+
+ len = Tcl_DStringLength(resultPtr);
+
+ familyName = FamilyToPsFamily(family);
+ if (familyName == NULL) {
+ Tcl_UniChar ch;
+ char *src, *dest;
+ int upper;
+
+ /*
+ * Inline, capitalize the first letter of each word, lowercase the
+ * rest of the letters in each word, and then take out the spaces
+ * between the words. This may make the DString shorter, which is
+ * safe to do.
+ */
+ Tcl_DStringAppend(resultPtr, family, -1);
+ src = dest = Tcl_DStringValue(resultPtr) + len;
+ upper = TRUE;
+ while (*src != '\0') {
+ while (isspace(*src)) { /* INTL: ISO space */
+ src++;
+ upper = TRUE;
+ }
+ src += Tcl_UtfToUniChar(src, &ch);
+ if (upper) {
+ ch = Tcl_UniCharToUpper(ch);
+ upper = FALSE;
+ } else {
+ ch = Tcl_UniCharToLower(ch);
+ }
+ dest += Tcl_UniCharToUtf(ch, dest);
+ }
+ *dest = '\0';
+ Tcl_DStringSetLength(resultPtr, dest - Tcl_DStringValue(resultPtr));
+ familyName = Tcl_DStringValue(resultPtr) + len;
+ }
+ if (familyName != Tcl_DStringValue(resultPtr) + len) {
+ Tcl_DStringAppend(resultPtr, familyName, -1);
+ familyName = Tcl_DStringValue(resultPtr) + len;
+ }
+ if (strcasecmp(familyName, "NewCenturySchoolbook") == 0) {
+ Tcl_DStringSetLength(resultPtr, len);
+ Tcl_DStringAppend(resultPtr, "NewCenturySchlbk", -1);
+ familyName = Tcl_DStringValue(resultPtr) + len;
+ }
+
+ /* Get the string to use for the weight. */
+ weightName = NULL;
+ if (flags & FONT_BOLD) {
+ if ((strcmp(familyName, "Bookman") == 0) ||
+ (strcmp(familyName, "AvantGarde") == 0)) {
+ weightName = "Demi";
+ } else {
+ weightName = "Bold";
+ }
+ } else {
+ if (strcmp(familyName, "Bookman") == 0) {
+ weightName = "Light";
+ } else if (strcmp(familyName, "AvantGarde") == 0) {
+ weightName = "Book";
+ } else if (strcmp(familyName, "ZapfChancery") == 0) {
+ weightName = "Medium";
+ }
+ }
+
+ /* Get the string to use for the slant. */
+ slantName = NULL;
+ if (flags & FONT_ITALIC) {
+ if ((strcmp(familyName, "Helvetica") == 0) ||
+ (strcmp(familyName, "Courier") == 0) ||
+ (strcmp(familyName, "AvantGarde") == 0)) {
+ slantName = "Oblique";
+ } else {
+ slantName = "Italic";
+ }
+ }
+
+ /*
+ * The string "Roman" needs to be added to some fonts that are not bold
+ * and not italic.
+ */
+ if ((slantName == NULL) && (weightName == NULL)) {
+ if ((strcmp(familyName, "Times") == 0) ||
+ (strcmp(familyName, "NewCenturySchlbk") == 0) ||
+ (strcmp(familyName, "Palatino") == 0)) {
+ Tcl_DStringAppend(resultPtr, "-Roman", -1);
+ }
+ } else {
+ Tcl_DStringAppend(resultPtr, "-", -1);
+ if (weightName != NULL) {
+ Tcl_DStringAppend(resultPtr, weightName, -1);
+ }
+ if (slantName != NULL) {
+ Tcl_DStringAppend(resultPtr, slantName, -1);
+ }
+ }
+}
diff --git a/src/bltGrPSOutput.h b/src/bltGrPSOutput.h
new file mode 100644
index 0000000..944faf8
--- /dev/null
+++ b/src/bltGrPSOutput.h
@@ -0,0 +1,215 @@
+
+/*
+ * bltPs.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_PS_H
+#define _BLT_PS_H
+
+#include "bltPicture.h"
+
+/*
+ * PageSetup --
+ *
+ * Structure contains information specific to the layout of the page for
+ * printing the graph.
+ *
+ */
+typedef struct {
+ /* User configurable fields */
+
+ int reqWidth, reqHeight; /* If greater than zero, represents the
+ * requested dimensions of the printed graph */
+ int reqPaperWidth;
+ int reqPaperHeight; /* Requested dimensions for the PostScript
+ * page. Can constrain the size of the graph
+ * if the graph (plus padding) is larger than
+ * the size of the page. */
+ Blt_Pad xPad, yPad; /* Requested padding on the exterior of the
+ * graph. This forms the bounding box for
+ * the page. */
+ const char *colorVarName; /* If non-NULL, is the name of a TCL array
+ * variable containing X to output device color
+ * translations */
+ const char *fontVarName; /* If non-NULL, is the name of a TCL array
+ * variable containing X to output device font
+ * translations */
+ int level; /* PostScript Language level 1-3 */
+ unsigned int flags;
+
+ const char **comments; /* User supplied comments to be added. */
+
+ /* Computed fields */
+
+ short int left, bottom; /* Bounding box of the plot in the page. */
+ short int right, top;
+
+ float scale; /* Scale of page. Set if "-maxpect" option
+ * is set, otherwise 1.0. */
+
+ int paperHeight;
+ int paperWidth;
+
+} PageSetup;
+
+#define PS_GREYSCALE (1<<0)
+#define PS_LANDSCAPE (1<<2)
+#define PS_CENTER (1<<3)
+#define PS_MAXPECT (1<<4)
+#define PS_DECORATIONS (1<<5)
+#define PS_FOOTER (1<<6)
+#define PS_FMT_NONE 0
+#define PS_FMT_MASK (PS_FMT_WMF|PS_FMT_EPSI|PS_FMT_TIFF)
+#define PS_FMT_WMF (1<<8)
+#define PS_FMT_EPSI (1<<9)
+#define PS_FMT_TIFF (1<<10)
+
+typedef struct _Blt_Ps *Blt_Ps;
+
+BLT_EXTERN Blt_Ps Blt_Ps_Create(Tcl_Interp *interp, PageSetup *setupPtr);
+
+BLT_EXTERN void Blt_Ps_Free(Blt_Ps ps);
+
+BLT_EXTERN const char *Blt_Ps_GetValue(Blt_Ps ps, int *lengthPtr);
+
+BLT_EXTERN Tcl_Interp *Blt_Ps_GetInterp(Blt_Ps ps);
+
+BLT_EXTERN Tcl_DString *Blt_Ps_GetDString(Blt_Ps ps);
+
+BLT_EXTERN char *Blt_Ps_GetScratchBuffer(Blt_Ps ps);
+
+BLT_EXTERN void Blt_Ps_SetInterp(Blt_Ps ps, Tcl_Interp *interp);
+
+BLT_EXTERN void Blt_Ps_Append(Blt_Ps ps, const char *string);
+
+BLT_EXTERN void Blt_Ps_AppendBytes(Blt_Ps ps, const char *string, int nBytes);
+
+BLT_EXTERN void Blt_Ps_VarAppend TCL_VARARGS(Blt_Ps, ps);
+
+BLT_EXTERN void Blt_Ps_Format TCL_VARARGS(Blt_Ps, ps);
+
+BLT_EXTERN void Blt_Ps_SetClearBackground(Blt_Ps ps);
+
+BLT_EXTERN int Blt_Ps_IncludeFile(Tcl_Interp *interp, Blt_Ps ps,
+ const char *fileName);
+
+BLT_EXTERN int Blt_Ps_GetPicaFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ int *picaPtr);
+
+BLT_EXTERN int Blt_Ps_GetPadFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Blt_Pad *padPtr);
+
+BLT_EXTERN int Blt_Ps_ComputeBoundingBox(PageSetup *setupPtr, int w, int h);
+
+BLT_EXTERN void Blt_Ps_DrawPicture(Blt_Ps ps, Blt_Picture picture,
+ double x, double y);
+
+BLT_EXTERN void Blt_Ps_Rectangle(Blt_Ps ps, int x, int y, int w, int h);
+
+
+BLT_EXTERN int Blt_Ps_SaveFile(Tcl_Interp *interp, Blt_Ps ps,
+ const char *fileName);
+
+#ifdef _TK
+
+#include "bltFont.h"
+#include "bltText.h"
+
+BLT_EXTERN void Blt_Ps_XSetLineWidth(Blt_Ps ps, int lineWidth);
+
+BLT_EXTERN void Blt_Ps_XSetBackground(Blt_Ps ps, XColor *colorPtr);
+
+BLT_EXTERN void Blt_Ps_XSetBitmapData(Blt_Ps ps, Display *display,
+ Pixmap bitmap, int width, int height);
+
+BLT_EXTERN void Blt_Ps_XSetForeground(Blt_Ps ps, XColor *colorPtr);
+
+BLT_EXTERN void Blt_Ps_XSetFont(Blt_Ps ps, Blt_Font font);
+
+BLT_EXTERN void Blt_Ps_XSetDashes(Blt_Ps ps, Blt_Dashes *dashesPtr);
+
+BLT_EXTERN void Blt_Ps_XSetLineAttributes(Blt_Ps ps, XColor *colorPtr,
+ int lineWidth, Blt_Dashes *dashesPtr, int capStyle, int joinStyle);
+
+BLT_EXTERN void Blt_Ps_XSetStipple(Blt_Ps ps, Display *display, Pixmap bitmap);
+
+BLT_EXTERN void Blt_Ps_Polyline(Blt_Ps ps, Point2d *screenPts, int nScreenPts);
+
+BLT_EXTERN void Blt_Ps_XDrawLines(Blt_Ps ps, XPoint *points, int n);
+
+BLT_EXTERN void Blt_Ps_XDrawSegments(Blt_Ps ps, XSegment *segments,
+ int nSegments);
+
+BLT_EXTERN void Blt_Ps_DrawPolyline(Blt_Ps ps, Point2d *points, int n);
+
+BLT_EXTERN void Blt_Ps_Draw2DSegments(Blt_Ps ps, Segment2d *segments,
+ int nSegments);
+
+BLT_EXTERN void Blt_Ps_Draw3DRectangle(Blt_Ps ps, Tk_3DBorder border,
+ double x, double y, int width, int height, int borderWidth, int relief);
+
+BLT_EXTERN void Blt_Ps_Fill3DRectangle(Blt_Ps ps, Tk_3DBorder border, double x,
+ double y, int width, int height, int borderWidth, int relief);
+
+BLT_EXTERN void Blt_Ps_XFillRectangle(Blt_Ps ps, double x, double y,
+ int width, int height);
+
+BLT_EXTERN void Blt_Ps_XFillRectangles(Blt_Ps ps, XRectangle *rects, int n);
+
+BLT_EXTERN void Blt_Ps_XFillPolygon(Blt_Ps ps, Point2d *screenPts,
+ int nScreenPts);
+
+BLT_EXTERN void Blt_Ps_DrawPhoto(Blt_Ps ps, Tk_PhotoHandle photoToken,
+ double x, double y);
+
+BLT_EXTERN void Blt_Ps_XDrawWindow(Blt_Ps ps, Tk_Window tkwin,
+ double x, double y);
+
+BLT_EXTERN void Blt_Ps_DrawText(Blt_Ps ps, const char *string,
+ TextStyle *attrPtr, double x, double y);
+
+BLT_EXTERN void Blt_Ps_DrawBitmap(Blt_Ps ps, Display *display, Pixmap bitmap,
+ double scaleX, double scaleY);
+
+BLT_EXTERN void Blt_Ps_XSetCapStyle(Blt_Ps ps, int capStyle);
+
+BLT_EXTERN void Blt_Ps_XSetJoinStyle(Blt_Ps ps, int joinStyle);
+
+BLT_EXTERN void Blt_Ps_PolylineFromXPoints(Blt_Ps ps, XPoint *points, int n);
+
+BLT_EXTERN void Blt_Ps_Polygon(Blt_Ps ps, Point2d *screenPts, int nScreenPts);
+
+BLT_EXTERN void Blt_Ps_SetPrinting(Blt_Ps ps, int value);
+BLT_EXTERN int Blt_Ps_IsPrinting(void);
+
+BLT_EXTERN int Blt_Ps_TextWidth(Blt_Font font, const char *string, int nBytes);
+
+BLT_EXTERN int Blt_Ps_GetFontMetrics(Blt_Font font, Blt_FontMetrics *fmPtr);
+
+BLT_EXTERN void Blt_Ps_FontName(const char *family, int flags,
+ Tcl_DString *resultPtr);
+
+#endif /* _TK */
+
+#endif /* BLT_PS_H */
diff --git a/src/bltGrPenOp.C b/src/bltGrPenOp.C
new file mode 100644
index 0000000..2e13b3a
--- /dev/null
+++ b/src/bltGrPenOp.C
@@ -0,0 +1,765 @@
+
+/*
+ * bltGrPen.c --
+ *
+ * This module implements pens for the BLT graph widget.
+ *
+ * Copyright 1996-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include <X11/Xutil.h>
+
+typedef int (GraphPenProc)(Tcl_Interp *interp, Graph *graphPtr, int objc,
+ Tcl_Obj *const *objv);
+
+static Blt_OptionFreeProc FreeColor;
+static Blt_OptionParseProc ObjToColor;
+static Blt_OptionPrintProc ColorToObj;
+Blt_CustomOption bltColorOption = {
+ ObjToColor, ColorToObj, FreeColor, (ClientData)0
+};
+
+static Blt_OptionFreeProc FreePen;
+static Blt_OptionParseProc ObjToPen;
+static Blt_OptionPrintProc PenToObj;
+Blt_CustomOption bltBarPenOption = {
+ ObjToPen, PenToObj, FreePen, (ClientData)CID_ELEM_BAR
+};
+Blt_CustomOption bltLinePenOption = {
+ ObjToPen, PenToObj, FreePen, (ClientData)CID_ELEM_LINE
+};
+
+/*ARGSUSED*/
+static void
+FreeColor(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ XColor *colorPtr = *(XColor **)(widgRec + offset);
+
+ if ((colorPtr != NULL) && (colorPtr != COLOR_DEFAULT)) {
+ Tk_FreeColor(colorPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+
+ * ObjToColor --
+ *
+ * Convert the string representation of a color into a XColor pointer.
+ *
+ * Results:
+ * The return value is a standard TCL result. The color pointer is
+ * written into the widget record.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToColor(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing color */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ XColor **colorPtrPtr = (XColor **)(widgRec + offset);
+ XColor *colorPtr;
+ const char *string;
+ char c;
+ int length;
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ c = string[0];
+
+ if ((c == '\0') && (flags & BLT_CONFIG_NULL_OK)) {
+ if ((*colorPtrPtr != NULL) && (*colorPtrPtr != COLOR_DEFAULT)) {
+ Tk_FreeColor(*colorPtrPtr);
+ }
+ *colorPtrPtr = NULL;
+ return TCL_OK;
+ }
+ if ((c == 'd') && (strncmp(string, "defcolor", length) == 0)) {
+ if ((*colorPtrPtr != NULL) && (*colorPtrPtr != COLOR_DEFAULT)) {
+ Tk_FreeColor(*colorPtrPtr);
+ }
+ *colorPtrPtr = COLOR_DEFAULT;
+ return TCL_OK;
+ }
+ colorPtr = Tk_AllocColorFromObj(interp, tkwin, objPtr);
+ if (colorPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if ((*colorPtrPtr != NULL) && (*colorPtrPtr != COLOR_DEFAULT)) {
+ Tk_FreeColor(*colorPtrPtr);
+ }
+ *colorPtrPtr = colorPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ColorToObj --
+ *
+ * Convert the color value into a string.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+ColorToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ XColor *colorPtr = *(XColor **)(widgRec + offset);
+ Tcl_Obj *objPtr;
+
+ if (colorPtr == NULL) {
+ objPtr = Tcl_NewStringObj("", -1);
+ } else if (colorPtr == COLOR_DEFAULT) {
+ objPtr = Tcl_NewStringObj("defcolor", -1);
+ } else {
+ objPtr = Tcl_NewStringObj(Tk_NameOfColor(colorPtr), -1);
+ }
+ return objPtr;
+}
+
+/*ARGSUSED*/
+static void
+FreePen(
+ ClientData clientData, /* Not used. */
+ Display *display, /* Not used. */
+ char *widgRec,
+ int offset)
+{
+ Pen **penPtrPtr = (Pen **)(widgRec + offset);
+
+ if (*penPtrPtr != NULL) {
+ Blt_FreePen(*penPtrPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToPen --
+ *
+ * Convert the color value into a string.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToPen(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ Tk_Window tkwin, /* Not used. */
+ Tcl_Obj *objPtr, /* String representing pen */
+ char *widgRec, /* Widget record */
+ int offset, /* Offset to field in structure */
+ int flags)
+{
+ Pen **penPtrPtr = (Pen **)(widgRec + offset);
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ if ((string[0] == '\0') && (flags & BLT_CONFIG_NULL_OK)) {
+ Blt_FreePen(*penPtrPtr);
+ *penPtrPtr = NULL;
+ } else {
+ Pen *penPtr;
+ Graph *graphPtr;
+ ClassId classId = (ClassId)clientData; /* Element type. */
+
+ graphPtr = Blt_GetGraphFromWindowData(tkwin);
+ assert(graphPtr);
+
+ if (classId == CID_NONE) {
+ classId = graphPtr->classId;
+ }
+ if (Blt_GetPenFromObj(interp, graphPtr, objPtr, classId, &penPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_FreePen(*penPtrPtr);
+ *penPtrPtr = penPtr;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PenToObj --
+ *
+ * Parse the name of the name.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static Tcl_Obj *
+PenToObj(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tk_Window tkwin, /* Not used. */
+ char *widgRec, /* Widget information record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Pen *penPtr = *(Pen **)(widgRec + offset);
+
+ if (penPtr == NULL) {
+ return Tcl_NewStringObj("", -1);
+ } else {
+ return Tcl_NewStringObj(penPtr->name, -1);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetPenFromObj --
+ *
+ * Find and return the pen style from a given name.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+GetPenFromObj(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ Pen **penPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+ Pen *penPtr;
+ const char *name;
+
+ penPtr = NULL;
+ name = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&graphPtr->penTable, name);
+ if (hPtr != NULL) {
+ penPtr = Blt_GetHashValue(hPtr);
+ if (penPtr->flags & DELETE_PENDING) {
+ penPtr = NULL;
+ }
+ }
+ if (penPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find pen \"", name, "\" in \"",
+ Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *penPtrPtr = penPtr;
+ return TCL_OK;
+}
+
+static void
+DestroyPen(Pen *penPtr)
+{
+ Graph *graphPtr = penPtr->graphPtr;
+
+ Blt_FreeOptions(penPtr->configSpecs, (char *)penPtr, graphPtr->display, 0);
+ (*penPtr->destroyProc) (graphPtr, penPtr);
+ if ((penPtr->name != NULL) && (penPtr->name[0] != '\0')) {
+ Blt_Free(penPtr->name);
+ }
+ if (penPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&graphPtr->penTable, penPtr->hashPtr);
+ }
+ Blt_Free(penPtr);
+}
+
+void
+Blt_FreePen(Pen *penPtr)
+{
+ if (penPtr != NULL) {
+ penPtr->refCount--;
+ if ((penPtr->refCount == 0) && (penPtr->flags & DELETE_PENDING)) {
+ DestroyPen(penPtr);
+ }
+ }
+}
+
+Pen *
+Blt_CreatePen(Graph *graphPtr, const char *penName, ClassId classId,
+ int objc, Tcl_Obj *const *objv)
+{
+ Pen *penPtr;
+ Blt_HashEntry *hPtr;
+ unsigned int configFlags;
+ int isNew;
+ int i;
+
+ /*
+ * Scan the option list for a "-type" entry. This will indicate what type
+ * of pen we are creating. Otherwise we'll default to the suggested type.
+ * Last -type option wins.
+ */
+ for (i = 0; i < objc; i += 2) {
+ char *string;
+ int length;
+
+ string = Tcl_GetStringFromObj(objv[i], &length);
+ if ((length > 2) && (strncmp(string, "-type", length) == 0)) {
+ char *arg;
+
+ arg = Tcl_GetString(objv[i + 1]);
+ if (strcmp(arg, "bar") == 0) {
+ classId = CID_ELEM_BAR;
+ } else if (strcmp(arg, "line") == 0) {
+ classId = CID_ELEM_LINE;
+ } else if (strcmp(arg, "strip") == 0) {
+ classId = CID_ELEM_LINE;
+ } else if (strcmp(arg, "contour") == 0) {
+ classId = CID_ELEM_CONTOUR;
+ } else {
+ Tcl_AppendResult(graphPtr->interp, "unknown pen type \"",
+ arg, "\" specified", (char *)NULL);
+ return NULL;
+ }
+ }
+ }
+ if (classId == CID_ELEM_STRIP) {
+ classId = CID_ELEM_LINE;
+ }
+ hPtr = Blt_CreateHashEntry(&graphPtr->penTable, penName, &isNew);
+ if (!isNew) {
+ penPtr = Blt_GetHashValue(hPtr);
+ if ((penPtr->flags & DELETE_PENDING) == 0) {
+ Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+ "\" already exists in \"", Tk_PathName(graphPtr->tkwin), "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ if (penPtr->classId != classId) {
+ Tcl_AppendResult(graphPtr->interp, "pen \"", penName,
+ "\" in-use: can't change pen type from \"",
+ Blt_GraphClassName(penPtr->classId), "\" to \"",
+ Blt_GraphClassName(classId), "\"", (char *)NULL);
+ return NULL;
+ }
+ penPtr->flags &= ~DELETE_PENDING; /* Undelete the pen. */
+ } else {
+ if (classId == CID_ELEM_BAR) {
+ penPtr = Blt_BarPen(penName);
+ } else {
+ penPtr = Blt_LinePen(penName);
+ }
+ penPtr->classId = classId;
+ penPtr->hashPtr = hPtr;
+ penPtr->graphPtr = graphPtr;
+ Blt_SetHashValue(hPtr, penPtr);
+ }
+ configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+ if (Blt_ConfigureComponentFromObj(graphPtr->interp, graphPtr->tkwin,
+ penPtr->name, "Pen", penPtr->configSpecs, objc, objv,
+ (char *)penPtr, configFlags) != TCL_OK) {
+ if (isNew) {
+ DestroyPen(penPtr);
+ }
+ return NULL;
+ }
+ (*penPtr->configProc) (graphPtr, penPtr);
+ return penPtr;
+}
+
+int
+Blt_GetPenFromObj(Tcl_Interp *interp, Graph *graphPtr, Tcl_Obj *objPtr,
+ ClassId classId, Pen **penPtrPtr)
+{
+ Blt_HashEntry *hPtr;
+ Pen *penPtr;
+ const char *name;
+
+ penPtr = NULL;
+ name = Tcl_GetString(objPtr);
+ hPtr = Blt_FindHashEntry(&graphPtr->penTable, name);
+ if (hPtr != NULL) {
+ penPtr = Blt_GetHashValue(hPtr);
+ if (penPtr->flags & DELETE_PENDING) {
+ penPtr = NULL;
+ }
+ }
+ if (penPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find pen \"", name, "\" in \"",
+ Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ if (classId == CID_ELEM_STRIP) {
+ classId = CID_ELEM_LINE;
+ }
+ if (penPtr->classId != classId) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "pen \"", name,
+ "\" is the wrong type (is \"",
+ Blt_GraphClassName(penPtr->classId), "\"", ", wanted \"",
+ Blt_GraphClassName(classId), "\")", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ penPtr->refCount++;
+ *penPtrPtr = penPtr;
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DestroyPens --
+ *
+ * Release memory and resources allocated for the style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the pen style is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DestroyPens(Graph *graphPtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->penTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Pen *penPtr;
+
+ penPtr = Blt_GetHashValue(hPtr);
+ penPtr->hashPtr = NULL;
+ DestroyPen(penPtr);
+ }
+ Blt_DeleteHashTable(&graphPtr->penTable);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CgetOp --
+ *
+ * Queries axis attributes (font, line width, label, etc).
+ *
+ * Results:
+ * A standard TCL result. If querying configuration values,
+ * interp->result will contain the results.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static int
+CgetOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Pen *penPtr;
+ unsigned int configFlags;
+
+ if (GetPenFromObj(interp, graphPtr, objv[3], &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ configFlags = (penPtr->flags & (ACTIVE_PEN | NORMAL_PEN));
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin,
+ penPtr->configSpecs, (char *)penPtr, objv[4], configFlags);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureOp --
+ *
+ * Queries or resets pen attributes (font, line width, color, etc).
+ *
+ * Results:
+ * A standard TCL result. If querying configuration values,
+ * interp->result will contain the results.
+ *
+ * Side Effects:
+ * Pen resources are possibly allocated (GC, font).
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ConfigureOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Pen *penPtr;
+ int nNames, nOpts;
+ int redraw;
+ Tcl_Obj *const *options;
+ int i;
+
+ /* Figure out where the option value pairs begin */
+ objc -= 3;
+ objv += 3;
+ for (i = 0; i < objc; i++) {
+ char *string;
+
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ if (GetPenFromObj(interp, graphPtr, objv[i], &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ nNames = i; /* Number of pen names specified */
+ nOpts = objc - i; /* Number of options specified */
+ options = objv + i; /* Start of options in objv */
+
+ redraw = 0;
+ for (i = 0; i < nNames; i++) {
+ int flags;
+
+ if (GetPenFromObj(interp, graphPtr, objv[i], &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ flags = BLT_CONFIG_OBJV_ONLY | (penPtr->flags&(ACTIVE_PEN|NORMAL_PEN));
+ if (nOpts == 0) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ penPtr->configSpecs, (char *)penPtr, (Tcl_Obj *)NULL, flags);
+ } else if (nOpts == 1) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin,
+ penPtr->configSpecs, (char *)penPtr, options[0], flags);
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin,
+ penPtr->configSpecs, nOpts, options, (char *)penPtr, flags)
+ != TCL_OK) {
+ break;
+ }
+ (*penPtr->configProc) (graphPtr, penPtr);
+ if (penPtr->refCount > 0) {
+ redraw++;
+ }
+ }
+ if (redraw) {
+ graphPtr->flags |= CACHE_DIRTY;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ if (i < nNames) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateOp --
+ *
+ * Adds a new penstyle to the graph.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+CreateOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Pen *penPtr;
+
+ penPtr = Blt_CreatePen(graphPtr, Tcl_GetString(objv[3]), graphPtr->classId,
+ objc - 4, objv + 4);
+ if (penPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, objv[3]);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Delete the given pen.
+ *
+ * Results:
+ * Always returns TCL_OK. The interp->result field is a list of the
+ * graph axis limits.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+DeleteOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ Pen *penPtr;
+
+ if (GetPenFromObj(interp, graphPtr, objv[i], &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (penPtr->flags & DELETE_PENDING) {
+ Tcl_AppendResult(interp, "can't find pen \"",
+ Tcl_GetString(objv[i]), "\" in \"",
+ Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ penPtr->flags |= DELETE_PENDING;
+ if (penPtr->refCount == 0) {
+ DestroyPen(penPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NamesOp --
+ *
+ * Return a list of the names of all the axes.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NamesOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (objc == 3) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->penTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Pen *penPtr;
+
+ penPtr = Blt_GetHashValue(hPtr);
+ if ((penPtr->flags & DELETE_PENDING) == 0) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(penPtr->name, -1));
+ }
+ }
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch iter;
+
+ for (hPtr = Blt_FirstHashEntry(&graphPtr->penTable, &iter);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&iter)) {
+ Pen *penPtr;
+
+ penPtr = Blt_GetHashValue(hPtr);
+ if ((penPtr->flags & DELETE_PENDING) == 0) {
+ int i;
+
+ for (i = 3; i < objc; i++) {
+ char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ if (Tcl_StringMatch(penPtr->name, pattern)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(penPtr->name, -1));
+ break;
+ }
+ }
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TypeOp --
+ *
+ * Return the type of pen.
+ *
+ * Results:
+ * Returns a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TypeOp(Tcl_Interp *interp, Graph *graphPtr, int objc, Tcl_Obj *const *objv)
+{
+ Pen *penPtr;
+
+ if (GetPenFromObj(interp, graphPtr, objv[3], &penPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ Blt_GraphClassName(penPtr->classId), -1);
+ return TCL_OK;
+}
+
+static Blt_OpSpec penOps[] =
+{
+ {"cget", 2, CgetOp, 5, 5, "penName option",},
+ {"configure", 2, ConfigureOp, 4, 0,
+ "penName ?penName?... ?option value?...",},
+ {"create", 2, CreateOp, 4, 0, "penName ?option value?...",},
+ {"delete", 2, DeleteOp, 3, 0, "?penName?...",},
+ {"names", 1, NamesOp, 3, 0, "?pattern?...",},
+ {"type", 1, TypeOp, 4, 4, "penName",},
+};
+static int nPenOps = sizeof(penOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_PenOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ GraphPenProc *proc;
+
+ proc = Blt_GetOpFromObj(interp, nPenOps, penOps, BLT_OP_ARG2,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (interp, graphPtr, objc, objv);
+}
diff --git a/src/bltGrText.C b/src/bltGrText.C
new file mode 100644
index 0000000..d9cb33b
--- /dev/null
+++ b/src/bltGrText.C
@@ -0,0 +1,2018 @@
+
+/*
+ * bltText.c --
+ *
+ * This module implements multi-line, rotate-able text for the BLT toolkit.
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include <bltHash.h>
+#include <X11/Xutil.h>
+#include "tkIntBorder.h"
+#include "tkDisplay.h"
+#include "bltImage.h"
+#include "bltBitmap.h"
+#include "bltFont.h"
+#include "bltText.h"
+#include "bltBgStyle.h"
+
+#define WINDEBUG 0
+
+static Blt_HashTable bitmapGCTable;
+static int initialized;
+
+GC
+Blt_GetBitmapGC(Tk_Window tkwin)
+{
+ int isNew;
+ GC gc;
+ Display *display;
+ Blt_HashEntry *hPtr;
+
+ if (!initialized) {
+ Blt_InitHashTable(&bitmapGCTable, BLT_ONE_WORD_KEYS);
+ initialized = TRUE;
+ }
+ display = Tk_Display(tkwin);
+ hPtr = Blt_CreateHashEntry(&bitmapGCTable, (char *)display, &isNew);
+ if (isNew) {
+ Pixmap bitmap;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ Window root;
+
+ root = Tk_RootWindow(tkwin);
+ bitmap = Tk_GetPixmap(display, root, 1, 1, 1);
+ gcValues.foreground = gcValues.background = 0;
+ gcMask = (GCForeground | GCBackground);
+ gc = Blt_GetPrivateGCFromDrawable(display, bitmap, gcMask, &gcValues);
+ Tk_FreePixmap(display, bitmap);
+ Blt_SetHashValue(hPtr, gc);
+ } else {
+ gc = (GC)Blt_GetHashValue(hPtr);
+ }
+ return gc;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetTextExtents --
+ *
+ * Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ * Returns via *widthPtr* and *heightPtr* the dimensions of the text
+ * string.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_GetTextExtents(
+ Blt_Font font,
+ int leader,
+ const char *text, /* Text string to be measured. */
+ int textLen, /* Length of the text. If -1, indicates that
+ * text is an ASCIZ string that the length
+ * should be computed with strlen. */
+ unsigned int *widthPtr,
+ unsigned int *heightPtr)
+{
+ unsigned int lineHeight;
+
+ if (text == NULL) {
+ return; /* NULL string? */
+ }
+ {
+ Blt_FontMetrics fm;
+
+ Blt_GetFontMetrics(font, &fm);
+ lineHeight = fm.linespace;
+ }
+ if (textLen < 0) {
+ textLen = strlen(text);
+ }
+ {
+ unsigned int lineLen; /* # of characters on each line */
+ const char *p, *pend;
+ const char *line;
+ unsigned int maxWidth, maxHeight;
+
+ maxWidth = maxHeight = 0;
+ lineLen = 0;
+ for (p = line = text, pend = text + textLen; p < pend; p++) {
+ if (*p == '\n') {
+ if (lineLen > 0) {
+ unsigned int lineWidth;
+
+ lineWidth = Blt_TextWidth(font, line, lineLen);
+ if (lineWidth > maxWidth) {
+ maxWidth = lineWidth;
+ }
+ }
+ maxHeight += lineHeight;
+ line = p + 1; /* Point to the start of the next line. */
+ lineLen = 0; /* Reset counter to indicate the start of a
+ * new line. */
+ continue;
+ }
+ lineLen++;
+ }
+ if ((lineLen > 0) && (*(p - 1) != '\n')) {
+ unsigned int lineWidth;
+
+ maxHeight += lineHeight;
+ lineWidth = Blt_TextWidth(font, line, lineLen);
+ if (lineWidth > maxWidth) {
+ maxWidth = lineWidth;
+ }
+ }
+ *widthPtr = maxWidth;
+ *heightPtr = maxHeight;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_GetExtents --
+ *
+ * Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ * Returns via *widthPtr* and *heightPtr* the dimensions of
+ * the text string.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ts_GetExtents(TextStyle *tsPtr, const char *text, unsigned int *widthPtr,
+ unsigned int *heightPtr)
+{
+
+ if (text == NULL) {
+ *widthPtr = *heightPtr = 0;
+ } else {
+ unsigned int w, h;
+
+ Blt_GetTextExtents(tsPtr->font, tsPtr->leader, text, -1, &w, &h);
+ *widthPtr = w + PADDING(tsPtr->xPad);
+ *heightPtr = h + PADDING(tsPtr->yPad);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetBoundingBox
+ *
+ * Computes the dimensions of the bounding box surrounding a rectangle
+ * rotated about its center. If pointArr isn't NULL, the coordinates of
+ * the rotated rectangle are also returned.
+ *
+ * The dimensions are determined by rotating the rectangle, and doubling
+ * the maximum x-coordinate and y-coordinate.
+ *
+ * w = 2 * maxX, h = 2 * maxY
+ *
+ * Since the rectangle is centered at 0,0, the coordinates of the
+ * bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2).
+ *
+ * 0 ------- 1
+ * | |
+ * | x |
+ * | |
+ * 3 ------- 2
+ *
+ * Results:
+ * The width and height of the bounding box containing the rotated
+ * rectangle are returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_GetBoundingBox(
+ int width, int height, /* Unrotated region */
+ float angle, /* Rotation of box */
+ double *rotWidthPtr,
+ double *rotHeightPtr, /* (out) Bounding box region */
+ Point2d *bbox) /* (out) Points of the rotated box */
+{
+ int i;
+ double sinTheta, cosTheta;
+ double radians;
+ double xMax, yMax;
+ double x, y;
+ Point2d corner[4];
+
+ angle = FMOD(angle, 360.0);
+ if (FMOD(angle, (double)90.0) == 0.0) {
+ int ll, ur, ul, lr;
+ double rotWidth, rotHeight;
+ int quadrant;
+
+ /* Handle right-angle rotations specially. */
+
+ quadrant = (int)(angle / 90.0);
+ switch (quadrant) {
+ case ROTATE_270: /* 270 degrees */
+ ul = 3, ur = 0, lr = 1, ll = 2;
+ rotWidth = (double)height;
+ rotHeight = (double)width;
+ break;
+ case ROTATE_90: /* 90 degrees */
+ ul = 1, ur = 2, lr = 3, ll = 0;
+ rotWidth = (double)height;
+ rotHeight = (double)width;
+ break;
+ case ROTATE_180: /* 180 degrees */
+ ul = 2, ur = 3, lr = 0, ll = 1;
+ rotWidth = (double)width;
+ rotHeight = (double)height;
+ break;
+ default:
+ case ROTATE_0: /* 0 degrees */
+ ul = 0, ur = 1, lr = 2, ll = 3;
+ rotWidth = (double)width;
+ rotHeight = (double)height;
+ break;
+ }
+ if (bbox != NULL) {
+ x = rotWidth * 0.5;
+ y = rotHeight * 0.5;
+ bbox[ll].x = bbox[ul].x = -x;
+ bbox[ur].y = bbox[ul].y = -y;
+ bbox[lr].x = bbox[ur].x = x;
+ bbox[ll].y = bbox[lr].y = y;
+ }
+ *rotWidthPtr = rotWidth;
+ *rotHeightPtr = rotHeight;
+ return;
+ }
+ /* Set the four corners of the rectangle whose center is the origin. */
+ corner[1].x = corner[2].x = (double)width * 0.5;
+ corner[0].x = corner[3].x = -corner[1].x;
+ corner[2].y = corner[3].y = (double)height * 0.5;
+ corner[0].y = corner[1].y = -corner[2].y;
+
+ radians = (-angle / 180.0) * M_PI;
+ sinTheta = sin(radians), cosTheta = cos(radians);
+ xMax = yMax = 0.0;
+
+ /* Rotate the four corners and find the maximum X and Y coordinates */
+
+ for (i = 0; i < 4; i++) {
+ x = (corner[i].x * cosTheta) - (corner[i].y * sinTheta);
+ y = (corner[i].x * sinTheta) + (corner[i].y * cosTheta);
+ if (x > xMax) {
+ xMax = x;
+ }
+ if (y > yMax) {
+ yMax = y;
+ }
+ if (bbox != NULL) {
+ bbox[i].x = x;
+ bbox[i].y = y;
+ }
+ }
+
+ /*
+ * By symmetry, the width and height of the bounding box are twice the
+ * maximum x and y coordinates.
+ */
+ *rotWidthPtr = xMax + xMax;
+ *rotHeightPtr = yMax + yMax;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_TranslateAnchor --
+ *
+ * Translate the coordinates of a given bounding box based upon the
+ * anchor specified. The anchor indicates where the given x-y position
+ * is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | |
+ * w center e
+ * | |
+ * sw --- s --- se
+ *
+ * The coordinates returned are translated to the origin of the bounding
+ * box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_TranslateAnchor(
+ int x, int y, /* Window coordinates of anchor */
+ int w, int h, /* Extents of the bounding box */
+ Tk_Anchor anchor, /* Direction of the anchor */
+ int *xPtr, int *yPtr)
+{
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y -= (h / 2);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y -= h;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x -= (w / 2);
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ x -= (w / 2);
+ y -= (h / 2);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x -= (w / 2);
+ y -= h;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x -= w;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x -= w;
+ y -= (h / 2);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x -= w;
+ y -= h;
+ break;
+ }
+ *xPtr = x;
+ *yPtr = y;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_AnchorPoint --
+ *
+ * Translates a position, using both the dimensions of the bounding box,
+ * and the anchor direction, returning the coordinates of the upper-left
+ * corner of the box. The anchor indicates where the given x-y position
+ * is in relation to the bounding box.
+ *
+ * nw --- n --- ne
+ * | |
+ * w center e
+ * | |
+ * sw --- s --- se
+ *
+ * The coordinates returned are translated to the origin of the bounding
+ * box (suitable for giving to XCopyArea, XCopyPlane, etc.)
+ *
+ * Results:
+ * The translated coordinates of the bounding box are returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+Point2d
+Blt_AnchorPoint(
+ double x, double y, /* Coordinates of anchor. */
+ double w, double h, /* Extents of the bounding box */
+ Tk_Anchor anchor) /* Direction of the anchor */
+{
+ Point2d t;
+
+ switch (anchor) {
+ case TK_ANCHOR_NW: /* Upper left corner */
+ break;
+ case TK_ANCHOR_W: /* Left center */
+ y -= (h * 0.5);
+ break;
+ case TK_ANCHOR_SW: /* Lower left corner */
+ y -= h;
+ break;
+ case TK_ANCHOR_N: /* Top center */
+ x -= (w * 0.5);
+ break;
+ case TK_ANCHOR_CENTER: /* Center */
+ x -= (w * 0.5);
+ y -= (h * 0.5);
+ break;
+ case TK_ANCHOR_S: /* Bottom center */
+ x -= (w * 0.5);
+ y -= h;
+ break;
+ case TK_ANCHOR_NE: /* Upper right corner */
+ x -= w;
+ break;
+ case TK_ANCHOR_E: /* Right center */
+ x -= w;
+ y -= (h * 0.5);
+ break;
+ case TK_ANCHOR_SE: /* Lower right corner */
+ x -= w;
+ y -= h;
+ break;
+ }
+ t.x = x;
+ t.y = y;
+ return t;
+}
+
+static INLINE int
+SizeOfUtfChar(const char *s) /* Buffer in which the UTF-8 representation of
+ * the Tcl_UniChar is stored. Buffer must be
+ * large enough to hold the UTF-8 character
+ * (at most TCL_UTF_MAX bytes). */
+{
+ int byte;
+
+ byte = *((unsigned char *)s);
+ if (byte < 0xC0) {
+ return 1;
+ } else if ((byte < 0xE0) && ((s[1] & 0xC0) == 0x80)) {
+ return 2;
+ } else if ((byte < 0xF0) &&
+ ((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) {
+ return 3;
+ }
+ return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_MeasureText --
+ *
+ * Draw a string of characters on the screen. Blt_DrawChars()
+ * expands control characters that occur in the string to
+ * \xNN sequences.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_MeasureText(
+ Blt_Font font, /* Font in which characters will be drawn;
+ * must be the same as font used in GC. */
+ const char *text, /* UTF-8 string to be displayed. Need not be
+ * '\0' terminated. All Tk meta-characters
+ * (tabs, control characters, and newlines)
+ * should be stripped out of the string that
+ * is passed to this function. If they are
+ * not stripped out, they will be displayed as
+ * regular printing characters. */
+ int textLen, /* # of bytes to draw in text string. */
+ int maxLength,
+ int *countPtr)
+{
+ int elWidth;
+ const char *s, *send;
+ int accum, count, threshold;
+ int nBytes;
+
+ if (maxLength < 0) {
+ if (countPtr != NULL) {
+ nBytes = textLen;
+ }
+ return Blt_TextWidth(font, text, textLen);
+ }
+ elWidth = Blt_TextWidth(font, "...", 3);
+ threshold = maxLength - elWidth;
+ if (threshold <= 0) {
+ return 0;
+ }
+#if !HAVE_UTF
+ nBytes = 1;
+#endif /* !HAVE_UTF */
+ count = accum = 0;
+ for (s = text, send = s + textLen; s < send; s += nBytes) {
+#if HAVE_UTF
+ Tcl_UniChar ch;
+#endif /* HAVE_UTF */
+ int w;
+#if HAVE_UTF
+ nBytes = Tcl_UtfToUniChar (s, &ch);
+#endif /* HAVE_UTF */
+ w = Blt_TextWidth(font, s, nBytes);
+ if ((accum + w) > threshold) {
+ if (countPtr != NULL) {
+ *countPtr = count;
+ }
+ return accum + elWidth;
+ }
+ accum += w;
+ count += nBytes;
+ }
+ if (countPtr != NULL) {
+ *countPtr = count;
+ }
+ return accum;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_CreateLayout --
+ *
+ * Get the extents of a possibly multiple-lined text string.
+ *
+ * Results:
+ * Returns via *widthPtr* and *heightPtr* the dimensions of the text
+ * string.
+ *
+ *---------------------------------------------------------------------------
+ */
+TextLayout *
+Blt_Ts_CreateLayout(const char *text, int textLen, TextStyle *tsPtr)
+{
+ TextFragment *fp;
+ TextLayout *layoutPtr;
+ Blt_FontMetrics fm;
+ int count; /* Count # of characters on each line */
+ int lineHeight;
+ size_t maxHeight, maxWidth;
+ size_t nFrags;
+ int width; /* Running dimensions of the text */
+ const char *p, *endp, *start;
+ int i;
+ size_t size;
+
+ nFrags = 0;
+ endp = text + ((textLen < 0) ? strlen(text) : textLen);
+ for (p = text; p < endp; p++) {
+ if (*p == '\n') {
+ nFrags++;
+ }
+ }
+ if ((p != text) && (*(p - 1) != '\n')) {
+ nFrags++;
+ }
+ size = sizeof(TextLayout) + (sizeof(TextFragment) * (nFrags - 1));
+
+ layoutPtr = Blt_AssertCalloc(1, size);
+ layoutPtr->nFrags = nFrags;
+
+ nFrags = count = 0;
+ width = maxWidth = 0;
+ maxHeight = tsPtr->padTop;
+ Blt_GetFontMetrics(tsPtr->font, &fm);
+ lineHeight = fm.linespace + tsPtr->leader;
+
+ fp = layoutPtr->fragments;
+ for (p = start = text; p < endp; p++) {
+ if (*p == '\n') {
+ if (count > 0) {
+ width = Blt_TextWidth(tsPtr->font, start, count);
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ } else {
+ width = 0;
+ }
+ fp->width = width;
+ fp->count = count;
+ fp->sy = fp->y = maxHeight + fm.ascent;
+ fp->text = start;
+ maxHeight += lineHeight;
+ fp++;
+ nFrags++;
+ start = p + 1; /* Start the text on the next line */
+ count = 0; /* Reset to indicate the start of a new
+ * line */
+ continue;
+ }
+ count++;
+ }
+
+ if (nFrags < layoutPtr->nFrags) {
+ width = Blt_TextWidth(tsPtr->font, start, count);
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ fp->width = width;
+ fp->count = count;
+ fp->sy = fp->y = maxHeight + fm.ascent;
+ fp->text = start;
+ maxHeight += lineHeight;
+ nFrags++;
+ }
+ maxHeight += tsPtr->padBottom;
+ maxWidth += PADDING(tsPtr->xPad);
+ fp = layoutPtr->fragments;
+ for (i = 0; i < nFrags; i++, fp++) {
+ switch (tsPtr->justify) {
+ default:
+ case TK_JUSTIFY_LEFT:
+ /* No offset for left justified text strings */
+ fp->x = fp->sx = tsPtr->padLeft;
+ break;
+ case TK_JUSTIFY_RIGHT:
+ fp->x = fp->sx = (maxWidth - fp->width) - tsPtr->padRight;
+ break;
+ case TK_JUSTIFY_CENTER:
+ fp->x = fp->sx = (maxWidth - fp->width) / 2;
+ break;
+ }
+ }
+ if (tsPtr->underline >= 0) {
+ fp = layoutPtr->fragments;
+ for (i = 0; i < nFrags; i++, fp++) {
+ int first, last;
+
+ first = fp->text - text;
+ last = first + fp->count;
+ if ((tsPtr->underline >= first) && (tsPtr->underline < last)) {
+ layoutPtr->underlinePtr = fp;
+ layoutPtr->underline = tsPtr->underline - first;
+ break;
+ }
+ }
+ }
+ layoutPtr->width = maxWidth;
+ layoutPtr->height = maxHeight - tsPtr->leader;
+ return layoutPtr;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DrawCharsWithEllipsis --
+ *
+ * Draw a string of characters on the screen. Blt_DrawChars()
+ * expands control characters that occur in the string to
+ * \xNN sequences.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_DrawCharsWithEllipsis(
+ Tk_Window tkwin, /* Display on which to draw. */
+ Drawable drawable, /* Window or pixmap in which to draw. */
+ GC gc, /* Graphics context for drawing characters. */
+ Blt_Font font, /* Font in which characters will be drawn;
+ * must be the same as font used in GC. */
+ int depth,
+ float angle,
+ const char *text, /* UTF-8 string to be displayed. Need not be
+ * '\0' terminated. All Tk meta-characters
+ * (tabs, control characters, and newlines)
+ * should be stripped out of the string that
+ * is passed to this function. If they are
+ * not stripped out, they will be displayed as
+ * regular printing characters. */
+ int textLen, /* # of bytes to draw in text string. */
+ int x, int y, /* Coordinates at which to place origin of
+ * string when drawing. */
+ int maxLength)
+{
+ int elWidth;
+ const char *s, *send;
+ Tcl_DString dString;
+ int nBytes;
+ int accum, threshold;
+
+#if HAVE_UTF
+ Tcl_UniChar ch;
+#endif /* HAVE_UTF */
+ accum = 0;
+ elWidth = Blt_TextWidth(font, "...", 3);
+ if (maxLength < elWidth) {
+ return;
+ }
+ threshold = maxLength - elWidth;
+ Tcl_DStringInit(&dString);
+#if !HAVE_UTF
+ nBytes = 1;
+#endif /* !HAVE_UTF */
+ for (s = text, send = s + textLen; s < send; s += nBytes) {
+#if HAVE_UTF
+ nBytes = Tcl_UtfToUniChar (s, &ch);
+#endif /* HAVE_UTF */
+ accum += Blt_TextWidth(font, s, nBytes);
+ if (accum > threshold) {
+ break;
+ }
+ Tcl_DStringAppend(&dString, s, nBytes);
+ }
+ if (s < send) {
+ Tcl_DStringAppend(&dString, "...", 3);
+ }
+ Blt_DrawChars(Tk_Display(tkwin), drawable, gc, font, depth, angle,
+ Tcl_DStringValue(&dString), Tcl_DStringLength(&dString), x, y);
+ Tcl_DStringFree(&dString);
+}
+
+void
+Blt_DrawLayout(Tk_Window tkwin, Drawable drawable, GC gc, Blt_Font font,
+ int depth, float angle, int x, int y, TextLayout *layoutPtr,
+ int maxLength)
+{
+ TextFragment *fp, *fend;
+ Blt_FontMetrics fm;
+
+ Blt_GetFontMetrics(font, &fm);
+ for (fp = layoutPtr->fragments, fend = fp + layoutPtr->nFrags;
+ fp < fend; fp++) {
+ int sx, sy;
+
+ sx = x + fp->sx, sy = y + fp->sy;
+ if ((maxLength > 0) && ((fp->width + fp->x) > maxLength)) {
+ Blt_DrawCharsWithEllipsis(tkwin, drawable, gc, font, depth, angle,
+ fp->text, fp->count, sx, sy, maxLength - fp->x);
+ } else {
+ Blt_DrawChars(Tk_Display(tkwin), drawable, gc, font, depth, angle,
+ fp->text, fp->count, sx, sy);
+ }
+ }
+ if (layoutPtr->underlinePtr != NULL) {
+ fp = layoutPtr->underlinePtr;
+ Blt_UnderlineChars(Tk_Display(tkwin), drawable, gc, font, fp->text,
+ fp->count, x + fp->sx, y + fp->sy, layoutPtr->underline,
+ layoutPtr->underline + 1, maxLength);
+ }
+}
+
+
+#ifdef WIN32
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_Bitmap --
+ *
+ * Draw a bitmap, using the the given window coordinates as an anchor for
+ * the text bounding box.
+ *
+ * Results:
+ * Returns the bitmap representing the text string.
+ *
+ * Side Effects:
+ * Bitmap is drawn using the given font and GC in the drawable at the
+ * given coordinates, anchor, and rotation.
+ *
+ *---------------------------------------------------------------------------
+ */
+Pixmap
+Blt_Ts_Bitmap(
+ Tk_Window tkwin,
+ TextLayout *layoutPtr, /* Text string to draw */
+ TextStyle *stylePtr, /* Text attributes: rotation, color,
+ * font, linespacing, justification,
+ * etc. */
+ int *bmWidthPtr,
+ int *bmHeightPtr) /* Extents of rotated text string */
+{
+ Pixmap bitmap;
+ Window root;
+ GC gc;
+ HDC hDC;
+ TkWinDCState state;
+
+ /* Create a temporary bitmap to contain the text string */
+ root = Tk_RootWindow(tkwin);
+ bitmap = Tk_GetPixmap(Tk_Display(tkwin), root, layoutPtr->width,
+ layoutPtr->height, 1);
+ assert(bitmap != None);
+ if (bitmap == None) {
+ return None; /* Can't allocate pixmap. */
+ }
+ gc = Blt_GetBitmapGC(tkwin);
+
+ /* Clear the pixmap and draw the text string into it */
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), bitmap, &state);
+ PatBlt(hDC, 0, 0, layoutPtr->width, layoutPtr->height, WHITENESS);
+ TkWinReleaseDrawableDC(bitmap, hDC, &state);
+
+ XSetFont(Tk_Display(tkwin), gc, Blt_FontId(stylePtr->font));
+ XSetForeground(Tk_Display(tkwin), gc, 1);
+ Blt_DrawLayout(tkwin, bitmap, gc, stylePtr->font, 1, 0.0f, 0, 0, layoutPtr,
+ stylePtr->maxLength);
+
+ /*
+ * Under Win32, 1 is off and 0 is on. That's why we're inverting the
+ * bitmap here.
+ */
+ hDC = TkWinGetDrawableDC(Tk_Display(tkwin), bitmap, &state);
+ PatBlt(hDC, 0, 0, layoutPtr->width, layoutPtr->height, DSTINVERT);
+ TkWinReleaseDrawableDC(bitmap, hDC, &state);
+
+ *bmWidthPtr = layoutPtr->width, *bmHeightPtr = layoutPtr->height;
+ return bitmap;
+}
+#else
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_Bitmap --
+ *
+ * Draw a bitmap, using the the given window coordinates as an anchor for
+ * the text bounding box.
+ *
+ * Results:
+ * Returns the bitmap representing the text string.
+ *
+ * Side Effects:
+ * Bitmap is drawn using the given font and GC in the drawable at the
+ * given coordinates, anchor, and rotation.
+ *
+ *---------------------------------------------------------------------------
+ */
+Pixmap
+Blt_Ts_Bitmap(
+ Tk_Window tkwin,
+ TextLayout *layoutPtr, /* Text string to draw */
+ TextStyle *stylePtr, /* Text attributes: rotation, color,
+ * font, linespacing, justification,
+ * etc. */
+ int *bmWidthPtr,
+ int *bmHeightPtr) /* Extents of rotated text string */
+{
+ Pixmap bitmap;
+ GC gc;
+
+ /* Create a bitmap big enough to contain the text. */
+ bitmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_RootWindow(tkwin),
+ layoutPtr->width, layoutPtr->height, 1);
+ assert(bitmap != None);
+ if (bitmap == None) {
+ return None; /* Can't allocate pixmap. */
+ }
+ gc = Blt_GetBitmapGC(tkwin);
+
+ /* Clear the bitmap. Background is 0. */
+ XSetForeground(Tk_Display(tkwin), gc, 0);
+ XFillRectangle(Tk_Display(tkwin), bitmap, gc, 0, 0, layoutPtr->width,
+ layoutPtr->height);
+
+ /* Draw the text into the bitmap. Foreground is 1. */
+ XSetFont(Tk_Display(tkwin), gc, Blt_FontId(stylePtr->font));
+ XSetForeground(Tk_Display(tkwin), gc, 1);
+ Blt_DrawLayout(tkwin, bitmap, gc, stylePtr->font, 1, 0.0f, 0, 0, layoutPtr,
+ stylePtr->maxLength);
+ *bmWidthPtr = layoutPtr->width, *bmHeightPtr = layoutPtr->height;
+ return bitmap;
+}
+#endif /* WIN32 */
+
+void
+Blt_Ts_SetDrawStyle(
+ TextStyle *stylePtr,
+ Blt_Font font,
+ GC gc,
+ XColor *normalColor,
+ float angle,
+ Tk_Anchor anchor,
+ Tk_Justify justify,
+ int leader)
+{
+ stylePtr->xPad.side1 = stylePtr->xPad.side2 = 0;
+ stylePtr->yPad.side1 = stylePtr->yPad.side2 = 0;
+ stylePtr->state = 0;
+ stylePtr->anchor = anchor;
+ stylePtr->color = normalColor;
+ stylePtr->font = font;
+ stylePtr->gc = gc;
+ stylePtr->justify = justify;
+ stylePtr->leader = leader;
+ stylePtr->angle = (float)angle;
+}
+
+static void
+DrawStandardLayout(Tk_Window tkwin, Drawable drawable, TextStyle *stylePtr,
+ TextLayout *layoutPtr, int x, int y)
+{
+ int w, h;
+ /*
+ * This is the easy case of no rotation. Simply draw the text
+ * using the standard drawing routines. Handle offset printing
+ * for engraved (disabled) text.
+ */
+ w = layoutPtr->width;
+ h = layoutPtr->height;
+ if ((stylePtr->maxLength > 0) && (stylePtr->maxLength < w)) {
+ w = stylePtr->maxLength;
+ }
+ Blt_TranslateAnchor(x, y, w, h, stylePtr->anchor, &x, &y);
+ if (stylePtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *) Blt_BackgroundBorder(stylePtr->bg);
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (stylePtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, color1->pixel);
+ }
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), 0.0f, x+1, y+1, layoutPtr,stylePtr->maxLength);
+ if (color2 != NULL) {
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, color2->pixel);
+ }
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), 0.0f, x, y, layoutPtr, stylePtr->maxLength);
+
+ /* Reset the foreground color back to its original setting, so not to
+ * invalidate the GC cache. */
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, stylePtr->color->pixel);
+
+ return; /* Done */
+ }
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), 0.0f, x, y, layoutPtr, stylePtr->maxLength);
+}
+
+
+static void
+RotateStartingTextPositions(TextLayout *lPtr, int w, int h, float angle)
+{
+ Point2d off1, off2;
+ TextFragment *fp, *fend;
+ double radians;
+ double rw, rh;
+ double sinTheta, cosTheta;
+
+ Blt_GetBoundingBox(w, h, angle, &rw, &rh, (Point2d *)NULL);
+ off1.x = (double)w * 0.5;
+ off1.y = (double)h * 0.5;
+ off2.x = rw * 0.5;
+ off2.y = rh * 0.5;
+ radians = (-angle / 180.0) * M_PI;
+
+ sinTheta = sin(radians), cosTheta = cos(radians);
+ for (fp = lPtr->fragments, fend = fp + lPtr->nFrags; fp < fend; fp++) {
+ Point2d p, q;
+
+ p.x = fp->x - off1.x;
+ p.y = fp->y - off1.y;
+ q.x = (p.x * cosTheta) - (p.y * sinTheta);
+ q.y = (p.x * sinTheta) + (p.y * cosTheta);
+ q.x += off2.x;
+ q.y += off2.y;
+ fp->sx = ROUND(q.x);
+ fp->sy = ROUND(q.y);
+ }
+}
+
+void
+Blt_RotateStartingTextPositions(TextLayout *lPtr, float angle)
+{
+ RotateStartingTextPositions(lPtr, lPtr->width, lPtr->height, angle);
+}
+
+int
+Blt_DrawTextWithRotatedFont(Tk_Window tkwin, Drawable drawable, float angle,
+ TextStyle *stylePtr, TextLayout *layoutPtr,
+ int x, int y)
+{
+ double rw, rh;
+ int w, h;
+
+ w = layoutPtr->width;
+ h = layoutPtr->height;
+ if ((stylePtr->maxLength > 0) && (stylePtr->maxLength < w)) {
+ w = stylePtr->maxLength;
+ }
+ RotateStartingTextPositions(layoutPtr, w, h, angle);
+ Blt_GetBoundingBox(w, h, angle, &rw, &rh, (Point2d *)NULL);
+ Blt_TranslateAnchor(x, y, (int)rw, (int)rh, stylePtr->anchor, &x, &y);
+ if (stylePtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *)Blt_BackgroundBorder(stylePtr->bg);
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (stylePtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, color1->pixel);
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), angle, x, y, layoutPtr, stylePtr->maxLength);
+ }
+ if (color2 != NULL) {
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, color2->pixel);
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), angle, x, y, layoutPtr, stylePtr->maxLength);
+ }
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, stylePtr->color->pixel);
+ return TRUE;
+ }
+ XSetForeground(Tk_Display(tkwin), stylePtr->gc, stylePtr->color->pixel);
+ Blt_DrawLayout(tkwin, drawable, stylePtr->gc, stylePtr->font,
+ Tk_Depth(tkwin), angle, x, y, layoutPtr, stylePtr->maxLength);
+ return TRUE;
+}
+
+static void
+Blt_DrawTextWithRotatedBitmap(
+ Tk_Window tkwin,
+ Drawable drawable,
+ float angle,
+ TextStyle *stylePtr, /* Text attribute information */
+ TextLayout *layoutPtr,
+ int x, int y) /* Window coordinates to draw text */
+{
+ int width, height;
+ Display *display;
+ Pixmap bitmap;
+
+ display = Tk_Display(tkwin);
+ /*
+ * Rotate the text by writing the text into a bitmap and rotating the
+ * bitmap. Set the clip mask and origin in the GC first. And make sure
+ * we restore the GC because it may be shared.
+ */
+ stylePtr->angle = angle;
+
+ bitmap = Blt_Ts_Bitmap(tkwin, layoutPtr, stylePtr, &width, &height);
+ if (bitmap == None) {
+ return;
+ }
+ if ((bitmap != None) && (stylePtr->angle != 0.0)) {
+ Pixmap rotated;
+
+ rotated = Blt_RotateBitmap(tkwin, bitmap, width, height,
+ stylePtr->angle, &width, &height);
+ Tk_FreePixmap(display, bitmap);
+ bitmap = rotated;
+ }
+ Blt_TranslateAnchor(x, y, width, height, stylePtr->anchor, &x, &y);
+ XSetClipMask(display, stylePtr->gc, bitmap);
+
+ if (stylePtr->state & (STATE_DISABLED | STATE_EMPHASIS)) {
+ TkBorder *borderPtr = (TkBorder *) Blt_BackgroundBorder(stylePtr->bg);
+ XColor *color1, *color2;
+
+ color1 = borderPtr->lightColor, color2 = borderPtr->darkColor;
+ if (stylePtr->state & STATE_EMPHASIS) {
+ XColor *hold;
+
+ hold = color1, color1 = color2, color2 = hold;
+ }
+ if (color1 != NULL) {
+ XSetForeground(display, stylePtr->gc, color1->pixel);
+ }
+ XSetClipOrigin(display, stylePtr->gc, x + 1, y + 1);
+ XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width,
+ height, x + 1, y + 1, 1);
+ if (color2 != NULL) {
+ XSetForeground(display, stylePtr->gc, color2->pixel);
+ }
+ XSetClipOrigin(display, stylePtr->gc, x, y);
+ XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width,
+ height, x, y, 1);
+ XSetForeground(display, stylePtr->gc, stylePtr->color->pixel);
+ } else {
+ XSetForeground(display, stylePtr->gc, stylePtr->color->pixel);
+ XSetClipOrigin(display, stylePtr->gc, x, y);
+ XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width, height,
+ x, y, 1);
+ }
+ XSetClipMask(display, stylePtr->gc, None);
+ Tk_FreePixmap(display, bitmap);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_DrawLayout --
+ *
+ * Draw a text string, possibly rotated, using the the given window
+ * coordinates as an anchor for the text bounding box. If the text is
+ * not rotated, simply use the X text drawing routines. Otherwise,
+ * generate a bitmap of the rotated text.
+ *
+ * Results:
+ * Returns the x-coordinate to the right of the text.
+ *
+ * Side Effects:
+ * Text string is drawn using the given font and GC at the the given
+ * window coordinates.
+ *
+ * The Stipple, FillStyle, and TSOrigin fields of the GC are modified for
+ * rotated text. This assumes the GC is private, *not* shared (via
+ * Tk_GetGC)
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ts_DrawLayout(
+ Tk_Window tkwin,
+ Drawable drawable,
+ TextLayout *layoutPtr,
+ TextStyle *stylePtr, /* Text attribute information */
+ int x, int y) /* Window coordinates to draw text */
+{
+ float angle;
+
+ if ((stylePtr->gc == NULL) || (stylePtr->flags & UPDATE_GC)) {
+ Blt_Ts_ResetStyle(tkwin, stylePtr);
+ }
+ angle = (float)FMOD(stylePtr->angle, 360.0);
+ if (angle < 0.0) {
+ angle += 360.0;
+ }
+ if (angle == 0.0) {
+ /*
+ * This is the easy case of no rotation. Simply draw the text using
+ * the standard drawing routines. Handle offset printing for engraved
+ * (disabled) text.
+ */
+ DrawStandardLayout(tkwin, drawable, stylePtr, layoutPtr, x, y);
+ return;
+ }
+ if (Blt_CanRotateFont(stylePtr->font, angle)) {
+ if (Blt_DrawTextWithRotatedFont(tkwin, drawable, angle, stylePtr,
+ layoutPtr, x, y)) {
+ return; /* Success. */
+ }
+ }
+ /*Fallthru*/
+ stylePtr->angle = (float)angle;
+ Blt_DrawTextWithRotatedBitmap(tkwin, drawable, angle, stylePtr, layoutPtr,
+ x, y);
+}
+
+void
+Blt_Ts_UnderlineLayout(
+ Tk_Window tkwin,
+ Drawable drawable,
+ TextLayout *layoutPtr,
+ TextStyle *stylePtr, /* Text attribute information */
+ int x, int y) /* Window coordinates to draw text */
+{
+ float angle;
+
+ if ((stylePtr->gc == NULL) || (stylePtr->flags & UPDATE_GC)) {
+ Blt_Ts_ResetStyle(tkwin, stylePtr);
+ }
+ angle = (float)FMOD(stylePtr->angle, 360.0);
+ if (angle < 0.0) {
+ angle += 360.0;
+ }
+ if (angle == 0.0) {
+ TextFragment *fp, *fend;
+
+ for (fp = layoutPtr->fragments, fend = fp + layoutPtr->nFrags;
+ fp < fend; fp++) {
+ int sx, sy;
+
+ sx = x + fp->sx, sy = y + fp->sy;
+ Blt_UnderlineChars(Tk_Display(tkwin), drawable, stylePtr->gc,
+ stylePtr->font, fp->text, fp->count, sx, sy, 0, fp->count,
+ stylePtr->maxLength);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Ts_DrawLayout --
+ *
+ * Draw a text string, possibly rotated, using the the given window
+ * coordinates as an anchor for the text bounding box. If the text is
+ * not rotated, simply use the X text drawing routines. Otherwise,
+ * generate a bitmap of the rotated text.
+ *
+ * Results:
+ * Returns the x-coordinate to the right of the text.
+ *
+ * Side Effects:
+ * Text string is drawn using the given font and GC at the the given
+ * window coordinates.
+ *
+ * The Stipple, FillStyle, and TSOrigin fields of the GC are modified for
+ * rotated text. This assumes the GC is private, *not* shared (via
+ * Tk_GetGC)
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Ts_DrawText(
+ Tk_Window tkwin,
+ Drawable drawable,
+ const char *text,
+ int textLen,
+ TextStyle *stylePtr, /* Text attribute information */
+ int x, int y) /* Window coordinates to draw text */
+{
+ TextLayout *layoutPtr;
+
+ layoutPtr = Blt_Ts_CreateLayout(text, textLen, stylePtr);
+ Blt_Ts_DrawLayout(tkwin, drawable, layoutPtr, stylePtr, x, y);
+ Blt_Free(layoutPtr);
+}
+
+void
+Blt_DrawText2(
+ Tk_Window tkwin,
+ Drawable drawable,
+ const char *string,
+ TextStyle *stylePtr, /* Text attribute information */
+ int x, int y, /* Window coordinates to draw text */
+ Dim2D *areaPtr)
+{
+ TextLayout *layoutPtr;
+ int width, height;
+ float angle;
+
+ if ((string == NULL) || (*string == '\0')) {
+ return; /* Empty string, do nothing */
+ }
+ layoutPtr = Blt_Ts_CreateLayout(string, -1, stylePtr);
+ Blt_Ts_DrawLayout(tkwin, drawable, layoutPtr, stylePtr, x, y);
+ angle = FMOD(stylePtr->angle, 360.0);
+ if (angle < 0.0) {
+ angle += 360.0;
+ }
+ width = layoutPtr->width;
+ height = layoutPtr->height;
+ if (angle != 0.0) {
+ double rotWidth, rotHeight;
+
+ Blt_GetBoundingBox(width, height, angle, &rotWidth, &rotHeight,
+ (Point2d *)NULL);
+ width = ROUND(rotWidth);
+ height = ROUND(rotHeight);
+ }
+ areaPtr->width = width;
+ areaPtr->height = height;
+ Blt_Free(layoutPtr);
+}
+
+void
+Blt_DrawText(
+ Tk_Window tkwin,
+ Drawable drawable,
+ const char *string,
+ TextStyle *stylePtr, /* Text attribute information */
+ int x, int y) /* Window coordinates to draw text */
+{
+ TextLayout *layoutPtr;
+
+ if ((string == NULL) || (*string == '\0')) {
+ return; /* Empty string, do nothing */
+ }
+ layoutPtr = Blt_Ts_CreateLayout(string, -1, stylePtr);
+ Blt_Ts_DrawLayout(tkwin, drawable, layoutPtr, stylePtr, x, y);
+ Blt_Free(layoutPtr);
+}
+
+void
+Blt_Ts_ResetStyle(Tk_Window tkwin, TextStyle *stylePtr)
+{
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ gcMask = GCFont;
+ gcValues.font = Blt_FontId(stylePtr->font);
+ if (stylePtr->color != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = stylePtr->color->pixel;
+ }
+ newGC = Tk_GetGC(tkwin, gcMask, &gcValues);
+ if (stylePtr->gc != NULL) {
+ Tk_FreeGC(Tk_Display(tkwin), stylePtr->gc);
+ }
+ stylePtr->gc = newGC;
+ stylePtr->flags &= ~UPDATE_GC;
+}
+
+void
+Blt_Ts_FreeStyle(Display *display, TextStyle *stylePtr)
+{
+ if (stylePtr->gc != NULL) {
+ Tk_FreeGC(display, stylePtr->gc);
+ }
+}
+
+/*
+ * The following two structures are used to keep track of string
+ * measurement information when using the text layout facilities.
+ *
+ * A LayoutChunk represents a contiguous range of text that can be measured
+ * and displayed by low-level text calls. In general, chunks will be
+ * delimited by newlines and tabs. Low-level, platform-specific things
+ * like kerning and non-integer character widths may occur between the
+ * characters in a single chunk, but not between characters in different
+ * chunks.
+ *
+ * A TextLayout is a collection of LayoutChunks. It can be displayed with
+ * respect to any origin. It is the implementation of the Tk_TextLayout
+ * opaque token.
+ */
+
+typedef struct LayoutChunk {
+ const char *start; /* Pointer to simple string to be displayed.
+ * This is a pointer into the TkTextLayout's
+ * string. */
+ int numBytes; /* The number of bytes in this chunk. */
+ int numChars; /* The number of characters in this chunk. */
+ int numDisplayChars; /* The number of characters to display when
+ * this chunk is displayed. Can be less than
+ * numChars if extra space characters were
+ * absorbed by the end of the chunk. This
+ * will be < 0 if this is a chunk that is
+ * holding a tab or newline. */
+ int x, y; /* The origin of the first character in this
+ * chunk with respect to the upper-left hand
+ * corner of the TextLayout. */
+ int totalWidth; /* Width in pixels of this chunk. Used
+ * when hit testing the invisible spaces at
+ * the end of a chunk. */
+ int displayWidth; /* Width in pixels of the displayable
+ * characters in this chunk. Can be less than
+ * width if extra space characters were
+ * absorbed by the end of the chunk. */
+} LayoutChunk;
+
+typedef struct TkTextLayout {
+ Blt_Font font; /* The font used when laying out the text. */
+ const char *string; /* The string that was layed out. */
+ int width; /* The maximum width of all lines in the
+ * text layout. */
+ int numChunks; /* Number of chunks actually used in
+ * following array. */
+ LayoutChunk chunks[1]; /* Array of chunks. The actual size will
+ * be maxChunks. THIS FIELD MUST BE THE LAST
+ * IN THE STRUCTURE. */
+} TkTextLayout;
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_FreeTextLayout --
+ *
+ * This procedure is called to release the storage associated with
+ * a Tk_TextLayout when it is no longer needed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+Blt_FreeTextLayout(Tk_TextLayout textLayout)
+{
+ TkTextLayout *layoutPtr = (TkTextLayout *) textLayout;
+
+ if (layoutPtr != NULL) {
+ Blt_Free(layoutPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NewChunk --
+ *
+ * Helper function for Blt_ComputeTextLayout(). Encapsulates a
+ * measured set of characters in a chunk that can be quickly
+ * drawn.
+ *
+ * Results:
+ * A pointer to the new chunk in the text layout.
+ *
+ * Side effects:
+ * The text layout is reallocated to hold more chunks as necessary.
+ *
+ * Currently, Tk_ComputeTextLayout() stores contiguous ranges of
+ * "normal" characters in a chunk, along with individual tab
+ * and newline chars in their own chunks. All characters in the
+ * text layout are accounted for.
+ *
+ *---------------------------------------------------------------------------
+ */
+static LayoutChunk *
+NewChunk(TkTextLayout **layoutPtrPtr, int *maxPtr, const char *start,
+ int numBytes, int curX, int newX, int y)
+{
+ TkTextLayout *layoutPtr;
+ LayoutChunk *chunkPtr;
+ int maxChunks, numChars;
+ size_t s;
+
+ layoutPtr = *layoutPtrPtr;
+ maxChunks = *maxPtr;
+ if (layoutPtr->numChunks == maxChunks) {
+ maxChunks *= 2;
+ s = sizeof(TkTextLayout) + ((maxChunks - 1) * sizeof(LayoutChunk));
+ layoutPtr = Blt_Realloc(layoutPtr, s);
+ *layoutPtrPtr = layoutPtr;
+ *maxPtr = maxChunks;
+ }
+ numChars = Tcl_NumUtfChars(start, numBytes);
+ chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
+ chunkPtr->start = start;
+ chunkPtr->numBytes = numBytes;
+ chunkPtr->numChars = numChars;
+ chunkPtr->numDisplayChars = numChars;
+ chunkPtr->x = curX;
+ chunkPtr->y = y;
+ chunkPtr->totalWidth = newX - curX;
+ chunkPtr->displayWidth = newX - curX;
+ layoutPtr->numChunks++;
+
+ return chunkPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ComputeTextLayout --
+ *
+ * Computes the amount of screen space needed to display a multi-line,
+ * justified string of text. Records all the measurements that were done
+ * to determine to size and positioning of the individual lines of text;
+ * this information can be used by the Tk_DrawTextLayout() procedure to
+ * display the text quickly (without remeasuring it).
+ *
+ * This procedure is useful for simple widgets that want to display
+ * single-font, multi-line text and want Tk to handle the details.
+ *
+ * Results:
+ * The return value is a Tk_TextLayout token that holds the measurement
+ * information for the given string. The token is only valid for the
+ * given string. If the string is freed, the token is no longer valid
+ * and must also be freed. To free the token, call Tk_FreeTextLayout().
+ *
+ * The dimensions of the screen area needed to display the text are
+ * stored in *widthPtr and *heightPtr.
+ *
+ * Side effects:
+ * Memory is allocated to hold the measurement information.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tk_TextLayout
+Blt_ComputeTextLayout(
+ Blt_Font font, /* Font that will be used to display text. */
+ const char *string, /* String whose dimensions are to be
+ * computed. */
+ int numChars, /* Number of characters to consider from
+ * string, or < 0 for strlen(). */
+ int wrapLength, /* Longest permissible line length, in
+ * pixels. <= 0 means no automatic wrapping:
+ * just let lines get as long as needed. */
+ Tk_Justify justify, /* How to justify lines. */
+ int flags, /* Flag bits OR-ed together.
+ * TK_IGNORE_TABS means that tab characters
+ * should not be expanded. TK_IGNORE_NEWLINES
+ * means that newline characters should not
+ * cause a line break. */
+ int *widthPtr, /* Filled with width of string. */
+ int *heightPtr) /* Filled with height of string. */
+{
+ const char *start, *end, *special;
+ int n, y, bytesThisChunk, maxChunks;
+ int baseline, height, curX, newX, maxWidth;
+ TkTextLayout *layoutPtr;
+ LayoutChunk *chunkPtr;
+ Blt_FontMetrics fm;
+ Tcl_DString lineBuffer;
+ int *lineLengths;
+ int curLine, layoutHeight;
+
+ Tcl_DStringInit(&lineBuffer);
+
+ if ((font == NULL) || (string == NULL)) {
+ if (widthPtr != NULL) {
+ *widthPtr = 0;
+ }
+ if (heightPtr != NULL) {
+ *heightPtr = 0;
+ }
+ return NULL;
+ }
+
+ Blt_GetFontMetrics(font, &fm);
+ height = fm.ascent + fm.descent;
+
+ if (numChars < 0) {
+ numChars = Tcl_NumUtfChars(string, -1);
+ }
+ if (wrapLength == 0) {
+ wrapLength = -1;
+ }
+
+ maxChunks = 1;
+
+ layoutPtr = Blt_AssertMalloc(sizeof(TkTextLayout) + (maxChunks - 1) *
+ sizeof(LayoutChunk));
+ layoutPtr->font = font;
+ layoutPtr->string = string;
+ layoutPtr->numChunks = 0;
+
+ baseline = fm.ascent;
+ maxWidth = 0;
+
+ /*
+ * Divide the string up into simple strings and measure each string.
+ */
+
+ curX = 0;
+
+ end = Tcl_UtfAtIndex(string, numChars);
+ special = string;
+
+ flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
+ flags |= TK_WHOLE_WORDS | TK_AT_LEAST_ONE;
+ for (start = string; start < end; ) {
+ if (start >= special) {
+ /*
+ * Find the next special character in the string.
+ *
+ * INTL: Note that it is safe to increment by byte, because we are
+ * looking for 7-bit characters that will appear unchanged in
+ * UTF-8. At some point we may need to support the full Unicode
+ * whitespace set.
+ */
+
+ for (special = start; special < end; special++) {
+ if (!(flags & TK_IGNORE_NEWLINES)) {
+ if ((*special == '\n') || (*special == '\r')) {
+ break;
+ }
+ }
+ if (!(flags & TK_IGNORE_TABS)) {
+ if (*special == '\t') {
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Special points at the next special character (or the end of the
+ * string). Process characters between start and special.
+ */
+
+ chunkPtr = NULL;
+ if (start < special) {
+ bytesThisChunk = Blt_MeasureChars(font, start, special - start,
+ wrapLength - curX, flags, &newX);
+ newX += curX;
+ flags &= ~TK_AT_LEAST_ONE;
+ if (bytesThisChunk > 0) {
+ chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
+ bytesThisChunk, curX, newX, baseline);
+
+ start += bytesThisChunk;
+ curX = newX;
+ }
+ }
+
+ if ((start == special) && (special < end)) {
+ /*
+ * Handle the special character.
+ *
+ * INTL: Special will be pointing at a 7-bit character so we
+ * can safely treat it as a single byte.
+ */
+
+ chunkPtr = NULL;
+ if (*special == '\t') {
+ newX = curX + fm.tabWidth;
+ newX -= newX % fm.tabWidth;
+ NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
+ baseline)->numDisplayChars = -1;
+ start++;
+ if ((start < end) &&
+ ((wrapLength <= 0) || (newX <= wrapLength))) {
+ /*
+ * More chars can still fit on this line.
+ */
+
+ curX = newX;
+ flags &= ~TK_AT_LEAST_ONE;
+ continue;
+ }
+ } else {
+ NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
+ baseline)->numDisplayChars = -1;
+ start++;
+ goto wrapLine;
+ }
+ }
+
+ /*
+ * No more characters are going to go on this line, either because
+ * no more characters can fit or there are no more characters left.
+ * Consume all extra spaces at end of line.
+ */
+
+ while ((start < end) && isspace(UCHAR(*start))) { /* INTL: ISO space */
+ if (!(flags & TK_IGNORE_NEWLINES)) {
+ if ((*start == '\n') || (*start == '\r')) {
+ break;
+ }
+ }
+ if (!(flags & TK_IGNORE_TABS)) {
+ if (*start == '\t') {
+ break;
+ }
+ }
+ start++;
+ }
+ if (chunkPtr != NULL) {
+ const char *end;
+
+ /*
+ * Append all the extra spaces on this line to the end of the
+ * last text chunk. This is a little tricky because we are
+ * switching back and forth between characters and bytes.
+ */
+
+ end = chunkPtr->start + chunkPtr->numBytes;
+ bytesThisChunk = start - end;
+ if (bytesThisChunk > 0) {
+ bytesThisChunk = Blt_MeasureChars(font, end, bytesThisChunk,
+ -1, 0, &chunkPtr->totalWidth);
+ chunkPtr->numBytes += bytesThisChunk;
+ chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
+ chunkPtr->totalWidth += curX;
+ }
+ }
+
+ wrapLine:
+ flags |= TK_AT_LEAST_ONE;
+
+ /*
+ * Save current line length, then move current position to start of
+ * next line.
+ */
+
+ if (curX > maxWidth) {
+ maxWidth = curX;
+ }
+
+ /*
+ * Remember width of this line, so that all chunks on this line
+ * can be centered or right justified, if necessary.
+ */
+
+ Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
+
+ curX = 0;
+ baseline += height;
+ }
+
+ /*
+ * If last line ends with a newline, then we need to make a 0 width
+ * chunk on the next line. Otherwise "Hello" and "Hello\n" are the
+ * same height.
+ */
+
+ if ((layoutPtr->numChunks > 0) && ((flags & TK_IGNORE_NEWLINES) == 0)) {
+ if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
+ chunkPtr = NewChunk(&layoutPtr, &maxChunks, start, 0, curX,
+ curX, baseline);
+ chunkPtr->numDisplayChars = -1;
+ Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
+ baseline += height;
+ }
+ }
+
+ layoutPtr->width = maxWidth;
+ layoutHeight = baseline - fm.ascent;
+ if (layoutPtr->numChunks == 0) {
+ layoutHeight = height;
+
+ /*
+ * This fake chunk is used by the other procedures so that they can
+ * pretend that there is a chunk with no chars in it, which makes
+ * the coding simpler.
+ */
+
+ layoutPtr->numChunks = 1;
+ layoutPtr->chunks[0].start = string;
+ layoutPtr->chunks[0].numBytes = 0;
+ layoutPtr->chunks[0].numChars = 0;
+ layoutPtr->chunks[0].numDisplayChars = -1;
+ layoutPtr->chunks[0].x = 0;
+ layoutPtr->chunks[0].y = fm.ascent;
+ layoutPtr->chunks[0].totalWidth = 0;
+ layoutPtr->chunks[0].displayWidth = 0;
+ } else {
+ /*
+ * Using maximum line length, shift all the chunks so that the lines
+ * are all justified correctly.
+ */
+
+ curLine = 0;
+ chunkPtr = layoutPtr->chunks;
+ y = chunkPtr->y;
+ lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
+ for (n = 0; n < layoutPtr->numChunks; n++) {
+ int extra;
+
+ if (chunkPtr->y != y) {
+ curLine++;
+ y = chunkPtr->y;
+ }
+ extra = maxWidth - lineLengths[curLine];
+ if (justify == TK_JUSTIFY_CENTER) {
+ chunkPtr->x += extra / 2;
+ } else if (justify == TK_JUSTIFY_RIGHT) {
+ chunkPtr->x += extra;
+ }
+ chunkPtr++;
+ }
+ }
+
+ if (widthPtr != NULL) {
+ *widthPtr = layoutPtr->width;
+ }
+ if (heightPtr != NULL) {
+ *heightPtr = layoutHeight;
+ }
+ Tcl_DStringFree(&lineBuffer);
+
+ return (Tk_TextLayout) layoutPtr;
+}
+
+void
+Blt_DrawTextLayout(
+ Display *display, /* Display on which to draw. */
+ Drawable drawable, /* Window or pixmap in which to draw. */
+ GC gc, /* Graphics context to use for drawing text. */
+ Tk_TextLayout layout, /* Layout information, from a previous call
+ * to Blt_ComputeTextLayout(). */
+ int x, int y, /* Upper-left hand corner of rectangle in
+ * which to draw (pixels). */
+ int firstChar, /* The index of the first character to draw
+ * from the given text item. 0 specfies the
+ * beginning. */
+ int lastChar) /* The index just after the last character
+ * to draw from the given text item. A number
+ * < 0 means to draw all characters. */
+{
+ TkTextLayout *layoutPtr;
+ int i, numDisplayChars, drawX;
+ const char *firstByte;
+ const char *lastByte;
+ LayoutChunk *chunkPtr;
+ int depth = 24;
+
+ layoutPtr = (TkTextLayout *) layout;
+ if (layoutPtr == NULL) {
+ return;
+ }
+
+ if (lastChar < 0) {
+ lastChar = 100000000;
+ }
+ chunkPtr = layoutPtr->chunks;
+ for (i = 0; i < layoutPtr->numChunks; i++) {
+ numDisplayChars = chunkPtr->numDisplayChars;
+ if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
+ if (firstChar <= 0) {
+ drawX = 0;
+ firstChar = 0;
+ firstByte = chunkPtr->start;
+ } else {
+ firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar);
+ Blt_MeasureChars(layoutPtr->font, chunkPtr->start,
+ firstByte - chunkPtr->start, -1, 0, &drawX);
+ }
+ if (lastChar < numDisplayChars) {
+ numDisplayChars = lastChar;
+ }
+ lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
+ Blt_DrawChars(display, drawable, gc, layoutPtr->font, depth, 0.0f,
+ firstByte, lastByte - firstByte,
+ x + chunkPtr->x + drawX, y + chunkPtr->y);
+ }
+ firstChar -= chunkPtr->numChars;
+ lastChar -= chunkPtr->numChars;
+ if (lastChar <= 0) {
+ break;
+ }
+ chunkPtr++;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_CharBbox --
+ *
+ * Use the information in the Tk_TextLayout token to return the
+ * bounding box for the character specified by index.
+ *
+ * The width of the bounding box is the advance width of the
+ * character, and does not include and left- or right-bearing.
+ * Any character that extends partially outside of the
+ * text layout is considered to be truncated at the edge. Any
+ * character which is located completely outside of the text
+ * layout is considered to be zero-width and pegged against
+ * the edge.
+ *
+ * The height of the bounding box is the line height for this font,
+ * extending from the top of the ascent to the bottom of the
+ * descent. Information about the actual height of the individual
+ * letter is not available.
+ *
+ * A text layout that contains no characters is considered to
+ * contain a single zero-width placeholder character.
+ *
+ * Results:
+ * The return value is 0 if the index did not specify a character
+ * in the text layout, or non-zero otherwise. In that case,
+ * *bbox is filled with the bounding box of the character.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Blt_CharBbox(
+ Tk_TextLayout layout, /* Layout information, from a previous call to
+ * Tk_ComputeTextLayout(). */
+ int index, /* The index of the character whose bbox is
+ * desired. */
+ int *xPtr, int *yPtr, /* Filled with the upper-left hand corner, in
+ * pixels, of the bounding box for the character
+ * specified by index, if non-NULL. */
+ int *widthPtr,
+ int *heightPtr) /* Filled with the width and height of the
+ * bounding box for the character specified by
+ * index, if non-NULL. */
+{
+ TkTextLayout *layoutPtr;
+ LayoutChunk *chunkPtr;
+ int i, x, w;
+ Blt_Font font;
+ const char *end;
+ Blt_FontMetrics fm;
+
+ if (index < 0) {
+ return 0;
+ }
+
+ layoutPtr = (TkTextLayout *) layout;
+ chunkPtr = layoutPtr->chunks;
+ font = layoutPtr->font;
+
+ Blt_GetFontMetrics(font, &fm);
+ for (i = 0; i < layoutPtr->numChunks; i++) {
+ if (chunkPtr->numDisplayChars < 0) {
+ if (index == 0) {
+ x = chunkPtr->x;
+ w = chunkPtr->totalWidth;
+ goto check;
+ }
+ } else if (index < chunkPtr->numChars) {
+ end = Tcl_UtfAtIndex(chunkPtr->start, index);
+ if (xPtr != NULL) {
+ Blt_MeasureChars(font, chunkPtr->start,
+ end - chunkPtr->start, -1, 0, &x);
+ x += chunkPtr->x;
+ }
+ if (widthPtr != NULL) {
+ Blt_MeasureChars(font, end, Tcl_UtfNext(end) - end, -1, 0, &w);
+ }
+ goto check;
+ }
+ index -= chunkPtr->numChars;
+ chunkPtr++;
+ }
+ if (index == 0) {
+ /*
+ * Special case to get location just past last char in layout.
+ */
+
+ chunkPtr--;
+ x = chunkPtr->x + chunkPtr->totalWidth;
+ w = 0;
+ } else {
+ return 0;
+ }
+
+ /*
+ * Ensure that the bbox lies within the text layout. This forces all
+ * chars that extend off the right edge of the text layout to have
+ * truncated widths, and all chars that are completely off the right
+ * edge of the text layout to peg to the edge and have 0 width.
+ */
+ check:
+ if (yPtr != NULL) {
+ *yPtr = chunkPtr->y - fm.ascent;
+ }
+ if (heightPtr != NULL) {
+ *heightPtr = fm.ascent + fm.descent;
+ }
+
+ if (x > layoutPtr->width) {
+ x = layoutPtr->width;
+ }
+ if (xPtr != NULL) {
+ *xPtr = x;
+ }
+ if (widthPtr != NULL) {
+ if (x + w > layoutPtr->width) {
+ w = layoutPtr->width - x;
+ }
+ *widthPtr = w;
+ }
+
+ return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_UnderlineTextLayout --
+ *
+ * Use the information in the Tk_TextLayout token to display an
+ * underline below an individual character. This procedure does
+ * not draw the text, just the underline.
+ *
+ * This procedure is useful for simple widgets that need to
+ * display single-font, multi-line text with an individual
+ * character underlined and want Tk to handle the details.
+ * To display larger amounts of underlined text, construct
+ * and use an underlined font.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Underline drawn on the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+Blt_UnderlineTextLayout(
+ Display *display, /* Display on which to draw. */
+ Drawable drawable, /* Window or pixmap in which to draw. */
+ GC gc, /* Graphics context to use for drawing text. */
+ Tk_TextLayout layout, /* Layout information, from a previous call
+ * to Tk_ComputeTextLayout(). */
+ int x, int y, /* Upper-left hand corner of rectangle in
+ * which to draw (pixels). */
+ int underline) /* Index of the single character to
+ * underline, or -1 for no underline. */
+{
+ TkTextLayout *layoutPtr;
+ int xx, yy, width, height;
+
+ if ((Blt_CharBbox(layout, underline, &xx, &yy, &width, &height) != 0)
+ && (width != 0)) {
+ Blt_FontMetrics fm;
+ layoutPtr = (TkTextLayout *) layout;
+ Blt_GetFontMetrics(layoutPtr->font, &fm);
+ XFillRectangle(display, drawable, gc, x + xx,
+ y + yy + fm.ascent + fm.underlinePos,
+ (unsigned int) width, fm.underlineHeight);
+ }
+}
diff --git a/src/bltGrText.h b/src/bltGrText.h
new file mode 100644
index 0000000..7d4ef3d
--- /dev/null
+++ b/src/bltGrText.h
@@ -0,0 +1,265 @@
+
+/*
+ * bltText.h --
+ *
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_TEXT_H
+#define _BLT_TEXT_H
+
+#include "bltBgStyle.h"
+
+#define DEF_TEXT_FLAGS (TK_PARTIAL_OK | TK_IGNORE_NEWLINES)
+#define UPDATE_GC 1
+
+/*
+ * TextFragment --
+ */
+typedef struct {
+ const char *text; /* Text string to be displayed */
+
+ size_t count; /* Number of bytes in text. The actual
+ * character count may differ because of
+ * multi-byte UTF encodings. */
+
+ short x, y; /* X-Y offset of the baseline from the
+ * upper-left corner of the bbox. */
+
+ short sx, sy; /* Starting offset of text using rotated
+ * font. */
+
+ int width; /* Width of segment in pixels. This
+ * information is used to draw
+ * PostScript strings the same width
+ * as X. */
+} TextFragment;
+
+
+/*
+ * TextItem --
+ *
+ * Parsed form for markup string. Each item is a scrap of text
+ * describes the font, position, and characters to be displayed.
+ *
+ * subscript x_y very small subset of latex markup.
+ * superscript x^y
+ * grouping a^{x+y} a_{i,j}
+ * supersuper a^{10^8}
+ * \hat{a} \bar{b} \vec{c}
+ * \overline{} \underline{}
+ * \frac \tfrac
+ * \Alpha \Beta ...
+ * \mathbf{} \mathit{} \mathrm{} \boldsymbol{}
+ * \angstrom \degree
+ *
+ * -mathtext instead of -text
+ *
+ * Can use TextItem where you don't directly edit the text:
+ * label, treeview, graph, barchart...
+ *
+ * Font selector (bold, italic, size adjust) from base font.
+ * Global font table reference counted.
+ *
+ */
+typedef struct {
+ const char *text; /* Text string to be displayed */
+
+ size_t count; /* Number of bytes in text. The actual
+ * character count may differ because of
+ * multi-byte UTF encodings. */
+
+ short int x, y; /* X-Y offset of the baseline from the
+ * upper-left corner of the bbox. */
+
+ short int sx, sy; /* Starting offset of text using rotated
+ * font. */
+
+ Blt_Font font; /* Allocated font for this chunk.
+ * If NULL, use the global font. */
+
+ int underline; /* Text is underlined */
+
+ int width; /* Width of segment in pixels. This
+ * information is used to draw
+ * PostScript strings the same width
+ * as X. (deprecated) */
+} TextItem;
+
+/*
+ * TextLayout --
+ */
+typedef struct {
+ TextFragment *underlinePtr;
+ int underline;
+ size_t width, height; /* Dimensions of text bounding box */
+ size_t nFrags; /* # fragments of text */
+ TextFragment fragments[1]; /* Information about each fragment of text */
+} TextLayout;
+
+/*
+ * TextStyle --
+ *
+ * A somewhat convenient structure to hold text attributes that determine
+ * how a text string is to be displayed on the screen or drawn with
+ * PostScript commands. The alternative is to pass lots of parameters to
+ * the drawing and printing routines. This seems like a more efficient
+ * and less cumbersome way of passing parameters.
+ */
+typedef struct {
+ unsigned int state; /* If non-zero, indicates to draw text
+ * in the active color */
+ XColor *color; /* Color to draw the text. */
+ Blt_Font font; /* Font to use to draw text */
+ Blt_Background bg; /* Background color of text. This is
+ * also used for drawing disabled
+ * text. */
+ float angle; /* Rotation of text in degrees. */
+ Tk_Justify justify; /* Justification of the text
+ * string. This only matters if the
+ * text is composed of multiple
+ * lines. */
+ Tk_Anchor anchor; /* Indicates how the text is anchored
+ * around its x,y coordinates. */
+ Blt_Pad xPad, yPad; /* # pixels padding of around text
+ * region. */
+ unsigned short int leader; /* # pixels spacing between lines of
+ * text. */
+ short int underline; /* Index of character to be underlined,
+ * -1 if no underline. */
+ int maxLength; /* Maximum length in pixels of text */
+ /* Private fields. */
+ unsigned short flags;
+ GC gc; /* GC used to draw the text */
+} TextStyle;
+
+BLT_EXTERN TextLayout *Blt_Ts_CreateLayout(const char *string, int length,
+ TextStyle *tsPtr);
+
+BLT_EXTERN void Blt_Ts_DrawLayout(Tk_Window tkwin, Drawable drawable,
+ TextLayout *textPtr, TextStyle *tsPtr, int x, int y);
+
+BLT_EXTERN void Blt_Ts_GetExtents(TextStyle *tsPtr, const char *text,
+ unsigned int *widthPtr, unsigned int *heightPtr);
+
+BLT_EXTERN void Blt_Ts_ResetStyle(Tk_Window tkwin, TextStyle *tsPtr);
+
+BLT_EXTERN void Blt_Ts_FreeStyle(Display *display, TextStyle *tsPtr);
+
+BLT_EXTERN void Blt_Ts_SetDrawStyle (TextStyle *tsPtr, Blt_Font font, GC gc,
+ XColor *fgColor, float angle, Tk_Anchor anchor, Tk_Justify justify,
+ int leader);
+
+BLT_EXTERN void Blt_Ts_SetPrintStyle(TextStyle *tsPtr, Blt_Font font,
+ XColor *fgColor, XColor *bgColor, float angle, Tk_Anchor anchor,
+ Tk_Justify justify, int leader);
+
+BLT_EXTERN void Blt_DrawText(Tk_Window tkwin, Drawable drawable,
+ const char *string, TextStyle *tsPtr, int x, int y);
+
+BLT_EXTERN void Blt_DrawText2(Tk_Window tkwin, Drawable drawable,
+ const char *string, TextStyle *tsPtr, int x, int y, Dim2D * dimPtr);
+
+BLT_EXTERN Pixmap Blt_Ts_Bitmap(Tk_Window tkwin, TextLayout *textPtr,
+ TextStyle *tsPtr, int *widthPtr, int *heightPtr);
+
+BLT_EXTERN int Blt_DrawTextWithRotatedFont(Tk_Window tkwin, Drawable drawable,
+ float angle, TextStyle *tsPtr, TextLayout *textPtr, int x, int y);
+
+BLT_EXTERN void Blt_DrawLayout(Tk_Window tkwin, Drawable drawable, GC gc,
+ Blt_Font font, int depth, float angle, int x, int y,
+ TextLayout *layoutPtr, int maxLength);
+
+BLT_EXTERN void Blt_GetTextExtents(Blt_Font font, int leader, const char *text,
+ int textLen, unsigned int *widthPtr, unsigned int *heightPtr);
+
+BLT_EXTERN void Blt_RotateStartingTextPositions(TextLayout *textPtr,
+ float angle);
+
+BLT_EXTERN Tk_TextLayout Blt_ComputeTextLayout(Blt_Font font,
+ const char *string, int numChars, int wrapLength, Tk_Justify justify,
+ int flags, int *widthPtr, int *heightPtr);
+
+BLT_EXTERN void Blt_DrawTextLayout(Display *display, Drawable drawable, GC gc,
+ Tk_TextLayout layout, int x, int y, int firstChar, int lastChar);
+
+BLT_EXTERN int Blt_CharBbox(Tk_TextLayout layout, int index, int *xPtr,
+ int *yPtr, int *widthPtr, int *heightPtr);
+
+BLT_EXTERN void Blt_UnderlineTextLayout(Display *display, Drawable drawable,
+ GC gc, Tk_TextLayout layout, int x, int y, int underline);
+
+BLT_EXTERN void Blt_Ts_UnderlineLayout(Tk_Window tkwin, Drawable drawable,
+ TextLayout *layoutPtr, TextStyle *tsPtr, int x, int y);
+
+BLT_EXTERN void Blt_Ts_DrawText(Tk_Window tkwin, Drawable drawable,
+ const char *text, int textLen, TextStyle *tsPtr, int x, int y);
+
+BLT_EXTERN int Blt_MeasureText(Blt_Font font, const char *text, int textLen,
+ int maxLength, int *nBytesPtr);
+
+BLT_EXTERN void Blt_FreeTextLayout(Tk_TextLayout layout);
+
+#define Blt_Ts_GetAnchor(ts) ((ts).anchor)
+#define Blt_Ts_GetAngle(ts) ((ts).angle)
+#define Blt_Ts_GetBackground(ts) ((ts).bg)
+#define Blt_Ts_GetFont(ts) ((ts).font)
+#define Blt_Ts_GetForeground(ts) ((ts).color)
+#define Blt_Ts_GetJustify(ts) ((ts).justify)
+#define Blt_Ts_GetLeader(ts) ((ts).leader)
+
+#define Blt_Ts_SetAnchor(ts, a) ((ts).anchor = (a))
+#define Blt_Ts_SetAngle(ts, r) ((ts).angle = (float)(r))
+#define Blt_Ts_SetBackground(ts, b) ((ts).bg = (b))
+#define Blt_Ts_SetFont(ts, f) \
+ (((ts).font != (f)) ? ((ts).font = (f), (ts).flags |= UPDATE_GC) : 0)
+#define Blt_Ts_SetForeground(ts, c) \
+ (((ts).color != (c)) ? ((ts).color = (c), (ts).flags |= UPDATE_GC) : 0)
+#define Blt_Ts_SetGC(ts, g) ((ts).gc = (g))
+#define Blt_Ts_SetJustify(ts, j) ((ts).justify = (j))
+#define Blt_Ts_SetLeader(ts, l) ((ts).leader = (l))
+#define Blt_Ts_SetMaxLength(ts, l) ((ts).maxLength = (l))
+#define Blt_Ts_SetPadding(ts, l, r, t, b) \
+ ((ts).xPad.side1 = (l), \
+ (ts).xPad.side2 = (r), \
+ (ts).yPad.side1 = (t), \
+ (ts).yPad.side2 = (b))
+#define Blt_Ts_SetState(ts, s) ((ts).state = (s))
+#define Blt_Ts_SetUnderline(ts, ul) ((ts).underline = (ul))
+
+#define Blt_Ts_InitStyle(ts) \
+ ((ts).anchor = TK_ANCHOR_NW, \
+ (ts).color = (XColor *)NULL, \
+ (ts).font = NULL, \
+ (ts).justify = TK_JUSTIFY_LEFT, \
+ (ts).leader = 0, \
+ (ts).underline = -1, \
+ (ts).xPad.side1 = (ts).xPad.side2 = 0, \
+ (ts).yPad.side1 = (ts).yPad.side2 = 0, \
+ (ts).state = 0, \
+ (ts).flags = 0, \
+ (ts).gc = NULL, \
+ (ts).maxLength = -1, \
+ (ts).angle = 0.0)
+
+#endif /* _BLT_TEXT_H */
diff --git a/src/bltGraph.C b/src/bltGraph.C
new file mode 100644
index 0000000..de768f1
--- /dev/null
+++ b/src/bltGraph.C
@@ -0,0 +1,2381 @@
+
+/*
+ * bltGraph.c --
+ *
+ * This module implements a graph widget for the BLT toolkit.
+ *
+ * The graph widget was created by Sani Nassif and George Howlett.
+ *
+ * Copyright 1991-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * To do:
+ *
+ * 2) Update manual pages.
+ *
+ * 3) Update comments.
+ *
+ * 5) Surface, contour, and flow graphs
+ *
+ * 7) Arrows for line markers
+ *
+ */
+
+#include "bltGraph.h"
+#include "bltOp.h"
+#include "bltBind.h"
+#include "bltGrElem.h"
+#include "bltGrLegd.h"
+#include "bltSwitch.h"
+#include <X11/Xutil.h>
+#include "tkDisplay.h"
+#include "bltPicture.h"
+
+typedef int (GraphCmdProc)(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+/*
+ * Objects in the graph have their own class names. These class names are
+ * used for the resource database and bindings. Example.
+ *
+ * option add *X.title "X Axis Title" widgetDefault
+ * .g marker bind BitmapMarker <Enter> { ... }
+ *
+ * The option database trick is performed by creating a temporary window when
+ * an object is initially configured. The class name of the temporary window
+ * will be from the list below.
+ */
+static const char *objectClassNames[] = {
+ "unknown",
+ "XAxis",
+ "YAxis",
+ "BarElement",
+ "ContourElement",
+ "LineElement",
+ "StripElement",
+ "BitmapMarker",
+ "ImageMarker",
+ "LineMarker",
+ "PolygonMarker",
+ "TextMarker",
+ "WindowMarker",
+};
+
+BLT_EXTERN Blt_CustomOption bltLinePenOption;
+BLT_EXTERN Blt_CustomOption bltBarPenOption;
+BLT_EXTERN Blt_CustomOption bltBarModeOption;
+
+#define DEF_GRAPH_ASPECT_RATIO "0.0"
+#define DEF_GRAPH_BAR_BASELINE "0.0"
+#define DEF_GRAPH_BAR_MODE "normal"
+#define DEF_GRAPH_BAR_WIDTH "0.9"
+#define DEF_GRAPH_BACKGROUND STD_NORMAL_BACKGROUND
+#define DEF_GRAPH_BORDERWIDTH STD_BORDERWIDTH
+#define DEF_GRAPH_BUFFER_ELEMENTS "yes"
+#define DEF_GRAPH_BUFFER_GRAPH "1"
+#define DEF_GRAPH_CURSOR "crosshair"
+#define DEF_GRAPH_FONT "{Sans Serif} 12"
+#define DEF_GRAPH_HALO "2m"
+#define DEF_GRAPH_HALO_BAR "0.1i"
+#define DEF_GRAPH_HEIGHT "4i"
+#define DEF_GRAPH_HIGHLIGHT_BACKGROUND STD_NORMAL_BACKGROUND
+#define DEF_GRAPH_HIGHLIGHT_COLOR RGB_BLACK
+#define DEF_GRAPH_HIGHLIGHT_WIDTH "2"
+#define DEF_GRAPH_INVERT_XY "0"
+#define DEF_GRAPH_JUSTIFY "center"
+#define DEF_GRAPH_MARGIN "0"
+#define DEF_GRAPH_MARGIN_VAR (char *)NULL
+#define DEF_GRAPH_PLOT_BACKGROUND RGB_WHITE
+#define DEF_GRAPH_PLOT_BORDERWIDTH "1"
+#define DEF_GRAPH_PLOT_PADX "0"
+#define DEF_GRAPH_PLOT_PADY "0"
+#define DEF_GRAPH_PLOT_RELIEF "solid"
+#define DEF_GRAPH_RELIEF "flat"
+#define DEF_GRAPH_SHOW_VALUES "no"
+#define DEF_GRAPH_STACK_AXES "no"
+#define DEF_GRAPH_TAKE_FOCUS ""
+#define DEF_GRAPH_TITLE (char *)NULL
+#define DEF_GRAPH_TITLE_COLOR STD_NORMAL_FOREGROUND
+#define DEF_GRAPH_WIDTH "5i"
+#define DEF_GRAPH_DATA (char *)NULL
+#define DEF_GRAPH_DATA_COMMAND (char *)NULL
+#define DEF_GRAPH_UNMAP_HIDDEN_ELEMENTS "0"
+
+static Blt_ConfigSpec configSpecs[] =
+{
+ {BLT_CONFIG_FLOAT, "-aspect", "aspect", "Aspect", DEF_GRAPH_ASPECT_RATIO,
+ Blt_Offset(Graph, aspect), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BACKGROUND, "-background", "background", "Background",
+ DEF_GRAPH_BACKGROUND, Blt_Offset(Graph, normalBg), 0},
+ {BLT_CONFIG_CUSTOM, "-barmode", "barMode", "BarMode", DEF_GRAPH_BAR_MODE,
+ Blt_Offset(Graph, mode), BLT_CONFIG_DONT_SET_DEFAULT,
+ &bltBarModeOption},
+ {BLT_CONFIG_FLOAT, "-barwidth", "barWidth", "BarWidth",
+ DEF_GRAPH_BAR_WIDTH, Blt_Offset(Graph, barWidth), 0},
+ {BLT_CONFIG_FLOAT, "-baseline", "baseline", "Baseline",
+ DEF_GRAPH_BAR_BASELINE, Blt_Offset(Graph, baseline), 0},
+ {BLT_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL,0, 0},
+ {BLT_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_SYNONYM, "-bm", "bottomMargin", (char *)NULL, (char *)NULL,
+ 0, 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_GRAPH_BORDERWIDTH, Blt_Offset(Graph, borderWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-bottommargin", "bottomMargin", "Margin",
+ DEF_GRAPH_MARGIN, Blt_Offset(Graph, bottomMargin.reqSize), 0},
+ {BLT_CONFIG_STRING, "-bottomvariable", "bottomVariable", "BottomVariable",
+ DEF_GRAPH_MARGIN_VAR, Blt_Offset(Graph, bottomMargin.varName),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_BOOLEAN, "-bufferelements", "bufferElements", "BufferElements",
+ DEF_GRAPH_BUFFER_ELEMENTS, Blt_Offset(Graph, backingStore),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BOOLEAN, "-buffergraph", "bufferGraph", "BufferGraph",
+ DEF_GRAPH_BUFFER_GRAPH, Blt_Offset(Graph, doubleBuffer),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_GRAPH_CURSOR, Blt_Offset(Graph, cursor), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-data", "data", "Data",
+ (char *)NULL, Blt_Offset(Graph, data), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-datacommand", "dataCommand", "DataCommand",
+ (char *)NULL, Blt_Offset(Graph, dataCmd), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_FONT, "-font", "font", "Font",
+ DEF_GRAPH_FONT, Blt_Offset(Graph, titleTextStyle.font), 0},
+ {BLT_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_GRAPH_TITLE_COLOR, Blt_Offset(Graph, titleTextStyle.color), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-halo", "halo", "Halo", DEF_GRAPH_HALO,
+ Blt_Offset(Graph, halo), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-height", "height", "Height", DEF_GRAPH_HEIGHT,
+ Blt_Offset(Graph, reqHeight), 0},
+ {BLT_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_GRAPH_HIGHLIGHT_BACKGROUND,
+ Blt_Offset(Graph, highlightBgColor), 0},
+ {BLT_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_GRAPH_HIGHLIGHT_COLOR, Blt_Offset(Graph, highlightColor), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-highlightthickness", "highlightThickness",
+ "HighlightThickness", DEF_GRAPH_HIGHLIGHT_WIDTH,
+ Blt_Offset(Graph, highlightWidth), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_BITMASK, "-unmaphiddenelements", "unmapHiddenElements",
+ "UnmapHiddenElements", DEF_GRAPH_UNMAP_HIDDEN_ELEMENTS,
+ Blt_Offset(Graph, flags), ALL_GRAPHS | BLT_CONFIG_DONT_SET_DEFAULT,
+ (Blt_CustomOption *)UNMAP_HIDDEN},
+ {BLT_CONFIG_BOOLEAN, "-invertxy", "invertXY", "InvertXY",
+ DEF_GRAPH_INVERT_XY, Blt_Offset(Graph, inverted),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_JUSTIFY, "-justify", "justify", "Justify", DEF_GRAPH_JUSTIFY,
+ Blt_Offset(Graph, titleTextStyle.justify), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-leftmargin", "leftMargin", "Margin",
+ DEF_GRAPH_MARGIN, Blt_Offset(Graph, leftMargin.reqSize),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-leftvariable", "leftVariable", "LeftVariable",
+ DEF_GRAPH_MARGIN_VAR, Blt_Offset(Graph, leftMargin.varName),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-lm", "leftMargin", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_BACKGROUND, "-plotbackground", "plotBackground", "Background",
+ DEF_GRAPH_PLOT_BACKGROUND, Blt_Offset(Graph, plotBg), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-plotborderwidth", "plotBorderWidth",
+ "PlotBorderWidth", DEF_GRAPH_PLOT_BORDERWIDTH,
+ Blt_Offset(Graph, plotBW), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-plotpadx", "plotPadX", "PlotPad", DEF_GRAPH_PLOT_PADX,
+ Blt_Offset(Graph, xPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PAD, "-plotpady", "plotPadY", "PlotPad", DEF_GRAPH_PLOT_PADY,
+ Blt_Offset(Graph, yPad), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-plotrelief", "plotRelief", "Relief",
+ DEF_GRAPH_PLOT_RELIEF, Blt_Offset(Graph, plotRelief),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_GRAPH_RELIEF,
+ Blt_Offset(Graph, relief), BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-rightmargin", "rightMargin", "Margin",
+ DEF_GRAPH_MARGIN, Blt_Offset(Graph, rightMargin.reqSize),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-rightvariable", "rightVariable", "RightVariable",
+ DEF_GRAPH_MARGIN_VAR, Blt_Offset(Graph, rightMargin.varName),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-rm", "rightMargin", (char *)NULL, (char *)NULL, 0,0},
+ {BLT_CONFIG_BOOLEAN, "-stackaxes", "stackAxes", "StackAxes",
+ DEF_GRAPH_STACK_AXES, Blt_Offset(Graph, stackAxes),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_GRAPH_TAKE_FOCUS, Blt_Offset(Graph, takeFocus), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_STRING, "-title", "title", "Title", DEF_GRAPH_TITLE,
+ Blt_Offset(Graph, title), BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_SYNONYM, "-tm", "topMargin", (char *)NULL, (char *)NULL, 0, 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-topmargin", "topMargin", "Margin",
+ DEF_GRAPH_MARGIN, Blt_Offset(Graph, topMargin.reqSize),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_STRING, "-topvariable", "topVariable", "TopVariable",
+ DEF_GRAPH_MARGIN_VAR, Blt_Offset(Graph, topMargin.varName),
+ BLT_CONFIG_NULL_OK},
+ {BLT_CONFIG_PIXELS_NNEG, "-width", "width", "Width", DEF_GRAPH_WIDTH,
+ Blt_Offset(Graph, reqWidth), 0},
+ {BLT_CONFIG_PIXELS_NNEG, "-plotwidth", "plotWidth", "PlotWidth",
+ (char *)NULL, Blt_Offset(Graph, reqPlotWidth),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_PIXELS_NNEG, "-plotheight", "plotHeight", "PlotHeight",
+ (char *)NULL, Blt_Offset(Graph, reqPlotHeight),
+ BLT_CONFIG_DONT_SET_DEFAULT},
+ {BLT_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Blt_SwitchParseProc ObjToFormat;
+static Blt_SwitchCustom formatSwitch =
+{
+ ObjToFormat, NULL, (ClientData)0,
+};
+
+typedef struct {
+ const char *name;
+ int width, height;
+ int format;
+} SnapSwitches;
+
+enum SnapFormats { FORMAT_PICTURE, FORMAT_PHOTO, FORMAT_EMF, FORMAT_WMF };
+
+static Blt_SwitchSpec snapSwitches[] =
+{
+ {BLT_SWITCH_INT_POS, "-width", "width",
+ Blt_Offset(SnapSwitches, width), 0},
+ {BLT_SWITCH_INT_POS, "-height", "height",
+ Blt_Offset(SnapSwitches, height), 0},
+ {BLT_SWITCH_CUSTOM, "-format", "format",
+ Blt_Offset(SnapSwitches, format), 0, 0, &formatSwitch},
+ {BLT_SWITCH_END}
+};
+
+static Tcl_IdleProc DisplayGraph;
+static Tcl_FreeProc DestroyGraph;
+static Tk_EventProc GraphEventProc;
+Tcl_ObjCmdProc Blt_GraphInstCmdProc;
+
+static Blt_BindPickProc PickEntry;
+static Tcl_ObjCmdProc StripchartCmd;
+static Tcl_ObjCmdProc BarchartCmd;
+static Tcl_ObjCmdProc GraphCmd;
+static Tcl_CmdDeleteProc GraphInstCmdDeleteProc;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_UpdateGraph --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at the next
+ * idle point. This request is made only if the window is displayed and
+ * no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_UpdateGraph(ClientData clientData)
+{
+ Graph *graphPtr = clientData;
+
+ graphPtr->flags |= REDRAW_WORLD;
+ if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayGraph, graphPtr);
+ graphPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_EventuallyRedrawGraph --
+ *
+ * Tells the Tk dispatcher to call the graph display routine at the next
+ * idle point. This request is made only if the window is displayed and
+ * no other redraw request is pending.
+ *
+ * Results: None.
+ *
+ * Side effects:
+ * The window is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_EventuallyRedrawGraph(Graph *graphPtr)
+{
+ if ((graphPtr->tkwin != NULL) && !(graphPtr->flags & REDRAW_PENDING)) {
+ Tcl_DoWhenIdle(DisplayGraph, graphPtr);
+ graphPtr->flags |= REDRAW_PENDING;
+ }
+}
+
+const char *
+Blt_GraphClassName(ClassId classId)
+{
+ if ((classId >= CID_NONE) && (classId <= CID_MARKER_WINDOW)) {
+ return objectClassNames[classId];
+ }
+ return NULL;
+}
+
+void
+Blt_GraphSetObjectClass(GraphObj *graphObjPtr, ClassId classId)
+{
+ graphObjPtr->classId = classId;
+ graphObjPtr->className = Blt_GraphClassName(classId);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GraphEventProc --
+ *
+ * This procedure is invoked by the Tk dispatcher for various events on
+ * graphs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * When the window gets deleted, internal structures get cleaned up.
+ * When it gets exposed, the graph is eventually redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GraphEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ Graph *graphPtr = clientData;
+
+ if (eventPtr->type == Expose) {
+ if (eventPtr->xexpose.count == 0) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ if (eventPtr->type == FocusIn) {
+ graphPtr->flags |= FOCUS;
+ } else {
+ graphPtr->flags &= ~FOCUS;
+ }
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ if (graphPtr->tkwin != NULL) {
+ Blt_DeleteWindowInstanceData(graphPtr->tkwin);
+ graphPtr->tkwin = NULL;
+ Tcl_DeleteCommandFromToken(graphPtr->interp, graphPtr->cmdToken);
+ }
+ if (graphPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayGraph, graphPtr);
+ }
+ Tcl_EventuallyFree(graphPtr, DestroyGraph);
+ } else if (eventPtr->type == ConfigureNotify) {
+ graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GraphInstCmdDeleteProc --
+ *
+ * This procedure is invoked when a widget command is deleted. If the
+ * widget isn't already in the process of being destroyed, this command
+ * destroys it.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The widget is destroyed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+GraphInstCmdDeleteProc(ClientData clientData) /* Pointer to widget record. */
+{
+ Graph *graphPtr = clientData;
+
+ if (graphPtr->tkwin != NULL) { /* NULL indicates window has already
+ * been destroyed. */
+ Tk_Window tkwin;
+
+ tkwin = graphPtr->tkwin;
+ graphPtr->tkwin = NULL;
+ Blt_DeleteWindowInstanceData(tkwin);
+ Tk_DestroyWindow(tkwin);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AdjustAxisPointers --
+ *
+ * Sets the axis pointers according to whether the axis is inverted on
+ * not. The axis sites are also reset.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+AdjustAxisPointers(Graph *graphPtr)
+{
+ if (graphPtr->inverted) {
+ graphPtr->leftMargin.axes = graphPtr->axisChain[0];
+ graphPtr->bottomMargin.axes = graphPtr->axisChain[1];
+ graphPtr->rightMargin.axes = graphPtr->axisChain[2];
+ graphPtr->topMargin.axes = graphPtr->axisChain[3];
+ } else {
+ graphPtr->leftMargin.axes = graphPtr->axisChain[1];
+ graphPtr->bottomMargin.axes = graphPtr->axisChain[0];
+ graphPtr->rightMargin.axes = graphPtr->axisChain[3];
+ graphPtr->topMargin.axes = graphPtr->axisChain[2];
+ }
+}
+
+static int
+InitPens(Graph *graphPtr)
+{
+ Blt_InitHashTable(&graphPtr->penTable, BLT_STRING_KEYS);
+ if (Blt_CreatePen(graphPtr, "activeLine", CID_ELEM_LINE, 0, NULL) == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_CreatePen(graphPtr, "activeBar", CID_ELEM_BAR, 0, NULL) == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GraphTags --
+ *
+ * Sets the binding tags for a graph obj. This routine is called by Tk
+ * when an event occurs in the graph. It fills an array of pointers with
+ * bind tag addresses.
+ *
+ * The object addresses are strings hashed in one of two tag tables: one
+ * for elements and the another for markers. Note that there's only one
+ * binding table for elements and markers. [We don't want to trigger
+ * both a marker and element bind command for the same event.] But we
+ * don't want a marker and element with the same tag name to activate the
+ * others bindings. A tag "all" for markers should mean all markers, not
+ * all markers and elements. As a result, element and marker tags are
+ * stored in separate hash tables, which means we can't generate the same
+ * tag address for both an elements and marker, even if they have the
+ * same name.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * This information will be used by the binding code in bltUtil.c to
+ * determine what graph objects match the current event. The tags are
+ * placed in tagArr and *nTagsPtr is set with the number of tags found.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+void
+Blt_GraphTags(
+ Blt_BindTable table,
+ ClientData object,
+ ClientData context, /* Not used. */
+ Blt_List list)
+{
+ GraphObj *graphObjPtr;
+ MakeTagProc *tagProc;
+ Graph *graphPtr;
+
+ graphPtr = (Graph *)Blt_GetBindingData(table);
+
+ /*
+ * All graph objects (markers, elements, axes, etc) have the same starting
+ * fields in their structures, such as "classId", "name", "className", and
+ * "tags".
+ */
+ graphObjPtr = (GraphObj *)object;
+
+ switch (graphObjPtr->classId) {
+ case CID_ELEM_BAR:
+ case CID_ELEM_CONTOUR:
+ case CID_ELEM_LINE:
+ case CID_ELEM_STRIP:
+ tagProc = Blt_MakeElementTag;
+ break;
+ case CID_AXIS_X:
+ case CID_AXIS_Y:
+ tagProc = Blt_MakeAxisTag;
+ break;
+ case CID_MARKER_BITMAP:
+ case CID_MARKER_IMAGE:
+ case CID_MARKER_LINE:
+ case CID_MARKER_POLYGON:
+ case CID_MARKER_TEXT:
+ case CID_MARKER_WINDOW:
+ tagProc = Blt_MakeMarkerTag;
+ break;
+ case CID_NONE:
+ panic("unknown object type");
+ tagProc = NULL;
+ break;
+ default:
+ panic("bogus object type");
+ tagProc = NULL;
+ break;
+ }
+ assert(graphObjPtr->name != NULL);
+
+ /* Always add the name of the object to the tag array. */
+ Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->name), 0);
+ Blt_List_Append(list, (*tagProc)(graphPtr, graphObjPtr->className), 0);
+ if (graphObjPtr->tags != NULL) {
+ const char **p;
+
+ for (p = graphObjPtr->tags; *p != NULL; p++) {
+ Blt_List_Append(list, (*tagProc) (graphPtr, *p), 0);
+ }
+ }
+}
+
+/*
+ * Find the closest point from the set of displayed elements, searching
+ * the display list from back to front. That way, if the points from
+ * two different elements overlay each other exactly, the one that's on
+ * top (visible) is picked.
+ */
+/*ARGSUSED*/
+static ClientData
+PickEntry(ClientData clientData, int x, int y, ClientData *contextPtr)
+{
+ Graph *graphPtr = clientData;
+ Blt_ChainLink link;
+ Element *elemPtr;
+ Marker *markerPtr;
+ Region2d exts;
+
+ if (graphPtr->flags & MAP_ALL) {
+ return NULL; /* Don't pick anything until the next
+ * redraw occurs. */
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+
+ if ((x >= exts.right) || (x < exts.left) ||
+ (y >= exts.bottom) || (y < exts.top)) {
+ /*
+ * Sample coordinate is in one of the graph margins. Can only pick an
+ * axis.
+ */
+ return Blt_NearestAxis(graphPtr, x, y);
+ }
+ /*
+ * From top-to-bottom check:
+ * 1. markers drawn on top (-under false).
+ * 2. elements using its display list back to front.
+ * 3. markers drawn under element (-under true).
+ */
+ markerPtr = Blt_NearestMarker(graphPtr, x, y, FALSE);
+ if (markerPtr != NULL) {
+ return markerPtr; /* Found a marker (-under false). */
+ }
+ {
+ ClosestSearch search;
+
+ search.along = SEARCH_BOTH;
+ search.halo = graphPtr->halo;
+ search.index = -1;
+ search.x = x;
+ search.y = y;
+ search.dist = (double)(search.halo + 1);
+ search.mode = SEARCH_AUTO;
+
+ for (link = Blt_Chain_LastLink(graphPtr->elements.displayList);
+ link != NULL; link = Blt_Chain_PrevLink(link)) {
+ elemPtr = Blt_Chain_GetValue(link);
+ if (elemPtr->flags & (HIDE|MAP_ITEM)) {
+ continue;
+ }
+ if (elemPtr->state == STATE_NORMAL) {
+ (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search);
+ }
+ }
+ if (search.dist <= (double)search.halo) {
+ return search.elemPtr; /* Found an element within the minimum
+ * halo distance. */
+ }
+ }
+ markerPtr = Blt_NearestMarker(graphPtr, x, y, TRUE);
+ if (markerPtr != NULL) {
+ return markerPtr; /* Found a marker (-under true) */
+ }
+ return NULL; /* Nothing found. */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ConfigureGraph --
+ *
+ * Allocates resources for the graph.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Configuration information, such as text string, colors, font, etc. get
+ * set for graphPtr; old resources get freed, if there were any. The
+ * graph is redisplayed.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+ConfigureGraph(Graph *graphPtr)
+{
+ XColor *colorPtr;
+ GC newGC;
+ XGCValues gcValues;
+ unsigned long gcMask;
+
+ /* Don't allow negative bar widths. Reset to an arbitrary value (0.1) */
+ if (graphPtr->barWidth <= 0.0f) {
+ graphPtr->barWidth = 0.8f;
+ }
+ graphPtr->inset = graphPtr->borderWidth + graphPtr->highlightWidth;
+ if ((graphPtr->reqHeight != Tk_ReqHeight(graphPtr->tkwin)) ||
+ (graphPtr->reqWidth != Tk_ReqWidth(graphPtr->tkwin))) {
+ Tk_GeometryRequest(graphPtr->tkwin, graphPtr->reqWidth,
+ graphPtr->reqHeight);
+ }
+ Tk_SetInternalBorder(graphPtr->tkwin, graphPtr->borderWidth);
+ colorPtr = Blt_BackgroundBorderColor(graphPtr->normalBg);
+
+ graphPtr->titleWidth = graphPtr->titleHeight = 0;
+ if (graphPtr->title != NULL) {
+ unsigned int w, h;
+
+ Blt_Ts_GetExtents(&graphPtr->titleTextStyle, graphPtr->title, &w, &h);
+ graphPtr->titleHeight = h;
+ }
+
+ /*
+ * Create GCs for interior and exterior regions, and a background GC for
+ * clearing the margins with XFillRectangle
+ */
+
+ /* Margin GC */
+
+ gcValues.foreground =
+ Blt_Ts_GetForeground(graphPtr->titleTextStyle)->pixel;
+ gcValues.background = colorPtr->pixel;
+ gcMask = (GCForeground | GCBackground);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (graphPtr->drawGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+ }
+ graphPtr->drawGC = newGC;
+
+ if (graphPtr->plotBg != NULL) {
+ Blt_SetBackgroundChangedProc(graphPtr->plotBg, Blt_UpdateGraph,
+ graphPtr);
+ }
+ if (graphPtr->normalBg != NULL) {
+ Blt_SetBackgroundChangedProc(graphPtr->normalBg, Blt_UpdateGraph,
+ graphPtr);
+ }
+ if (Blt_ConfigModified(configSpecs, "-invertxy", (char *)NULL)) {
+
+ /*
+ * If the -inverted option changed, we need to readjust the pointers
+ * to the axes and recompute the their scales.
+ */
+
+ AdjustAxisPointers(graphPtr);
+ graphPtr->flags |= RESET_AXES;
+ }
+ if ((!graphPtr->backingStore) && (graphPtr->cache != None)) {
+ /*
+ * Free the pixmap if we're not buffering the display of elements
+ * anymore.
+ */
+ Tk_FreePixmap(graphPtr->display, graphPtr->cache);
+ graphPtr->cache = None;
+ }
+ /*
+ * Reconfigure the crosshairs, just in case the background color of the
+ * plotarea has been changed.
+ */
+ Blt_ConfigureCrosshairs(graphPtr);
+
+ /*
+ * Update the layout of the graph (and redraw the elements) if any of the
+ * following graph options which affect the size of * the plotting area
+ * has changed.
+ *
+ * -aspect
+ * -borderwidth, -plotborderwidth
+ * -font, -title
+ * -width, -height
+ * -invertxy
+ * -bottommargin, -leftmargin, -rightmargin, -topmargin,
+ * -barmode, -barwidth
+ */
+ if (Blt_ConfigModified(configSpecs, "-invertxy", "-title", "-font",
+ "-*margin", "-*width", "-height", "-barmode", "-*pad*",
+ "-aspect", "-*borderwidth", "-plot*", "-*width", "-*height",
+ "-unmaphiddenelements", (char *)NULL)) {
+ graphPtr->flags |= RESET_WORLD | CACHE_DIRTY;
+ }
+ if (Blt_ConfigModified(configSpecs, "-plot*", "-*background",
+ (char *)NULL)) {
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ graphPtr->flags |= REDRAW_WORLD;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DestroyGraph --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
+ * clean up the internal structure of a graph at a safe time (when no-one
+ * is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the widget is freed up.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DestroyGraph(DestroyData dataPtr)
+{
+ Graph *graphPtr = (Graph *)dataPtr;
+
+ Blt_FreeOptions(configSpecs, (char *)graphPtr, graphPtr->display, 0);
+ /*
+ * Destroy the individual components of the graph: elements, markers,
+ * axes, legend, display lists etc. Be careful to remove them in
+ * order. For example, axes are used by elements and markers, so they have
+ * to be removed after the markers and elements. Same it true with the
+ * legend and pens (they use elements), so can't be removed until the
+ * elements are destroyed.
+ */
+ Blt_DestroyMarkers(graphPtr);
+ Blt_DestroyElements(graphPtr);
+ Blt_DestroyLegend(graphPtr);
+ Blt_DestroyAxes(graphPtr);
+ Blt_DestroyPens(graphPtr);
+ Blt_DestroyCrosshairs(graphPtr);
+ Blt_DestroyPageSetup(graphPtr);
+ Blt_DestroyBarSets(graphPtr);
+ /* Destroy table clients after elements are destroyed. */
+ Blt_DestroyTableClients(graphPtr);
+ if (graphPtr->bindTable != NULL) {
+ Blt_DestroyBindingTable(graphPtr->bindTable);
+ }
+
+ /* Release allocated X resources and memory. */
+ if (graphPtr->drawGC != NULL) {
+ Tk_FreeGC(graphPtr->display, graphPtr->drawGC);
+ }
+ Blt_Ts_FreeStyle(graphPtr->display, &graphPtr->titleTextStyle);
+ if (graphPtr->cache != None) {
+ Tk_FreePixmap(graphPtr->display, graphPtr->cache);
+ }
+ Blt_Free(graphPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CreateGraph --
+ *
+ * This procedure creates and initializes a new widget.
+ *
+ * Results:
+ * The return value is a pointer to a structure describing the new
+ * widget. If an error occurred, then the return value is NULL and an
+ * error message is left in interp->result.
+ *
+ * Side effects:
+ * Memory is allocated, a Tk_Window is created, etc.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static Graph *
+CreateGraph(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, ClassId classId)
+{
+ Graph *graphPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
+ Tcl_GetString(objv[1]), (char *)NULL);
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ graphPtr = Blt_AssertCalloc(1, sizeof(Graph));
+
+ /* Initialize the graph data structure. */
+
+ graphPtr->tkwin = tkwin;
+ graphPtr->display = Tk_Display(tkwin);
+ graphPtr->interp = interp;
+ graphPtr->classId = classId;
+ graphPtr->backingStore = TRUE;
+ graphPtr->doubleBuffer = TRUE;
+ graphPtr->borderWidth = 2;
+ graphPtr->plotBW = 1;
+ graphPtr->highlightWidth = 2;
+ graphPtr->plotRelief = TK_RELIEF_SOLID;
+ graphPtr->relief = TK_RELIEF_FLAT;
+ graphPtr->flags = RESET_WORLD;
+ graphPtr->nextMarkerId = 1;
+ graphPtr->padLeft = graphPtr->padRight = 0;
+ graphPtr->padTop = graphPtr->padBottom = 0;
+ graphPtr->bottomMargin.site = MARGIN_BOTTOM;
+ graphPtr->leftMargin.site = MARGIN_LEFT;
+ graphPtr->topMargin.site = MARGIN_TOP;
+ graphPtr->rightMargin.site = MARGIN_RIGHT;
+ Blt_Ts_InitStyle(graphPtr->titleTextStyle);
+ Blt_Ts_SetAnchor(graphPtr->titleTextStyle, TK_ANCHOR_N);
+
+ Blt_InitHashTable(&graphPtr->axes.table, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->axes.tagTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->elements.table, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->elements.tagTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->markers.table, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->markers.tagTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&graphPtr->dataTables, BLT_STRING_KEYS);
+ graphPtr->elements.displayList = Blt_Chain_Create();
+ graphPtr->markers.displayList = Blt_Chain_Create();
+ graphPtr->axes.displayList = Blt_Chain_Create();
+
+ switch (classId) {
+ case CID_ELEM_LINE:
+ Tk_SetClass(tkwin, "Graph");
+ break;
+ case CID_ELEM_BAR:
+ Tk_SetClass(tkwin, "Barchart");
+ break;
+ case CID_ELEM_STRIP:
+ Tk_SetClass(tkwin, "Stripchart");
+ default:
+ Tk_SetClass(tkwin, "???");
+ break;
+ }
+ Blt_SetWindowInstanceData(tkwin, graphPtr);
+
+ if (InitPens(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_ConfigureWidgetFromObj(interp, tkwin, configSpecs, objc - 2,
+ objv + 2, (char *)graphPtr, 0) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_DefaultAxes(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ AdjustAxisPointers(graphPtr);
+
+ if (Blt_CreatePageSetup(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_CreateCrosshairs(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ if (Blt_CreateLegend(graphPtr) != TCL_OK) {
+ goto error;
+ }
+ Tk_CreateEventHandler(graphPtr->tkwin,
+ ExposureMask | StructureNotifyMask | FocusChangeMask, GraphEventProc,
+ graphPtr);
+
+ graphPtr->cmdToken = Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]),
+ Blt_GraphInstCmdProc, graphPtr, GraphInstCmdDeleteProc);
+ ConfigureGraph(graphPtr);
+ graphPtr->bindTable = Blt_CreateBindingTable(interp, tkwin, graphPtr,
+ PickEntry, Blt_GraphTags);
+
+ Tcl_SetObjResult(interp, objv[1]);
+ return graphPtr;
+
+ error:
+ DestroyGraph((DestroyData)graphPtr);
+ return NULL;
+}
+
+/* Widget sub-commands */
+
+/*ARGSUSED*/
+static int
+XAxisOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM;
+ return Blt_AxisOp(interp, graphPtr, margin, objc, objv);
+}
+
+static int
+X2AxisOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_RIGHT : MARGIN_TOP;
+ return Blt_AxisOp(interp, graphPtr, margin, objc, objv);
+}
+
+static int
+YAxisOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT;
+ return Blt_AxisOp(interp, graphPtr, margin, objc, objv);
+}
+
+static int
+Y2AxisOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int margin;
+
+ margin = (graphPtr->inverted) ? MARGIN_TOP : MARGIN_RIGHT;
+ return Blt_AxisOp(interp, graphPtr, margin, objc, objv);
+}
+
+static int
+BarOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ElementOp(graphPtr, interp, objc, objv, CID_ELEM_BAR);
+}
+
+/*ARGSUSED*/
+static int
+LineOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ElementOp(graphPtr, interp, objc, objv, CID_ELEM_LINE);
+}
+
+static int
+ElementOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ElementOp(graphPtr, interp, objc, objv, graphPtr->classId);
+}
+
+static int
+ConfigureOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int flags;
+
+ flags = BLT_CONFIG_OBJV_ONLY;
+ if (objc == 2) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, (Tcl_Obj *)NULL, flags);
+ } else if (objc == 3) {
+ return Blt_ConfigureInfoFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, objv[2], flags);
+ } else {
+ if (Blt_ConfigureWidgetFromObj(interp, graphPtr->tkwin, configSpecs,
+ objc - 2, objv + 2, (char *)graphPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ConfigureGraph(graphPtr);
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return TCL_OK;
+ }
+}
+
+/* ARGSUSED*/
+static int
+CgetOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ return Blt_ConfigureValueFromObj(interp, graphPtr->tkwin, configSpecs,
+ (char *)graphPtr, objv[2], 0);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ExtentsOp --
+ *
+ * Reports the size of one of several items within the graph. The
+ * following are valid items:
+ *
+ * "bottommargin" Height of the bottom margin
+ * "leftmargin" Width of the left margin
+ * "legend" x y w h of the legend
+ * "plotarea" x y w h of the plotarea
+ * "plotheight" Height of the plot area
+ * "rightmargin" Width of the right margin
+ * "topmargin" Height of the top margin
+ * "plotwidth" Width of the plot area
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+ExtentsOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ const char *string;
+ char c;
+ int length;
+
+ string = Tcl_GetStringFromObj(objv[2], &length);
+ c = string[0];
+ if ((c == 'p') && (length > 4) &&
+ (strncmp("plotheight", string, length) == 0)) {
+ int height;
+
+ height = graphPtr->bottom - graphPtr->top + 1;
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), height);
+ } else if ((c == 'p') && (length > 4) &&
+ (strncmp("plotwidth", string, length) == 0)) {
+ int width;
+
+ width = graphPtr->right - graphPtr->left + 1;
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), width);
+ } else if ((c == 'p') && (length > 4) &&
+ (strncmp("plotarea", string, length) == 0)) {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(graphPtr->left));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(graphPtr->top));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(graphPtr->right - graphPtr->left + 1));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(graphPtr->bottom - graphPtr->top + 1));
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else if ((c == 'l') && (length > 2) &&
+ (strncmp("legend", string, length) == 0)) {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(Blt_Legend_X(graphPtr)));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(Blt_Legend_Y(graphPtr)));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(Blt_Legend_Width(graphPtr)));
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(Blt_Legend_Height(graphPtr)));
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else if ((c == 'l') && (length > 2) &&
+ (strncmp("leftmargin", string, length) == 0)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), graphPtr->leftMargin.width);
+ } else if ((c == 'r') && (length > 1) &&
+ (strncmp("rightmargin", string, length) == 0)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), graphPtr->rightMargin.width);
+ } else if ((c == 't') && (length > 1) &&
+ (strncmp("topmargin", string, length) == 0)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), graphPtr->topMargin.height);
+ } else if ((c == 'b') && (length > 1) &&
+ (strncmp("bottommargin", string, length) == 0)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), graphPtr->bottomMargin.height);
+ } else {
+ Tcl_AppendResult(interp, "bad extent item \"", objv[2],
+ "\": should be plotheight, plotwidth, leftmargin, rightmargin, \
+topmargin, bottommargin, plotarea, or legend", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InsideOp --
+ *
+ * Returns true of false whether the given point is inside the plotting
+ * area (defined by left,bottom right, top).
+ *
+ * Results:
+ * Always returns TCL_OK. interp->result will contain the boolean string
+ * representation.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED*/
+static int
+InsideOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int x, y;
+ Region2d exts;
+ int result;
+
+ if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ result = PointInRegion(&exts, x, y);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), result);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InvtransformOp --
+ *
+ * This procedure returns a list of the graph coordinate values
+ * corresponding with the given window X and Y coordinate positions.
+ *
+ * Results:
+ * Returns a standard TCL result. If an error occurred while parsing the
+ * window positions, TCL_ERROR is returned, and interp->result will
+ * contain the error message. Otherwise interp->result will contain a
+ * Tcl list of the x and y coordinates.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InvtransformOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ double x, y;
+ Point2d point;
+ Axis2d axes;
+ Tcl_Obj *listObjPtr;
+
+ if ((Blt_ExprDoubleFromObj(interp, objv[2], &x) != TCL_OK) ||
+ (Blt_ExprDoubleFromObj(interp, objv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ /* Perform the reverse transformation, converting from window coordinates
+ * to graph data coordinates. Note that the point is always mapped to the
+ * bottom and left axes (which may not be what the user wants). */
+
+ /* Pick the first pair of axes */
+ axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+ axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+ point = Blt_InvMap2D(graphPtr, x, y, &axes);
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.x));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.y));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TransformOp --
+ *
+ * This procedure returns a list of the window coordinates corresponding
+ * with the given graph x and y coordinates.
+ *
+ * Results:
+ * Returns a standard TCL result. interp->result contains the list of
+ * the graph coordinates. If an error occurred while parsing the window
+ * positions, TCL_ERROR is returned, then interp->result will contain an
+ * error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransformOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ double x, y;
+ Point2d point;
+ Axis2d axes;
+ Tcl_Obj *listObjPtr;
+
+ if ((Blt_ExprDoubleFromObj(interp, objv[2], &x) != TCL_OK) ||
+ (Blt_ExprDoubleFromObj(interp, objv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ /*
+ * Perform the transformation from window to graph coordinates. Note that
+ * the points are always mapped onto the bottom and left axes (which may
+ * not be the what the user wants).
+ */
+ axes.x = Blt_GetFirstAxis(graphPtr->axisChain[0]);
+ axes.y = Blt_GetFirstAxis(graphPtr->axisChain[1]);
+
+ point = Blt_Map2D(graphPtr, x, y, &axes);
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(ROUND(point.x)));
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(ROUND(point.y)));
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+#ifndef NO_PRINTER
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Print1Op --
+ *
+ * Prints the equivalent of a screen snapshot of the graph to the
+ * designated printer.
+ *
+ * Results:
+ * Returns a standard TCL result. If an error occurred TCL_ERROR is
+ * returned and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+Print1Op(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ BITMAPINFO info;
+ void *data;
+ TkWinDCState state;
+ TkWinBitmap bd;
+ DIBSECTION ds;
+ Drawable drawable;
+ HBITMAP hBitmap;
+ HDC hDC;
+ DOCINFO di;
+ double pageWidth, pageHeight;
+ int result;
+ double scale, sx, sy;
+ int jobId;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+ graphPtr->width = graphPtr->reqWidth;
+ }
+ if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+ graphPtr->height = graphPtr->reqHeight;
+ }
+ if (objc == 2) {
+ result = Blt_PrintDialog(interp, &drawable);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (result == TCL_RETURN) {
+ return TCL_OK;
+ }
+ } else {
+ if (Blt_GetOpenPrinter(interp, Tcl_GetString(objv[2]), &drawable)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ /*
+ * This is a taken from Blt_SnapPhoto. The difference is that here we're
+ * using the DIBSection directly, without converting the section into a
+ * Picture.
+ */
+ ZeroMemory(&info, sizeof(info));
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = graphPtr->width;
+ info.bmiHeader.biHeight = graphPtr->height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ hDC = TkWinGetDrawableDC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
+ &state);
+ hBitmap = CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &data, NULL, 0);
+ TkWinReleaseDrawableDC(Tk_WindowId(graphPtr->tkwin), hDC, &state);
+
+ /*
+ * Create our own drawable by hand using the DIB we just created. We'll
+ * then draw into it using the standard drawing functions.
+ */
+ bd.type = TWD_BITMAP;
+ bd.handle = hBitmap;
+ bd.colormap = DefaultColormap(graphPtr->display,
+ DefaultScreen(graphPtr->display));
+ bd.depth = Tk_Depth(graphPtr->tkwin);
+
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, (Drawable)&bd);
+
+ /*
+ * Now that the DIB contains the image of the graph, get the the data bits
+ * and write them to the printer device, stretching the image to the fit
+ * the printer's resolution.
+ */
+ result = TCL_ERROR;
+ if (GetObject(hBitmap, sizeof(DIBSECTION), &ds) == 0) {
+ Tcl_AppendResult(interp, "can't get object: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ hDC = ((TkWinDC *) drawable)->hdc;
+ /* Get the resolution of the printer device. */
+ sx = (double)GetDeviceCaps(hDC, HORZRES) / (double)graphPtr->width;
+ sy = (double)GetDeviceCaps(hDC, VERTRES) / (double)graphPtr->height;
+ scale = MIN(sx, sy);
+ pageWidth = scale * graphPtr->width;
+ pageHeight = scale * graphPtr->height;
+
+ ZeroMemory(&di, sizeof(di));
+ di.cbSize = sizeof(di);
+ di.lpszDocName = "Graph Contents";
+ jobId = StartDoc(hDC, &di);
+ if (jobId <= 0) {
+ Tcl_AppendResult(interp, "can't start document: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ if (StartPage(hDC) <= 0) {
+ Tcl_AppendResult(interp, "error starting page: ", Blt_LastError(),
+ (char *)NULL);
+ goto done;
+ }
+ StretchDIBits(hDC, 0, 0, ROUND(pageWidth), ROUND(pageHeight), 0, 0,
+ graphPtr->width, graphPtr->height, ds.dsBm.bmBits,
+ (LPBITMAPINFO)&ds.dsBmih, DIB_RGB_COLORS, SRCCOPY);
+ EndPage(hDC);
+ EndDoc(hDC);
+ result = TCL_OK;
+ done:
+ DeleteBitmap(hBitmap);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Print2Op --
+ *
+ * Prints directly to the designated printer device.
+ *
+ * Results:
+ * Returns a standard TCL result. If an error occurred, TCL_ERROR is
+ * returned and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+Print2Op(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Drawable drawable;
+ int result;
+
+ graphPtr->width = Tk_Width(graphPtr->tkwin);
+ graphPtr->height = Tk_Height(graphPtr->tkwin);
+ if ((graphPtr->width < 2) && (graphPtr->reqWidth > 0)) {
+ graphPtr->width = graphPtr->reqWidth;
+ }
+ if ((graphPtr->height < 2) && (graphPtr->reqHeight > 0)) {
+ graphPtr->height = graphPtr->reqHeight;
+ }
+ if (objc == 2) {
+ result = Blt_PrintDialog(interp, &drawable);
+ if (result == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (result == TCL_RETURN) {
+ return TCL_OK;
+ }
+ } else {
+ result = Blt_GetOpenPrinter(interp, Tcl_GetString(objv[2]), &drawable);
+ }
+ if (result == TCL_OK) {
+ int oldMode;
+ HDC hDC;
+ double xRatio, yRatio;
+ TkWinDC *drawPtr;
+ int w, h;
+
+ drawPtr = (TkWinDC *) drawable;
+ hDC = drawPtr->hdc;
+ Blt_GetPrinterScale(hDC, &xRatio, &yRatio);
+ oldMode = SetMapMode(hDC, MM_ISOTROPIC);
+ if (oldMode == 0) {
+ Tcl_AppendResult(interp, "can't set mode for printer DC: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ w = (int)round(graphPtr->width * xRatio);
+ h = (int)round(graphPtr->height * yRatio);
+ SetViewportExtEx(hDC, w, h, NULL);
+ SetWindowExtEx(hDC, graphPtr->width, graphPtr->height, NULL);
+
+ Blt_StartPrintJob(interp, drawable);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, drawable);
+ Blt_EndPrintJob(interp, drawable);
+ }
+ return result;
+}
+
+#endif /* NO_PRINTER */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToFormat --
+ *
+ * Convert a string represent a node number into its integer value.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToFormat(
+ ClientData clientData, /* Not used.*/
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ const char *switchName, /* Not used. */
+ Tcl_Obj *objPtr, /* String representation */
+ char *record, /* Structure record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ int *formatPtr = (int *)(record + offset);
+ char c;
+ const char *string;
+
+ string = Tcl_GetString(objPtr);
+ c = string[0];
+ if ((c == 'p') && (strcmp(string, "picture") == 0)) {
+ *formatPtr = FORMAT_PHOTO;
+ } else if ((c == 'p') && (strcmp(string, "photo") == 0)) {
+ *formatPtr = FORMAT_PHOTO;
+#ifdef WIN32
+ } else if ((c == 'e') && (strcmp(string, "emf") == 0)) {
+ *formatPtr = FORMAT_EMF;
+ } else if ((c == 'w') && (strcmp(string, "wmf") == 0)) {
+ *formatPtr = FORMAT_WMF;
+#endif /* WIN32 */
+ } else {
+#ifdef WIN32
+ Tcl_AppendResult(interp, "bad format \"", string,
+ "\": should be picture, photo, emf, or wmf.", (char *)NULL);
+#else
+ Tcl_AppendResult(interp, "bad format \"", string,
+ "\": should be picture or photo.", (char *)NULL);
+#endif /* WIN32 */
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+#ifdef WIN32
+static int InitMetaFileHeader(
+ Tk_Window tkwin,
+ int width, int height,
+ APMHEADER *mfhPtr)
+{
+ unsigned int *p;
+ unsigned int sum;
+#define MM_INCH 25.4
+ int xdpi, ydpi;
+
+ mfhPtr->key = 0x9ac6cdd7L;
+ mfhPtr->hmf = 0;
+ mfhPtr->inch = 1440;
+
+ Blt_ScreenDPI(tkwin, &xdpi, &ydpi);
+ mfhPtr->bbox.Left = mfhPtr->bbox.Top = 0;
+ mfhPtr->bbox.Bottom = (SHORT)((width * 1440)/ (float)xdpi);
+ mfhPtr->bbox.Right = (SHORT)((height * 1440) / (float)ydpi);
+ mfhPtr->reserved = 0;
+ sum = 0;
+ for (p = (unsigned int *)mfhPtr;
+ p < (unsigned int *)&(mfhPtr->checksum); p++) {
+ sum ^= *p;
+ }
+ mfhPtr->checksum = sum;
+ return TCL_OK;
+}
+
+static int
+CreateAPMetaFile(Tcl_Interp *interp, HANDLE hMetaFile, HDC hDC,
+ APMHEADER *mfhPtr, const char *fileName)
+{
+ HANDLE hFile;
+ HANDLE hMem;
+ LPVOID buffer;
+ int result;
+ DWORD count, nBytes;
+
+ result = TCL_ERROR;
+ hMem = NULL;
+ hFile = CreateFile(
+ fileName, /* File path */
+ GENERIC_WRITE, /* Access mode */
+ 0, /* No sharing. */
+ NULL, /* Security attributes */
+ CREATE_ALWAYS, /* Overwrite any existing file */
+ FILE_ATTRIBUTE_NORMAL,
+ NULL); /* No template file */
+ if (hFile == INVALID_HANDLE_VALUE) {
+ Tcl_AppendResult(interp, "can't create metafile \"", fileName,
+ "\":", Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((!WriteFile(hFile, (LPVOID)mfhPtr, sizeof(APMHEADER), &count,
+ NULL)) || (count != sizeof(APMHEADER))) {
+ Tcl_AppendResult(interp, "can't create metafile header to \"",
+ fileName, "\":", Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ nBytes = GetWinMetaFileBits(hMetaFile, 0, NULL, MM_ANISOTROPIC, hDC);
+ hMem = GlobalAlloc(GHND, nBytes);
+ if (hMem == NULL) {
+ Tcl_AppendResult(interp, "can't create allocate global memory:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ buffer = (LPVOID)GlobalLock(hMem);
+ if (!GetWinMetaFileBits(hMetaFile, nBytes, buffer, MM_ANISOTROPIC, hDC)) {
+ Tcl_AppendResult(interp, "can't get metafile bits:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ if ((!WriteFile(hFile, buffer, nBytes, &count, NULL)) ||
+ (count != nBytes)) {
+ Tcl_AppendResult(interp, "can't write metafile bits:",
+ Blt_LastError(), (char *)NULL);
+ goto error;
+ }
+ result = TCL_OK;
+ error:
+ CloseHandle(hFile);
+ if (hMem != NULL) {
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ return result;
+}
+#endif /*WIN32*/
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SnapOp --
+ *
+ * Snaps a picture of the graph and stores it in the specified image.
+ *
+ * Results:
+ * Returns a standard TCL result. interp->result contains
+ * the list of the graph coordinates. If an error occurred
+ * while parsing the window positions, TCL_ERROR is returned,
+ * then interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+SnapOp(Graph *graphPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int result;
+ Pixmap drawable;
+ int i;
+ SnapSwitches switches;
+
+ /* .g snap ?switches? name */
+ switches.height = Tk_Height(graphPtr->tkwin);
+ if ((switches.height < 2) && (graphPtr->reqHeight > 0)) {
+ switches.height = graphPtr->reqHeight;
+ }
+ switches.width = Tk_Width(graphPtr->tkwin);
+ if ((switches.width < 2) && (graphPtr->reqWidth > 0)) {
+ switches.width = graphPtr->reqWidth;
+ }
+ switches.format = FORMAT_PICTURE;
+ /* Process switches */
+ i = Blt_ParseSwitches(interp, snapSwitches, objc - 2, objv + 2, &switches,
+ BLT_SWITCH_OBJV_PARTIAL);
+ if (i < 0) {
+ return TCL_ERROR;
+ }
+ i += 2;
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "missing name argument: should be \"",
+ Tcl_GetString(objv[0]), "snap ?switches? name\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ switches.name = Tcl_GetString(objv[i]);
+ if (switches.width < 2) {
+ switches.width = Tk_ReqWidth(graphPtr->tkwin);
+ }
+ if (switches.height < 2) {
+ switches.width = Tk_ReqHeight(graphPtr->tkwin);
+ }
+ /* Always re-compute the layout of the graph before snapping the picture. */
+ graphPtr->width = switches.width;
+ graphPtr->height = switches.height;
+ Blt_MapGraph(graphPtr);
+
+ drawable = Tk_WindowId(graphPtr->tkwin);
+ switch (switches.format) {
+ case FORMAT_PICTURE:
+ case FORMAT_PHOTO:
+ drawable = Tk_GetPixmap(graphPtr->display, drawable, graphPtr->width,
+ graphPtr->height, Tk_Depth(graphPtr->tkwin));
+#ifdef WIN32
+ assert(drawable != None);
+#endif
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, drawable);
+ if (switches.format == FORMAT_PICTURE) {
+ result = Blt_SnapPicture(interp, graphPtr->tkwin, drawable, 0, 0,
+ switches.width, switches.height, switches.width,
+ switches.height, switches.name, 1.0);
+ } else {
+ result = Blt_SnapPhoto(interp, graphPtr->tkwin, drawable, 0, 0,
+ switches.width, switches.height, switches.width,
+ switches.height, switches.name, 1.0);
+ }
+ Tk_FreePixmap(graphPtr->display, drawable);
+ break;
+
+#ifdef WIN32
+ case FORMAT_WMF:
+ case FORMAT_EMF:
+ {
+ TkWinDC drawableDC;
+ TkWinDCState state;
+ HDC hRefDC, hDC;
+ HENHMETAFILE hMetaFile;
+ Tcl_DString dString;
+ const char *title;
+
+ hRefDC = TkWinGetDrawableDC(graphPtr->display, drawable, &state);
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, "BLT Graph ", -1);
+ Tcl_DStringAppend(&dString, BLT_VERSION, -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ Tcl_DStringAppend(&dString, Tk_PathName(graphPtr->tkwin), -1);
+ Tcl_DStringAppend(&dString, "\0", -1);
+ title = Tcl_DStringValue(&dString);
+ hDC = CreateEnhMetaFile(hRefDC, NULL, NULL, title);
+ Tcl_DStringFree(&dString);
+
+ if (hDC == NULL) {
+ Tcl_AppendResult(interp, "can't create metafile: ",
+ Blt_LastError(), (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ drawableDC.hdc = hDC;
+ drawableDC.type = TWD_WINDC;
+
+ graphPtr->width = switches.width;
+ graphPtr->height = switches.height;
+ Blt_MapGraph(graphPtr);
+ graphPtr->flags |= RESET_WORLD;
+ Blt_DrawGraph(graphPtr, (Drawable)&drawableDC);
+ hMetaFile = CloseEnhMetaFile(hDC);
+ if (strcmp(switches.name, "CLIPBOARD") == 0) {
+ HWND hWnd;
+
+ hWnd = Tk_GetHWND(drawable);
+ OpenClipboard(hWnd);
+ EmptyClipboard();
+ SetClipboardData(CF_ENHMETAFILE, hMetaFile);
+ CloseClipboard();
+ result = TCL_OK;
+ } else {
+ result = TCL_ERROR;
+ if (switches.format == FORMAT_WMF) {
+ APMHEADER mfh;
+
+ assert(sizeof(mfh) == 22);
+ InitMetaFileHeader(graphPtr->tkwin, switches.width,
+ switches.height, &mfh);
+ result = CreateAPMetaFile(interp, hMetaFile, hRefDC, &mfh,
+ switches.name);
+ } else {
+ HENHMETAFILE hMetaFile2;
+
+ hMetaFile2 = CopyEnhMetaFile(hMetaFile, switches.name);
+ if (hMetaFile2 != NULL) {
+ result = TCL_OK;
+ DeleteEnhMetaFile(hMetaFile2);
+ }
+ }
+ DeleteEnhMetaFile(hMetaFile);
+ }
+ TkWinReleaseDrawableDC(drawable, hRefDC, &state);
+ }
+ break;
+#endif /*WIN32*/
+ default:
+ Tcl_AppendResult(interp, "bad snapshot format", (char *)NULL);
+ return TCL_ERROR;
+ }
+ graphPtr->flags |= MAP_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GraphWidgetCmd --
+ *
+ * This procedure is invoked to process the TCL command that
+ * corresponds to a widget managed by this module. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Blt_OpSpec graphOps[] =
+{
+ {"axis", 1, Blt_VirtualAxisOp, 2, 0, "oper ?args?",},
+ {"bar", 2, BarOp, 2, 0, "oper ?args?",},
+ {"cget", 2, CgetOp, 3, 3, "option",},
+ {"configure", 2, ConfigureOp, 2, 0, "?option value?...",},
+ {"crosshairs", 2, Blt_CrosshairsOp, 2, 0, "oper ?args?",},
+ {"element", 2, ElementOp, 2, 0, "oper ?args?",},
+ {"extents", 2, ExtentsOp, 3, 3, "item",},
+ {"inside", 3, InsideOp, 4, 4, "winX winY",},
+ {"invtransform", 3, InvtransformOp, 4, 4, "winX winY",},
+ {"legend", 2, Blt_LegendOp, 2, 0, "oper ?args?",},
+ {"line", 2, LineOp, 2, 0, "oper ?args?",},
+ {"marker", 2, Blt_MarkerOp, 2, 0, "oper ?args?",},
+ {"pen", 2, Blt_PenOp, 2, 0, "oper ?args?",},
+ {"postscript", 2, Blt_PostScriptOp, 2, 0, "oper ?args?",},
+#ifndef NO_PRINTER
+ {"print1", 2, Print1Op, 2, 3, "?printerName?",},
+ {"print2", 2, Print2Op, 2, 3, "?printerName?",},
+#endif /*NO_PRINTER*/
+ {"snap", 1, SnapOp, 3, 0, "?switches? name",},
+ {"transform", 1, TransformOp, 4, 4, "x y",},
+ {"x2axis", 2, X2AxisOp, 2, 0, "oper ?args?",},
+ {"xaxis", 2, XAxisOp, 2, 0, "oper ?args?",},
+ {"y2axis", 2, Y2AxisOp, 2, 0, "oper ?args?",},
+ {"yaxis", 2, YAxisOp, 2, 0, "oper ?args?",},
+};
+static int nGraphOps = sizeof(graphOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_GraphInstCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ GraphCmdProc *proc;
+ int result;
+ Graph *graphPtr = clientData;
+
+ proc = Blt_GetOpFromObj(interp, nGraphOps, graphOps, BLT_OP_ARG1,
+ objc, objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(graphPtr);
+ result = (*proc) (graphPtr, interp, objc, objv);
+ Tcl_Release(graphPtr);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NewGraph --
+ *
+ * Creates a new window and TCL command representing an instance of a
+ * graph widget.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NewGraph(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, ClassId classId)
+{
+ Graph *graphPtr;
+
+ if (objc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " pathName ?option value?...\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ graphPtr = CreateGraph(interp, objc, objv, classId);
+ if (graphPtr == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GraphCmd --
+ *
+ * Creates a new window and TCL command representing an instance of a
+ * graph widget.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GraphCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ return NewGraph(interp, objc, objv, CID_ELEM_LINE);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BarchartCmd --
+ *
+ * Creates a new window and TCL command representing an instance of a
+ * barchart widget.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BarchartCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ return NewGraph(interp, objc, objv, CID_ELEM_BAR);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * StripchartCmd --
+ *
+ * Creates a new window and TCL command representing an instance of a
+ * barchart widget.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StripchartCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ return NewGraph(interp, objc, objv, CID_ELEM_STRIP);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawMargins --
+ *
+ * Draws the exterior region of the graph (axes, ticks, titles, etc)
+ * onto a pixmap. The interior region is defined by the given rectangle
+ * structure.
+ *
+ * ---------------------------------
+ * | |
+ * | rectArr[0] |
+ * | |
+ * ---------------------------------
+ * | |top right| |
+ * | | | |
+ * | | | |
+ * | [1] | | [2] |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | | |
+ * | |left bottom| |
+ * ---------------------------------
+ * | |
+ * | rectArr[3] |
+ * | |
+ * ---------------------------------
+ *
+ * X coordinate axis
+ * Y coordinate axis
+ * legend
+ * interior border
+ * exterior border
+ * titles (X and Y axis, graph)
+ *
+ * Returns:
+ * None.
+ *
+ * Side Effects:
+ * Exterior of graph is displayed in its window.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawMargins(Graph *graphPtr, Drawable drawable)
+{
+ XRectangle rects[4];
+ int site;
+
+ /*
+ * Draw the four outer rectangles which encompass the plotting
+ * surface. This clears the surrounding area and clips the plot.
+ */
+ rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0;
+ rects[0].width = rects[3].width = (short int)graphPtr->width;
+ rects[0].height = (short int)graphPtr->top;
+ rects[3].y = graphPtr->bottom;
+ rects[3].height = graphPtr->height - graphPtr->bottom;
+ rects[2].y = rects[1].y = graphPtr->top;
+ rects[1].width = graphPtr->left;
+ rects[2].height = rects[1].height = graphPtr->bottom - graphPtr->top;
+ rects[2].x = graphPtr->right;
+ rects[2].width = graphPtr->width - graphPtr->right;
+
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg,
+ rects[0].x, rects[0].y, rects[0].width, rects[0].height,
+ 0, TK_RELIEF_FLAT);
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg,
+ rects[1].x, rects[1].y, rects[1].width, rects[1].height,
+ 0, TK_RELIEF_FLAT);
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg,
+ rects[2].x, rects[2].y, rects[2].width, rects[2].height,
+ 0, TK_RELIEF_FLAT);
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->normalBg,
+ rects[3].x, rects[3].y, rects[3].width, rects[3].height,
+ 0, TK_RELIEF_FLAT);
+
+ /* Draw 3D border around the plotting area */
+
+ if (graphPtr->plotBW > 0) {
+ int x, y, w, h;
+
+ x = graphPtr->left - graphPtr->plotBW;
+ y = graphPtr->top - graphPtr->plotBW;
+ w = (graphPtr->right - graphPtr->left) + (2*graphPtr->plotBW);
+ h = (graphPtr->bottom - graphPtr->top) + (2*graphPtr->plotBW);
+ Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable,
+ graphPtr->normalBg, x, y, w, h, graphPtr->plotBW,
+ graphPtr->plotRelief);
+ }
+ site = Blt_Legend_Site(graphPtr);
+ if (site & LEGEND_MARGIN_MASK) {
+ /* Legend is drawn on one of the graph margins */
+ Blt_DrawLegend(graphPtr, drawable);
+ } else if (site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ if (graphPtr->title != NULL) {
+ Blt_DrawText(graphPtr->tkwin, drawable, graphPtr->title,
+ &graphPtr->titleTextStyle, graphPtr->titleX, graphPtr->titleY);
+ }
+ Blt_DrawAxes(graphPtr, drawable);
+ graphPtr->flags &= ~DRAW_MARGINS;
+}
+
+#ifdef notdef
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawPlotRegion --
+ *
+ * Draws the contents of the plotting area. This consists of the
+ * elements, markers (draw under elements), axis limits, and possibly the
+ * legend. Typically, the output will be cached into a backing store
+ * pixmap, so that redraws can occur quickly.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawPlotRegion(Graph *graphPtr, Drawable drawable)
+{
+ int site;
+
+ /* Clear the background of the plotting area. */
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->plotBg,
+ graphPtr->left, graphPtr->top, graphPtr->right - graphPtr->left + 1,
+ graphPtr->bottom - graphPtr->top + 1, TK_RELIEF_FLAT, 0);
+
+ /* Draw the elements, markers, legend, and axis limits. */
+
+ Blt_DrawGrids(graphPtr, drawable);
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
+ site = Blt_Legend_Site(graphPtr);
+ if ((site & LEGEND_PLOTAREA_MASK) &&
+ (!Blt_Legend_IsRaised(graphPtr))) {
+ Blt_DrawLegend(graphPtr, drawable);
+ } else if (site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ Blt_DrawAxisLimits(graphPtr, drawable);
+ DrawMargins(graphPtr, drawable);
+ Blt_DrawElements(graphPtr, drawable);
+ Blt_DrawAxes(graphPtr, drawable);
+}
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DrawPlot --
+ *
+ * Draws the contents of the plotting area. This consists of the
+ * elements, markers (draw under elements), axis limits, and possibly the
+ * legend. Typically, the output will be cached into a backing store
+ * pixmap, so that redraws can occur quickly.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DrawPlot(Graph *graphPtr, Drawable drawable)
+{
+ int site;
+
+ DrawMargins(graphPtr, drawable);
+
+ /* Draw the background of the plotting area with 3D border. */
+ Blt_FillBackgroundRectangle(graphPtr->tkwin, drawable, graphPtr->plotBg,
+ graphPtr->left - graphPtr->plotBW,
+ graphPtr->top - graphPtr->plotBW,
+ graphPtr->right - graphPtr->left + 1 + 2 * graphPtr->plotBW,
+ graphPtr->bottom - graphPtr->top + 1 + 2 * graphPtr->plotBW,
+ graphPtr->plotBW, graphPtr->plotRelief);
+
+ /* Draw the elements, markers, legend, and axis limits. */
+ Blt_DrawAxes(graphPtr, drawable);
+ Blt_DrawGrids(graphPtr, drawable);
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_UNDER);
+
+ site = Blt_Legend_Site(graphPtr);
+ if ((site & LEGEND_PLOTAREA_MASK) && (!Blt_Legend_IsRaised(graphPtr))) {
+ Blt_DrawLegend(graphPtr, drawable);
+ } else if (site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ Blt_DrawAxisLimits(graphPtr, drawable);
+ Blt_DrawElements(graphPtr, drawable);
+ /* Blt_DrawAxes(graphPtr, drawable); */
+}
+
+void
+Blt_MapGraph(Graph *graphPtr)
+{
+ if (graphPtr->flags & RESET_AXES) {
+ Blt_ResetAxes(graphPtr);
+ }
+ if (graphPtr->flags & LAYOUT_NEEDED) {
+ Blt_LayoutGraph(graphPtr);
+ graphPtr->flags &= ~LAYOUT_NEEDED;
+ }
+ /* Compute coordinate transformations for graph components */
+ if ((graphPtr->vRange > 1) && (graphPtr->hRange > 1)) {
+ if (graphPtr->flags & MAP_WORLD) {
+ Blt_MapAxes(graphPtr);
+ }
+ Blt_MapElements(graphPtr);
+ Blt_MapMarkers(graphPtr);
+ graphPtr->flags &= ~(MAP_ALL);
+ }
+}
+
+void
+Blt_DrawGraph(Graph *graphPtr, Drawable drawable)
+{
+ DrawPlot(graphPtr, drawable);
+ /* Draw markers above elements */
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
+ Blt_DrawActiveElements(graphPtr, drawable);
+
+ /* Don't draw legend in the plot area. */
+ if ((Blt_Legend_Site(graphPtr) & LEGEND_PLOTAREA_MASK) &&
+ (Blt_Legend_IsRaised(graphPtr))) {
+ Blt_DrawLegend(graphPtr, drawable);
+ }
+ /* Draw 3D border just inside of the focus highlight ring. */
+ if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
+ Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable,
+ graphPtr->normalBg, graphPtr->highlightWidth,
+ graphPtr->highlightWidth,
+ graphPtr->width - 2 * graphPtr->highlightWidth,
+ graphPtr->height - 2 * graphPtr->highlightWidth,
+ graphPtr->borderWidth, graphPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & FOCUS)) {
+ GC gc;
+
+ gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
+ Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
+ drawable);
+ }
+}
+
+static void
+UpdateMarginTraces(Graph *graphPtr)
+{
+ Margin *marginPtr, *endPtr;
+
+ for (marginPtr = graphPtr->margins, endPtr = marginPtr + 4;
+ marginPtr < endPtr; marginPtr++) {
+ if (marginPtr->varName != NULL) { /* Trigger variable traces */
+ int size;
+
+ if ((marginPtr->site == MARGIN_LEFT) ||
+ (marginPtr->site == MARGIN_RIGHT)) {
+ size = marginPtr->width;
+ } else {
+ size = marginPtr->height;
+ }
+ Tcl_SetVar(graphPtr->interp, marginPtr->varName, Blt_Itoa(size),
+ TCL_GLOBAL_ONLY);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DisplayGraph --
+ *
+ * This procedure is invoked to display a graph widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the graph in its current mode.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DisplayGraph(ClientData clientData)
+{
+ Graph *graphPtr = clientData;
+ Pixmap drawable;
+ Tk_Window tkwin;
+ int site;
+
+ graphPtr->flags &= ~REDRAW_PENDING;
+ if (graphPtr->tkwin == NULL) {
+ return; /* Window has been destroyed (we
+ * should not get here) */
+ }
+ tkwin = graphPtr->tkwin;
+#ifdef notdef
+ fprintf(stderr, "Calling DisplayGraph(%s)\n", Tk_PathName(tkwin));
+#endif
+ if ((Tk_Width(tkwin) <= 1) || (Tk_Height(tkwin) <= 1)) {
+ /* Don't bother computing the layout until the size of the window is
+ * something reasonable. */
+ return;
+ }
+ graphPtr->width = Tk_Width(tkwin);
+ graphPtr->height = Tk_Height(tkwin);
+ Blt_MapGraph(graphPtr);
+ if (!Tk_IsMapped(tkwin)) {
+ /* The graph's window isn't displayed, so don't bother drawing
+ * anything. By getting this far, we've at least computed the
+ * coordinates of the graph's new layout. */
+ return;
+ }
+ /* Create a pixmap the size of the window for double buffering. */
+ if (graphPtr->doubleBuffer) {
+ drawable = Tk_GetPixmap(graphPtr->display, Tk_WindowId(tkwin),
+ graphPtr->width, graphPtr->height, Tk_Depth(tkwin));
+ } else {
+ drawable = Tk_WindowId(tkwin);
+ }
+ if (graphPtr->backingStore) {
+ if ((graphPtr->cache == None) ||
+ (graphPtr->cacheWidth != graphPtr->width) ||
+ (graphPtr->cacheHeight != graphPtr->height)) {
+ if (graphPtr->cache != None) {
+ Tk_FreePixmap(graphPtr->display, graphPtr->cache);
+ }
+
+
+ graphPtr->cache = Tk_GetPixmap(graphPtr->display,
+ Tk_WindowId(tkwin), graphPtr->width, graphPtr->height,
+ Tk_Depth(tkwin));
+ graphPtr->cacheWidth = graphPtr->width;
+ graphPtr->cacheHeight = graphPtr->height;
+ graphPtr->flags |= CACHE_DIRTY;
+ }
+ }
+#ifdef WIN32
+ assert(drawable != None);
+#endif
+ if (graphPtr->backingStore) {
+ if (graphPtr->flags & CACHE_DIRTY) {
+ /* The backing store is new or out-of-date. */
+ DrawPlot(graphPtr, graphPtr->cache);
+ graphPtr->flags &= ~CACHE_DIRTY;
+ }
+ /* Copy the pixmap to the one used for drawing the entire graph. */
+ XCopyArea(graphPtr->display, graphPtr->cache, drawable,
+ graphPtr->drawGC, 0, 0, Tk_Width(graphPtr->tkwin),
+ Tk_Height(graphPtr->tkwin), 0, 0);
+ } else {
+ DrawPlot(graphPtr, drawable);
+ }
+ /* Draw markers above elements */
+ Blt_DrawMarkers(graphPtr, drawable, MARKER_ABOVE);
+ Blt_DrawActiveElements(graphPtr, drawable);
+ /* Don't draw legend in the plot area. */
+ site = Blt_Legend_Site(graphPtr);
+ if ((site & LEGEND_PLOTAREA_MASK) && (Blt_Legend_IsRaised(graphPtr))) {
+ Blt_DrawLegend(graphPtr, drawable);
+ }
+ if (site == LEGEND_WINDOW) {
+ Blt_Legend_EventuallyRedraw(graphPtr);
+ }
+ /* Draw 3D border just inside of the focus highlight ring. */
+ if ((graphPtr->borderWidth > 0) && (graphPtr->relief != TK_RELIEF_FLAT)) {
+ Blt_DrawBackgroundRectangle(graphPtr->tkwin, drawable,
+ graphPtr->normalBg, graphPtr->highlightWidth,
+ graphPtr->highlightWidth,
+ graphPtr->width - 2 * graphPtr->highlightWidth,
+ graphPtr->height - 2 * graphPtr->highlightWidth,
+ graphPtr->borderWidth, graphPtr->relief);
+ }
+ /* Draw focus highlight ring. */
+ if ((graphPtr->highlightWidth > 0) && (graphPtr->flags & FOCUS)) {
+ GC gc;
+
+ gc = Tk_GCForColor(graphPtr->highlightColor, drawable);
+ Tk_DrawFocusHighlight(graphPtr->tkwin, gc, graphPtr->highlightWidth,
+ drawable);
+ }
+ /* Disable crosshairs before redisplaying to the screen */
+ Blt_DisableCrosshairs(graphPtr);
+ XCopyArea(graphPtr->display, drawable, Tk_WindowId(tkwin),
+ graphPtr->drawGC, 0, 0, graphPtr->width, graphPtr->height, 0, 0);
+ Blt_EnableCrosshairs(graphPtr);
+ if (graphPtr->doubleBuffer) {
+ Tk_FreePixmap(graphPtr->display, drawable);
+ }
+ graphPtr->flags &= ~RESET_WORLD;
+ UpdateMarginTraces(graphPtr);
+}
+
+/*LINTLIBRARY*/
+int
+Blt_GraphCmdInitProc(Tcl_Interp *interp)
+{
+ static Blt_InitCmdSpec cmdSpecs[] = {
+ {"graph", GraphCmd,},
+ {"barchart", BarchartCmd,},
+ {"stripchart", StripchartCmd,},
+ };
+ return Blt_InitCmds(interp, "::blt", cmdSpecs, 3);
+}
+
+Graph *
+Blt_GetGraphFromWindowData(Tk_Window tkwin)
+{
+ Graph *graphPtr;
+
+ while (tkwin != NULL) {
+ graphPtr = (Graph *)Blt_GetWindowInstanceData(tkwin);
+ if (graphPtr != NULL) {
+ return graphPtr;
+ }
+ tkwin = Tk_Parent(tkwin);
+ }
+ return NULL;
+}
+
+int
+Blt_GraphType(Graph *graphPtr)
+{
+ switch (graphPtr->classId) {
+ case CID_ELEM_LINE:
+ return GRAPH;
+ case CID_ELEM_BAR:
+ return BARCHART;
+ case CID_ELEM_STRIP:
+ return STRIPCHART;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ReconfigureGraph --
+ *
+ * Allocates resources for the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ReconfigureGraph(Graph *graphPtr)
+{
+ ConfigureGraph(graphPtr);
+ Blt_ConfigureLegend(graphPtr);
+ Blt_ConfigureElements(graphPtr);
+ Blt_ConfigureAxes(graphPtr);
+ Blt_ConfigureMarkers(graphPtr);
+}
diff --git a/src/bltGraph.h b/src/bltGraph.h
new file mode 100644
index 0000000..62ab031
--- /dev/null
+++ b/src/bltGraph.h
@@ -0,0 +1,699 @@
+
+/*
+ * bltGraph.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_GRAPH_H
+#define _BLT_GRAPH_H
+
+#include "bltInt.h"
+#include "bltHash.h"
+#include "bltBind.h"
+#include "bltChain.h"
+#include "bltPs.h"
+#include "bltBgStyle.h"
+
+typedef struct _Graph Graph;
+typedef struct _Element Element;
+typedef struct _Legend Legend;
+
+typedef enum {
+ CID_NONE,
+ CID_AXIS_X,
+ CID_AXIS_Y,
+ CID_ELEM_BAR,
+ CID_ELEM_CONTOUR,
+ CID_ELEM_LINE,
+ CID_ELEM_STRIP,
+ CID_MARKER_BITMAP,
+ CID_MARKER_IMAGE,
+ CID_MARKER_LINE,
+ CID_MARKER_POLYGON,
+ CID_MARKER_TEXT,
+ CID_MARKER_WINDOW,
+ CID_LEGEND_ENTRY,
+} ClassId;
+
+typedef struct {
+ /* Generic fields common to all graph objects. */
+ ClassId classId; /* Class type of object. */
+ const char *name; /* Identifier to refer the object. */
+ const char *className; /* Class name of object. */
+
+ Graph *graphPtr; /* Graph containing of the object. */
+
+ const char **tags; /* Binding tags for the object. */
+} GraphObj;
+
+#include "bltGrAxis.h"
+#include "bltGrLegd.h"
+
+#define MARKER_UNDER 1 /* Draw markers designated to lie underneath
+ * elements, grids, legend, etc. */
+#define MARKER_ABOVE 0 /* Draw markers designated to rest above
+ * elements, grids, legend, etc. */
+
+#define PADX 2 /* Padding between labels/titles */
+#define PADY 2 /* Padding between labels */
+
+#define MINIMUM_MARGIN 20 /* Minimum margin size */
+
+
+#define BOUND(x, lo, hi) \
+ (((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x))
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Graph component structure definitions
+ *
+ *---------------------------------------------------------------------------
+ */
+#define PointInGraph(g,x,y) \
+ (((x) <= (g)->right) && ((x) >= (g)->left) && \
+ ((y) <= (g)->bottom) && ((y) >= (g)->top))
+
+/*
+ * Mask values used to selectively enable GRAPH or BARCHART entries in the
+ * various configuration specs.
+ */
+#define GRAPH (BLT_CONFIG_USER_BIT << 1)
+#define STRIPCHART (BLT_CONFIG_USER_BIT << 2)
+#define BARCHART (BLT_CONFIG_USER_BIT << 3)
+#define LINE_GRAPHS (GRAPH | STRIPCHART)
+#define ALL_GRAPHS (GRAPH | BARCHART | STRIPCHART)
+
+#define ACTIVE_PEN (BLT_CONFIG_USER_BIT << 16)
+#define NORMAL_PEN (BLT_CONFIG_USER_BIT << 17)
+#define ALL_PENS (NORMAL_PEN | ACTIVE_PEN)
+
+typedef struct {
+ Segment2d *segments;
+ int length;
+ int *map;
+} GraphSegments;
+
+typedef struct {
+ Point2d *points;
+ int length;
+ int *map;
+} GraphPoints;
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BarGroup --
+ *
+ * Represents a sets of bars with the same abscissa. This structure is
+ * used to track the display of the bars in the group.
+ *
+ * Each unique abscissa has at least one group. Groups can be
+ * defined by the bar element's -group option. Multiple groups are
+ * needed when you are displaying/comparing similar sets of data (same
+ * abscissas) but belong to a separate group.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ int nSegments; /* Number of occurrences of
+ * x-coordinate */
+ Axis2d axes; /* The axes associated with this
+ * group. (mapped to the x-value) */
+ float sum; /* Sum of the ordinates (y-coorinate) of
+ * each duplicate abscissa. Used to
+ * determine height of stacked bars. */
+ int count; /* Current number of bars seen. Used to
+ * position of the next bar in the
+ * group. */
+ float lastY; /* y-cooridinate position of the
+ * last bar seen. */
+ size_t index; /* Order of group in set (an unique
+ * abscissa may have more than one
+ * group). */
+} BarGroup;
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BarSetKey --
+ *
+ * Key for hash table of set of bars. The bar set is defined by
+ * coordinates with the same abscissa (x-coordinate).
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ float value; /* Duplicated abscissa */
+ Axis2d axes; /* Axis mapping of element */
+} BarSetKey;
+
+/*
+ * BarModes --
+ *
+ * Bar elements are displayed according to their x-y coordinates. If two
+ * bars have the same abscissa (x-coordinate), the bar segments will be
+ * drawn according to one of the following modes:
+ */
+
+typedef enum BarModes {
+ BARS_INFRONT, /* Each successive bar in a group is
+ * drawn in front of the previous. */
+ BARS_STACKED, /* Each successive bar in a group is
+ * drawn stacked on top of the previous
+ * bar. */
+ BARS_ALIGNED, /* Each successive bar in a group is
+ * drawn aligned side-by-side to the
+ * previous from right-to-left. */
+ BARS_OVERLAP /* Like "aligned", each successive bar
+ * in a group is drawn from
+ * right-to-left. The bars will overlap
+ * each other by ~50%. */
+} BarMode;
+
+typedef struct _Pen Pen;
+typedef struct _Marker Marker;
+
+typedef Pen *(PenCreateProc)(void);
+typedef int (PenConfigureProc)(Graph *graphPtr, Pen *penPtr);
+typedef void (PenDestroyProc)(Graph *graphPtr, Pen *penPtr);
+
+struct _Pen {
+ const char *name; /* Pen style identifier. If NULL pen
+ * was statically allocated. */
+ ClassId classId; /* Type element using this pen. */
+ const char *typeId; /* String token identifying the type of
+ * pen. */
+ unsigned int flags; /* Indicates if the pen element is
+ * active or normal. */
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+
+ Blt_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+ Graph *graphPtr; /* Graph that the pen is associated
+ * with. */
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Crosshairs
+ *
+ * Contains the line segments positions and graphics context used to
+ * simulate crosshairs (by XOR-ing) on the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct _Crosshairs Crosshairs;
+
+typedef struct {
+ short int width, height; /* Dimensions of the margin */
+ short int axesOffset;
+ short int axesTitleLength; /* Width of the widest title to be
+ * shown. Multiple titles are displayed
+ * in another margin. This is the
+ * minimum space requirement. */
+ short int maxTickWidth;
+ short int maxTickHeight;
+ unsigned int nAxes; /* # of axes to be displayed */
+ Blt_Chain axes; /* Axes associated with this margin */
+ const char *varName; /* If non-NULL, name of variable to be
+ * updated when the margin size
+ * changes */
+ int reqSize; /* Requested size of margin */
+ int site; /* Indicates where margin is located:
+ * left, right, top, or bottom. */
+} Margin;
+
+#define MARGIN_NONE -1
+#define MARGIN_BOTTOM 0 /* x */
+#define MARGIN_LEFT 1 /* y */
+#define MARGIN_TOP 2 /* x2 */
+#define MARGIN_RIGHT 3 /* y2 */
+
+#define rightMargin margins[MARGIN_RIGHT]
+#define leftMargin margins[MARGIN_LEFT]
+#define topMargin margins[MARGIN_TOP]
+#define bottomMargin margins[MARGIN_BOTTOM]
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Graph --
+ *
+ * Top level structure containing everything pertaining to the graph.
+ *
+ *---------------------------------------------------------------------------
+ */
+struct _Graph {
+ unsigned int flags; /* Flags; see below for definitions. */
+ Tcl_Interp *interp; /* Interpreter associated with graph */
+ Tk_Window tkwin; /* Window that embodies the graph.
+ * NULL means that the window has been
+ * destroyed but the data structures
+ * haven't yet been cleaned up. */
+ Display *display; /* Display containing widget; used to
+ * release resources after tkwin has
+ * already gone away. */
+ Tcl_Command cmdToken; /* Token for graph's widget command. */
+ const char *data; /* This value isn't used in C code.
+ * It may be used in TCL bindings to
+ * associate extra data. */
+ Tk_Cursor cursor;
+ int inset; /* Sum of focus highlight and 3-D
+ * border. Indicates how far to
+ * offset the graph from outside edge
+ * of the window. */
+ int borderWidth; /* Width of the exterior border */
+ int relief; /* Relief of the exterior border. */
+ Blt_Background normalBg; /* 3-D border used to delineate the
+ * plot surface and outer edge of
+ * window. */
+ int highlightWidth; /* Width in pixels of highlight to
+ * draw around widget when it has the
+ * focus. <= 0 means don't draw a
+ * highlight. */
+ XColor *highlightBgColor; /* Color for drawing traversal
+ * highlight area when highlight is
+ * off. */
+ XColor *highlightColor; /* Color for drawing traversal
+ * highlight. */
+ const char *title; /* Graph title */
+ short int titleX, titleY; /* Position of title on graph. */
+ short int titleWidth, titleHeight; /* Dimensions of title. */
+ TextStyle titleTextStyle; /* Title attributes: font, color,
+ * etc.*/
+
+ const char *takeFocus; /* Not used in C code, indicates if
+ * widget should be included in focus
+ * traversal. */
+ Axis *focusPtr; /* The axis that currently has focus. */
+
+ int reqWidth, reqHeight; /* Requested size of graph window */
+ int reqPlotWidth, reqPlotHeight; /* Requested size of plot area. Zero
+ * means to adjust the dimension
+ * according to the available space
+ * left in the window. */
+ int width, height; /* Actual size (in pixels) of graph
+ * window or PostScript page. */
+ Blt_HashTable penTable; /* Table of pens */
+ struct Component {
+ Blt_HashTable table; /* Hash table of ids. */
+ Blt_Chain displayList; /* Display list. */
+ Blt_HashTable tagTable; /* Table of bind tags. */
+ } elements, markers, axes;
+
+ Blt_HashTable dataTables; /* Hash table of datatable clients. */
+ ClassId classId; /* Default element type */
+ Blt_BindTable bindTable;
+ int nextMarkerId; /* Tracks next marker identifier
+ * available */
+ Blt_Chain axisChain[4]; /* Chain of axes for each of the
+ * margins. They're separate from the
+ * margin structures to make it easier
+ * to invert the X-Y axes by simply
+ * switching chain pointers. */
+ Margin margins[4];
+ PageSetup *pageSetup; /* Page layout options: see bltGrPS.c */
+ Legend *legend; /* Legend information: see
+ * bltGrLegd.c */
+ Crosshairs *crosshairs; /* Crosshairs information: see
+ * bltGrHairs.c */
+ int halo; /* Maximum distance allowed between
+ * points when searching for a point */
+ int inverted; /* If non-zero, indicates the x and y
+ * axis positions should be inverted. */
+ int stackAxes; /* If non-zero, indicates to stack
+ * mulitple axes in a margin, rather
+ * than layering them one on top of
+ * another. */
+ GC drawGC; /* GC for drawing on the margins. This
+ * includes the axis lines */
+ int plotBW; /* Width of interior 3-D border. */
+ int plotRelief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+ Blt_Background plotBg; /* Color of plotting surface */
+
+ /* If non-zero, force plot to conform to aspect ratio W/H */
+ float aspect;
+
+ short int left, right; /* Coordinates of plot bbox */
+ short int top, bottom;
+
+ Blt_Pad xPad; /* Vertical padding for plotarea */
+ int vRange, vOffset; /* Vertical axis range and offset from
+ * the left side of the graph
+ * window. Used to transform coordinates
+ * to vertical axes. */
+ Blt_Pad yPad; /* Horizontal padding for plotarea */
+ int hRange, hOffset; /* Horizontal axis range and offset from
+ * the top of the graph window. Used to
+ * transform horizontal axes */
+ float vScale, hScale;
+
+ int doubleBuffer; /* If non-zero, draw the graph into a
+ * pixmap first to reduce flashing. */
+ int backingStore; /* If non-zero, cache elements by
+ * drawing them into a pixmap */
+ Pixmap cache; /* Pixmap used to cache elements
+ * displayed. If *backingStore* is
+ * non-zero, each element is drawn into
+ * this pixmap before it is copied onto
+ * the screen. The pixmap then acts as
+ * a cache (only the pixmap is
+ * redisplayed if the none of elements
+ * have changed). This is done so that
+ * markers can be redrawn quickly over
+ * elements without redrawing each
+ * element. */
+ short int cacheWidth, cacheHeight; /* Size of element backing store
+ * pixmap. */
+
+ /*
+ * barchart specific information
+ */
+ float baseline; /* Baseline from bar chart. */
+ float barWidth; /* Default width of each bar in graph
+ * units. The default width is 1.0
+ * units. */
+ BarMode mode; /* Mode describing how to display bars
+ * with the same x-coordinates. Mode can
+ * be "stacked", "aligned", "overlap",
+ * or "infront" */
+ BarGroup *barGroups; /* Contains information about duplicate
+ * x-values in bar elements (malloc-ed).
+ * This information can also be accessed
+ * by the group hash table */
+ int nBarGroups; /* # of entries in barGroups array. If
+ * zero, indicates nothing special
+ * needs to be * done for "stack" or
+ * "align" modes */
+ Blt_HashTable setTable; /* Table managing sets of bars with
+ * the same abscissas. The bars in a
+ * set may be displayed is various
+ * ways: aligned, overlap, infront, or
+ * stacked. */
+ int maxBarSetSize;
+ const char *dataCmd; /* New data callback? */
+
+};
+
+/*
+ * Bit flags definitions:
+ *
+ * All kinds of state information kept here. All these things happen
+ * when the window is available to draw into (DisplayGraph). Need the
+ * window width and height before we can calculate graph layout (i.e. the
+ * screen coordinates of the axes, elements, titles, etc). But we want to
+ * do this only when we have to, not every time the graph is redrawn.
+ *
+ * Same goes for maintaining a pixmap to double buffer graph elements.
+ * Need to mark when the pixmap needs to updated.
+ *
+ *
+ * MAP_ITEM Indicates that the element/marker/axis
+ * configuration has changed such that
+ * its layout of the item (i.e. its
+ * position in the graph window) needs
+ * to be recalculated.
+ *
+ * MAP_ALL Indicates that the layout of the axes and
+ * all elements and markers and the graph need
+ * to be recalculated. Otherwise, the layout
+ * of only those markers and elements that
+ * have changed will be reset.
+ *
+ * GET_AXIS_GEOMETRY Indicates that the size of the axes needs
+ * to be recalculated.
+ *
+ * RESET_AXES Flag to call to Blt_ResetAxes routine.
+ * This routine recalculates the scale offset
+ * (used for mapping coordinates) of each axis.
+ * If an axis limit has changed, then it sets
+ * flags to re-layout and redraw the entire
+ * graph. This needs to happend before the axis
+ * can compute transformations between graph and
+ * screen coordinates.
+ *
+ * LAYOUT_NEEDED
+ *
+ * CACHE_DIRTY If set, redraw all elements into the pixmap
+ * used for buffering elements.
+ *
+ * REDRAW_PENDING Non-zero means a DoWhenIdle handler has
+ * already been queued to redraw this window.
+ *
+ * DRAW_LEGEND Non-zero means redraw the legend. If this is
+ * the only DRAW_* flag, the legend display
+ * routine is called instead of the graph
+ * display routine.
+ *
+ * DRAW_MARGINS Indicates that the margins bordering
+ * the plotting area need to be redrawn.
+ * The possible reasons are:
+ *
+ * 1) an axis configuration changed
+ * 2) an axis limit changed
+ * 3) titles have changed
+ * 4) window was resized.
+ *
+ * GRAPH_FOCUS
+ */
+
+#define HIDE (1<<0) /* 0x0001 */
+#define DELETE_PENDING (1<<1) /* 0x0002 */
+#define REDRAW_PENDING (1<<2) /* 0x0004 */
+#define ACTIVE_PENDING (1<<3) /* 0x0008 */
+#define MAP_ITEM (1<<4) /* 0x0010 */
+#define DIRTY (1<<5) /* 0x0020 */
+#define ACTIVE (1<<6) /* 0x0040 */
+#define FOCUS (1<<7) /* 0x0080 */
+
+#define MAP_ALL (1<<8) /* 0x0100 */
+#define LAYOUT_NEEDED (1<<9) /* 0x0200 */
+#define RESET_AXES (1<<10)/* 0x0400 */
+#define GET_AXIS_GEOMETRY (1<<11)/* 0x0800 */
+
+#define DRAW_LEGEND (1<<12)/* 0x1000 */
+#define DRAW_MARGINS (1<<13)/* 0x2000 */
+#define CACHE_DIRTY (1<<14)/* 0x4000 */
+#define REQ_BACKING_STORE (1<<15)/* 0x8000 */
+#define UNMAP_HIDDEN (1<<16)
+
+#define MAP_WORLD (MAP_ALL|RESET_AXES|GET_AXIS_GEOMETRY)
+#define REDRAW_WORLD (DRAW_LEGEND)
+#define RESET_WORLD (REDRAW_WORLD | MAP_WORLD)
+
+/*
+ * ---------------------- Forward declarations ------------------------
+ */
+
+BLT_EXTERN int Blt_CreatePageSetup(Graph *graphPtr);
+
+BLT_EXTERN int Blt_CreateCrosshairs(Graph *graphPtr);
+
+BLT_EXTERN double Blt_InvHMap(Axis *axisPtr, double x);
+
+BLT_EXTERN double Blt_InvVMap(Axis *axisPtr, double x);
+
+BLT_EXTERN double Blt_HMap(Axis *axisPtr, double x);
+
+BLT_EXTERN double Blt_VMap(Axis *axisPtr, double y);
+
+BLT_EXTERN Point2d Blt_InvMap2D(Graph *graphPtr, double x, double y,
+ Axis2d *pairPtr);
+
+BLT_EXTERN Point2d Blt_Map2D(Graph *graphPtr, double x, double y,
+ Axis2d *pairPtr);
+
+BLT_EXTERN Graph *Blt_GetGraphFromWindowData(Tk_Window tkwin);
+
+BLT_EXTERN void Blt_AdjustAxisPointers(Graph *graphPtr);
+
+BLT_EXTERN int Blt_PolyRectClip(Region2d *extsPtr, Point2d *inputPts,
+ int nInputPts, Point2d *outputPts);
+
+BLT_EXTERN void Blt_ComputeBarStacks(Graph *graphPtr);
+
+BLT_EXTERN void Blt_ConfigureCrosshairs(Graph *graphPtr);
+BLT_EXTERN void Blt_ConfigureLegend(Graph *graphPtr);
+BLT_EXTERN void Blt_ConfigureElements(Graph *graphPtr);
+BLT_EXTERN void Blt_ConfigureAxes(Graph *graphPtr);
+BLT_EXTERN void Blt_ConfigureMarkers(Graph *graphPtr);
+BLT_EXTERN void Blt_ReconfigureGraph(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyAxes(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyCrosshairs(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyElements(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyMarkers(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyPageSetup(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DrawAxes(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_DrawAxisLimits(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_DrawElements(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_DrawActiveElements(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_DrawGraph(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_DrawMarkers(Graph *graphPtr, Drawable drawable, int under);
+
+BLT_EXTERN void Blt_Draw2DSegments(Display *display, Drawable drawable, GC gc,
+ Segment2d *segments, int nSegments);
+
+BLT_EXTERN int Blt_GetCoordinate(Tcl_Interp *interp, const char *string,
+ double *valuePtr);
+
+BLT_EXTERN void Blt_InitBarSetTable(Graph *graphPtr);
+
+BLT_EXTERN void Blt_LayoutGraph(Graph *graphPtr);
+
+BLT_EXTERN void Blt_EventuallyRedrawGraph(Graph *graphPtr);
+
+BLT_EXTERN void Blt_ResetAxes(Graph *graphPtr);
+
+BLT_EXTERN void Blt_ResetBarGroups(Graph *graphPtr);
+
+BLT_EXTERN void Blt_GraphExtents(Graph *graphPtr, Region2d *extsPtr);
+
+BLT_EXTERN void Blt_DisableCrosshairs(Graph *graphPtr);
+
+BLT_EXTERN void Blt_EnableCrosshairs(Graph *graphPtr);
+
+BLT_EXTERN void Blt_MapGraph(Graph *graphPtr);
+
+BLT_EXTERN void Blt_MapAxes(Graph *graphPtr);
+
+BLT_EXTERN void Blt_MapElements(Graph *graphPtr);
+
+BLT_EXTERN void Blt_MapMarkers(Graph *graphPtr);
+
+BLT_EXTERN void Blt_UpdateCrosshairs(Graph *graphPtr);
+
+BLT_EXTERN void Blt_DestroyPens(Graph *graphPtr);
+
+BLT_EXTERN int Blt_GetPenFromObj(Tcl_Interp *interp, Graph *graphPtr,
+ Tcl_Obj *objPtr, ClassId classId, Pen **penPtrPtr);
+
+BLT_EXTERN Pen *Blt_BarPen(const char *penName);
+
+BLT_EXTERN Pen *Blt_LinePen(const char *penName);
+
+BLT_EXTERN Pen *Blt_CreatePen(Graph *graphPtr, const char *penName,
+ ClassId classId, int objc, Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_InitLinePens(Graph *graphPtr);
+
+BLT_EXTERN int Blt_InitBarPens(Graph *graphPtr);
+
+BLT_EXTERN void Blt_FreePen(Pen *penPtr);
+
+BLT_EXTERN int Blt_VirtualAxisOp(Graph *graphPtr, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_AxisOp(Tcl_Interp *interp, Graph *graphPtr, int margin,
+ int objc, Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_ElementOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv, ClassId classId);
+
+BLT_EXTERN int Blt_CrosshairsOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_MarkerOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_PenOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_PointInPolygon(Point2d *samplePtr, Point2d *screenPts,
+ int nScreenPts);
+
+BLT_EXTERN int Blt_RegionInPolygon(Region2d *extsPtr, Point2d *points,
+ int nPoints, int enclosed);
+
+BLT_EXTERN int Blt_PointInSegments(Point2d *samplePtr, Segment2d *segments,
+ int nSegments, double halo);
+
+BLT_EXTERN int Blt_PostScriptOp(Graph *graphPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+BLT_EXTERN int Blt_GraphUpdateNeeded(Graph *graphPtr);
+
+BLT_EXTERN int Blt_DefaultAxes(Graph *graphPtr);
+
+BLT_EXTERN Axis *Blt_GetFirstAxis(Blt_Chain chain);
+
+BLT_EXTERN void Blt_UpdateAxisBackgrounds(Graph *graphPtr);
+
+BLT_EXTERN Marker *Blt_NearestMarker(Graph *graphPtr, int x, int y, int under);
+
+BLT_EXTERN Axis *Blt_NearestAxis(Graph *graphPtr, int x, int y);
+
+typedef ClientData (MakeTagProc)(Graph *graphPtr, const char *tagName);
+
+BLT_EXTERN MakeTagProc Blt_MakeElementTag;
+BLT_EXTERN MakeTagProc Blt_MakeMarkerTag;
+BLT_EXTERN MakeTagProc Blt_MakeAxisTag;
+BLT_EXTERN Blt_BindTagProc Blt_GraphTags;
+BLT_EXTERN Blt_BindTagProc Blt_AxisTags;
+
+BLT_EXTERN int Blt_GraphType(Graph *graphPtr);
+
+BLT_EXTERN void Blt_UpdateGraph(ClientData clientData);
+
+BLT_EXTERN void Blt_GraphSetObjectClass(GraphObj *graphObjPtr,ClassId classId);
+
+BLT_EXTERN void Blt_MarkersToPostScript(Graph *graphPtr, Blt_Ps ps, int under);
+BLT_EXTERN void Blt_ElementsToPostScript(Graph *graphPtr, Blt_Ps ps);
+BLT_EXTERN void Blt_ActiveElementsToPostScript(Graph *graphPtr, Blt_Ps ps);
+BLT_EXTERN void Blt_LegendToPostScript(Graph *graphPtr, Blt_Ps ps);
+BLT_EXTERN void Blt_AxesToPostScript(Graph *graphPtr, Blt_Ps ps);
+BLT_EXTERN void Blt_AxisLimitsToPostScript(Graph *graphPtr, Blt_Ps ps);
+
+BLT_EXTERN Element *Blt_LineElement(Graph *graphPtr, const char *name,
+ ClassId classId);
+BLT_EXTERN Element *Blt_BarElement(Graph *graphPtr, const char *name,
+ ClassId classId);
+
+BLT_EXTERN void Blt_DrawGrids(Graph *graphPtr, Drawable drawable);
+
+BLT_EXTERN void Blt_GridsToPostScript(Graph *graphPtr, Blt_Ps ps);
+BLT_EXTERN void Blt_InitBarSetTable(Graph *graphPtr);
+BLT_EXTERN void Blt_DestroyBarSets(Graph *graphPtr);
+
+/* ---------------------- Global declarations ------------------------ */
+
+BLT_EXTERN const char *Blt_GraphClassName(ClassId classId);
+
+#endif /* _BLT_GRAPH_H */
diff --git a/src/bltInt.C b/src/bltInt.C
new file mode 100644
index 0000000..ac4640c
--- /dev/null
+++ b/src/bltInt.C
@@ -0,0 +1,375 @@
+
+/*
+ * bltCoreInit.c --
+ *
+ * This module initials the non-Tk command of the BLT toolkit, registering the
+ * commands with the TCL interpreter.
+ *
+ * Copyright 1991-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include "bltNsUtil.h"
+#include "bltArrayObj.h"
+#include "bltMath.h"
+
+#ifndef BLT_LIBRARY
+# ifdef WIN32
+# define BLT_LIBRARY "c:/Program Files/Tcl/lib/blt"##BLT_VERSION
+# else
+# define BLT_LIBRARY "unknown"
+# endif
+#endif
+
+#if (_TCL_VERSION >= _VERSION(8,5,0))
+#define TCL_VERSION_LOADED TCL_PATCH_LEVEL
+#else
+#define TCL_VERSION_LOADED TCL_VERSION
+#endif
+
+static double bltNaN;
+
+BLT_EXTERN Tcl_AppInitProc Blt_core_Init;
+BLT_EXTERN Tcl_AppInitProc Blt_core_SafeInit;
+
+static Tcl_MathProc MinMathProc, MaxMathProc;
+static char libPath[1024] =
+{
+ BLT_LIBRARY
+};
+
+/*
+ * Script to set the BLT library path in the variable global "blt_library"
+ *
+ * Checks the usual locations for a file (bltGraph.pro) from the BLT library.
+ * The places searched in order are
+ *
+ * $BLT_LIBRARY
+ * $BLT_LIBRARY/blt2.4
+ * $BLT_LIBRARY/..
+ * $BLT_LIBRARY/../blt2.4
+ * $blt_libPath
+ * $blt_libPath/blt2.4
+ * $blt_libPath/..
+ * $blt_libPath/../blt2.4
+ * $tcl_library
+ * $tcl_library/blt2.4
+ * $tcl_library/..
+ * $tcl_library/../blt2.4
+ * $env(TCL_LIBRARY)
+ * $env(TCL_LIBRARY)/blt2.4
+ * $env(TCL_LIBRARY)/..
+ * $env(TCL_LIBRARY)/../blt2.4
+ *
+ * The TCL variable "blt_library" is set to the discovered path. If the file
+ * wasn't found, no error is returned. The actual usage of $blt_library is
+ * purposely deferred so that it can be set from within a script.
+ */
+
+/* FIXME: Change this to a namespace procedure in 3.0 */
+
+static char initScript[] =
+{"\n\
+global blt_library blt_libPath blt_version tcl_library env\n\
+set blt_library {}\n\
+set path {}\n\
+foreach var { env(BLT_LIBRARY) blt_libPath tcl_library env(TCL_LIBRARY) } { \n\
+ if { ![info exists $var] } { \n\
+ continue \n\
+ } \n\
+ set path [set $var] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file join $path blt$blt_version ] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file dirname [set $var]] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+ set path [file join $path blt$blt_version ] \n\
+ if { [file readable [file join $path bltGraph.pro]] } { \n\
+ set blt_library $path\n\
+ break \n\
+ } \n\
+} \n\
+if { $blt_library != \"\" } { \n\
+ global auto_path \n\
+ lappend auto_path $blt_library \n\
+}\n\
+unset var path\n\
+\n"
+};
+
+
+static Tcl_AppInitProc *cmdProcs[] =
+{
+#ifndef NO_BASE64
+ Blt_Base64CmdInitProc,
+#endif
+#ifndef NO_BGEXEC
+ Blt_BgexecCmdInitProc,
+#endif
+#ifndef NO_PTYEXEC
+ Blt_PtyExecCmdInitProc,
+#endif
+#ifndef NO_CRC32
+ Blt_Crc32CmdInitProc,
+#endif
+#ifndef NO_CSV
+ Blt_CsvCmdInitProc,
+#endif
+#ifndef NO_DATATABLE
+ Blt_TableCmdInitProc,
+#endif
+#ifndef NO_DDE
+ Blt_DdeCmdInitProc,
+#endif
+#ifndef NO_DEBUG
+ Blt_DebugCmdInitProc,
+#endif
+#ifndef NO_SPLINE
+ Blt_SplineCmdInitProc,
+#endif
+#ifndef NO_TREE
+ Blt_TreeCmdInitProc,
+#endif
+#ifndef NO_VECTOR
+ Blt_VectorCmdInitProc,
+#endif
+#ifndef NO_WATCH
+ Blt_WatchCmdInitProc,
+#endif
+ (Tcl_AppInitProc *) NULL
+};
+
+double
+Blt_NaN(void)
+{
+ return bltNaN;
+}
+
+static double
+MakeNaN(void)
+{
+ union DoubleValue {
+ unsigned int words[2];
+ double value;
+ } result;
+
+#ifdef WORDS_BIGENDIAN
+ result.words[0] = 0x7ff80000;
+ result.words[1] = 0x00000000;
+#else
+ result.words[0] = 0x00000000;
+ result.words[1] = 0x7ff80000;
+#endif
+ return result.value;
+}
+
+/* ARGSUSED */
+static int
+MinMathProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tcl_Value *argsPtr,
+ Tcl_Value *resultPtr)
+{
+ Tcl_Value *op1Ptr, *op2Ptr;
+
+ op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+ if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+ resultPtr->intValue = MIN(op1Ptr->intValue, op2Ptr->intValue);
+ resultPtr->type = TCL_INT;
+ } else {
+ double a, b;
+
+ a = (op1Ptr->type == TCL_INT)
+ ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+ b = (op2Ptr->type == TCL_INT)
+ ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+ resultPtr->doubleValue = MIN(a, b);
+ resultPtr->type = TCL_DOUBLE;
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+MaxMathProc(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Not used. */
+ Tcl_Value *argsPtr,
+ Tcl_Value *resultPtr)
+{
+ Tcl_Value *op1Ptr, *op2Ptr;
+
+ op1Ptr = argsPtr, op2Ptr = argsPtr + 1;
+ if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) {
+ resultPtr->intValue = MAX(op1Ptr->intValue, op2Ptr->intValue);
+ resultPtr->type = TCL_INT;
+ } else {
+ double a, b;
+
+ a = (op1Ptr->type == TCL_INT)
+ ? (double)op1Ptr->intValue : op1Ptr->doubleValue;
+ b = (op2Ptr->type == TCL_INT)
+ ? (double)op2Ptr->intValue : op2Ptr->doubleValue;
+ resultPtr->doubleValue = MAX(a, b);
+ resultPtr->type = TCL_DOUBLE;
+ }
+ return TCL_OK;
+}
+
+static int
+SetLibraryPath(Tcl_Interp *interp)
+{
+ Tcl_DString dString;
+ const char *value;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, libPath, -1);
+#ifdef WIN32
+ {
+ HKEY key;
+ DWORD result;
+# ifndef BLT_REGISTRY_KEY
+# define BLT_REGISTRY_KEY "Software\\BLT\\" BLT_VERSION "\\" TCL_VERSION
+# endif
+ result = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE, /* Parent key. */
+ BLT_REGISTRY_KEY, /* Path to sub-key. */
+ 0, /* Reserved. */
+ KEY_READ, /* Security access mask. */
+ &key); /* Resulting key.*/
+
+ if (result == ERROR_SUCCESS) {
+ DWORD size;
+
+ /* Query once to get the size of the string needed */
+ result = RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL, NULL,
+ &size);
+ if (result == ERROR_SUCCESS) {
+ Tcl_DStringSetLength(&dString, size);
+ /* And again to collect the string. */
+ RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL,
+ (LPBYTE)Tcl_DStringValue(&dString), &size);
+ RegCloseKey(key);
+ }
+ }
+ }
+#endif /* WIN32 */
+ value = Tcl_SetVar(interp, "blt_libPath", Tcl_DStringValue(&dString),
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
+ Tcl_DStringFree(&dString);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+
+/*LINTLIBRARY*/
+int
+Blt_core_Init(Tcl_Interp *interp) /* Interpreter to add extra commands */
+{
+ Tcl_AppInitProc **p;
+ Tcl_Namespace *nsPtr;
+ Tcl_ValueType args[2];
+ const char *result;
+ const int isExact = 1;
+
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, TCL_VERSION_LOADED, isExact) == NULL) {
+ return TCL_ERROR;
+ };
+#endif
+ Blt_AllocInit(NULL, NULL, NULL);
+
+ /*
+ * Check that the versions of TCL that have been loaded are the same ones
+ * that BLT was compiled against.
+ */
+ if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION_LOADED, isExact) == NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Set the "blt_version", "blt_patchLevel", and "blt_libPath" Tcl
+ * variables. We'll use them in the following script. */
+
+ result = Tcl_SetVar(interp, "blt_version", BLT_VERSION, TCL_GLOBAL_ONLY);
+ if (result == NULL) {
+ return TCL_ERROR;
+ }
+ result = Tcl_SetVar(interp, "blt_patchLevel", BLT_PATCH_LEVEL,
+ TCL_GLOBAL_ONLY);
+ if (result == NULL) {
+ return TCL_ERROR;
+ }
+ if (SetLibraryPath(interp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_Eval(interp, initScript) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+
+ nsPtr = Tcl_FindNamespace(interp, "::blt", (Tcl_Namespace *)NULL, 0);
+ if (nsPtr == NULL) {
+ nsPtr = Tcl_CreateNamespace(interp, "::blt", NULL, NULL);
+ if (nsPtr == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ /* Initialize the BLT commands that only require Tcl. */
+ for (p = cmdProcs; *p != NULL; p++) {
+ if ((**p) (interp) != TCL_OK) {
+ Tcl_DeleteNamespace(nsPtr);
+ return TCL_ERROR;
+ }
+ }
+ args[0] = args[1] = TCL_EITHER;
+ Tcl_CreateMathFunc(interp, "min", 2, args, MinMathProc, (ClientData)0);
+ Tcl_CreateMathFunc(interp, "max", 2, args, MaxMathProc, (ClientData)0);
+ Blt_RegisterArrayObj();
+ bltNaN = MakeNaN();
+ if (Tcl_PkgProvide(interp, "blt_core", BLT_VERSION) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+int
+Blt_core_SafeInit(Tcl_Interp *interp) /* Interpreter to add extra commands */
+{
+ return Blt_core_Init(interp);
+}
+
+#ifdef USE_DLL
+# include "bltWinDll.c"
+#endif
diff --git a/src/bltNsUtil.C b/src/bltNsUtil.C
new file mode 100644
index 0000000..62f727d
--- /dev/null
+++ b/src/bltNsUtil.C
@@ -0,0 +1,174 @@
+
+/*
+ * bltNsUtil.c --
+ *
+ * This module implements utility namespace procedures for the BLT toolkit.
+ *
+ * Copyright 1997-2008 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include "bltNsUtil.h"
+
+/*
+ * A Command structure exists for each command in a namespace. The Tcl_Command
+ * opaque type actually refers to these structures.
+ */
+
+typedef struct CompileProc CompileProc;
+typedef struct ImportRef ImportRef;
+typedef struct CommandTrace CommandTrace;
+
+typedef struct {
+ Tcl_HashEntry *hPtr; /* Pointer to the hash table entry that refers
+ * to this command. The hash table is either a
+ * namespace's command table or an
+ * interpreter's hidden command table. This
+ * pointer is used to get a command's name
+ * from its Tcl_Command handle. NULL means
+ * that the hash table entry has been removed
+ * already (this can happen if deleteProc
+ * causes the command to be deleted or
+ * recreated). */
+ Tcl_Namespace *nsPtr; /* Points to the namespace containing this
+ * command. */
+ int refCount; /* 1 if in command hashtable plus 1 for each
+ * reference from a CmdName TCL object
+ * representing a command's name in a ByteCode
+ * instruction sequence. This structure can be
+ * freed when refCount becomes zero. */
+ int cmdEpoch; /* Incremented to invalidate any references
+ * that point to this command when it is
+ * renamed, deleted, hidden, or exposed. */
+ CompileProc *compileProc; /* Procedure called to compile command. NULL
+ * if no compile proc exists for command. */
+ Tcl_ObjCmdProc *objProc; /* Object-based command procedure. */
+ ClientData objClientData; /* Arbitrary value passed to object proc. */
+ Tcl_CmdProc *proc; /* String-based command procedure. */
+ ClientData clientData; /* Arbitrary value passed to string proc. */
+ Tcl_CmdDeleteProc *deleteProc;
+ /* Procedure invoked when deleting command
+ * to, e.g., free all client data. */
+ ClientData deleteData; /* Arbitrary value passed to deleteProc. */
+ int flags; /* Means that the command is in the process of
+ * being deleted (its deleteProc is currently
+ * executing). Other attempts to delete the
+ * command should be ignored. */
+ ImportRef *importRefPtr; /* List of each imported Command created in
+ * another namespace when this command is
+ * imported. These imported commands redirect
+ * invocations back to this command. The list
+ * is used to remove all those imported
+ * commands when deleting this "real"
+ * command. */
+ CommandTrace *tracePtr; /* First in list of all traces set for this
+ * command. */
+} Command;
+
+/*ARGSUSED*/
+Tcl_Namespace *
+Blt_GetCommandNamespace(Tcl_Command cmdToken)
+{
+ Command *cmdPtr = (Command *)cmdToken;
+
+ return (Tcl_Namespace *)cmdPtr->nsPtr;
+}
+
+Tcl_CallFrame *
+Blt_EnterNamespace(Tcl_Interp *interp, Tcl_Namespace *nsPtr)
+{
+ Tcl_CallFrame *framePtr;
+
+ framePtr = Blt_AssertMalloc(sizeof(Tcl_CallFrame));
+ if (Tcl_PushCallFrame(interp, framePtr, (Tcl_Namespace *)nsPtr, 0)
+ != TCL_OK) {
+ Blt_Free(framePtr);
+ return NULL;
+ }
+ return framePtr;
+}
+
+void
+Blt_LeaveNamespace(Tcl_Interp *interp, Tcl_CallFrame *framePtr)
+{
+ Tcl_PopCallFrame(interp);
+ Blt_Free(framePtr);
+}
+
+int
+Blt_ParseObjectName(Tcl_Interp *interp, const char *path,
+ Blt_ObjectName *namePtr, unsigned int flags)
+{
+ char *last, *colon;
+
+ namePtr->nsPtr = NULL;
+ namePtr->name = NULL;
+ colon = NULL;
+
+ /* Find the last namespace separator in the qualified name. */
+ last = (char *)(path + strlen(path));
+ while (--last > path) {
+ if ((*last == ':') && (*(last - 1) == ':')) {
+ last++; /* just after the last "::" */
+ colon = last - 2;
+ break;
+ }
+ }
+ if (colon == NULL) {
+ namePtr->name = path;
+ if ((flags & BLT_NO_DEFAULT_NS) == 0) {
+ namePtr->nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ return TRUE; /* No namespace designated in name. */
+ }
+
+ /* Separate the namespace and the object name. */
+ *colon = '\0';
+ if (path[0] == '\0') {
+ namePtr->nsPtr = Tcl_GetGlobalNamespace(interp);
+ } else {
+ namePtr->nsPtr = Tcl_FindNamespace(interp, (char *)path, NULL,
+ (flags & BLT_NO_ERROR_MSG) ? 0 : TCL_LEAVE_ERR_MSG);
+ }
+ /* Repair the string. */ *colon = ':';
+
+ if (namePtr->nsPtr == NULL) {
+ return FALSE; /* Namespace doesn't exist. */
+ }
+ namePtr->name =last;
+ return TRUE;
+}
+
+char *
+Blt_MakeQualifiedName(Blt_ObjectName *namePtr, Tcl_DString *resultPtr)
+{
+ Tcl_DStringInit(resultPtr);
+ if ((namePtr->nsPtr->fullName[0] != ':') ||
+ (namePtr->nsPtr->fullName[1] != ':') ||
+ (namePtr->nsPtr->fullName[2] != '\0')) {
+ Tcl_DStringAppend(resultPtr, namePtr->nsPtr->fullName, -1);
+ }
+ Tcl_DStringAppend(resultPtr, "::", -1);
+ Tcl_DStringAppend(resultPtr, (char *)namePtr->name, -1);
+ return Tcl_DStringValue(resultPtr);
+}
+
diff --git a/src/bltNsUtil.h b/src/bltNsUtil.h
new file mode 100644
index 0000000..00769f3
--- /dev/null
+++ b/src/bltNsUtil.h
@@ -0,0 +1,101 @@
+/*
+ * bltNsUtil.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef BLT_NS_UTIL_H
+#define BLT_NS_UTIL_H 1
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define NS_SEARCH_NONE (0)
+#define NS_SEARCH_CURRENT (1<<0)
+#define NS_SEARCH_GLOBAL (1<<1)
+#define NS_SEARCH_BOTH (NS_SEARCH_GLOBAL | NS_SEARCH_CURRENT)
+
+typedef struct {
+ const char *name;
+ Tcl_Namespace *nsPtr;
+} Blt_ObjectName;
+
+#define BLT_NO_DEFAULT_NS (1<<0)
+#define BLT_NO_ERROR_MSG (1<<1)
+
+#ifndef USE_TCL_STUBS
+
+BLT_EXTERN Tcl_Command Tcl_FindCommand(Tcl_Interp *interp, const char *name,
+ Tcl_Namespace *nsPtr, int flags);
+/*
+ * Namespace procedures not prototyped defined in Tcl.h
+ */
+BLT_EXTERN Tcl_Namespace *Tcl_GetCurrentNamespace(Tcl_Interp *interp);
+
+BLT_EXTERN Tcl_Namespace *Tcl_GetGlobalNamespace(Tcl_Interp *interp);
+
+BLT_EXTERN Tcl_Namespace *Tcl_CreateNamespace(Tcl_Interp *interp,
+ const char *name, ClientData clientData,
+ Tcl_NamespaceDeleteProc *nsDelProc);
+
+BLT_EXTERN void Tcl_DeleteNamespace(Tcl_Namespace *nsPtr);
+
+BLT_EXTERN Tcl_Namespace *Tcl_FindNamespace(Tcl_Interp *interp,
+ const char *name, Tcl_Namespace *context, int flags);
+
+BLT_EXTERN int Tcl_Export(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
+ const char *name, int resetFlag);
+
+BLT_EXTERN Tcl_Var Tcl_FindNamespaceVar(Tcl_Interp *interp, const char *name,
+ Tcl_Namespace *contextNsPtr, int flags);
+
+BLT_EXTERN void Tcl_PopCallFrame(Tcl_Interp *interp);
+
+BLT_EXTERN int Tcl_PushCallFrame(Tcl_Interp *interp, Tcl_CallFrame *framePtr,
+ Tcl_Namespace *nsPtr, int isProcCallFrame);
+
+#endif /* USE_TCL_STUBS */
+
+/*
+ * Auxillary procedures
+ */
+BLT_EXTERN Tcl_Namespace *Blt_GetVariableNamespace(Tcl_Interp *interp,
+ const char *varName);
+
+BLT_EXTERN Tcl_Namespace *Blt_GetCommandNamespace(Tcl_Command cmdToken);
+
+BLT_EXTERN Tcl_CallFrame *Blt_EnterNamespace(Tcl_Interp *interp,
+ Tcl_Namespace *nsPtr);
+
+BLT_EXTERN void Blt_LeaveNamespace(Tcl_Interp *interp, Tcl_CallFrame *framePtr);
+
+BLT_EXTERN int Blt_ParseObjectName(Tcl_Interp *interp, const char *name,
+ Blt_ObjectName *objNamePtr, unsigned int flags);
+
+BLT_EXTERN char *Blt_MakeQualifiedName(Blt_ObjectName *objNamePtr,
+ Tcl_DString *resultPtr);
+
+#endif /* BLT_NS_UTIL_H */
diff --git a/src/bltOp.h b/src/bltOp.h
new file mode 100644
index 0000000..5887a46
--- /dev/null
+++ b/src/bltOp.h
@@ -0,0 +1,37 @@
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_OpSpec --
+ *
+ * Structure to specify a set of operations for a TCL command.
+ * This is passed to the Blt_GetOp procedure to look
+ * for a function pointer associated with the operation name.
+ *
+ *---------------------------------------------------------------------------
+ */
+typedef struct {
+ const char *name; /* Name of operation */
+ int minChars; /* Minimum # characters to disambiguate */
+ void *proc;
+ int minArgs; /* Minimum # args required */
+ int maxArgs; /* Maximum # args required */
+ const char *usage; /* Usage message */
+} Blt_OpSpec;
+
+typedef enum {
+ BLT_OP_ARG0, /* Op is the first argument. */
+ BLT_OP_ARG1, /* Op is the second argument. */
+ BLT_OP_ARG2, /* Op is the third argument. */
+ BLT_OP_ARG3, /* Op is the fourth argument. */
+ BLT_OP_ARG4 /* Op is the fifth argument. */
+
+} Blt_OpIndex;
+
+#define BLT_OP_BINARY_SEARCH 0
+#define BLT_OP_LINEAR_SEARCH 1
+
+BLT_EXTERN void *Blt_GetOpFromObj(Tcl_Interp *interp, int nSpecs,
+ Blt_OpSpec *specs, int operPos, int objc, Tcl_Obj *const *objv,
+ int flags);
+
diff --git a/src/bltParse.C b/src/bltParse.C
new file mode 100644
index 0000000..10703f3
--- /dev/null
+++ b/src/bltParse.C
@@ -0,0 +1,546 @@
+
+/*
+ * bltParse.c --
+ *
+ * Contains a collection of procedures that are used to parse Tcl
+ * commands or parts of commands (like quoted strings or nested
+ * sub-commands).
+ *
+ * This file is copied from tclParse.c in the TCL library distribution.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of
+ * California.
+ *
+ * Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ *
+ */
+
+/*
+ * Since TCL 8.1.0 these routines have been replaced by ones that
+ * generate byte-codes. But since these routines are used in vector
+ * expressions, where no such byte-compilation is necessary, I now
+ * include them. In fact, the byte-compiled versions would be slower
+ * since the compiled code typically runs only one time.
+ */
+#include <bltInt.h>
+
+#include "bltParse.h"
+
+/*
+ * A table used to classify input characters to assist in parsing
+ * TCL commands. The table should be indexed with a signed character
+ * using the CHAR_TYPE macro. The character may have a negative
+ * value. The CHAR_TYPE macro takes a pointer to a signed character
+ * and a pointer to the last character in the source string. If the
+ * src pointer is pointing at the terminating null of the string,
+ * CHAR_TYPE returns TCL_COMMAND_END.
+ */
+
+#define STATIC_STRING_SPACE 150
+#define UCHAR(c) ((unsigned char) (c))
+#define TCL_NORMAL 0x01
+#define TCL_SPACE 0x02
+#define TCL_COMMAND_END 0x04
+#define TCL_QUOTE 0x08
+#define TCL_OPEN_BRACKET 0x10
+#define TCL_OPEN_BRACE 0x20
+#define TCL_CLOSE_BRACE 0x40
+#define TCL_BACKSLASH 0x80
+#define TCL_DOLLAR 0x00
+
+/*
+ * The following table assigns a type to each character. Only types
+ * meaningful to TCL parsing are represented here. The table is
+ * designed to be referenced with either signed or unsigned characters,
+ * so it has 384 entries. The first 128 entries correspond to negative
+ * character values, the next 256 correspond to positive character
+ * values. The last 128 entries are identical to the first 128. The
+ * table is always indexed with a 128-byte offset (the 128th entry
+ * corresponds to a 0 character value).
+ */
+
+static unsigned char tclTypeTable[] =
+{
+ /*
+ * Negative character values, from -128 to -1:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+ * Positive character values, from 0-127:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE,
+ TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL,
+ TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET,
+ TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE,
+ TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL,
+
+ /*
+ * Large unsigned character values, from 128-255:
+ */
+
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+ TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,
+};
+
+#define CHAR_TYPE(src,last) \
+ (((src)==(last))?TCL_COMMAND_END:(tclTypeTable+128)[(int)*(src)])
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ParseNestedCmd --
+ *
+ * This procedure parses a nested TCL command between
+ * brackets, returning the result of the command.
+ *
+ * Results:
+ * The return value is a standard TCL result, which is
+ * TCL_OK unless there was an error while executing the
+ * nested command. If an error occurs then interp->result
+ * contains a standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one processed; this is usually the character just
+ * after the matching close-bracket, or the null character
+ * at the end of the string if the close-bracket was missing
+ * (a missing close bracket is an error). The result returned
+ * by the command is stored in standard fashion in *parsePtr,
+ * null-terminated, with parsePtr->next pointing to the null
+ * character.
+ *
+ * Side effects:
+ * The storage space at *parsePtr may be expanded.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ParseNestedCmd(
+ Tcl_Interp *interp, /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ const char *string, /* Character just after opening bracket. */
+ int flags, /* Flags to pass to nested Tcl_Eval. */
+ const char **termPtr, /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr) /* Information about where to place
+ * result of command. */
+{
+ int result, length, shortfall;
+ Interp *iPtr = (Interp *)interp;
+
+ iPtr->evalFlags = flags | TCL_BRACKET_TERM;
+ result = Tcl_Eval(interp, string);
+ *termPtr = (string + iPtr->termOffset);
+ if (result != TCL_OK) {
+ /*
+ * The increment below results in slightly cleaner message in
+ * the errorInfo variable (the close-bracket will appear).
+ */
+
+ if (**termPtr == ']') {
+ *termPtr += 1;
+ }
+ return result;
+ }
+ (*termPtr) += 1;
+ length = (int)strlen(iPtr->result);
+ shortfall = length + 1 - (parsePtr->end - parsePtr->next);
+ if (shortfall > 0) {
+ (*parsePtr->expandProc) (parsePtr, shortfall);
+ }
+ strcpy(parsePtr->next, iPtr->result);
+ parsePtr->next += length;
+
+ Tcl_FreeResult(interp);
+ iPtr->result = iPtr->resultSpace;
+ iPtr->resultSpace[0] = '\0';
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ParseBraces --
+ *
+ * This procedure scans the information between matching
+ * curly braces.
+ *
+ * Results:
+ * The return value is a standard TCL result, which is
+ * TCL_OK unless there was an error while parsing string.
+ * If an error occurs then interp->result contains a
+ * standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one successfully processed; this is usually the
+ * character just after the matching close-brace. The
+ * information between curly braces is stored in standard
+ * fashion in *parsePtr, null-terminated with parsePtr->next
+ * pointing to the terminating null character.
+ *
+ * Side effects:
+ * The storage space at *parsePtr may be expanded.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Blt_ParseBraces(
+ Tcl_Interp *interp, /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ const char *string, /* Character just after opening bracket. */
+ const char **termPtr, /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr) /* Information about where to place
+ * result of command. */
+{
+ int level;
+ const char *src;
+ char *dest, *end;
+ char c;
+ const char *lastChar = string + strlen(string);
+
+ src = string;
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ level = 1;
+
+ /*
+ * Copy the characters one at a time to the result area, stopping
+ * when the matching close-brace is found.
+ */
+
+ for (;;) {
+ c = *src;
+ src++;
+
+ if (dest == end) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 20);
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ }
+ *dest = c;
+ dest++;
+
+ if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+ continue;
+ } else if (c == '{') {
+ level++;
+ } else if (c == '}') {
+ level--;
+ if (level == 0) {
+ dest--; /* Don't copy the last close brace. */
+ break;
+ }
+ } else if (c == '\\') {
+ int count;
+
+ /*
+ * Must always squish out backslash-newlines, even when in
+ * braces. This is needed so that this sequence can appear
+ * anywhere in a command, such as the middle of an expression.
+ */
+
+ if (*src == '\n') {
+ dest[-1] = Tcl_Backslash(src - 1, &count);
+ src += count - 1;
+ } else {
+ Tcl_Backslash(src - 1, &count);
+ while (count > 1) {
+ if (dest == end) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 20);
+ dest = parsePtr->next;
+ end = parsePtr->end;
+ }
+ *dest = *src;
+ dest++;
+ src++;
+ count--;
+ }
+ }
+ } else if (c == '\0') {
+ Tcl_AppendResult(interp, "missing close-brace", (char *)NULL);
+ *termPtr = string - 1;
+ return TCL_ERROR;
+ }
+ }
+
+ *dest = '\0';
+ parsePtr->next = dest;
+ *termPtr = src;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ExpandParseValue --
+ *
+ * This procedure is commonly used as the value of the
+ * expandProc in a ParseValue. It uses malloc to allocate
+ * more space for the result of a parse.
+ *
+ * Results:
+ * The buffer space in *parsePtr is reallocated to something
+ * larger, and if parsePtr->clientData is non-zero the old
+ * buffer is freed. Information is copied from the old
+ * buffer to the new one.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_ExpandParseValue(
+ ParseValue *parsePtr, /* Information about buffer that
+ * must be expanded. If the clientData
+ * in the structure is non-zero, it
+ * means that the current buffer is
+ * dynamically allocated. */
+ int needed) /* Minimum amount of additional space
+ * to allocate. */
+{
+ int size;
+ char *buffer;
+
+ /*
+ * Either double the size of the buffer or add enough new space
+ * to meet the demand, whichever produces a larger new buffer.
+ */
+ size = (parsePtr->end - parsePtr->buffer) + 1;
+ if (size < needed) {
+ size += needed;
+ } else {
+ size += size;
+ }
+ buffer = Blt_AssertMalloc((unsigned int)size);
+
+ /*
+ * Copy from old buffer to new, free old buffer if needed, and
+ * mark new buffer as malloc-ed.
+ */
+ memcpy((VOID *) buffer, (VOID *) parsePtr->buffer,
+ (size_t) (parsePtr->next - parsePtr->buffer));
+ parsePtr->next = buffer + (parsePtr->next - parsePtr->buffer);
+ if (parsePtr->clientData != 0) {
+ Blt_Free(parsePtr->buffer);
+ }
+ parsePtr->buffer = buffer;
+ parsePtr->end = buffer + size - 1;
+ parsePtr->clientData = (ClientData)1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ParseQuotes --
+ *
+ * This procedure parses a double-quoted string such as a
+ * quoted TCL command argument or a quoted value in a Tcl
+ * expression. This procedure is also used to parse array
+ * element names within parentheses, or anything else that
+ * needs all the substitutions that happen in quotes.
+ *
+ * Results:
+ * The return value is a standard TCL result, which is
+ * TCL_OK unless there was an error while parsing the
+ * quoted string. If an error occurs then interp->result
+ * contains a standard error message. *TermPtr is filled
+ * in with the address of the character just after the
+ * last one successfully processed; this is usually the
+ * character just after the matching close-quote. The
+ * fully-substituted contents of the quotes are stored in
+ * standard fashion in *parsePtr, null-terminated with
+ * parsePtr->next pointing to the terminating null character.
+ *
+ * Side effects:
+ * The buffer space in parsePtr may be enlarged by calling its
+ * expandProc.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ParseQuotes(
+ Tcl_Interp *interp, /* Interpreter to use for nested command
+ * evaluations and error messages. */
+ const char *string, /* Character just after opening double-
+ * quote. */
+ int termChar, /* Character that terminates "quoted" string
+ * (usually double-quote, but sometimes
+ * right-paren or something else). */
+ int flags, /* Flags to pass to nested Tcl_Eval calls. */
+ const char **termPtr, /* Store address of terminating character
+ * here. */
+ ParseValue *parsePtr) /* Information about where to place
+ * fully-substituted result of parse. */
+{
+ const char *src;
+ char *dest, c;
+ const char *lastChar = string + strlen(string);
+
+ src = string;
+ dest = parsePtr->next;
+
+ for (;;) {
+ if (dest == parsePtr->end) {
+ /*
+ * Target buffer space is about to run out. Make more space.
+ */
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, 1);
+ dest = parsePtr->next;
+ }
+ c = *src;
+ src++;
+ if (c == termChar) {
+ *dest = '\0';
+ parsePtr->next = dest;
+ *termPtr = src;
+ return TCL_OK;
+ } else if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) {
+ copy:
+ *dest = c;
+ dest++;
+ continue;
+ } else if (c == '$') {
+ int length;
+ const char *value;
+
+ value = Tcl_ParseVar(interp, src - 1, termPtr);
+ if (value == NULL) {
+ return TCL_ERROR;
+ }
+ src = *termPtr;
+ length = strlen(value);
+ if ((parsePtr->end - dest) <= length) {
+ parsePtr->next = dest;
+ (*parsePtr->expandProc) (parsePtr, length);
+ dest = parsePtr->next;
+ }
+ strcpy(dest, value);
+ dest += length;
+ continue;
+ } else if (c == '[') {
+ int result;
+
+ parsePtr->next = dest;
+ result = Blt_ParseNestedCmd(interp, src, flags, termPtr, parsePtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ src = *termPtr;
+ dest = parsePtr->next;
+ continue;
+ } else if (c == '\\') {
+ int nRead;
+
+ src--;
+ *dest = Tcl_Backslash(src, &nRead);
+ dest++;
+ src += nRead;
+ continue;
+ } else if (c == '\0') {
+ char buf[10];
+
+ Tcl_ResetResult(interp);
+ sprintf_s(buf, 10, "missing %c", termChar);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), buf, 9);
+ *termPtr = string - 1;
+ return TCL_ERROR;
+ } else {
+ goto copy;
+ }
+ }
+}
+
diff --git a/src/bltParse.h b/src/bltParse.h
new file mode 100644
index 0000000..3ff26cb
--- /dev/null
+++ b/src/bltParse.h
@@ -0,0 +1,43 @@
+
+/*
+ * bltParse.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_PARSE_H
+#define _BLT_PARSE_H
+
+#include "tclInterp.h"
+
+BLT_EXTERN int Blt_ParseBraces(Tcl_Interp *interp, const char *string,
+ const char **termPtr, ParseValue *pvPtr);
+
+BLT_EXTERN int Blt_ParseNestedCmd(Tcl_Interp *interp, const char *string,
+ int flags, const char **termPtr, ParseValue *pvPtr);
+
+BLT_EXTERN int Blt_ParseQuotes(Tcl_Interp *interp, const char *string,
+ int termChar, int flags, const char **termPtr, ParseValue * pvPtr);
+
+BLT_EXTERN void Blt_ExpandParseValue(ParseValue *pvPtr, int needed);
+
+#endif
diff --git a/src/bltSwitch.C b/src/bltSwitch.C
new file mode 100644
index 0000000..35228d1
--- /dev/null
+++ b/src/bltSwitch.C
@@ -0,0 +1,554 @@
+
+/*
+ * bltSwitch.c --
+ *
+ * This module implements command/argument switch parsing procedures for the
+ * BLT toolkit.
+ *
+ * Copyright 1991-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltInt.h"
+#include <stdarg.h>
+
+#include "bltSwitch.h"
+
+static void
+DoHelp(Tcl_Interp *interp, Blt_SwitchSpec *specs)
+{
+ Tcl_DString ds;
+ Blt_SwitchSpec *sp;
+
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppend(&ds, "following switches are available:", -1);
+ for (sp = specs; sp->type != BLT_SWITCH_END; sp++) {
+ Tcl_DStringAppend(&ds, "\n ", 4);
+ Tcl_DStringAppend(&ds, sp->switchName, -1);
+ Tcl_DStringAppend(&ds, " ", 1);
+ Tcl_DStringAppend(&ds, sp->help, -1);
+ }
+ Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL);
+ Tcl_DStringFree(&ds);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FindSwitchSpec --
+ *
+ * Search through a table of configuration specs, looking for one that
+ * matches a given argvName.
+ *
+ * Results:
+ * The return value is a pointer to the matching entry, or NULL if
+ * nothing matched. In that case an error message is left in the
+ * interp's result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Blt_SwitchSpec *
+FindSwitchSpec(
+ Tcl_Interp *interp, /* Used for reporting errors. */
+ Blt_SwitchSpec *specs, /* Pointer to table of configuration
+ * specifications for a widget. */
+ const char *name, /* Name identifying a particular switch. */
+ int length, /* Length of name. */
+ int needFlags, /* Flags that must be present in matching
+ * entry. */
+ int hateFlags) /* Flags that must NOT be present in matching
+ * entry. */
+{
+ Blt_SwitchSpec *sp;
+ char c; /* First character of current argument. */
+ Blt_SwitchSpec *matchPtr; /* Matching spec, or NULL. */
+
+ c = name[1];
+ matchPtr = NULL;
+ for (sp = specs; sp->type != BLT_SWITCH_END; sp++) {
+ if (sp->switchName == NULL) {
+ continue;
+ }
+ if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) {
+ continue;
+ }
+ if ((sp->switchName[1] != c) ||
+ (strncmp(sp->switchName, name, length) != 0)) {
+ continue;
+ }
+ if (sp->switchName[length] == '\0') {
+ return sp; /* Stop on a perfect match. */
+ }
+ if (matchPtr != NULL) {
+ Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n",
+ (char *) NULL);
+ DoHelp(interp, specs);
+ return NULL;
+ }
+ matchPtr = sp;
+ }
+ if (strcmp(name, "-help") == 0) {
+ DoHelp(interp, specs);
+ return NULL;
+ }
+ if (matchPtr == NULL) {
+ Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n",
+ (char *)NULL);
+ DoHelp(interp, specs);
+ return NULL;
+ }
+ return matchPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DoSwitch --
+ *
+ * This procedure applies a single configuration switch to a widget
+ * record.
+ *
+ * Results:
+ * A standard TCL return value.
+ *
+ * Side effects:
+ * WidgRec is modified as indicated by specPtr and value. The old value
+ * is recycled, if that is appropriate for the value type.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+DoSwitch(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Blt_SwitchSpec *sp, /* Specifier to apply. */
+ Tcl_Obj *objPtr, /* Value to use to fill in widgRec. */
+ void *record) /* Record whose fields are to be modified.
+ * Values must be properly initialized. */
+{
+ do {
+ char *ptr;
+
+ ptr = (char *)record + sp->offset;
+ switch (sp->type) {
+ case BLT_SWITCH_BOOLEAN:
+ {
+ int bool;
+
+ if (Tcl_GetBooleanFromObj(interp, objPtr, &bool) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (sp->mask > 0) {
+ if (bool) {
+ *((int *)ptr) |= sp->mask;
+ } else {
+ *((int *)ptr) &= ~sp->mask;
+ }
+ } else {
+ *((int *)ptr) = bool;
+ }
+ }
+ break;
+
+ case BLT_SWITCH_DOUBLE:
+ if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_OBJ:
+ Tcl_IncrRefCount(objPtr);
+ *(Tcl_Obj **)ptr = objPtr;
+ break;
+
+ case BLT_SWITCH_FLOAT:
+ {
+ double value;
+
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(float *)ptr = (float)value;
+ }
+ break;
+
+ case BLT_SWITCH_INT:
+ if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_INT_NNEG:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = (int)value;
+ }
+ break;
+
+ case BLT_SWITCH_INT_POS:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(int *)ptr = (int)value;
+ }
+ break;
+
+ case BLT_SWITCH_LIST:
+ {
+ int argc;
+
+ if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc,
+ (const char ***)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ break;
+
+ case BLT_SWITCH_LONG:
+ if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ case BLT_SWITCH_LONG_NNEG:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG,
+ &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(long *)ptr = value;
+ }
+ break;
+
+ case BLT_SWITCH_LONG_POS:
+ {
+ long value;
+
+ if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, &value)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *(long *)ptr = value;
+ }
+ break;
+
+ case BLT_SWITCH_STRING:
+ {
+ char *value;
+
+ value = Tcl_GetString(objPtr);
+ value = (*value == '\0') ? NULL : Blt_AssertStrdup(value);
+ if (*(char **)ptr != NULL) {
+ Blt_Free(*(char **)ptr);
+ }
+ *(char **)ptr = value;
+ }
+#ifdef notdef
+ {
+ char *old, *new, **strPtr;
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ strPtr = (char **)ptr;
+ new = ((*string == '\0') && (sp->flags & BLT_SWITCH_NULL_OK))
+ ? NULL : Blt_AssertStrdup(string);
+ old = *strPtr;
+ if (old != NULL) {
+ Blt_Free(old);
+ }
+ *strPtr = new;
+ }
+#endif
+ break;
+
+ case BLT_SWITCH_CUSTOM:
+ assert(sp->customPtr != NULL);
+ if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp,
+ sp->switchName, objPtr, (char *)record, sp->offset, sp->flags)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "bad switch table: unknown type \"",
+ Blt_Itoa(sp->type), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ sp++;
+ } while ((sp->switchName == NULL) && (sp->type != BLT_SWITCH_END));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ParseSwitches --
+ *
+ * Process command-line switches to fill in fields of a record with
+ * resources and other parameters.
+ *
+ * Results:
+ * Returns the number of arguments comsumed by parsing the command line.
+ * If an error occurred, -1 will be returned and an error messages can be
+ * found as the interpreter result.
+ *
+ * Side effects:
+ * The fields of widgRec get filled in with information from argc/argv.
+ * Old information in widgRec's fields gets recycled.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ParseSwitches(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Blt_SwitchSpec *specs, /* Describes legal switches. */
+ int objc, /* Number of elements in argv. */
+ Tcl_Obj *const *objv, /* Command-line switches. */
+ void *record, /* Record whose fields are to be modified.
+ * Values must be properly initialized. */
+ int flags) /* Used to specify additional flags that must
+ * be present in switch specs for them to be
+ * considered. */
+{
+ Blt_SwitchSpec *sp;
+ int count;
+ int needFlags; /* Specs must contain this set of flags or
+ * else they are not considered. */
+ int hateFlags; /* If a spec contains any bits here, it's not
+ * considered. */
+
+ needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1);
+ hateFlags = 0;
+
+ /*
+ * Pass 1: Clear the change flags on all the specs so that we
+ * can check it later.
+ */
+ for (sp = specs; sp->type != BLT_SWITCH_END; sp++) {
+ sp->flags &= ~BLT_SWITCH_SPECIFIED;
+ }
+ /*
+ * Pass 2: Process the arguments that match entries in the specs.
+ * It's an error if the argument doesn't match anything.
+ */
+ for (count = 0; count < objc; count++) {
+ char *arg;
+ int length;
+
+ arg = Tcl_GetStringFromObj(objv[count], &length);
+ if (flags & BLT_SWITCH_OBJV_PARTIAL) {
+ /*
+ * If the argument doesn't start with a '-' (not a switch) or is
+ * '--', stop processing and return the number of arguments
+ * comsumed.
+ */
+ if (arg[0] != '-') {
+ return count;
+ }
+ if ((arg[1] == '-') && (arg[2] == '\0')) {
+ return count + 1; /* include the "--" in the count. */
+ }
+ }
+ sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags);
+ if (sp == NULL) {
+ return -1;
+ }
+ if (sp->type == BLT_SWITCH_BITMASK) {
+ char *ptr;
+
+ ptr = (char *)record + sp->offset;
+ *((int *)ptr) |= sp->mask;
+ } else if (sp->type == BLT_SWITCH_BITMASK_INVERT) {
+ char *ptr;
+
+ ptr = (char *)record + sp->offset;
+ *((int *)ptr) &= ~sp->mask;
+ } else if (sp->type == BLT_SWITCH_VALUE) {
+ char *ptr;
+
+ ptr = (char *)record + sp->offset;
+ *((int *)ptr) = sp->mask;
+ } else {
+ count++;
+ if (count == objc) {
+ Tcl_AppendResult(interp, "value for \"", arg, "\" missing",
+ (char *) NULL);
+ return -1;
+ }
+ if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) {
+ char msg[200];
+
+ sprintf_s(msg, 200, "\n (processing \"%.40s\" switch)",
+ sp->switchName);
+ Tcl_AddErrorInfo(interp, msg);
+ return -1;
+ }
+ }
+ sp->flags |= BLT_SWITCH_SPECIFIED;
+ }
+ return count;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_FreeSwitches --
+ *
+ * Free up all resources associated with switches.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+void
+Blt_FreeSwitches(
+ Blt_SwitchSpec *specs, /* Describes legal switches. */
+ void *record, /* Record whose fields contain current values
+ * for switches. */
+ int needFlags) /* Used to specify additional flags that must
+ * be present in config specs for them to be
+ * considered. */
+{
+ Blt_SwitchSpec *sp;
+
+ for (sp = specs; sp->type != BLT_SWITCH_END; sp++) {
+ if ((sp->flags & needFlags) == needFlags) {
+ char *ptr;
+
+ ptr = (char *)record + sp->offset;
+ switch (sp->type) {
+ case BLT_SWITCH_STRING:
+ case BLT_SWITCH_LIST:
+ if (*((char **) ptr) != NULL) {
+ Blt_Free(*((char **) ptr));
+ *((char **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_SWITCH_OBJ:
+ if (*((Tcl_Obj **) ptr) != NULL) {
+ Tcl_DecrRefCount(*((Tcl_Obj **)ptr));
+ *((Tcl_Obj **) ptr) = NULL;
+ }
+ break;
+
+ case BLT_SWITCH_CUSTOM:
+ assert(sp->customPtr != NULL);
+ if ((*(char **)ptr != NULL) &&
+ (sp->customPtr->freeProc != NULL)) {
+ (*sp->customPtr->freeProc)((char *)record, sp->offset,
+ sp->flags);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_SwitchModified --
+ *
+ * Given the configuration specifications and one or more switch patterns
+ * (terminated by a NULL), indicate if any of the matching switches has
+ * been reset.
+ *
+ * Results:
+ * Returns 1 if one of the switches have changed, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_SwitchChanged TCL_VARARGS_DEF(Blt_SwitchSpec *, arg1)
+{
+ va_list argList;
+ Blt_SwitchSpec *specs;
+ Blt_SwitchSpec *sp;
+ char *switchName;
+
+ specs = TCL_VARARGS_START(Blt_SwitchSpec *, arg1, argList);
+ while ((switchName = va_arg(argList, char *)) != NULL) {
+ for (sp = specs; sp->type != BLT_SWITCH_END; sp++) {
+ if ((Tcl_StringMatch(sp->switchName, switchName)) &&
+ (sp->flags & BLT_SWITCH_SPECIFIED)) {
+ va_end(argList);
+ return 1;
+ }
+ }
+ }
+ va_end(argList);
+ return 0;
+}
+
+int
+Blt_ExprDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *valuePtr)
+{
+ /* First try to extract the value as a double precision number. */
+ if (Tcl_GetDoubleFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) {
+ return TCL_OK;
+ }
+ /* Then try to parse it as an expression. */
+ if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) {
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+int
+Blt_ExprIntFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ int *valuePtr)
+{
+ long lvalue;
+
+ /* First try to extract the value as a simple integer. */
+ if (Tcl_GetIntFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) {
+ return TCL_OK;
+ }
+ /* Otherwise try to parse it as an expression. */
+ if (Tcl_ExprLong(interp, Tcl_GetString(objPtr), &lvalue) == TCL_OK) {
+ *valuePtr = lvalue;
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
diff --git a/src/bltSwitch.h b/src/bltSwitch.h
new file mode 100644
index 0000000..603c73f
--- /dev/null
+++ b/src/bltSwitch.h
@@ -0,0 +1,136 @@
+
+/*
+ * bltSwitch.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef BLT_SWITCH_H
+#define BLT_SWITCH_H
+
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+
+#ifndef Blt_Offset
+#ifdef offsetof
+#define Blt_Offset(type, field) ((int) offsetof(type, field))
+#else
+#define Blt_Offset(type, field) ((int) ((char *) &((type *) 0)->field))
+#endif
+#endif /* Blt_Offset */
+
+typedef int (Blt_SwitchParseProc)(ClientData clientData, Tcl_Interp *interp,
+ const char *switchName, Tcl_Obj *valueObjPtr, char *record, int offset,
+ int flags);
+typedef void (Blt_SwitchFreeProc)(char *record, int offset, int flags);
+
+typedef struct {
+ Blt_SwitchParseProc *parseProc; /* Procedure to parse a switch
+ * value and store it in its *
+ * converted form in the data *
+ * record. */
+
+ Blt_SwitchFreeProc *freeProc; /* Procedure to free a switch. */
+
+ ClientData clientData; /* Arbitrary one-word value used by
+ * switch parser, passed to
+ * parseProc. */
+} Blt_SwitchCustom;
+
+
+/*
+ * Type values for Blt_SwitchSpec structures. See the user
+ * documentation for details.
+ */
+typedef enum {
+ BLT_SWITCH_BOOLEAN,
+ BLT_SWITCH_DOUBLE,
+ BLT_SWITCH_BITMASK,
+ BLT_SWITCH_BITMASK_INVERT,
+ BLT_SWITCH_FLOAT,
+ BLT_SWITCH_INT,
+ BLT_SWITCH_INT_NNEG,
+ BLT_SWITCH_INT_POS,
+ BLT_SWITCH_LIST,
+ BLT_SWITCH_LONG,
+ BLT_SWITCH_LONG_NNEG,
+ BLT_SWITCH_LONG_POS,
+ BLT_SWITCH_OBJ,
+ BLT_SWITCH_STRING,
+ BLT_SWITCH_VALUE,
+ BLT_SWITCH_CUSTOM,
+ BLT_SWITCH_END
+} Blt_SwitchTypes;
+
+
+typedef struct {
+ Blt_SwitchTypes type; /* Type of option, such as
+ * BLT_SWITCH_COLOR; see definitions
+ * below. Last option in table must
+ * have type BLT_SWITCH_END. */
+
+ const char *switchName; /* Switch used to specify option in
+ * argv. NULL means this spec is part
+ * of a group. */
+
+ const char *help; /* Help string. */
+ int offset; /* Where in widget record to store
+ * value; use Blt_Offset macro to
+ * generate values for this. */
+
+ int flags; /* Any combination of the values
+ * defined below. */
+
+ unsigned int mask;
+
+ Blt_SwitchCustom *customPtr; /* If type is BLT_SWITCH_CUSTOM then
+ * this is a pointer to info about how
+ * to parse and print the option.
+ * Otherwise it is irrelevant. */
+} Blt_SwitchSpec;
+
+#define BLT_SWITCH_DEFAULTS (0)
+#define BLT_SWITCH_ARGV_PARTIAL (1<<1)
+#define BLT_SWITCH_OBJV_PARTIAL (1<<1)
+
+/*
+ * Possible flag values for Blt_SwitchSpec structures. Any bits at or
+ * above BLT_SWITCH_USER_BIT may be used by clients for selecting
+ * certain entries.
+ */
+#define BLT_SWITCH_NULL_OK (1<<0)
+#define BLT_SWITCH_DONT_SET_DEFAULT (1<<3)
+#define BLT_SWITCH_SPECIFIED (1<<4)
+#define BLT_SWITCH_USER_BIT (1<<8)
+
+BLT_EXTERN int Blt_ParseSwitches(Tcl_Interp *interp, Blt_SwitchSpec *specPtr,
+ int objc, Tcl_Obj *const *objv, void *rec, int flags);
+
+BLT_EXTERN void Blt_FreeSwitches(Blt_SwitchSpec *specs, void *rec, int flags);
+
+BLT_EXTERN int Blt_SwitchChanged TCL_VARARGS(Blt_SwitchSpec *, specs);
+
+#endif /* BLT_SWITCH_H */
diff --git a/src/bltVecCmd.C b/src/bltVecCmd.C
new file mode 100644
index 0000000..c113347
--- /dev/null
+++ b/src/bltVecCmd.C
@@ -0,0 +1,2414 @@
+
+/*
+ * bltVecCmd.c --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Code for binary data read operation was donated by Harold Kirsch.
+ *
+ */
+
+/*
+ * TODO:
+ * o Add H. Kirsch's vector binary read operation
+ * x binread file0
+ * x binread -file file0
+ *
+ * o Add ASCII/binary file reader
+ * x read fileName
+ *
+ * o Allow Tcl-based client notifications.
+ * vector x
+ * x notify call Display
+ * x notify delete Display
+ * x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+#include "bltOp.h"
+#include "bltNsUtil.h"
+#include "bltSwitch.h"
+
+typedef int (VectorCmdProc)(Vector *vPtr, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
+
+static Blt_SwitchParseProc ObjToFFTVector;
+static Blt_SwitchCustom fftVectorSwitch = {
+ ObjToFFTVector, NULL, (ClientData)0,
+};
+
+static Blt_SwitchParseProc ObjToIndex;
+static Blt_SwitchCustom indexSwitch = {
+ ObjToIndex, NULL, (ClientData)0,
+};
+
+typedef struct {
+ Tcl_Obj *formatObjPtr;
+ int from, to;
+} PrintSwitches;
+
+static Blt_SwitchSpec printSwitches[] =
+{
+ {BLT_SWITCH_OBJ, "-format", "string",
+ Blt_Offset(PrintSwitches, formatObjPtr), 0},
+ {BLT_SWITCH_CUSTOM, "-from", "index",
+ Blt_Offset(PrintSwitches, from), 0, 0, &indexSwitch},
+ {BLT_SWITCH_CUSTOM, "-to", "index",
+ Blt_Offset(PrintSwitches, to), 0, 0, &indexSwitch},
+ {BLT_SWITCH_END}
+};
+
+
+typedef struct {
+ int flags;
+} SortSwitches;
+
+#define SORT_DECREASING (1<<0)
+#define SORT_UNIQUE (1<<1)
+
+static Blt_SwitchSpec sortSwitches[] =
+{
+ {BLT_SWITCH_BITMASK, "-decreasing", "",
+ Blt_Offset(SortSwitches, flags), 0, SORT_DECREASING},
+ {BLT_SWITCH_BITMASK, "-reverse", "",
+ Blt_Offset(SortSwitches, flags), 0, SORT_DECREASING},
+ {BLT_SWITCH_BITMASK, "-uniq", "",
+ Blt_Offset(SortSwitches, flags), 0, SORT_UNIQUE},
+ {BLT_SWITCH_END}
+};
+
+typedef struct {
+ double delta;
+ Vector *imagPtr; /* Vector containing imaginary part. */
+ Vector *freqPtr; /* Vector containing frequencies. */
+ VectorInterpData *dataPtr;
+ int mask; /* Flags controlling FFT. */
+} FFTData;
+
+
+static Blt_SwitchSpec fftSwitches[] = {
+ {BLT_SWITCH_CUSTOM, "-imagpart", "vector",
+ Blt_Offset(FFTData, imagPtr), 0, 0, &fftVectorSwitch},
+ {BLT_SWITCH_BITMASK, "-noconstant", "",
+ Blt_Offset(FFTData, mask), 0, FFT_NO_CONSTANT},
+ {BLT_SWITCH_BITMASK, "-spectrum", "",
+ Blt_Offset(FFTData, mask), 0, FFT_SPECTRUM},
+ {BLT_SWITCH_BITMASK, "-bartlett", "",
+ Blt_Offset(FFTData, mask), 0, FFT_BARTLETT},
+ {BLT_SWITCH_DOUBLE, "-delta", "float",
+ Blt_Offset(FFTData, mask), 0, 0, },
+ {BLT_SWITCH_CUSTOM, "-frequencies", "vector",
+ Blt_Offset(FFTData, freqPtr), 0, 0, &fftVectorSwitch},
+ {BLT_SWITCH_END}
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToFFTVector --
+ *
+ * Convert a string representing a vector into its vector structure.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToFFTVector(
+ ClientData clientData, /* Not used. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ const char *switchName, /* Not used. */
+ Tcl_Obj *objPtr, /* Name of vector. */
+ char *record, /* Structure record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ FFTData *dataPtr = (FFTData *)record;
+ Vector *vPtr;
+ Vector **vPtrPtr = (Vector **)(record + offset);
+ int isNew; /* Not used. */
+ char *string;
+
+ string = Tcl_GetString(objPtr);
+ vPtr = Blt_Vec_Create(dataPtr->dataPtr, string, string, string, &isNew);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ *vPtrPtr = vPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ObjToIndex --
+ *
+ * Convert a string representing a vector into its vector structure.
+ *
+ * Results:
+ * The return value is a standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ObjToIndex(
+ ClientData clientData, /* Contains the vector in question to verify
+ * its length. */
+ Tcl_Interp *interp, /* Interpreter to send results back to */
+ const char *switchName, /* Not used. */
+ Tcl_Obj *objPtr, /* Name of vector. */
+ char *record, /* Structure record */
+ int offset, /* Offset to field in structure */
+ int flags) /* Not used. */
+{
+ Vector *vPtr = clientData;
+ int *indexPtr = (int *)(record + offset);
+ int index;
+
+ if (Blt_Vec_GetIndex(interp, vPtr, Tcl_GetString(objPtr), &index,
+ INDEX_CHECK, (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *indexPtr = index;
+ return TCL_OK;
+
+}
+
+static Tcl_Obj *
+GetValues(Vector *vPtr, int first, int last)
+{
+ Tcl_Obj *listObjPtr;
+ double *vp, *vend;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (vp = vPtr->valueArr + first, vend = vPtr->valueArr + last; vp <= vend;
+ vp++) {
+ Tcl_ListObjAppendElement(vPtr->interp, listObjPtr,
+ Tcl_NewDoubleObj(*vp));
+ }
+ return listObjPtr;
+}
+
+static void
+ReplicateValue(Vector *vPtr, int first, int last, double value)
+{
+ double *vp, *vend;
+
+ for (vp = vPtr->valueArr + first, vend = vPtr->valueArr + last;
+ vp <= vend; vp++) {
+ *vp = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+}
+
+static int
+CopyList(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+
+ if (Blt_Vec_SetLength(interp, vPtr, objc) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < objc; i++) {
+ double value;
+
+ if (Blt_ExprDoubleFromObj(interp, objv[i], &value) != TCL_OK) {
+ Blt_Vec_SetLength(interp, vPtr, i);
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[i] = value;
+ }
+ return TCL_OK;
+}
+
+static int
+AppendVector(Vector *destPtr, Vector *srcPtr)
+{
+ size_t nBytes;
+ size_t oldSize, newSize;
+
+ oldSize = destPtr->length;
+ newSize = oldSize + srcPtr->last - srcPtr->first + 1;
+ if (Blt_Vec_ChangeLength(destPtr->interp, destPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nBytes = (newSize - oldSize) * sizeof(double);
+ memcpy((char *)(destPtr->valueArr + oldSize),
+ (srcPtr->valueArr + srcPtr->first), nBytes);
+ destPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+static int
+AppendList(Vector *vPtr, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Interp *interp = vPtr->interp;
+ int count;
+ int i;
+ double value;
+ int oldSize;
+
+ oldSize = vPtr->length;
+ if (Blt_Vec_ChangeLength(interp, vPtr, vPtr->length + objc) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = oldSize;
+ for (i = 0; i < objc; i++) {
+ if (Blt_ExprDoubleFromObj(interp, objv[i], &value) != TCL_OK) {
+ Blt_Vec_ChangeLength(interp, vPtr, count);
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[count++] = value;
+ }
+ vPtr->notifyFlags |= UPDATE_RANGE;
+ return TCL_OK;
+}
+
+/* Vector instance option commands */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AppendOp --
+ *
+ * Appends one of more TCL lists of values, or vector objects onto the
+ * end of the current vector object.
+ *
+ * Results:
+ * A standard TCL result. If a current vector can't be created,
+ * resized, any of the named vectors can't be found, or one of lists of
+ * values is invalid, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of current vector will be notified of the change.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+AppendOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+ int result;
+ Vector *v2Ptr;
+
+ for (i = 2; i < objc; i++) {
+ v2Ptr = Blt_Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[i]), (const char **)NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ result = AppendVector(vPtr, v2Ptr);
+ } else {
+ int nElem;
+ Tcl_Obj **elemObjArr;
+
+ if (Tcl_ListObjGetElements(interp, objv[i], &nElem, &elemObjArr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = AppendList(vPtr, nElem, elemObjArr);
+ }
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (objc > 2) {
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ClearOp --
+ *
+ * Deletes all the accumulated array indices for the TCL array associated
+ * will the vector. This routine can be used to free excess memory from
+ * a large vector.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side Effects:
+ * Memory used for the entries of the TCL array variable is freed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ClearOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Blt_Vec_FlushCache(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeleteOp --
+ *
+ * Deletes the given indices from the vector. If no indices are provided
+ * the entire vector is deleted.
+ *
+ * Results:
+ * A standard TCL result. If any of the given indices is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * The clients of the vector will be notified of the vector
+ * deletions.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DeleteOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ unsigned char *unsetArr;
+ int i, j;
+ int count;
+ char *string;
+
+ /* FIXME: Don't delete vector with no indices. */
+ if (objc == 2) {
+ Blt_Vec_Free(vPtr);
+ return TCL_OK;
+ }
+
+ /* Allocate an "unset" bitmap the size of the vector. */
+ unsetArr = Blt_AssertCalloc(sizeof(unsigned char), (vPtr->length + 7) / 8);
+#define SetBit(i) \
+ unsetArr[(i) >> 3] |= (1 << ((i) & 0x07))
+#define GetBit(i) \
+ (unsetArr[(i) >> 3] & (1 << ((i) & 0x07)))
+
+ for (i = 2; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (Blt_Vec_GetIndexRange(interp, vPtr, string,
+ (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL)
+ != TCL_OK) {
+ Blt_Free(unsetArr);
+ return TCL_ERROR;
+ }
+ for (j = vPtr->first; j <= vPtr->last; j++) {
+ SetBit(j); /* Mark the range of elements for deletion. */
+ }
+ }
+ count = 0;
+ for (i = 0; i < vPtr->length; i++) {
+ if (GetBit(i)) {
+ continue; /* Skip elements marked for deletion. */
+ }
+ if (count < i) {
+ vPtr->valueArr[count] = vPtr->valueArr[i];
+ }
+ count++;
+ }
+ Blt_Free(unsetArr);
+ vPtr->length = count;
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DupOp --
+ *
+ * Creates one or more duplicates of the vector object.
+ *
+ * Results:
+ * A standard TCL result. If a new vector can't be created,
+ * or and existing vector resized, TCL_ERROR is returned.
+ *
+ * Side Effects:
+ * Clients of existing vectors will be notified of the change.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DupOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+
+ for (i = 2; i < objc; i++) {
+ Vector *v2Ptr;
+ char *name;
+ int isNew;
+
+ name = Tcl_GetString(objv[i]);
+ v2Ptr = Blt_Vec_Create(vPtr->dataPtr, name, name, name, &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (v2Ptr == vPtr) {
+ continue;
+ }
+ if (Blt_Vec_Duplicate(v2Ptr, vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ }
+ }
+ return TCL_OK;
+}
+
+
+/* spinellia@acm.org START */
+
+/* fft implementation */
+/*ARGSUSED*/
+static int
+FFTOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Vector *v2Ptr = NULL;
+ int isNew;
+ FFTData data;
+ char *realVecName;
+
+ memset(&data, 0, sizeof(data));
+ data.delta = 1.0;
+
+ realVecName = Tcl_GetString(objv[2]);
+ v2Ptr = Blt_Vec_Create(vPtr->dataPtr, realVecName, realVecName,
+ realVecName, &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (v2Ptr == vPtr) {
+ Tcl_AppendResult(interp, "real vector \"", realVecName, "\"",
+ " can't be the same as the source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_ParseSwitches(interp, fftSwitches, objc - 3, objv + 3, &data,
+ BLT_SWITCH_DEFAULTS) < 0) {
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_FFT(interp, v2Ptr, data.imagPtr, data.freqPtr, data.delta,
+ data.mask, vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /* Update bookkeeping. */
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ }
+ if (data.imagPtr != NULL) {
+ if (data.imagPtr->flush) {
+ Blt_Vec_FlushCache(data.imagPtr);
+ }
+ Blt_Vec_UpdateClients(data.imagPtr);
+ }
+ if (data.freqPtr != NULL) {
+ if (data.freqPtr->flush) {
+ Blt_Vec_FlushCache(data.freqPtr);
+ }
+ Blt_Vec_UpdateClients(data.freqPtr);
+ }
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+InverseFFTOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int isNew;
+ char *name;
+ Vector *srcImagPtr;
+ Vector *destRealPtr;
+ Vector *destImagPtr;
+
+ name = Tcl_GetString(objv[2]);
+ if (Blt_Vec_LookupName(vPtr->dataPtr, name, &srcImagPtr) != TCL_OK ) {
+ return TCL_ERROR;
+ }
+ name = Tcl_GetString(objv[3]);
+ destRealPtr = Blt_Vec_Create(vPtr->dataPtr, name, name, name, &isNew);
+ name = Tcl_GetString(objv[4]);
+ destImagPtr = Blt_Vec_Create(vPtr->dataPtr, name, name, name, &isNew);
+
+ if (Blt_Vec_InverseFFT(interp, srcImagPtr, destRealPtr, destImagPtr, vPtr)
+ != TCL_OK ){
+ return TCL_ERROR;
+ }
+ if (destRealPtr->flush) {
+ Blt_Vec_FlushCache(destRealPtr);
+ }
+ Blt_Vec_UpdateClients(destRealPtr);
+
+ if (destImagPtr->flush) {
+ Blt_Vec_FlushCache(destImagPtr);
+ }
+ Blt_Vec_UpdateClients(destImagPtr);
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * IndexOp --
+ *
+ * Sets or reads the value of the index. This simulates what the
+ * vector's variable does.
+ *
+ * Results:
+ * A standard TCL result. If the index is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the values.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+IndexOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int first, last;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ if (Blt_Vec_GetIndexRange(interp, vPtr, string, INDEX_ALL_FLAGS,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ first = vPtr->first, last = vPtr->last;
+ if (objc == 3) {
+ Tcl_Obj *listObjPtr;
+
+ if (first == vPtr->length) {
+ Tcl_AppendResult(interp, "can't get index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Can't read from index "++end" */
+ }
+ listObjPtr = GetValues(vPtr, first, last);
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else {
+ double value;
+
+ /* FIXME: huh? Why set values here?. */
+ if (first == SPECIAL_INDEX) {
+ Tcl_AppendResult(interp, "can't set index \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR; /* Tried to set "min" or "max" */
+ }
+ if (Blt_ExprDoubleFromObj(interp, objv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first == vPtr->length) {
+ if (Blt_Vec_ChangeLength(interp, vPtr, vPtr->length + 1)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ ReplicateValue(vPtr, first, last, value);
+ Tcl_SetObjResult(interp, objv[3]);
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * LengthOp --
+ *
+ * Returns the length of the vector. If a new size is given, the
+ * vector is resized to the new vector.
+ *
+ * Results:
+ * A standard TCL result. If the new length is invalid,
+ * interp->result will an error message and TCL_ERROR is returned.
+ * Otherwise interp->result will contain the length of the vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+LengthOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ int nElem;
+
+ if (Tcl_GetIntFromObj(interp, objv[2], &nElem) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (nElem < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"",
+ Tcl_GetString(objv[2]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((Blt_Vec_SetSize(interp, vPtr, nElem) != TCL_OK) ||
+ (Blt_Vec_SetLength(interp, vPtr, nElem) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->length);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MapOp --
+ *
+ * Queries or sets the offset of the array index from the base
+ * address of the data array of values.
+ *
+ * Results:
+ * A standard TCL result. If the source vector doesn't exist
+ * or the source list is not a valid list of numbers, TCL_ERROR
+ * returned. Otherwise TCL_OK is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MapOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc > 2) {
+ if (Blt_Vec_MapVariable(interp, vPtr, Tcl_GetString(objv[2]))
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (vPtr->arrayName != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->arrayName, -1);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MaxOp --
+ *
+ * Returns the maximum value of the vector.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MaxOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Blt_Vec_Max(vPtr));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MergeOp --
+ *
+ * Merges the values from the given vectors to the current vector.
+ *
+ * Results:
+ * A standard TCL result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MergeOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Vector **vecArr;
+ int refSize, nElem;
+ int i;
+ double *valuePtr, *valueArr;
+ Vector **vPtrPtr;
+
+ /* Allocate an array of vector pointers of each vector to be
+ * merged in the current vector. */
+ vecArr = Blt_AssertMalloc(sizeof(Vector *) * objc);
+ vPtrPtr = vecArr;
+
+ refSize = -1;
+ nElem = 0;
+ for (i = 2; i < objc; i++) {
+ Vector *v2Ptr;
+ int length;
+
+ if (Blt_Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr)
+ != TCL_OK) {
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ /* Check that all the vectors are the same length */
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (refSize < 0) {
+ refSize = length;
+ } else if (length != refSize) {
+ Tcl_AppendResult(vPtr->interp, "vectors \"", vPtr->name,
+ "\" and \"", v2Ptr->name, "\" differ in length",
+ (char *)NULL);
+ Blt_Free(vecArr);
+ return TCL_ERROR;
+ }
+ *vPtrPtr++ = v2Ptr;
+ nElem += refSize;
+ }
+ *vPtrPtr = NULL;
+
+ valueArr = Blt_Malloc(sizeof(double) * nElem);
+ if (valueArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ",
+ Blt_Itoa(nElem), " vector elements", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /* Merge the values from each of the vectors into the current vector */
+ valuePtr = valueArr;
+ for (i = 0; i < refSize; i++) {
+ Vector **vpp;
+
+ for (vpp = vecArr; *vpp != NULL; vpp++) {
+ *valuePtr++ = (*vpp)->valueArr[i + (*vpp)->first];
+ }
+ }
+ Blt_Free(vecArr);
+ Blt_Vec_Reset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MinOp --
+ *
+ * Returns the minimum value of the vector.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+MinOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Blt_Vec_Min(vPtr));
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NormalizeOp --
+ *
+ * Normalizes the vector.
+ *
+ * Results:
+ * A standard TCL result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NormalizeOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+ double range;
+
+ Blt_Vec_UpdateRange(vPtr);
+ range = vPtr->max - vPtr->min;
+ if (objc > 2) {
+ Vector *v2Ptr;
+ int isNew;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ v2Ptr = Blt_Vec_Create(vPtr->dataPtr, string, string, string, &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_SetLength(interp, v2Ptr, vPtr->length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (i = 0; i < vPtr->length; i++) {
+ v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range;
+ }
+ Blt_Vec_UpdateRange(v2Ptr);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ }
+ } else {
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = 0; i < vPtr->length; i++) {
+ double norm;
+
+ norm = (vPtr->valueArr[i] - vPtr->min) / range;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(norm));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NotifyOp --
+ *
+ * Notify clients of vector.
+ *
+ * Results:
+ * A standard TCL result. If any of the given vectors differ in size,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * vector data will contain merged values of the given vectors.
+ *
+ * x vector notify now
+ * x vector notify always
+ * x vector notify whenidle
+ * x vector notify update {}
+ * x vector notify delete {}
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+NotifyOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int option;
+ int bool;
+ enum optionIndices {
+ OPTION_ALWAYS, OPTION_NEVER, OPTION_WHENIDLE,
+ OPTION_NOW, OPTION_CANCEL, OPTION_PENDING
+ };
+ static const char *optionArr[] = {
+ "always", "never", "whenidle", "now", "cancel", "pending", NULL
+ };
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], optionArr, "qualifier", TCL_EXACT,
+ &option) != TCL_OK) {
+ return TCL_OK;
+ }
+ switch (option) {
+ case OPTION_ALWAYS:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_ALWAYS;
+ break;
+ case OPTION_NEVER:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_NEVER;
+ break;
+ case OPTION_WHENIDLE:
+ vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK;
+ vPtr->notifyFlags |= NOTIFY_WHENIDLE;
+ break;
+ case OPTION_NOW:
+ /* FIXME: How does this play when an update is pending? */
+ Blt_Vec_NotifyClients(vPtr);
+ break;
+ case OPTION_CANCEL:
+ if (vPtr->notifyFlags & NOTIFY_PENDING) {
+ vPtr->notifyFlags &= ~NOTIFY_PENDING;
+ Tcl_CancelIdleCall(Blt_Vec_NotifyClients, (ClientData)vPtr);
+ }
+ break;
+ case OPTION_PENDING:
+ bool = (vPtr->notifyFlags & NOTIFY_PENDING);
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), bool);
+ break;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PopulateOp --
+ *
+ * Creates or resizes a new vector based upon the density specified.
+ *
+ * Results:
+ * A standard TCL result. If the density is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PopulateOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Vector *v2Ptr;
+ int size, density;
+ int isNew;
+ int i, j;
+ double *valuePtr;
+ int count;
+ char *string;
+
+ string = Tcl_GetString(objv[2]);
+ v2Ptr = Blt_Vec_Create(vPtr->dataPtr, string, string, string, &isNew);
+ if (v2Ptr == NULL) {
+ return TCL_ERROR;
+ }
+ if (vPtr->length == 0) {
+ return TCL_OK; /* Source vector is empty. */
+ }
+ if (Tcl_GetIntFromObj(interp, objv[3], &density) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (density < 1) {
+ Tcl_AppendResult(interp, "bad density \"", Tcl_GetString(objv[3]),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ size = (vPtr->length - 1) * (density + 1) + 1;
+ if (Blt_Vec_SetLength(interp, v2Ptr, size) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ count = 0;
+ valuePtr = v2Ptr->valueArr;
+ for (i = 0; i < (vPtr->length - 1); i++) {
+ double slice, range;
+
+ range = vPtr->valueArr[i + 1] - vPtr->valueArr[i];
+ slice = range / (double)(density + 1);
+ for (j = 0; j <= density; j++) {
+ *valuePtr = vPtr->valueArr[i] + (slice * (double)j);
+ valuePtr++;
+ count++;
+ }
+ }
+ count++;
+ *valuePtr = vPtr->valueArr[i];
+ assert(count == v2Ptr->length);
+ if (!isNew) {
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ValuesOp --
+ *
+ * Print the values vector.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ValuesOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ PrintSwitches switches;
+
+ switches.formatObjPtr = NULL;
+ switches.from = 0;
+ switches.to = vPtr->length - 1;
+ indexSwitch.clientData = vPtr;
+ if (Blt_ParseSwitches(interp, printSwitches, objc - 2, objv + 2, &switches,
+ BLT_SWITCH_DEFAULTS) < 0) {
+ return TCL_ERROR;
+ }
+ if (switches.from > switches.to) {
+ int tmp;
+ /* swap positions. */
+ tmp = switches.to;
+ switches.to = switches.from;
+ switches.from = tmp;
+ }
+ if (switches.formatObjPtr == NULL) {
+ Tcl_Obj *listObjPtr;
+ int i;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ for (i = switches.from; i <= switches.to; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else {
+ Tcl_DString ds;
+ char buffer[200];
+ const char *fmt;
+ int i;
+
+ Tcl_DStringInit(&ds);
+ fmt = Tcl_GetString(switches.formatObjPtr);
+ for (i = switches.from; i <= switches.to; i++) {
+ sprintf(buffer, fmt, vPtr->valueArr[i]);
+ Tcl_DStringAppend(&ds, buffer, -1);
+ }
+ Tcl_DStringResult(interp, &ds);
+ Tcl_DStringFree(&ds);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RangeOp --
+ *
+ * Returns a TCL list of the range of vector values specified.
+ *
+ * Results:
+ * A standard TCL result. If the given range is invalid, TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned and interp->result
+ * will contain the list of values.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RangeOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Obj *listObjPtr;
+ int first, last;
+ int i;
+
+ if (objc == 2) {
+ first = 0;
+ last = vPtr->length - 1;
+ } else if (objc == 4) {
+ if ((Blt_Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[2]), &first,
+ INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK) ||
+ (Blt_Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[3]), &last,
+ INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetString(objv[0]), " range ?first last?", (char *)NULL);
+ return TCL_ERROR;
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (first > last) {
+ /* Return the list reversed */
+ for (i = last; i <= first; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ } else {
+ for (i = first; i <= last; i++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InRange --
+ *
+ * Determines if a value lies within a given range.
+ *
+ * The value is normalized and compared against the interval
+ * [0..1], where 0.0 is the minimum and 1.0 is the maximum.
+ * DBL_EPSILON is the smallest number that can be represented
+ * on the host machine, such that (1.0 + epsilon) != 1.0.
+ *
+ * Please note, min cannot be greater than max.
+ *
+ * Results:
+ * If the value is within of the interval [min..max], 1 is
+ * returned; 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+INLINE static int
+InRange(double value, double min, double max)
+{
+ double range;
+
+ range = max - min;
+ if (range < DBL_EPSILON) {
+ return (FABS(max - value) < DBL_EPSILON);
+ } else {
+ double norm;
+
+ norm = (value - min) / range;
+ return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON));
+ }
+}
+
+enum NativeFormats {
+ FMT_UNKNOWN = -1,
+ FMT_UCHAR, FMT_CHAR,
+ FMT_USHORT, FMT_SHORT,
+ FMT_UINT, FMT_INT,
+ FMT_ULONG, FMT_LONG,
+ FMT_FLOAT, FMT_DOUBLE
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetBinaryFormat
+ *
+ * Translates a format string into a native type. Valid formats are
+ *
+ * signed i1, i2, i4, i8
+ * unsigned u1, u2, u4, u8
+ * real r4, r8, r16
+ *
+ * There must be a corresponding native type. For example, this for
+ * reading 2-byte binary integers from an instrument and converting them
+ * to unsigned shorts or ints.
+ *
+ *---------------------------------------------------------------------------
+ */
+static enum NativeFormats
+GetBinaryFormat(Tcl_Interp *interp, char *string, int *sizePtr)
+{
+ char c;
+
+ c = tolower(string[0]);
+ if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) {
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": incorrect byte size", (char *)NULL);
+ return FMT_UNKNOWN;
+ }
+ switch (c) {
+ case 'r':
+ if (*sizePtr == sizeof(double)) {
+ return FMT_DOUBLE;
+ } else if (*sizePtr == sizeof(float)) {
+ return FMT_FLOAT;
+ }
+ break;
+
+ case 'i':
+ if (*sizePtr == sizeof(char)) {
+ return FMT_CHAR;
+ } else if (*sizePtr == sizeof(int)) {
+ return FMT_INT;
+ } else if (*sizePtr == sizeof(long)) {
+ return FMT_LONG;
+ } else if (*sizePtr == sizeof(short)) {
+ return FMT_SHORT;
+ }
+ break;
+
+ case 'u':
+ if (*sizePtr == sizeof(unsigned char)) {
+ return FMT_UCHAR;
+ } else if (*sizePtr == sizeof(unsigned int)) {
+ return FMT_UINT;
+ } else if (*sizePtr == sizeof(unsigned long)) {
+ return FMT_ULONG;
+ } else if (*sizePtr == sizeof(unsigned short)) {
+ return FMT_USHORT;
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "unknown binary format \"", string,
+ "\": should be either i#, r#, u# (where # is size in bytes)",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+ }
+ Tcl_AppendResult(interp, "can't handle format \"", string, "\"",
+ (char *)NULL);
+ return FMT_UNKNOWN;
+}
+
+static int
+CopyValues(Vector *vPtr, char *byteArr, enum NativeFormats fmt, int size,
+ int length, int swap, int *indexPtr)
+{
+ int i, n;
+ int newSize;
+
+ if ((swap) && (size > 1)) {
+ int nBytes = size * length;
+ unsigned char *p;
+ int left, right;
+
+ for (i = 0; i < nBytes; i += size) {
+ p = (unsigned char *)(byteArr + i);
+ for (left = 0, right = size - 1; left < right; left++, right--) {
+ p[left] ^= p[right];
+ p[right] ^= p[left];
+ p[left] ^= p[right];
+ }
+
+ }
+ }
+ newSize = *indexPtr + length;
+ if (newSize > vPtr->length) {
+ if (Blt_Vec_ChangeLength(vPtr->interp, vPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+#define CopyArrayToVector(vPtr, arr) \
+ for (i = 0, n = *indexPtr; i < length; i++, n++) { \
+ (vPtr)->valueArr[n] = (double)(arr)[i]; \
+ }
+
+ switch (fmt) {
+ case FMT_CHAR:
+ CopyArrayToVector(vPtr, (char *)byteArr);
+ break;
+
+ case FMT_UCHAR:
+ CopyArrayToVector(vPtr, (unsigned char *)byteArr);
+ break;
+
+ case FMT_INT:
+ CopyArrayToVector(vPtr, (int *)byteArr);
+ break;
+
+ case FMT_UINT:
+ CopyArrayToVector(vPtr, (unsigned int *)byteArr);
+ break;
+
+ case FMT_LONG:
+ CopyArrayToVector(vPtr, (long *)byteArr);
+ break;
+
+ case FMT_ULONG:
+ CopyArrayToVector(vPtr, (unsigned long *)byteArr);
+ break;
+
+ case FMT_SHORT:
+ CopyArrayToVector(vPtr, (short int *)byteArr);
+ break;
+
+ case FMT_USHORT:
+ CopyArrayToVector(vPtr, (unsigned short int *)byteArr);
+ break;
+
+ case FMT_FLOAT:
+ CopyArrayToVector(vPtr, (float *)byteArr);
+ break;
+
+ case FMT_DOUBLE:
+ CopyArrayToVector(vPtr, (double *)byteArr);
+ break;
+
+ case FMT_UNKNOWN:
+ break;
+ }
+ *indexPtr += length;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * BinreadOp --
+ *
+ * Reads binary values from a TCL channel. Values are either appended to
+ * the end of the vector or placed at a given index (using the "-at"
+ * option), overwriting existing values. Data is read until EOF is found
+ * on the channel or a specified number of values are read. (note that
+ * this is not necessarily the same as the number of bytes).
+ *
+ * The following flags are supported:
+ * -swap Swap bytes
+ * -at index Start writing data at the index.
+ * -format fmt Specifies the format of the data.
+ *
+ * This binary reader was created and graciously donated by Harald Kirsch
+ * (kir@iitb.fhg.de). Anything that's wrong is due to my (gah) munging
+ * of the code.
+ *
+ * Results:
+ * Returns a standard TCL result. The interpreter result will contain the
+ * number of values (not the number of bytes) read.
+ *
+ * Caveats:
+ * Channel reads must end on an element boundary.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+BinreadOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Tcl_Channel channel;
+ char *byteArr;
+ char *string;
+ enum NativeFormats fmt;
+ int arraySize, bytesRead;
+ int count, total;
+ int first;
+ int size, length, mode;
+ int swap;
+ int i;
+
+ string = Tcl_GetString(objv[2]);
+ channel = Tcl_GetChannel(interp, string, &mode);
+ if (channel == NULL) {
+ return TCL_ERROR;
+ }
+ if ((mode & TCL_READABLE) == 0) {
+ Tcl_AppendResult(interp, "channel \"", string,
+ "\" wasn't opened for reading", (char *)NULL);
+ return TCL_ERROR;
+ }
+ first = vPtr->length;
+ fmt = FMT_DOUBLE;
+ size = sizeof(double);
+ swap = FALSE;
+ count = 0;
+
+ if (objc > 3) {
+ string = Tcl_GetString(objv[3]);
+ if (string[0] != '-') {
+ long int value;
+ /* Get the number of values to read. */
+ if (Tcl_GetLongFromObj(interp, objv[3], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_AppendResult(interp, "count can't be negative",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ count = (size_t)value;
+ objc--, objv++;
+ }
+ }
+ /* Process any option-value pairs that remain. */
+ for (i = 3; i < objc; i++) {
+ string = Tcl_GetString(objv[i]);
+ if (strcmp(string, "-swap") == 0) {
+ swap = TRUE;
+ } else if (strcmp(string, "-format") == 0) {
+ i++;
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "missing arg after \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[i]);
+ fmt = GetBinaryFormat(interp, string, &size);
+ if (fmt == FMT_UNKNOWN) {
+ return TCL_ERROR;
+ }
+ } else if (strcmp(string, "-at") == 0) {
+ i++;
+ if (i >= objc) {
+ Tcl_AppendResult(interp, "missing arg after \"", string,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[i]);
+ if (Blt_Vec_GetIndex(interp, vPtr, string, &first, 0,
+ (Blt_VectorIndexProc **)NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (first > vPtr->length) {
+ Tcl_AppendResult(interp, "index \"", string,
+ "\" is out of range", (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+
+#define BUFFER_SIZE 1024
+ if (count == 0) {
+ arraySize = BUFFER_SIZE * size;
+ } else {
+ arraySize = count * size;
+ }
+
+ byteArr = Blt_AssertMalloc(arraySize);
+ /* FIXME: restore old channel translation later? */
+ if (Tcl_SetChannelOption(interp, channel, "-translation",
+ "binary") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total = 0;
+ while (!Tcl_Eof(channel)) {
+ bytesRead = Tcl_Read(channel, byteArr, arraySize);
+ if (bytesRead < 0) {
+ Tcl_AppendResult(interp, "error reading channel: ",
+ Tcl_PosixError(interp), (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((bytesRead % size) != 0) {
+ Tcl_AppendResult(interp, "error reading channel: short read",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ length = bytesRead / size;
+ if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ total += length;
+ if (count > 0) {
+ break;
+ }
+ }
+ Blt_Free(byteArr);
+
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+
+ /* Set the result as the number of values read. */
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SearchOp --
+ *
+ * Searches for a value in the vector. Returns the indices of all vector
+ * elements matching a particular value.
+ *
+ * Results:
+ * Always returns TCL_OK. interp->result will contain a list of the
+ * indices of array elements matching value. If no elements match,
+ * interp->result will contain the empty string.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SearchOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ double min, max;
+ int i;
+ int wantValue;
+ char *string;
+ Tcl_Obj *listObjPtr;
+
+ wantValue = FALSE;
+ string = Tcl_GetString(objv[2]);
+ if ((string[0] == '-') && (strcmp(string, "-value") == 0)) {
+ wantValue = TRUE;
+ objv++, objc--;
+ }
+ if (Blt_ExprDoubleFromObj(interp, objv[2], &min) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ max = min;
+ if (objc > 4) {
+ Tcl_AppendResult(interp, "wrong # arguments: should be \"",
+ Tcl_GetString(objv[0]), " search ?-value? min ?max?",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((objc > 3) &&
+ (Blt_ExprDoubleFromObj(interp, objv[3], &max) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((min - max) >= DBL_EPSILON) {
+ return TCL_OK; /* Bogus range. Don't bother looking. */
+ }
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ if (wantValue) {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(vPtr->valueArr[i]));
+ }
+ }
+ } else {
+ for (i = 0; i < vPtr->length; i++) {
+ if (InRange(vPtr->valueArr[i], min, max)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewIntObj(i + vPtr->offset));
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * OffsetOp --
+ *
+ * Queries or sets the offset of the array index from the base address of
+ * the data array of values.
+ *
+ * Results:
+ * A standard TCL result. If the source vector doesn't exist or the
+ * source list is not a valid list of numbers, TCL_ERROR returned.
+ * Otherwise TCL_OK is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+OffsetOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ if (objc == 3) {
+ int newOffset;
+
+ if (Tcl_GetIntFromObj(interp, objv[2], &newOffset) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->offset = newOffset;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->offset);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * RandomOp --
+ *
+ * Generates random values for the length of the vector.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+RandomOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int i;
+
+ for (i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = drand48();
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SeqOp --
+ *
+ * Generates a sequence of values in the vector.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SeqOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int n;
+ double start, stop;
+
+ if (Blt_ExprDoubleFromObj(interp, objv[2], &start) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_ExprDoubleFromObj(interp, objv[3], &stop) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ n = vPtr->length;
+ if ((objc > 4) && (Blt_ExprIntFromObj(interp, objv[4], &n) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (n > 1) {
+ int i;
+ double step;
+
+ if (Blt_Vec_SetLength(interp, vPtr, n) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ step = (stop - start) / (double)(n - 1);
+ for (i = 0; i < n; i++) {
+ vPtr->valueArr[i] = start + (step * i);
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SetOp --
+ *
+ * Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ * A standard TCL result. If the source vector doesn't exist or the
+ * source list is not a valid list of numbers, TCL_ERROR returned.
+ * Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified. Any
+ * cached array indices are flushed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SetOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int result;
+ Vector *v2Ptr;
+ int nElem;
+ Tcl_Obj **elemObjArr;
+
+ /* The source can be either a list of numbers or another vector. */
+
+ v2Ptr = Blt_Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[2]), NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ if (vPtr == v2Ptr) {
+ Vector *tmpPtr;
+ /*
+ * Source and destination vectors are the same. Copy the source
+ * first into a temporary vector to avoid memory overlaps.
+ */
+ tmpPtr = Blt_Vec_New(vPtr->dataPtr);
+ result = Blt_Vec_Duplicate(tmpPtr, v2Ptr);
+ if (result == TCL_OK) {
+ result = Blt_Vec_Duplicate(vPtr, tmpPtr);
+ }
+ Blt_Vec_Free(tmpPtr);
+ } else {
+ result = Blt_Vec_Duplicate(vPtr, v2Ptr);
+ }
+ } else if (Tcl_ListObjGetElements(interp, objv[2], &nElem, &elemObjArr)
+ == TCL_OK) {
+ result = CopyList(vPtr, interp, nElem, elemObjArr);
+ } else {
+ return TCL_ERROR;
+ }
+
+ if (result == TCL_OK) {
+ /*
+ * The vector has changed; so flush the array indices (they're wrong
+ * now), find the new range of the data, and notify the vector's
+ * clients that it's been modified.
+ */
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SimplifyOp --
+ *
+ * Sets the data of the vector object from a list of values.
+ *
+ * Results:
+ * A standard TCL result. If the source vector doesn't exist or the
+ * source list is not a valid list of numbers, TCL_ERROR returned.
+ * Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified. Any
+ * cached array indices are flushed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SimplifyOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ size_t i, n;
+ int length, nPoints;
+ int *simple;
+ double tolerance = 10.0;
+ Point2d *orig, *reduced;
+
+ length = vPtr->length;
+ nPoints = vPtr->length / 2;
+ simple = Blt_AssertMalloc(nPoints * sizeof(int));
+ reduced = Blt_AssertMalloc(nPoints * sizeof(Point2d));
+ orig = (Point2d *)vPtr->valueArr;
+ n = Blt_SimplifyLine(orig, 0, nPoints - 1, tolerance, simple);
+ for (i = 0; i < n; i++) {
+ reduced[i] = orig[simple[i]];
+ }
+ Blt_Free(simple);
+ Blt_Vec_Reset(vPtr, (double *)reduced, n * 2, vPtr->length, TCL_DYNAMIC);
+ /*
+ * The vector has changed; so flush the array indices (they're wrong
+ * now), find the new range of the data, and notify the vector's
+ * clients that it's been modified.
+ */
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SplitOp --
+ *
+ * Copies the values from the vector evenly into one of more vectors.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+SplitOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ int nVectors;
+
+ nVectors = objc - 2;
+ if ((vPtr->length % nVectors) != 0) {
+ Tcl_AppendResult(interp, "can't split vector \"", vPtr->name,
+ "\" into ", Blt_Itoa(nVectors), " even parts.", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nVectors > 0) {
+ Vector *v2Ptr;
+ char *string; /* Name of vector. */
+ int i, j, k;
+ int oldSize, newSize, extra, isNew;
+
+ extra = vPtr->length / nVectors;
+ for (i = 0; i < nVectors; i++) {
+ string = Tcl_GetString(objv[i+2]);
+ v2Ptr = Blt_Vec_Create(vPtr->dataPtr, string, string, string,
+ &isNew);
+ oldSize = v2Ptr->length;
+ newSize = oldSize + extra;
+ if (Blt_Vec_SetLength(interp, v2Ptr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ for (j = i, k = oldSize; j < vPtr->length; j += nVectors, k++) {
+ v2Ptr->valueArr[k] = vPtr->valueArr[j];
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+
+static Vector **sortVectors; /* Pointer to the array of values currently
+ * being sorted. */
+static int nSortVectors;
+static int sortDecreasing; /* Indicates the ordering of the sort. If
+ * non-zero, the vectors are sorted in
+ * decreasing order */
+
+static int
+CompareVectors(void *a, void *b)
+{
+ double delta;
+ int i;
+ int sign;
+ Vector *vPtr;
+
+ sign = (sortDecreasing) ? -1 : 1;
+ for (i = 0; i < nSortVectors; i++) {
+ vPtr = sortVectors[i];
+ delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b];
+ if (delta < 0.0) {
+ return (-1 * sign);
+ } else if (delta > 0.0) {
+ return (1 * sign);
+ }
+ }
+ return 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_SortMap --
+ *
+ * Returns an array of indices that represents the sorted mapping of the
+ * original vector.
+ *
+ * Results:
+ * A standard TCL result. If any of the auxiliary vectors are a
+ * different size than the sorted vector object, TCL_ERROR is returned.
+ * Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vectors are sorted.
+ *
+ * vecName sort ?switches? vecName vecName...
+ *---------------------------------------------------------------------------
+ */
+size_t *
+Blt_Vec_SortMap(Vector **vectors, int nVectors)
+{
+ size_t *map;
+ int i;
+ Vector *vPtr = *vectors;
+ int length;
+
+ length = vPtr->last - vPtr->first + 1;
+ map = Blt_AssertMalloc(sizeof(size_t) * length);
+ for (i = vPtr->first; i <= vPtr->last; i++) {
+ map[i] = i;
+ }
+ /* Set global variables for sorting routine. */
+ sortVectors = vectors;
+ nSortVectors = nVectors;
+ qsort((char *)map, length, sizeof(size_t),
+ (QSortCompareProc *)CompareVectors);
+ return map;
+}
+
+static size_t *
+SortVectors(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Vector **vectors, *v2Ptr;
+ size_t *map;
+ int i;
+
+ vectors = Blt_AssertMalloc(sizeof(Vector *) * (objc + 1));
+ vectors[0] = vPtr;
+ map = NULL;
+ for (i = 0; i < objc; i++) {
+ if (Blt_Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]),
+ &v2Ptr) != TCL_OK) {
+ goto error;
+ }
+ if (v2Ptr->length != vPtr->length) {
+ Tcl_AppendResult(interp, "vector \"", v2Ptr->name,
+ "\" is not the same size as \"", vPtr->name, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ vectors[i + 1] = v2Ptr;
+ }
+ map = Blt_Vec_SortMap(vectors, objc + 1);
+ error:
+ Blt_Free(vectors);
+ return map;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * SortOp --
+ *
+ * Sorts the vector object and any other vectors according to sorting
+ * order of the vector object.
+ *
+ * Results:
+ * A standard TCL result. If any of the auxiliary vectors are a
+ * different size than the sorted vector object, TCL_ERROR is returned.
+ * Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vectors are sorted.
+ *
+ * vecName sort ?switches? vecName vecName...
+ *---------------------------------------------------------------------------
+ */
+static int
+SortOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ Vector *v2Ptr;
+ double *copy;
+ size_t *map;
+ size_t sortLength, nBytes;
+ int result;
+ int i;
+ unsigned int n;
+ SortSwitches switches;
+
+ sortDecreasing = FALSE;
+ switches.flags = 0;
+ i = Blt_ParseSwitches(interp, sortSwitches, objc - 2, objv + 2, &switches,
+ BLT_SWITCH_OBJV_PARTIAL);
+ if (i < 0) {
+ return TCL_ERROR;
+ }
+ objc -= i, objv += i;
+ sortDecreasing = (switches.flags & SORT_DECREASING);
+ if (objc > 2) {
+ map = SortVectors(vPtr, interp, objc - 2, objv + 2);
+ } else {
+ map = Blt_Vec_SortMap(&vPtr, 1);
+ }
+ if (map == NULL) {
+ return TCL_ERROR;
+ }
+ sortLength = vPtr->length;
+ /*
+ * Create an array to store a copy of the current values of the
+ * vector. We'll merge the values back into the vector based upon the
+ * indices found in the index array.
+ */
+ nBytes = sizeof(double) * sortLength;
+ copy = Blt_AssertMalloc(nBytes);
+ memcpy((char *)copy, (char *)vPtr->valueArr, nBytes);
+ if (switches.flags & SORT_UNIQUE) {
+ int count;
+
+ for (count = n = 1; n < sortLength; n++) {
+ size_t next, prev;
+
+ next = map[n];
+ prev = map[n - 1];
+ if (copy[next] != copy[prev]) {
+ map[count] = next;
+ count++;
+ }
+ }
+ sortLength = count;
+ nBytes = sortLength * sizeof(double);
+ }
+ if (sortLength != vPtr->length) {
+ Blt_Vec_SetLength(interp, vPtr, sortLength);
+ }
+ for (n = 0; n < sortLength; n++) {
+ vPtr->valueArr[n] = copy[map[n]];
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+
+ /* Now sort any other vectors in the same fashion. The vectors must be
+ * the same size as the map though. */
+ result = TCL_ERROR;
+ for (i = 2; i < objc; i++) {
+ if (Blt_Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]),
+ &v2Ptr) != TCL_OK) {
+ goto error;
+ }
+ if (sortLength != v2Ptr->length) {
+ Blt_Vec_SetLength(interp, v2Ptr, sortLength);
+ }
+ memcpy((char *)copy, (char *)v2Ptr->valueArr, nBytes);
+ for (n = 0; n < sortLength; n++) {
+ v2Ptr->valueArr[n] = copy[map[n]];
+ }
+ Blt_Vec_UpdateClients(v2Ptr);
+ if (v2Ptr->flush) {
+ Blt_Vec_FlushCache(v2Ptr);
+ }
+ }
+ result = TCL_OK;
+ error:
+ Blt_Free(copy);
+ Blt_Free(map);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * InstExprOp --
+ *
+ * Computes the result of the expression which may be either a scalar
+ * (single value) or vector (list of values).
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+InstExprOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+
+ if (Blt_ExprVector(interp, Tcl_GetString(objv[2]), (Blt_Vector *)vPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ArithOp --
+ *
+ * Results:
+ * A standard TCL result. If the source vector doesn't exist or the
+ * source list is not a valid list of numbers, TCL_ERROR returned.
+ * Otherwise TCL_OK is returned.
+ *
+ * Side Effects:
+ * The vector data is reset. Clients of the vector are notified.
+ * Any cached array indices are flushed.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ArithOp(Vector *vPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
+{
+ double value;
+ int i;
+ Vector *v2Ptr;
+ double scalar;
+ Tcl_Obj *listObjPtr;
+ char *string;
+
+ v2Ptr = Blt_Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr,
+ Tcl_GetString(objv[2]), NULL, NS_SEARCH_BOTH);
+ if (v2Ptr != NULL) {
+ int j;
+ int length;
+
+ length = v2Ptr->last - v2Ptr->first + 1;
+ if (length != vPtr->length) {
+ Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[0]),
+ "\" and \"", Tcl_GetString(objv[2]),
+ "\" are not the same length", (char *)NULL);
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[1]);
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ switch (string[0]) {
+ case '*':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] * v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '/':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] / v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '-':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] - v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '+':
+ for (i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) {
+ value = vPtr->valueArr[i] + v2Ptr->valueArr[j];
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+
+ } else if (Blt_ExprDoubleFromObj(interp, objv[2], &scalar) == TCL_OK) {
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+ string = Tcl_GetString(objv[1]);
+ switch (string[0]) {
+ case '*':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] * scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '/':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] / scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '-':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] - scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+
+ case '+':
+ for (i = 0; i < vPtr->length; i++) {
+ value = vPtr->valueArr[i] + scalar;
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewDoubleObj(value));
+ }
+ break;
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ } else {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorInstCmd --
+ *
+ * Parses and invokes the appropriate vector instance command option.
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Blt_OpSpec vectorInstOps[] =
+{
+ {"*", 1, ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"+", 1, ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"-", 1, ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"/", 1, ArithOp, 3, 3, "item",}, /*Deprecated*/
+ {"append", 1, AppendOp, 3, 0, "item ?item...?",},
+ {"binread", 1, BinreadOp, 3, 0, "channel ?numValues? ?flags?",},
+ {"clear", 1, ClearOp, 2, 2, "",},
+ {"delete", 2, DeleteOp, 2, 0, "index ?index...?",},
+ {"dup", 2, DupOp, 3, 0, "vecName",},
+ {"expr", 1, InstExprOp, 3, 3, "expression",},
+ {"fft", 1, FFTOp, 3, 0, "vecName ?switches?",},
+ {"index", 3, IndexOp, 3, 4, "index ?value?",},
+ {"inversefft",3, InverseFFTOp,4, 4, "vecName vecName",},
+ {"length", 1, LengthOp, 2, 3, "?newSize?",},
+ {"max", 2, MaxOp, 2, 2, "",},
+ {"merge", 2, MergeOp, 3, 0, "vecName ?vecName...?",},
+ {"min", 2, MinOp, 2, 2, "",},
+ {"normalize", 3, NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/
+ {"notify", 3, NotifyOp, 3, 3, "keyword",},
+ {"offset", 1, OffsetOp, 2, 3, "?offset?",},
+ {"populate", 1, PopulateOp, 4, 4, "vecName density",},
+ {"random", 4, RandomOp, 2, 2, "",}, /*Deprecated*/
+ {"range", 4, RangeOp, 2, 4, "first last",},
+ {"search", 3, SearchOp, 3, 5, "?-value? value ?value?",},
+ {"seq", 3, SeqOp, 4, 5, "begin end ?num?",},
+ {"set", 3, SetOp, 3, 3, "list",},
+ {"simplify", 2, SimplifyOp, 2, 2, },
+ {"sort", 2, SortOp, 2, 0, "?switches? ?vecName...?",},
+ {"split", 2, SplitOp, 2, 0, "?vecName...?",},
+ {"values", 3, ValuesOp, 2, 0, "?switches?",},
+ {"variable", 3, MapOp, 2, 3, "?varName?",},
+};
+
+static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec);
+
+int
+Blt_Vec_InstCmd(ClientData clientData, Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv)
+{
+ VectorCmdProc *proc;
+ Vector *vPtr = clientData;
+
+ vPtr->first = 0;
+ vPtr->last = vPtr->length - 1;
+ proc = Blt_GetOpFromObj(interp, nInstOps, vectorInstOps, BLT_OP_ARG1, objc,
+ objv, 0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (vPtr, interp, objc, objv);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_VarTrace --
+ *
+ * Results:
+ * Returns NULL on success. Only called from a variable trace.
+ *
+ * Side effects:
+ *
+ *---------------------------------------------------------------------------
+ */
+char *
+Blt_Vec_VarTrace(ClientData clientData, Tcl_Interp *interp, const char *part1,
+ const char *part2, int flags)
+{
+ Blt_VectorIndexProc *indexProc;
+ Vector *vPtr = clientData;
+ int first, last;
+ int varFlags;
+#define MAX_ERR_MSG 1023
+ static char message[MAX_ERR_MSG + 1];
+
+ if (part2 == NULL) {
+ if (flags & TCL_TRACE_UNSETS) {
+ Blt_Free(vPtr->arrayName);
+ vPtr->arrayName = NULL;
+ if (vPtr->freeOnUnset) {
+ Blt_Vec_Free(vPtr);
+ }
+ }
+ return NULL;
+ }
+ if (Blt_Vec_GetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, &indexProc)
+ != TCL_OK) {
+ goto error;
+ }
+ first = vPtr->first, last = vPtr->last;
+ varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags);
+ if (flags & TCL_TRACE_WRITES) {
+ double value;
+ Tcl_Obj *objPtr;
+
+ if (first == SPECIAL_INDEX) { /* Tried to set "min" or "max" */
+ return (char *)"read-only index";
+ }
+ objPtr = Tcl_GetVar2Ex(interp, part1, part2, varFlags);
+ if (objPtr == NULL) {
+ goto error;
+ }
+ if (Blt_ExprDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
+ if ((last == first) && (first >= 0)) {
+ /* Single numeric index. Reset the array element to
+ * its old value on errors */
+ Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags);
+ }
+ goto error;
+ }
+ if (first == vPtr->length) {
+ if (Blt_Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, vPtr->length + 1)
+ != TCL_OK) {
+ return (char *)"error resizing vector";
+ }
+ }
+ /* Set possibly an entire range of values */
+ ReplicateValue(vPtr, first, last, value);
+ } else if (flags & TCL_TRACE_READS) {
+ double value;
+ Tcl_Obj *objPtr;
+
+ if (vPtr->length == 0) {
+ if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) {
+ goto error;
+ }
+ return NULL;
+ }
+ if (first == vPtr->length) {
+ return (char *)"write-only index";
+ }
+ if (first == last) {
+ if (first >= 0) {
+ value = vPtr->valueArr[first];
+ } else {
+ vPtr->first = 0, vPtr->last = vPtr->length - 1;
+ value = (*indexProc) ((Blt_Vector *) vPtr);
+ }
+ objPtr = Tcl_NewDoubleObj(value);
+ if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) {
+ Tcl_DecrRefCount(objPtr);
+ goto error;
+ }
+ } else {
+ objPtr = GetValues(vPtr, first, last);
+ if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) {
+ Tcl_DecrRefCount(objPtr);
+ goto error;
+ }
+ }
+ } else if (flags & TCL_TRACE_UNSETS) {
+ int i, j;
+
+ if ((first == vPtr->length) || (first == SPECIAL_INDEX)) {
+ return (char *)"special vector index";
+ }
+ /*
+ * Collapse the vector from the point of the first unset element.
+ * Also flush any array variable entries so that the shift is
+ * reflected when the array variable is read.
+ */
+ for (i = first, j = last + 1; j < vPtr->length; i++, j++) {
+ vPtr->valueArr[i] = vPtr->valueArr[j];
+ }
+ vPtr->length -= ((last - first) + 1);
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ } else {
+ return (char *)"unknown variable trace flag";
+ }
+ if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) {
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ Tcl_ResetResult(interp);
+ return NULL;
+
+ error:
+ strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG);
+ message[MAX_ERR_MSG] = '\0';
+ return message;
+}
diff --git a/src/bltVecInt.h b/src/bltVecInt.h
new file mode 100644
index 0000000..5d30f97
--- /dev/null
+++ b/src/bltVecInt.h
@@ -0,0 +1,251 @@
+
+/*
+ * bltVecInt.h --
+ *
+ * Copyright 1995-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include "bltInt.h"
+#include <bltHash.h>
+#include <bltChain.h>
+#include <bltVector.h>
+
+#define VECTOR_THREAD_KEY "BLT Vector Data"
+#define VECTOR_MAGIC ((unsigned int) 0x46170277)
+
+/* These defines allow parsing of different types of indices */
+
+#define INDEX_SPECIAL (1<<0) /* Recognize "min", "max", and "++end" as
+ * valid indices */
+#define INDEX_COLON (1<<1) /* Also recognize a range of indices separated
+ * by a colon */
+#define INDEX_CHECK (1<<2) /* Verify that the specified index or range of
+ * indices are within limits */
+#define INDEX_ALL_FLAGS (INDEX_SPECIAL | INDEX_COLON | INDEX_CHECK)
+
+#define SPECIAL_INDEX -2
+
+#define FFT_NO_CONSTANT (1<<0)
+#define FFT_BARTLETT (1<<1)
+#define FFT_SPECTRUM (1<<2)
+
+typedef struct {
+ Blt_HashTable vectorTable; /* Table of vectors */
+ Blt_HashTable mathProcTable; /* Table of vector math functions */
+ Blt_HashTable indexProcTable;
+ Tcl_Interp *interp;
+ unsigned int nextId;
+} VectorInterpData;
+
+/*
+ * Vector --
+ *
+ * A vector is an array of double precision values. It can be accessed
+ * through a TCL command, a TCL array variable, or C API. The storage for
+ * the array points initially to a statically allocated buffer, but to
+ * malloc-ed memory if more is necessary.
+ *
+ * Vectors can be shared by several clients (for example, two different
+ * graph widgets). The data is shared. When a client wants to use a
+ * vector, it allocates a vector identifier, which identifies the client.
+ * Clients use this ID to specify a callback routine to be invoked
+ * whenever the vector is modified or destroyed. Whenever the vector is
+ * updated or destroyed, each client is notified of the change by their
+ * callback routine.
+ */
+
+typedef struct {
+
+ /*
+ * If you change these fields, make sure you change the definition of
+ * Blt_Vector in bltInt.h and blt.h too.
+ */
+
+ double *valueArr; /* Array of values (malloc-ed) */
+
+ int length; /* Current number of values in the array. */
+
+ int size; /* Maximum number of values that can be stored
+ * in the value array. */
+
+ double min, max; /* Minimum and maximum values in the vector */
+
+ int dirty; /* Indicates if the vector has been updated */
+
+ int reserved;
+
+ /* The following fields are local to this module */
+
+ const char *name; /* The namespace-qualified name of the vector.
+ * It points to the hash key allocated for the
+ * entry in the vector hash table. */
+
+ VectorInterpData *dataPtr;
+ Tcl_Interp *interp; /* Interpreter associated with the
+ * vector */
+
+ Blt_HashEntry *hashPtr; /* If non-NULL, pointer in a hash table to
+ * track the vectors in use. */
+
+ Tcl_FreeProc *freeProc; /* Address of procedure to call to release
+ * storage for the value array, Optionally can
+ * be one of the following: TCL_STATIC,
+ * TCL_DYNAMIC, or TCL_VOLATILE. */
+
+ const char *arrayName; /* The name of the TCL array variable mapped
+ * to the vector (malloc'ed). If NULL,
+ * indicates that the vector isn't mapped to
+ * any variable */
+
+ Tcl_Namespace *nsPtr; /* Namespace context of the vector itself. */
+
+ int offset; /* Offset from zero of the vector's starting
+ * index */
+
+ Tcl_Command cmdToken; /* Token for vector's TCL command. */
+
+ Blt_Chain chain; /* List of clients using this vector */
+
+ int notifyFlags; /* Notification flags. See definitions
+ * below */
+
+ int varFlags; /* Indicate if the variable is global,
+ * namespace, or local */
+
+ int freeOnUnset; /* For backward compatibility only: If
+ * non-zero, free the vector when its variable
+ * is unset. */
+ int flush;
+
+ int first, last; /* Selected region of vector. This is used
+ * mostly for the math routines */
+} Vector;
+
+#define NOTIFY_UPDATED ((int)BLT_VECTOR_NOTIFY_UPDATE)
+#define NOTIFY_DESTROYED ((int)BLT_VECTOR_NOTIFY_DESTROY)
+
+#define NOTIFY_NEVER (1<<3) /* Never notify clients of updates to
+ * the vector */
+#define NOTIFY_ALWAYS (1<<4) /* Notify clients after each update
+ * of the vector is made */
+#define NOTIFY_WHENIDLE (1<<5) /* Notify clients at the next idle point
+ * that the vector has been updated. */
+
+#define NOTIFY_PENDING (1<<6) /* A do-when-idle notification of the
+ * vector's clients is pending. */
+#define NOTIFY_NOW (1<<7) /* Notify clients of changes once
+ * immediately */
+
+#define NOTIFY_WHEN_MASK (NOTIFY_NEVER|NOTIFY_ALWAYS|NOTIFY_WHENIDLE)
+
+#define UPDATE_RANGE (1<<9) /* The data of the vector has changed.
+ * Update the min and max limits when
+ * they are needed */
+
+#define FindRange(array, first, last, min, max) \
+{ \
+ min = max = 0.0; \
+ if (first <= last) { \
+ register int i; \
+ min = max = array[first]; \
+ for (i = first + 1; i <= last; i++) { \
+ if (min > array[i]) { \
+ min = array[i]; \
+ } else if (max < array[i]) { \
+ max = array[i]; \
+ } \
+ } \
+ } \
+}
+
+BLT_EXTERN void Blt_Vec_InstallSpecialIndices(Blt_HashTable *tablePtr);
+
+BLT_EXTERN void Blt_Vec_InstallMathFunctions(Blt_HashTable *tablePtr);
+
+BLT_EXTERN void Blt_Vec_UninstallMathFunctions(Blt_HashTable *tablePtr);
+
+BLT_EXTERN VectorInterpData *Blt_Vec_GetInterpData (Tcl_Interp *interp);
+
+BLT_EXTERN double Blt_Vec_Max(Vector *vecObjPtr);
+BLT_EXTERN double Blt_Vec_Min(Vector *vecObjPtr);
+
+BLT_EXTERN Vector *Blt_Vec_New(VectorInterpData *dataPtr);
+
+BLT_EXTERN int Blt_Vec_Duplicate(Vector *destPtr, Vector *srcPtr);
+
+BLT_EXTERN int Blt_Vec_SetLength(Tcl_Interp *interp, Vector *vPtr,
+ int length);
+
+BLT_EXTERN int Blt_Vec_SetSize(Tcl_Interp *interp, Vector *vPtr,
+ int size);
+
+BLT_EXTERN int Blt_Vec_ChangeLength(Tcl_Interp *interp, Vector *vPtr,
+ int length);
+
+BLT_EXTERN Vector *Blt_Vec_ParseElement(Tcl_Interp *interp,
+ VectorInterpData *dataPtr, const char *start, const char **endPtr,
+ int flags);
+
+BLT_EXTERN void Blt_Vec_Free(Vector *vPtr);
+
+BLT_EXTERN size_t *Blt_Vec_SortMap(Vector **vectors, int nVectors);
+
+BLT_EXTERN int Blt_Vec_LookupName(VectorInterpData *dataPtr,
+ const char *vecName, Vector **vPtrPtr);
+
+BLT_EXTERN Vector *Blt_Vec_Create(VectorInterpData *dataPtr,
+ const char *name, const char *cmdName, const char *varName,
+ int *newPtr);
+
+BLT_EXTERN void Blt_Vec_UpdateRange(Vector *vPtr);
+
+BLT_EXTERN void Blt_Vec_UpdateClients(Vector *vPtr);
+
+BLT_EXTERN void Blt_Vec_FlushCache(Vector *vPtr);
+
+BLT_EXTERN int Blt_Vec_Reset(Vector *vPtr, double *dataArr,
+ int nValues, int arraySize, Tcl_FreeProc *freeProc);
+
+BLT_EXTERN int Blt_Vec_GetIndex(Tcl_Interp *interp, Vector *vPtr,
+ const char *string, int *indexPtr, int flags,
+ Blt_VectorIndexProc **procPtrPtr);
+
+BLT_EXTERN int Blt_Vec_GetIndexRange(Tcl_Interp *interp, Vector *vPtr,
+ const char *string, int flags, Blt_VectorIndexProc **procPtrPtr);
+
+BLT_EXTERN int Blt_Vec_MapVariable(Tcl_Interp *interp, Vector *vPtr,
+ const char *name);
+
+BLT_EXTERN int Blt_Vec_FFT(Tcl_Interp *interp, Vector *realPtr,
+ Vector *phasesPtr, Vector *freqPtr, double delta,
+ int flags, Vector *srcPtr);
+
+BLT_EXTERN int Blt_Vec_InverseFFT(Tcl_Interp *interp, Vector *iSrcPtr,
+ Vector *rDestPtr, Vector *iDestPtr, Vector *srcPtr);
+
+BLT_EXTERN Tcl_ObjCmdProc Blt_Vec_InstCmd;
+
+BLT_EXTERN Tcl_VarTraceProc Blt_Vec_VarTrace;
+
+BLT_EXTERN Tcl_IdleProc Blt_Vec_NotifyClients;
+
diff --git a/src/bltVecMath.C b/src/bltVecMath.C
new file mode 100644
index 0000000..a0bd4af
--- /dev/null
+++ b/src/bltVecMath.C
@@ -0,0 +1,1895 @@
+
+/*
+ * bltVecMath.c --
+ *
+ * This module implements mathematical expressions with vector data
+ * objects.
+ *
+ * Copyright 1995-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "bltVecInt.h"
+#include "bltNsUtil.h"
+#include "bltParse.h"
+
+/*
+ * Three types of math functions:
+ *
+ * ComponentProc Function is applied in multiple calls to
+ * each component of the vector.
+ * VectorProc Entire vector is passed, each component is
+ * modified.
+ * ScalarProc Entire vector is passed, single scalar value
+ * is returned.
+ */
+
+typedef double (ComponentProc)(double value);
+typedef int (VectorProc)(Vector *vPtr);
+typedef double (ScalarProc)(Vector *vPtr);
+
+/*
+ * Built-in math functions:
+ */
+typedef int (GenericMathProc) _ANSI_ARGS_(ANYARGS);
+
+/*
+ * MathFunction --
+ *
+ * Contains information about math functions that can be called
+ * for vectors. The table of math functions is global within the
+ * application. So you can't define two different "sqrt"
+ * functions.
+ */
+typedef struct {
+ const char *name; /* Name of built-in math function. If
+ * NULL, indicates that the function
+ * was user-defined and dynamically
+ * allocated. Function names are
+ * global across all interpreters. */
+
+ void *proc; /* Procedure that implements this math
+ * function. */
+
+ ClientData clientData; /* Argument to pass when invoking the
+ * function. */
+
+} MathFunction;
+
+
+/*
+ * Macros for testing floating-point values for certain special cases:
+ *
+ * IS_NAN Test for not-a-number by comparing a value against itself
+ * IF_INF Test for infinity by comparing against the largest floating
+ * point value.
+ */
+
+#define IS_NAN(v) ((v) != (v))
+
+#ifdef DBL_MAX
+# define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
+#else
+# define IS_INF(v) 0
+#endif
+
+/* The data structure below is used to describe an expression value,
+ * which can be either a double-precision floating-point value, or a
+ * string. A given number has only one value at a time. */
+
+#define STATIC_STRING_SPACE 150
+
+/*
+ * Tokens --
+ *
+ * The token types are defined below. In addition, there is a
+ * table associating a precedence with each operator. The order
+ * of types is important. Consult the code before changing it.
+ */
+enum Tokens {
+ VALUE, OPEN_PAREN, CLOSE_PAREN, COMMA, END, UNKNOWN,
+ MULT = 8, DIVIDE, MOD, PLUS, MINUS,
+ LEFT_SHIFT, RIGHT_SHIFT,
+ LESS, GREATER, LEQ, GEQ, EQUAL, NEQ,
+ OLD_BIT_AND, EXPONENT, OLD_BIT_OR, OLD_QUESTY, OLD_COLON,
+ AND, OR, UNARY_MINUS, OLD_UNARY_PLUS, NOT, OLD_BIT_NOT
+};
+
+typedef struct {
+ Vector *vPtr;
+ char staticSpace[STATIC_STRING_SPACE];
+ ParseValue pv; /* Used to hold a string value, if any. */
+} Value;
+
+/*
+ * ParseInfo --
+ *
+ * The data structure below describes the state of parsing an
+ * expression. It's passed among the routines in this module.
+ */
+typedef struct {
+ const char *expr; /* The entire right-hand side of the
+ * expression, as originally passed to
+ * Blt_ExprVector. */
+
+ const char *nextPtr; /* Position of the next character to
+ * be scanned from the expression
+ * string. */
+
+ enum Tokens token; /* Type of the last token to be parsed
+ * from nextPtr. See below for
+ * definitions. Corresponds to the
+ * characters just before nextPtr. */
+
+} ParseInfo;
+
+/*
+ * Precedence table. The values for non-operator token types are ignored.
+ */
+static int precTable[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 12, 12, /* MULT, DIVIDE, MOD */
+ 11, 11, /* PLUS, MINUS */
+ 10, 10, /* LEFT_SHIFT, RIGHT_SHIFT */
+ 9, 9, 9, 9, /* LESS, GREATER, LEQ, GEQ */
+ 8, 8, /* EQUAL, NEQ */
+ 7, /* OLD_BIT_AND */
+ 13, /* EXPONENTIATION */
+ 5, /* OLD_BIT_OR */
+ 4, /* AND */
+ 3, /* OR */
+ 2, /* OLD_QUESTY */
+ 1, /* OLD_COLON */
+ 14, 14, 14, 14 /* UNARY_MINUS, OLD_UNARY_PLUS, NOT,
+ * OLD_BIT_NOT */
+};
+
+
+/*
+ * Forward declarations.
+ */
+
+static int NextValue(Tcl_Interp *interp, ParseInfo *piPtr, int prec,
+ Value *valuePtr);
+
+#include <bltMath.h>
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Sort --
+ *
+ * A vector math function. Sorts the values of the given
+ * vector.
+ *
+ * Results:
+ * Always TCL_OK.
+ *
+ * Side Effects:
+ * The vector is sorted.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+Sort(Vector *vPtr)
+{
+ size_t *map;
+ double *values;
+ int i;
+
+ map = Blt_Vec_SortMap(&vPtr, 1);
+ values = Blt_AssertMalloc(sizeof(double) * vPtr->length);
+ for(i = vPtr->first; i <= vPtr->last; i++) {
+ values[i] = vPtr->valueArr[map[i]];
+ }
+ Blt_Free(map);
+ for (i = vPtr->first; i <= vPtr->last; i++) {
+ vPtr->valueArr[i] = values[i];
+ }
+ Blt_Free(values);
+ return TCL_OK;
+}
+
+static double
+Length(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+
+ return (double)(vPtr->last - vPtr->first + 1);
+}
+
+double
+Blt_VecMax(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+
+ return Blt_Vec_Max(vPtr);
+}
+
+double
+Blt_VecMin(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+
+ return Blt_Vec_Min(vPtr);
+}
+
+
+static double
+Product(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double prod;
+ double *vp, *vend;
+
+ prod = 1.0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ prod *= *vp;
+ }
+ return prod;
+}
+
+static double
+Sum(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double sum, c;
+ double *vp, *vend;
+
+ /* Kahan summation algorithm */
+
+ vp = vPtr->valueArr + vPtr->first;
+ sum = *vp++;
+ c = 0.0; /* A running compensation for lost
+ * low-order bits.*/
+ for (vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ double y, t;
+
+ y = *vp - c; /* So far, so good: c is zero.*/
+ t = sum + y; /* Alas, sum is big, y small, so
+ * low-order digits of y are lost.*/
+ c = (t - sum) - y; /* (t - sum) recovers the high-order
+ * part of y; subtracting y recovers
+ * -(low part of y) */
+ sum = t;
+ }
+ return sum;
+}
+
+static double
+Mean(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double sum;
+ int n;
+
+ sum = Sum(vectorPtr);
+ n = vPtr->last - vPtr->first + 1;
+ return sum / (double)n;
+}
+
+/*
+ * var = 1/N Sum( (x[i] - mean)^2 )
+ */
+static double
+Variance(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double var, mean;
+ double *vp, *vend;
+ int count;
+
+ mean = Mean(vectorPtr);
+ var = 0.0;
+ count = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ double dx;
+
+ dx = *vp - mean;
+ var += dx * dx;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ return var;
+}
+
+/*
+ * skew = Sum( (x[i] - mean)^3 ) / (var^3/2)
+ */
+static double
+Skew(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double diff, var, skew, mean, diffsq;
+ double *vp, *vend;
+ int count;
+
+ mean = Mean(vectorPtr);
+ var = skew = 0.0;
+ count = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ diff = *vp - mean;
+ diff = FABS(diff);
+ diffsq = diff * diff;
+ var += diffsq;
+ skew += diffsq * diff;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ skew /= count * var * sqrt(var);
+ return skew;
+}
+
+static double
+StdDeviation(Blt_Vector *vectorPtr)
+{
+ double var;
+
+ var = Variance(vectorPtr);
+ if (var > 0.0) {
+ return sqrt(var);
+ }
+ return 0.0;
+}
+
+
+static double
+AvgDeviation(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double diff, avg, mean;
+ double *vp, *vend;
+ int count;
+
+ mean = Mean(vectorPtr);
+ avg = 0.0;
+ count = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ diff = *vp - mean;
+ avg += FABS(diff);
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ avg /= (double)count;
+ return avg;
+}
+
+
+static double
+Kurtosis(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double diff, diffsq, kurt, var, mean;
+ double *vp, *vend;
+ int count;
+
+ mean = Mean(vectorPtr);
+ var = kurt = 0.0;
+ count = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ diff = *vp - mean;
+ diffsq = diff * diff;
+ var += diffsq;
+ kurt += diffsq * diffsq;
+ count++;
+ }
+ if (count < 2) {
+ return 0.0;
+ }
+ var /= (double)(count - 1);
+ if (var == 0.0) {
+ return 0.0;
+ }
+ kurt /= (count * var * var);
+ return kurt - 3.0; /* Fisher Kurtosis */
+}
+
+
+static double
+Median(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ size_t *map;
+ double q2;
+ int mid;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+ map = Blt_Vec_SortMap(&vPtr, 1);
+ mid = (vPtr->length - 1) / 2;
+
+ /*
+ * Determine Q2 by checking if the number of elements [0..n-1] is
+ * odd or even. If even, we must take the average of the two
+ * middle values.
+ */
+ if (vPtr->length & 1) { /* Odd */
+ q2 = vPtr->valueArr[map[mid]];
+ } else { /* Even */
+ q2 = (vPtr->valueArr[map[mid]] +
+ vPtr->valueArr[map[mid + 1]]) * 0.5;
+ }
+ Blt_Free(map);
+ return q2;
+}
+
+static double
+Q1(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double q1;
+ size_t *map;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+ map = Blt_Vec_SortMap(&vPtr, 1);
+
+ if (vPtr->length < 4) {
+ q1 = vPtr->valueArr[map[0]];
+ } else {
+ int mid, q;
+
+ mid = (vPtr->length - 1) / 2;
+ q = mid / 2;
+
+ /*
+ * Determine Q1 by checking if the number of elements in the
+ * bottom half [0..mid) is odd or even. If even, we must
+ * take the average of the two middle values.
+ */
+ if (mid & 1) { /* Odd */
+ q1 = vPtr->valueArr[map[q]];
+ } else { /* Even */
+ q1 = (vPtr->valueArr[map[q]] +
+ vPtr->valueArr[map[q + 1]]) * 0.5;
+ }
+ }
+ Blt_Free(map);
+ return q1;
+}
+
+static double
+Q3(Blt_Vector *vectorPtr)
+{
+ Vector *vPtr = (Vector *)vectorPtr;
+ double q3;
+ size_t *map;
+
+ if (vPtr->length == 0) {
+ return -DBL_MAX;
+ }
+
+ map = Blt_Vec_SortMap(&vPtr, 1);
+
+ if (vPtr->length < 4) {
+ q3 = vPtr->valueArr[map[vPtr->length - 1]];
+ } else {
+ int mid, q;
+
+ mid = (vPtr->length - 1) / 2;
+ q = (vPtr->length + mid) / 2;
+
+ /*
+ * Determine Q3 by checking if the number of elements in the
+ * upper half (mid..n-1] is odd or even. If even, we must
+ * take the average of the two middle values.
+ */
+ if (mid & 1) { /* Odd */
+ q3 = vPtr->valueArr[map[q]];
+ } else { /* Even */
+ q3 = (vPtr->valueArr[map[q]] +
+ vPtr->valueArr[map[q + 1]]) * 0.5;
+ }
+ }
+ Blt_Free(map);
+ return q3;
+}
+
+
+static int
+Norm(Blt_Vector *vector)
+{
+ Vector *vPtr = (Vector *)vector;
+ double norm, range, min, max;
+ int i;
+
+ min = Blt_Vec_Min(vPtr);
+ max = Blt_Vec_Max(vPtr);
+ range = max - min;
+ for (i = 0; i < vPtr->length; i++) {
+ norm = (vPtr->valueArr[i] - min) / range;
+ vPtr->valueArr[i] = norm;
+ }
+ return TCL_OK;
+}
+
+
+static double
+Nonzeros(Blt_Vector *vector)
+{
+ Vector *vPtr = (Vector *)vector;
+ int count;
+ double *vp, *vend;
+
+ count = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ if (*vp == 0.0) {
+ count++;
+ }
+ }
+ return (double) count;
+}
+
+static double
+Fabs(double value)
+{
+ if (value < 0.0) {
+ return -value;
+ }
+ return value;
+}
+
+static double
+Round(double value)
+{
+ if (value < 0.0) {
+ return ceil(value - 0.5);
+ } else {
+ return floor(value + 0.5);
+ }
+}
+
+static double
+Fmod(double x, double y)
+{
+ if (y == 0.0) {
+ return 0.0;
+ }
+ return x - (floor(x / y) * y);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MathError --
+ *
+ * This procedure is called when an error occurs during a
+ * floating-point operation. It reads errno and sets
+ * interp->result accordingly.
+ *
+ * Results:
+ * Interp->result is set to hold an error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+MathError(
+ Tcl_Interp *interp, /* Where to store error message. */
+ double value) /* Value returned after error; used to
+ * distinguish underflows from
+ * overflows. */
+{
+ if ((errno == EDOM) || (value != value)) {
+ Tcl_AppendResult(interp, "domain error: argument not in valid range",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", interp->result,
+ (char *)NULL);
+ } else if ((errno == ERANGE) || IS_INF(value)) {
+ if (value == 0.0) {
+ Tcl_AppendResult(interp,
+ "floating-point value too small to represent",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", interp->result,
+ (char *)NULL);
+ } else {
+ Tcl_AppendResult(interp,
+ "floating-point value too large to represent",
+ (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", interp->result,
+ (char *)NULL);
+ }
+ } else {
+ Tcl_AppendResult(interp, "unknown floating-point error, ",
+ "errno = ", Blt_Itoa(errno), (char *)NULL);
+ Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", interp->result,
+ (char *)NULL);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ParseString --
+ *
+ * Given a string (such as one coming from command or variable
+ * substitution), make a Value based on the string. The value
+ * will be a floating-point or integer, if possible, or else it
+ * will just be a copy of the string.
+ *
+ * Results:
+ * TCL_OK is returned under normal circumstances, and TCL_ERROR
+ * is returned if a floating-point overflow or underflow occurred
+ * while reading in a number. The value at *valuePtr is modified
+ * to hold a number, if possible.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+ParseString(
+ Tcl_Interp *interp, /* Where to store error message. */
+ const char *string, /* String to turn into value. */
+ Value *valuePtr) /* Where to store value information.
+ * Caller must have initialized pv field. */
+{
+ const char *endPtr;
+ double value;
+
+ errno = 0;
+
+ /*
+ * The string can be either a number or a vector. First try to
+ * convert the string to a number. If that fails then see if
+ * we can find a vector by that name.
+ */
+
+ value = strtod(string, (char **)&endPtr);
+ if ((endPtr != string) && (*endPtr == '\0')) {
+ if (errno != 0) {
+ Tcl_ResetResult(interp);
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ /* Numbers are stored as single element vectors. */
+ if (Blt_Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ valuePtr->vPtr->valueArr[0] = value;
+ return TCL_OK;
+ } else {
+ Vector *vPtr;
+
+ while (isspace(UCHAR(*string))) {
+ string++; /* Skip spaces leading the vector name. */
+ }
+ vPtr = Blt_Vec_ParseElement(interp, valuePtr->vPtr->dataPtr,
+ string, &endPtr, NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (*endPtr != '\0') {
+ Tcl_AppendResult(interp, "extra characters after vector",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /* Copy the designated vector to our temporary. */
+ Blt_Vec_Duplicate(valuePtr->vPtr, vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ParseMathFunction --
+ *
+ * This procedure is invoked to parse a math function from an
+ * expression string, carry out the function, and return the
+ * value computed.
+ *
+ * Results:
+ * TCL_OK is returned if all went well and the function's value
+ * was computed successfully. If the name doesn't match any
+ * known math function, returns TCL_RETURN. And if a format error
+ * was found, TCL_ERROR is returned and an error message is left
+ * in interp->result.
+ *
+ * After a successful return piPtr will be updated to point to
+ * the character just after the function call, the token is set
+ * to VALUE, and the value is stored in valuePtr.
+ *
+ * Side effects:
+ * Embedded commands could have arbitrary side-effects.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ParseMathFunction(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting. */
+ const char *start, /* Start of string to parse */
+ ParseInfo *piPtr, /* Describes the state of the parse.
+ * piPtr->nextPtr must point to the
+ * first character of the function's
+ * name. */
+ Value *valuePtr) /* Where to store value, if that is
+ * what's parsed from string. Caller
+ * must have initialized pv field
+ * correctly. */
+{
+ Blt_HashEntry *hPtr;
+ MathFunction *mathPtr; /* Info about math function. */
+ char *p;
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ GenericMathProc *proc;
+
+ /*
+ * Find the end of the math function's name and lookup the
+ * record for the function.
+ */
+ p = (char *)start;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ piPtr->nextPtr = p;
+ while (isalnum(UCHAR(*p)) || (*p == '_')) {
+ p++;
+ }
+ if (*p != '(') {
+ return TCL_RETURN; /* Must start with open parenthesis */
+ }
+ dataPtr = valuePtr->vPtr->dataPtr;
+ *p = '\0';
+ hPtr = Blt_FindHashEntry(&dataPtr->mathProcTable, piPtr->nextPtr);
+ *p = '(';
+ if (hPtr == NULL) {
+ return TCL_RETURN; /* Name doesn't match any known function */
+ }
+ /* Pick up the single value as the argument to the function */
+ piPtr->token = OPEN_PAREN;
+ piPtr->nextPtr = p + 1;
+ valuePtr->pv.next = valuePtr->pv.buffer;
+ if (NextValue(interp, piPtr, -1, valuePtr) != TCL_OK) {
+ return TCL_ERROR; /* Parse error */
+ }
+ if (piPtr->token != CLOSE_PAREN) {
+ Tcl_AppendResult(interp, "unmatched parentheses in expression \"",
+ piPtr->expr, "\"", (char *)NULL);
+ return TCL_ERROR; /* Missing right parenthesis */
+ }
+ mathPtr = Blt_GetHashValue(hPtr);
+ proc = mathPtr->proc;
+ if ((*proc) (mathPtr->clientData, interp, valuePtr->vPtr) != TCL_OK) {
+ return TCL_ERROR; /* Function invocation error */
+ }
+ piPtr->token = VALUE;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NextToken --
+ *
+ * Lexical analyzer for expression parser: parses a single value,
+ * operator, or other syntactic element from an expression string.
+ *
+ * Results:
+ * TCL_OK is returned unless an error occurred while doing lexical
+ * analysis or executing an embedded command. In that case a
+ * standard TCL error is returned, using interp->result to hold
+ * an error message. In the event of a successful return, the token
+ * and field in piPtr is updated to refer to the next symbol in
+ * the expression string, and the expr field is advanced past that
+ * token; if the token is a value, then the value is stored at
+ * valuePtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NextToken(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting. */
+ ParseInfo *piPtr, /* Describes the state of the parse. */
+ Value *valuePtr) /* Where to store value, if that is
+ * what's parsed from string. Caller
+ * must have initialized pv field
+ * correctly. */
+{
+ const char *p;
+ const char *endPtr;
+ const char *var;
+ int result;
+
+ p = piPtr->nextPtr;
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p == '\0') {
+ piPtr->token = END;
+ piPtr->nextPtr = p;
+ return TCL_OK;
+ }
+ /*
+ * Try to parse the token as a floating-point number. But check
+ * that the first character isn't a "-" or "+", which "strtod"
+ * will happily accept as an unary operator. Otherwise, we might
+ * accidently treat a binary operator as unary by mistake, which
+ * will eventually cause a syntax error.
+ */
+ if ((*p != '-') && (*p != '+')) {
+ double value;
+
+ errno = 0;
+ value = strtod(p, (char **)&endPtr);
+ if (endPtr != p) {
+ if (errno != 0) {
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ piPtr->token = VALUE;
+ piPtr->nextPtr = endPtr;
+
+ /*
+ * Save the single floating-point value as an 1-component vector.
+ */
+ if (Blt_Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ valuePtr->vPtr->valueArr[0] = value;
+ return TCL_OK;
+ }
+ }
+ piPtr->nextPtr = p + 1;
+ switch (*p) {
+ case '$':
+ piPtr->token = VALUE;
+ var = Tcl_ParseVar(interp, p, &endPtr);
+ if (var == NULL) {
+ return TCL_ERROR;
+ }
+ piPtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, var, valuePtr);
+ return result;
+
+ case '[':
+ piPtr->token = VALUE;
+ result = Blt_ParseNestedCmd(interp, p + 1, 0, &endPtr, &valuePtr->pv);
+ if (result != TCL_OK) {
+ return result;
+ }
+ piPtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '"':
+ piPtr->token = VALUE;
+ result = Blt_ParseQuotes(interp, p + 1, '"', 0, &endPtr, &valuePtr->pv);
+ if (result != TCL_OK) {
+ return result;
+ }
+ piPtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '{':
+ piPtr->token = VALUE;
+ result = Blt_ParseBraces(interp, p + 1, &endPtr, &valuePtr->pv);
+ if (result != TCL_OK) {
+ return result;
+ }
+ piPtr->nextPtr = endPtr;
+ Tcl_ResetResult(interp);
+ result = ParseString(interp, valuePtr->pv.buffer, valuePtr);
+ return result;
+
+ case '(':
+ piPtr->token = OPEN_PAREN;
+ break;
+
+ case ')':
+ piPtr->token = CLOSE_PAREN;
+ break;
+
+ case ',':
+ piPtr->token = COMMA;
+ break;
+
+ case '*':
+ piPtr->token = MULT;
+ break;
+
+ case '/':
+ piPtr->token = DIVIDE;
+ break;
+
+ case '%':
+ piPtr->token = MOD;
+ break;
+
+ case '+':
+ piPtr->token = PLUS;
+ break;
+
+ case '-':
+ piPtr->token = MINUS;
+ break;
+
+ case '^':
+ piPtr->token = EXPONENT;
+ break;
+
+ case '<':
+ switch (*(p + 1)) {
+ case '<':
+ piPtr->nextPtr = p + 2;
+ piPtr->token = LEFT_SHIFT;
+ break;
+ case '=':
+ piPtr->nextPtr = p + 2;
+ piPtr->token = LEQ;
+ break;
+ default:
+ piPtr->token = LESS;
+ break;
+ }
+ break;
+
+ case '>':
+ switch (*(p + 1)) {
+ case '>':
+ piPtr->nextPtr = p + 2;
+ piPtr->token = RIGHT_SHIFT;
+ break;
+ case '=':
+ piPtr->nextPtr = p + 2;
+ piPtr->token = GEQ;
+ break;
+ default:
+ piPtr->token = GREATER;
+ break;
+ }
+ break;
+
+ case '=':
+ if (*(p + 1) == '=') {
+ piPtr->nextPtr = p + 2;
+ piPtr->token = EQUAL;
+ } else {
+ piPtr->token = UNKNOWN;
+ }
+ break;
+
+ case '&':
+ if (*(p + 1) == '&') {
+ piPtr->nextPtr = p + 2;
+ piPtr->token = AND;
+ } else {
+ piPtr->token = UNKNOWN;
+ }
+ break;
+
+ case '|':
+ if (*(p + 1) == '|') {
+ piPtr->nextPtr = p + 2;
+ piPtr->token = OR;
+ } else {
+ piPtr->token = UNKNOWN;
+ }
+ break;
+
+ case '!':
+ if (*(p + 1) == '=') {
+ piPtr->nextPtr = p + 2;
+ piPtr->token = NEQ;
+ } else {
+ piPtr->token = NOT;
+ }
+ break;
+
+ default:
+ piPtr->token = VALUE;
+ result = ParseMathFunction(interp, p, piPtr, valuePtr);
+ if ((result == TCL_OK) || (result == TCL_ERROR)) {
+ return result;
+ } else {
+ Vector *vPtr;
+
+ while (isspace(UCHAR(*p))) {
+ p++; /* Skip spaces leading the vector name. */
+ }
+ vPtr = Blt_Vec_ParseElement(interp, valuePtr->vPtr->dataPtr,
+ p, &endPtr, NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Blt_Vec_Duplicate(valuePtr->vPtr, vPtr);
+ piPtr->nextPtr = endPtr;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * NextValue --
+ *
+ * Parse a "value" from the remainder of the expression in piPtr.
+ *
+ * Results:
+ * Normally TCL_OK is returned. The value of the expression is
+ * returned in *valuePtr. If an error occurred, then interp->result
+ * contains an error message and TCL_ERROR is returned.
+ * InfoPtr->token will be left pointing to the token AFTER the
+ * expression, and piPtr->nextPtr will point to the character just
+ * after the terminating token.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+NextValue(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting. */
+ ParseInfo *piPtr, /* Describes the state of the parse
+ * just before the value (i.e. NextToken will
+ * be called to get first token of value). */
+ int prec, /* Treat any un-parenthesized operator
+ * with precedence <= this as the end
+ * of the expression. */
+ Value *valuePtr) /* Where to store the value of the expression.
+ * Caller must have initialized pv field. */
+{
+ Value value2; /* Second operand for current operator. */
+ int operator; /* Current operator (either unary or binary). */
+ int gotOp; /* Non-zero means already lexed the operator
+ * (while picking up value for unary operator).
+ * Don't lex again. */
+ int result;
+ Vector *vPtr, *v2Ptr;
+ int i;
+
+ /*
+ * There are two phases to this procedure. First, pick off an initial
+ * value. Then, parse (binary operator, value) pairs until done.
+ */
+
+ vPtr = valuePtr->vPtr;
+ v2Ptr = Blt_Vec_New(vPtr->dataPtr);
+ gotOp = FALSE;
+ value2.vPtr = v2Ptr;
+ value2.pv.buffer = value2.pv.next = value2.staticSpace;
+ value2.pv.end = value2.pv.buffer + STATIC_STRING_SPACE - 1;
+ value2.pv.expandProc = Blt_ExpandParseValue;
+ value2.pv.clientData = NULL;
+
+ result = NextToken(interp, piPtr, valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if (piPtr->token == OPEN_PAREN) {
+
+ /* Parenthesized sub-expression. */
+
+ result = NextValue(interp, piPtr, -1, valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if (piPtr->token != CLOSE_PAREN) {
+ Tcl_AppendResult(interp, "unmatched parentheses in expression \"",
+ piPtr->expr, "\"", (char *)NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+ } else {
+ if (piPtr->token == MINUS) {
+ piPtr->token = UNARY_MINUS;
+ }
+ if (piPtr->token >= UNARY_MINUS) {
+ operator = piPtr->token;
+ result = NextValue(interp, piPtr, precTable[operator], valuePtr);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ gotOp = TRUE;
+ /* Process unary operators. */
+ switch (operator) {
+ case UNARY_MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = -(vPtr->valueArr[i]);
+ }
+ break;
+
+ case NOT:
+ for(i = 0; i < vPtr->length; i++) {
+ vPtr->valueArr[i] = (double)(!vPtr->valueArr[i]);
+ }
+ break;
+ default:
+ Tcl_AppendResult(interp, "unknown operator", (char *)NULL);
+ goto error;
+ }
+ } else if (piPtr->token != VALUE) {
+ Tcl_AppendResult(interp, "missing operand", (char *)NULL);
+ goto error;
+ }
+ }
+ if (!gotOp) {
+ result = NextToken(interp, piPtr, &value2);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ }
+ /*
+ * Got the first operand. Now fetch (operator, operand) pairs.
+ */
+ for (;;) {
+ operator = piPtr->token;
+
+ value2.pv.next = value2.pv.buffer;
+ if ((operator < MULT) || (operator >= UNARY_MINUS)) {
+ if ((operator == END) || (operator == CLOSE_PAREN) ||
+ (operator == COMMA)) {
+ result = TCL_OK;
+ goto done;
+ } else {
+ Tcl_AppendResult(interp, "bad operator", (char *)NULL);
+ goto error;
+ }
+ }
+ if (precTable[operator] <= prec) {
+ result = TCL_OK;
+ goto done;
+ }
+ result = NextValue(interp, piPtr, precTable[operator], &value2);
+ if (result != TCL_OK) {
+ goto done;
+ }
+ if ((piPtr->token < MULT) && (piPtr->token != VALUE) &&
+ (piPtr->token != END) && (piPtr->token != CLOSE_PAREN) &&
+ (piPtr->token != COMMA)) {
+ Tcl_AppendResult(interp, "unexpected token in expression",
+ (char *)NULL);
+ goto error;
+ }
+ /*
+ * At this point we have two vectors and an operator.
+ */
+
+ if (v2Ptr->length == 1) {
+ double *opnd;
+ double scalar;
+
+ /*
+ * 2nd operand is a scalar.
+ */
+ scalar = v2Ptr->valueArr[0];
+ opnd = vPtr->valueArr;
+ switch (operator) {
+ case MULT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] *= scalar;
+ }
+ break;
+
+ case DIVIDE:
+ if (scalar == 0.0) {
+ Tcl_AppendResult(interp, "divide by zero", (char *)NULL);
+ goto error;
+ }
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] /= scalar;
+ }
+ break;
+
+ case PLUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] += scalar;
+ }
+ break;
+
+ case MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] -= scalar;
+ }
+ break;
+
+ case EXPONENT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = pow(opnd[i], scalar);
+ }
+ break;
+
+ case MOD:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = Fmod(opnd[i], scalar);
+ }
+ break;
+
+ case LESS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] < scalar);
+ }
+ break;
+
+ case GREATER:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] > scalar);
+ }
+ break;
+
+ case LEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] <= scalar);
+ }
+ break;
+
+ case GEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] >= scalar);
+ }
+ break;
+
+ case EQUAL:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] == scalar);
+ }
+ break;
+
+ case NEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] != scalar);
+ }
+ break;
+
+ case AND:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] && scalar);
+ }
+ break;
+
+ case OR:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] || scalar);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ {
+ int offset;
+
+ offset = (int)scalar % vPtr->length;
+ if (offset > 0) {
+ double *hold;
+ int j;
+
+ hold = Blt_AssertMalloc(sizeof(double) * offset);
+ for (i = 0; i < offset; i++) {
+ hold[i] = opnd[i];
+ }
+ for (i = offset, j = 0; i < vPtr->length; i++, j++) {
+ opnd[j] = opnd[i];
+ }
+ for (i = 0, j = vPtr->length - offset;
+ j < vPtr->length; i++, j++) {
+ opnd[j] = hold[i];
+ }
+ Blt_Free(hold);
+ }
+ }
+ break;
+
+ case RIGHT_SHIFT:
+ {
+ int offset;
+
+ offset = (int)scalar % vPtr->length;
+ if (offset > 0) {
+ double *hold;
+ int j;
+
+ hold = Blt_AssertMalloc(sizeof(double) * offset);
+ for (i = vPtr->length - offset, j = 0;
+ i < vPtr->length; i++, j++) {
+ hold[j] = opnd[i];
+ }
+ for (i = vPtr->length - offset - 1,
+ j = vPtr->length - 1; i >= 0; i--, j--) {
+ opnd[j] = opnd[i];
+ }
+ for (i = 0; i < offset; i++) {
+ opnd[i] = hold[i];
+ }
+ Blt_Free(hold);
+ }
+ }
+ break;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+
+ } else if (vPtr->length == 1) {
+ double *opnd;
+ double scalar;
+
+ /*
+ * 1st operand is a scalar.
+ */
+ scalar = vPtr->valueArr[0];
+ Blt_Vec_Duplicate(vPtr, v2Ptr);
+ opnd = vPtr->valueArr;
+ switch (operator) {
+ case MULT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] *= scalar;
+ }
+ break;
+
+ case PLUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] += scalar;
+ }
+ break;
+
+ case DIVIDE:
+ for(i = 0; i < vPtr->length; i++) {
+ if (opnd[i] == 0.0) {
+ Tcl_AppendResult(interp, "divide by zero",
+ (char *)NULL);
+ goto error;
+ }
+ opnd[i] = (scalar / opnd[i]);
+ }
+ break;
+
+ case MINUS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = scalar - opnd[i];
+ }
+ break;
+
+ case EXPONENT:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = pow(scalar, opnd[i]);
+ }
+ break;
+
+ case MOD:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = Fmod(scalar, opnd[i]);
+ }
+ break;
+
+ case LESS:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar < opnd[i]);
+ }
+ break;
+
+ case GREATER:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar > opnd[i]);
+ }
+ break;
+
+ case LEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar >= opnd[i]);
+ }
+ break;
+
+ case GEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(scalar <= opnd[i]);
+ }
+ break;
+
+ case EQUAL:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] == scalar);
+ }
+ break;
+
+ case NEQ:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] != scalar);
+ }
+ break;
+
+ case AND:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] && scalar);
+ }
+ break;
+
+ case OR:
+ for(i = 0; i < vPtr->length; i++) {
+ opnd[i] = (double)(opnd[i] || scalar);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ case RIGHT_SHIFT:
+ Tcl_AppendResult(interp, "second shift operand must be scalar",
+ (char *)NULL);
+ goto error;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+ } else {
+ double *opnd1, *opnd2;
+ /*
+ * Carry out the function of the specified operator.
+ */
+ if (vPtr->length != v2Ptr->length) {
+ Tcl_AppendResult(interp, "vectors are different lengths",
+ (char *)NULL);
+ goto error;
+ }
+ opnd1 = vPtr->valueArr, opnd2 = v2Ptr->valueArr;
+ switch (operator) {
+ case MULT:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] *= opnd2[i];
+ }
+ break;
+
+ case DIVIDE:
+ for (i = 0; i < vPtr->length; i++) {
+ if (opnd2[i] == 0.0) {
+ Tcl_AppendResult(interp,
+ "can't divide by 0.0 vector component",
+ (char *)NULL);
+ goto error;
+ }
+ opnd1[i] /= opnd2[i];
+ }
+ break;
+
+ case PLUS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] += opnd2[i];
+ }
+ break;
+
+ case MINUS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] -= opnd2[i];
+ }
+ break;
+
+ case MOD:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = Fmod(opnd1[i], opnd2[i]);
+ }
+ break;
+
+ case EXPONENT:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = pow(opnd1[i], opnd2[i]);
+ }
+ break;
+
+ case LESS:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] < opnd2[i]);
+ }
+ break;
+
+ case GREATER:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] > opnd2[i]);
+ }
+ break;
+
+ case LEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] <= opnd2[i]);
+ }
+ break;
+
+ case GEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] >= opnd2[i]);
+ }
+ break;
+
+ case EQUAL:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] == opnd2[i]);
+ }
+ break;
+
+ case NEQ:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] != opnd2[i]);
+ }
+ break;
+
+ case AND:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] && opnd2[i]);
+ }
+ break;
+
+ case OR:
+ for (i = 0; i < vPtr->length; i++) {
+ opnd1[i] = (double)(opnd1[i] || opnd2[i]);
+ }
+ break;
+
+ case LEFT_SHIFT:
+ case RIGHT_SHIFT:
+ Tcl_AppendResult(interp, "second shift operand must be scalar",
+ (char *)NULL);
+ goto error;
+
+ default:
+ Tcl_AppendResult(interp, "unknown operator in expression",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ }
+ done:
+ if (value2.pv.buffer != value2.staticSpace) {
+ Blt_Free(value2.pv.buffer);
+ }
+ Blt_Vec_Free(v2Ptr);
+ return result;
+
+ error:
+ if (value2.pv.buffer != value2.staticSpace) {
+ Blt_Free(value2.pv.buffer);
+ }
+ Blt_Vec_Free(v2Ptr);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * EvaluateExpression --
+ *
+ * This procedure provides top-level functionality shared by
+ * procedures like Tcl_ExprInt, Tcl_ExprDouble, etc.
+ *
+ * Results:
+ * The result is a standard TCL return value. If an error
+ * occurs then an error message is left in interp->result.
+ * The value of the expression is returned in *valuePtr, in
+ * whatever form it ends up in (could be string or integer
+ * or double). Caller may need to convert result. Caller
+ * is also responsible for freeing string memory in *valuePtr,
+ * if any was allocated.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+EvaluateExpression(
+ Tcl_Interp *interp, /* Context in which to evaluate the
+ * expression. */
+ char *string, /* Expression to evaluate. */
+ Value *valuePtr) /* Where to store result. Should
+ * not be initialized by caller. */
+{
+ ParseInfo info;
+ int result;
+ Vector *vPtr;
+ double *vp, *vend;
+
+ info.expr = info.nextPtr = string;
+ valuePtr->pv.buffer = valuePtr->pv.next = valuePtr->staticSpace;
+ valuePtr->pv.end = valuePtr->pv.buffer + STATIC_STRING_SPACE - 1;
+ valuePtr->pv.expandProc = Blt_ExpandParseValue;
+ valuePtr->pv.clientData = NULL;
+
+ result = NextValue(interp, &info, -1, valuePtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ if (info.token != END) {
+ Tcl_AppendResult(interp, ": syntax error in expression \"",
+ string, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ vPtr = valuePtr->vPtr;
+
+ /* Check for NaN's and overflows. */
+ for (vp = vPtr->valueArr, vend = vp + vPtr->length; vp < vend; vp++) {
+ if (!FINITE(*vp)) {
+ /*
+ * IEEE floating-point error.
+ */
+ MathError(interp, *vp);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Math Functions --
+ *
+ * This page contains the procedures that implement all of the
+ * built-in math functions for expressions.
+ *
+ * Results:
+ * Each procedure returns TCL_OK if it succeeds and places result
+ * information at *resultPtr. If it fails it returns TCL_ERROR
+ * and leaves an error message in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static int
+ComponentFunc(
+ ClientData clientData, /* Contains address of procedure that
+ * takes one double argument and
+ * returns a double result. */
+ Tcl_Interp *interp,
+ Vector *vPtr)
+{
+ ComponentProc *procPtr = (ComponentProc *) clientData;
+ double *vp, *vend;
+
+ errno = 0;
+ for(vp = vPtr->valueArr + vPtr->first,
+ vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) {
+ *vp = (*procPtr) (*vp);
+ if (errno != 0) {
+ MathError(interp, *vp);
+ return TCL_ERROR;
+ }
+ if (!FINITE(*vp)) {
+ /*
+ * IEEE floating-point error.
+ */
+ MathError(interp, *vp);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+static int
+ScalarFunc(ClientData clientData, Tcl_Interp *interp, Vector *vPtr)
+{
+ double value;
+ ScalarProc *procPtr = (ScalarProc *) clientData;
+
+ errno = 0;
+ value = (*procPtr) (vPtr);
+ if (errno != 0) {
+ MathError(interp, value);
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_ChangeLength(interp, vPtr, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->valueArr[0] = value;
+ return TCL_OK;
+}
+
+/*ARGSUSED*/
+static int
+VectorFunc(ClientData clientData, Tcl_Interp *interp, Vector *vPtr)
+{
+ VectorProc *procPtr = (VectorProc *) clientData;
+
+ return (*procPtr) (vPtr);
+}
+
+
+static MathFunction mathFunctions[] =
+{
+ {"abs", ComponentFunc, Fabs},
+ {"acos", ComponentFunc, acos},
+ {"asin", ComponentFunc, asin},
+ {"atan", ComponentFunc, atan},
+ {"adev", ScalarFunc, AvgDeviation},
+ {"ceil", ComponentFunc, ceil},
+ {"cos", ComponentFunc, cos},
+ {"cosh", ComponentFunc, cosh},
+ {"exp", ComponentFunc, exp},
+ {"floor", ComponentFunc, floor},
+ {"kurtosis",ScalarFunc, Kurtosis},
+ {"length", ScalarFunc, Length},
+ {"log", ComponentFunc, log},
+ {"log10", ComponentFunc, log10},
+ {"max", ScalarFunc, Blt_VecMax},
+ {"mean", ScalarFunc, Mean},
+ {"median", ScalarFunc, Median},
+ {"min", ScalarFunc, Blt_VecMin},
+ {"norm", VectorFunc, Norm},
+ {"nz", ScalarFunc, Nonzeros},
+ {"q1", ScalarFunc, Q1},
+ {"q3", ScalarFunc, Q3},
+ {"prod", ScalarFunc, Product},
+ {"random", ComponentFunc, drand48},
+ {"round", ComponentFunc, Round},
+ {"sdev", ScalarFunc, StdDeviation},
+ {"sin", ComponentFunc, sin},
+ {"sinh", ComponentFunc, sinh},
+ {"skew", ScalarFunc, Skew},
+ {"sort", VectorFunc, Sort},
+ {"sqrt", ComponentFunc, sqrt},
+ {"sum", ScalarFunc, Sum},
+ {"tan", ComponentFunc, tan},
+ {"tanh", ComponentFunc, tanh},
+ {"var", ScalarFunc, Variance},
+ {(char *)NULL,},
+};
+
+void
+Blt_Vec_InstallMathFunctions(Blt_HashTable *tablePtr)
+{
+ MathFunction *mathPtr;
+
+ for (mathPtr = mathFunctions; mathPtr->name != NULL; mathPtr++) {
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ hPtr = Blt_CreateHashEntry(tablePtr, mathPtr->name, &isNew);
+ Blt_SetHashValue(hPtr, (ClientData)mathPtr);
+ }
+}
+
+void
+Blt_Vec_UninstallMathFunctions(Blt_HashTable *tablePtr)
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ MathFunction *mathPtr;
+
+ mathPtr = Blt_GetHashValue(hPtr);
+ if (mathPtr->name == NULL) {
+ Blt_Free(mathPtr);
+ }
+ }
+}
+
+
+static void
+InstallIndexProc(
+ Blt_HashTable *tablePtr,
+ const char *string,
+ Blt_VectorIndexProc *procPtr) /* Pointer to function to be called
+ * when the vector finds the named index.
+ * If NULL, this indicates to remove
+ * the index from the table.
+ */
+{
+ Blt_HashEntry *hPtr;
+ int dummy;
+
+ hPtr = Blt_CreateHashEntry(tablePtr, string, &dummy);
+ if (procPtr == NULL) {
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+ } else {
+ Blt_SetHashValue(hPtr, (ClientData)procPtr);
+ }
+}
+
+void
+Blt_Vec_InstallSpecialIndices(Blt_HashTable *tablePtr)
+{
+ InstallIndexProc(tablePtr, "min", Blt_VecMin);
+ InstallIndexProc(tablePtr, "max", Blt_VecMax);
+ InstallIndexProc(tablePtr, "mean", Mean);
+ InstallIndexProc(tablePtr, "sum", Sum);
+ InstallIndexProc(tablePtr, "prod", Product);
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ExprVector --
+ *
+ * Evaluates an vector expression and returns its value(s).
+ *
+ * Results:
+ * Each of the procedures below returns a standard TCL result.
+ * If an error occurs then an error message is left in
+ * interp->result. Otherwise the value of the expression,
+ * in the appropriate form, is stored at *resultPtr. If
+ * the expression had a result that was incompatible with the
+ * desired form then an error is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ExprVector(
+ Tcl_Interp *interp, /* Context in which to evaluate the
+ * expression. */
+ char *string, /* Expression to evaluate. */
+ Blt_Vector *vector) /* Where to store result. */
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr = (Vector *)vector;
+ Value value;
+
+ dataPtr = (vector != NULL)
+ ? vPtr->dataPtr : Blt_Vec_GetInterpData(interp);
+ value.vPtr = Blt_Vec_New(dataPtr);
+ if (EvaluateExpression(interp, string, &value) != TCL_OK) {
+ Blt_Vec_Free(value.vPtr);
+ return TCL_ERROR;
+ }
+ if (vPtr != NULL) {
+ Blt_Vec_Duplicate(vPtr, value.vPtr);
+ } else {
+ Tcl_Obj *listObjPtr;
+ double *vp, *vend;
+
+ /* No result vector. Put values in interp->result. */
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (vp = value.vPtr->valueArr, vend = vp + value.vPtr->length;
+ vp < vend; vp++) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(*vp));
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ }
+ Blt_Vec_Free(value.vPtr);
+ return TCL_OK;
+}
diff --git a/src/bltVector.C b/src/bltVector.C
new file mode 100644
index 0000000..e332b9d
--- /dev/null
+++ b/src/bltVector.C
@@ -0,0 +1,2808 @@
+
+/*
+ * bltVector.c --
+ *
+ * This module implements vector data objects.
+ *
+ * Copyright 1995-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * TODO:
+ * o Add H. Kirsch's vector binary read operation
+ * x binread file0
+ * x binread -file file0
+ *
+ * o Add ASCII/binary file reader
+ * x read fileName
+ *
+ * o Allow Tcl-based client notifications.
+ * vector x
+ * x notify call Display
+ * x notify delete Display
+ * x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+#include "bltOp.h"
+#include "bltNsUtil.h"
+#include "bltSwitch.h"
+#include <bltMath.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define DEF_ARRAY_SIZE 64
+#define TRACE_ALL (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS)
+
+
+#define VECTOR_CHAR(c) ((isalnum(UCHAR(c))) || \
+ (c == '_') || (c == ':') || (c == '@') || (c == '.'))
+
+
+/*
+ * VectorClient --
+ *
+ * A vector can be shared by several clients. Each client allocates this
+ * structure that acts as its key for using the vector. Clients can also
+ * designate a callback routine that is executed whenever the vector is
+ * updated or destroyed.
+ *
+ */
+typedef struct {
+ unsigned int magic; /* Magic value designating whether this really
+ * is a vector token or not */
+
+ Vector *serverPtr; /* Pointer to the master record of the vector.
+ * If NULL, indicates that the vector has been
+ * destroyed but as of yet, this client hasn't
+ * recognized it. */
+
+ Blt_VectorChangedProc *proc;/* Routine to call when the contents of the
+ * vector change or the vector is deleted. */
+
+ ClientData clientData; /* Data passed whenever the vector change
+ * procedure is called. */
+
+ Blt_ChainLink link; /* Used to quickly remove this entry from its
+ * server's client chain. */
+} VectorClient;
+
+static Tcl_CmdDeleteProc VectorInstDeleteProc;
+static Tcl_ObjCmdProc VectorCmd;
+static Tcl_InterpDeleteProc VectorInterpDeleteProc;
+
+typedef struct {
+ char *varName; /* Requested variable name. */
+ char *cmdName; /* Requested command name. */
+ int flush; /* Flush */
+ int watchUnset; /* Watch when variable is unset. */
+} CreateSwitches;
+
+static Blt_SwitchSpec createSwitches[] =
+{
+ {BLT_SWITCH_STRING, "-variable", "varName",
+ Blt_Offset(CreateSwitches, varName), BLT_SWITCH_NULL_OK},
+ {BLT_SWITCH_STRING, "-command", "command",
+ Blt_Offset(CreateSwitches, cmdName), BLT_SWITCH_NULL_OK},
+ {BLT_SWITCH_BOOLEAN, "-watchunset", "bool",
+ Blt_Offset(CreateSwitches, watchUnset), 0},
+ {BLT_SWITCH_BOOLEAN, "-flush", "bool",
+ Blt_Offset(CreateSwitches, flush), 0},
+ {BLT_SWITCH_END}
+};
+
+typedef int (VectorCmdProc)(Vector *vecObjPtr, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const *objv);
+
+static Vector *
+FindVectorInNamespace(
+ VectorInterpData *dataPtr, /* Interpreter-specific data. */
+ Blt_ObjectName *objNamePtr)
+{
+ Tcl_DString dString;
+ const char *name;
+ Blt_HashEntry *hPtr;
+
+ name = Blt_MakeQualifiedName(objNamePtr, &dString);
+ hPtr = Blt_FindHashEntry(&dataPtr->vectorTable, name);
+ Tcl_DStringFree(&dString);
+ if (hPtr != NULL) {
+ return Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetVectorObject --
+ *
+ * Searches for the vector associated with the name given. Allow for a
+ * range specification.
+ *
+ * Results:
+ * Returns a pointer to the vector if found, otherwise NULL.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Vector *
+GetVectorObject(
+ VectorInterpData *dataPtr, /* Interpreter-specific data. */
+ const char *name,
+ int flags)
+{
+ Blt_ObjectName objName;
+ Vector *vPtr;
+ Tcl_Interp *interp;
+
+ interp = dataPtr->interp;
+ if (!Blt_ParseObjectName(interp, name, &objName,
+ BLT_NO_ERROR_MSG | BLT_NO_DEFAULT_NS)) {
+ return NULL; /* Can't find namespace. */
+ }
+ vPtr = NULL;
+ if (objName.nsPtr != NULL) {
+ vPtr = FindVectorInNamespace(dataPtr, &objName);
+ } else {
+ if (flags & NS_SEARCH_CURRENT) {
+ objName.nsPtr = Tcl_GetCurrentNamespace(interp);
+ vPtr = FindVectorInNamespace(dataPtr, &objName);
+ }
+ if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+ objName.nsPtr = Tcl_GetGlobalNamespace(interp);
+ vPtr = FindVectorInNamespace(dataPtr, &objName);
+ }
+ }
+ return vPtr;
+}
+
+void
+Blt_Vec_UpdateRange(Vector *vPtr)
+{
+ double min, max;
+ double *vp, *vend;
+
+ vp = vPtr->valueArr + vPtr->first;
+ vend = vPtr->valueArr + vPtr->last;
+ min = max = *vp++;
+ for (/* empty */; vp <= vend; vp++) {
+ if (min > *vp) {
+ min = *vp;
+ } else if (max < *vp) {
+ max = *vp;
+ }
+ }
+ vPtr->min = min;
+ vPtr->max = max;
+ vPtr->notifyFlags &= ~UPDATE_RANGE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_GetIndex --
+ *
+ * Converts the string representing an index in the vector, to its
+ * numeric value. A valid index may be an numeric string of the string
+ * "end" (indicating the last element in the string).
+ *
+ * Results:
+ * A standard TCL result. If the string is a valid index, TCL_OK is
+ * returned. Otherwise TCL_ERROR is returned and interp->result will
+ * contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_GetIndex(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ const char *string,
+ int *indexPtr,
+ int flags,
+ Blt_VectorIndexProc **procPtrPtr)
+{
+ char c;
+ int value;
+
+ c = string[0];
+
+ /* Treat the index "end" like a numeric index. */
+
+ if ((c == 'e') && (strcmp(string, "end") == 0)) {
+ if (vPtr->length < 1) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad index \"end\": vector is empty",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *indexPtr = vPtr->length - 1;
+ return TCL_OK;
+ } else if ((c == '+') && (strcmp(string, "++end") == 0)) {
+ *indexPtr = vPtr->length;
+ return TCL_OK;
+ }
+ if (procPtrPtr != NULL) {
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&vPtr->dataPtr->indexProcTable, string);
+ if (hPtr != NULL) {
+ *indexPtr = SPECIAL_INDEX;
+ *procPtrPtr = Blt_GetHashValue(hPtr);
+ return TCL_OK;
+ }
+ }
+ if (Tcl_GetInt(interp, (char *)string, &value) != TCL_OK) {
+ long int lvalue;
+ /*
+ * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter, but the
+ * interp passed in may be NULL. So we have to use vPtr->interp and
+ * then reset the result.
+ */
+ if (Tcl_ExprLong(vPtr->interp, (char *)string, &lvalue) != TCL_OK) {
+ Tcl_ResetResult(vPtr->interp);
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad index \"", string, "\"",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ value = (int)lvalue;
+ }
+ /*
+ * Correct the index by the current value of the offset. This makes all
+ * the numeric indices non-negative, which is how we distinguish the
+ * special non-numeric indices.
+ */
+ value -= vPtr->offset;
+
+ if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "index \"", string, "\" is out of range",
+ (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *indexPtr = (int)value;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_GetIndexRange --
+ *
+ * Converts the string representing an index in the vector, to its
+ * numeric value. A valid index may be an numeric string of the string
+ * "end" (indicating the last element in the string).
+ *
+ * Results:
+ * A standard TCL result. If the string is a valid index, TCL_OK is
+ * returned. Otherwise TCL_ERROR is returned and interp->result will
+ * contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_GetIndexRange(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ const char *string,
+ int flags,
+ Blt_VectorIndexProc **procPtrPtr)
+{
+ int ielem;
+ char *colon;
+
+ colon = NULL;
+ if (flags & INDEX_COLON) {
+ colon = strchr(string, ':');
+ }
+ if (colon != NULL) {
+ if (string == colon) {
+ vPtr->first = 0; /* Default to the first index */
+ } else {
+ int result;
+
+ *colon = '\0';
+ result = Blt_Vec_GetIndex(interp, vPtr, string, &ielem, flags,
+ (Blt_VectorIndexProc **) NULL);
+ *colon = ':';
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->first = ielem;
+ }
+ if (*(colon + 1) == '\0') {
+ /* Default to the last index */
+ vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0;
+ } else {
+ if (Blt_Vec_GetIndex(interp, vPtr, colon + 1, &ielem, flags,
+ (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->last = ielem;
+ }
+ if (vPtr->first > vPtr->last) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad range \"", string,
+ "\" (first > last)", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ } else {
+ if (Blt_Vec_GetIndex(interp, vPtr, string, &ielem, flags,
+ procPtrPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ vPtr->last = vPtr->first = ielem;
+ }
+ return TCL_OK;
+}
+
+Vector *
+Blt_Vec_ParseElement(
+ Tcl_Interp *interp,
+ VectorInterpData *dataPtr, /* Interpreter-specific data. */
+ const char *start,
+ const char **endPtr,
+ int flags)
+{
+ char *p;
+ char saved;
+ Vector *vPtr;
+
+ p = (char *)start;
+ /* Find the end of the vector name */
+ while (VECTOR_CHAR(*p)) {
+ p++;
+ }
+ saved = *p;
+ *p = '\0';
+
+ vPtr = GetVectorObject(dataPtr, start, flags);
+ if (vPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find vector \"", start, "\"",
+ (char *)NULL);
+ }
+ *p = saved;
+ return NULL;
+ }
+ *p = saved;
+ vPtr->first = 0;
+ vPtr->last = vPtr->length - 1;
+ if (*p == '(') {
+ int count, result;
+
+ start = p + 1;
+ p++;
+
+ /* Find the matching right parenthesis */
+ count = 1;
+ while (*p != '\0') {
+ if (*p == ')') {
+ count--;
+ if (count == 0) {
+ break;
+ }
+ } else if (*p == '(') {
+ count++;
+ }
+ p++;
+ }
+ if (count > 0) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "unbalanced parentheses \"", start,
+ "\"", (char *)NULL);
+ }
+ return NULL;
+ }
+ *p = '\0';
+ result = Blt_Vec_GetIndexRange(interp, vPtr, start,
+ (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL);
+ *p = ')';
+ if (result != TCL_OK) {
+ return NULL;
+ }
+ p++;
+ }
+ if (endPtr != NULL) {
+ *endPtr = p;
+ }
+ return vPtr;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_NotifyClients --
+ *
+ * Notifies each client of the vector that the vector has changed
+ * (updated or destroyed) by calling the provided function back. The
+ * function pointer may be NULL, in that case the client is not notified.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The results depend upon what actions the client callbacks
+ * take.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Vec_NotifyClients(ClientData clientData)
+{
+ Vector *vPtr = clientData;
+ Blt_ChainLink link, next;
+ Blt_VectorNotify notify;
+
+ notify = (vPtr->notifyFlags & NOTIFY_DESTROYED)
+ ? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE;
+ vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING);
+ for (link = Blt_Chain_FirstLink(vPtr->chain); link != NULL; link = next) {
+ VectorClient *clientPtr;
+
+ next = Blt_Chain_NextLink(link);
+ clientPtr = Blt_Chain_GetValue(link);
+ if ((clientPtr->proc != NULL) && (clientPtr->serverPtr != NULL)) {
+ (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify);
+ }
+ }
+ /*
+ * Some clients may not handle the "destroy" callback properly (they
+ * should call Blt_FreeVectorId to release the client identifier), so mark
+ * any remaining clients to indicate that vector's server has gone away.
+ */
+ if (notify == BLT_VECTOR_NOTIFY_DESTROY) {
+ for (link = Blt_Chain_FirstLink(vPtr->chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ VectorClient *clientPtr;
+
+ clientPtr = Blt_Chain_GetValue(link);
+ clientPtr->serverPtr = NULL;
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_UpdateClients --
+ *
+ * Notifies each client of the vector that the vector has changed
+ * (updated or destroyed) by calling the provided function back.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The individual client callbacks are eventually invoked.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Vec_UpdateClients(Vector *vPtr)
+{
+ vPtr->dirty++;
+ vPtr->max = vPtr->min = Blt_NaN();
+ if (vPtr->notifyFlags & NOTIFY_NEVER) {
+ return;
+ }
+ vPtr->notifyFlags |= NOTIFY_UPDATED;
+ if (vPtr->notifyFlags & NOTIFY_ALWAYS) {
+ Blt_Vec_NotifyClients(vPtr);
+ return;
+ }
+ if (!(vPtr->notifyFlags & NOTIFY_PENDING)) {
+ vPtr->notifyFlags |= NOTIFY_PENDING;
+ Tcl_DoWhenIdle(Blt_Vec_NotifyClients, vPtr);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_FlushCache --
+ *
+ * Unsets all the elements of the TCL array variable associated with the
+ * vector, freeing memory associated with the variable. This includes
+ * both the hash table and the hash keys. The down side is that this
+ * effectively flushes the caching of vector elements in the array. This
+ * means that the subsequent reads of the array will require a decimal to
+ * string conversion.
+ *
+ * This is needed when the vector changes its values, making the array
+ * variable out-of-sync.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All elements of array variable (except one) are unset, freeing
+ * the memory associated with the variable.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Vec_FlushCache(Vector *vPtr)
+{
+ Tcl_Interp *interp = vPtr->interp;
+
+ if (vPtr->arrayName == NULL) {
+ return; /* Doesn't use the variable API */
+ }
+ /* Turn off the trace temporarily so that we can unset all the
+ * elements in the array. */
+
+ Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ TRACE_ALL | vPtr->varFlags, Blt_Vec_VarTrace, vPtr);
+
+ /* Clear all the element entries from the entire array */
+ Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+ /* Restore the "end" index by default and the trace on the entire array */
+ Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags);
+ Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ TRACE_ALL | vPtr->varFlags, Blt_Vec_VarTrace, vPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_LookupName --
+ *
+ * Searches for the vector associated with the name given. Allow for a
+ * range specification.
+ *
+ * Results:
+ * Returns a pointer to the vector if found, otherwise NULL. If the name
+ * is not associated with a vector and the TCL_LEAVE_ERR_MSG flag is set,
+ * and interp->result will contain an error message.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_LookupName(
+ VectorInterpData *dataPtr, /* Interpreter-specific data. */
+ const char *vecName,
+ Vector **vPtrPtr)
+{
+ Vector *vPtr;
+ const char *endPtr;
+
+ vPtr = Blt_Vec_ParseElement(dataPtr->interp, dataPtr, vecName, &endPtr,
+ NS_SEARCH_BOTH);
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (*endPtr != '\0') {
+ Tcl_AppendResult(dataPtr->interp,
+ "extra characters after vector name", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *vPtrPtr = vPtr;
+ return TCL_OK;
+}
+
+double
+Blt_Vec_Min(Vector *vecObjPtr)
+{
+ double *vp, *vend;
+ double min;
+
+ vp = vecObjPtr->valueArr + vecObjPtr->first;
+ vend = vecObjPtr->valueArr + vecObjPtr->last;
+ min = *vp++;
+ for (/* empty */; vp <= vend; vp++) {
+ if (min > *vp) {
+ min = *vp;
+ }
+ }
+ vecObjPtr->min = min;
+ return vecObjPtr->min;
+}
+
+double
+Blt_Vec_Max(Vector *vecObjPtr)
+{
+ double max;
+ double *vp, *vend;
+
+ max = Blt_NaN();
+ vp = vecObjPtr->valueArr + vecObjPtr->first;
+ vend = vecObjPtr->valueArr + vecObjPtr->last;
+ max = *vp++;
+ for (/* empty */; vp <= vend; vp++) {
+ if (max < *vp) {
+ max = *vp;
+ }
+ }
+ vecObjPtr->max = max;
+ return vecObjPtr->max;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * DeleteCommand --
+ *
+ * Deletes the TCL command associated with the vector, without triggering
+ * a callback to "VectorInstDeleteProc".
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+DeleteCommand(Vector *vPtr) /* Vector associated with the TCL command. */
+{
+ Tcl_Interp *interp = vPtr->interp;
+ char *qualName; /* Name of TCL command. */
+ Tcl_CmdInfo cmdInfo;
+ Tcl_DString dString;
+ Blt_ObjectName objName;
+
+ Tcl_DStringInit(&dString);
+ objName.name = Tcl_GetCommandName(interp, vPtr->cmdToken);
+ objName.nsPtr = Blt_GetCommandNamespace(vPtr->cmdToken);
+ qualName = Blt_MakeQualifiedName(&objName, &dString);
+ if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) {
+ /* Disable the callback before deleting the TCL command.*/
+ cmdInfo.deleteProc = NULL;
+ Tcl_SetCommandInfo(interp, qualName, &cmdInfo);
+ Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken);
+ }
+ Tcl_DStringFree(&dString);
+ vPtr->cmdToken = 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UnmapVariable --
+ *
+ * Destroys the trace on the current TCL variable designated to access
+ * the vector.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+UnmapVariable(Vector *vPtr)
+{
+ Tcl_Interp *interp = vPtr->interp;
+
+ /* Unset the entire array */
+ Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+ (TRACE_ALL | vPtr->varFlags), Blt_Vec_VarTrace, vPtr);
+ Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+ if (vPtr->arrayName != NULL) {
+ Blt_Free(vPtr->arrayName);
+ vPtr->arrayName = NULL;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_MapVariable --
+ *
+ * Sets up traces on a TCL variable to access the vector.
+ *
+ * If another variable is already mapped, it's first untraced and
+ * removed. Don't do anything else for variables named "" (even though
+ * Tcl allows this pathology). Saves the name of the new array variable.
+ *
+ * Results:
+ * A standard TCL result. If an error occurs setting the variable
+ * TCL_ERROR is returned and an error message is left in the interpreter.
+ *
+ * Side effects:
+ * Traces are set for the new variable. The new variable name is saved in
+ * a malloc'ed string in vPtr->arrayName. If this variable is non-NULL,
+ * it indicates that a TCL variable has been mapped to this vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_MapVariable(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ const char *path)
+{
+ Blt_ObjectName objName;
+ char *newPath;
+ const char *result;
+ Tcl_DString dString;
+
+ if (vPtr->arrayName != NULL) {
+ UnmapVariable(vPtr);
+ }
+ if ((path == NULL) || (path[0] == '\0')) {
+ return TCL_OK; /* If the variable pathname is the empty
+ * string, simply return after removing any
+ * existing variable. */
+ }
+ /* Get the variable name (without the namespace qualifier). */
+ if (!Blt_ParseObjectName(interp, path, &objName, BLT_NO_DEFAULT_NS)) {
+ return TCL_ERROR;
+ }
+ if (objName.nsPtr == NULL) {
+ /*
+ * If there was no namespace qualifier, try harder to see if the
+ * variable is non-local.
+ */
+ objName.nsPtr = Blt_GetVariableNamespace(interp, objName.name);
+ }
+ Tcl_DStringInit(&dString);
+ vPtr->varFlags = 0;
+ if (objName.nsPtr != NULL) { /* Global or namespace variable. */
+ newPath = Blt_MakeQualifiedName(&objName, &dString);
+ vPtr->varFlags |= (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY);
+ } else { /* Local variable. */
+ newPath = (char *)objName.name;
+ }
+
+ /*
+ * To play it safe, delete the variable first. This has the benefical
+ * side-effect of unmapping the variable from another vector that may be
+ * currently associated with it.
+ */
+ Tcl_UnsetVar2(interp, newPath, (char *)NULL, 0);
+
+ /*
+ * Set the index "end" in the array. This will create the variable
+ * immediately so that we can check its namespace context.
+ */
+ result = Tcl_SetVar2(interp, newPath, "end", "", TCL_LEAVE_ERR_MSG);
+ if (result == NULL) {
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ /* Create a full-array trace on reads, writes, and unsets. */
+ Tcl_TraceVar2(interp, newPath, (char *)NULL, TRACE_ALL, Blt_Vec_VarTrace,
+ vPtr);
+ vPtr->arrayName = Blt_AssertStrdup(newPath);
+ Tcl_DStringFree(&dString);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_SetSize --
+ *
+ * Resizes the vector to the designated new size.
+ *
+ * If the new size is the same as the old, simply return. Otherwise
+ * we're copying the data from one memory location to another.
+ *
+ * If the storage changed memory locations, free up the old location if
+ * it was dynamically allocated.
+ *
+ * Results:
+ * A standard TCL result. If the reallocation is successful,
+ * TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ * Memory for the array is reallocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_SetSize(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ int newSize) /* Size of array in elements */
+{
+ if (newSize <= 0) {
+ newSize = DEF_ARRAY_SIZE;
+ }
+ if (newSize == vPtr->size) {
+ /* Same size, use the current array. */
+ return TCL_OK;
+ }
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ double *newArr;
+
+ /* Old memory was dynamically allocated, so use realloc. */
+ newArr = Blt_Realloc(vPtr->valueArr, newSize * sizeof(double));
+ if (newArr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't reallocate ",
+ Blt_Itoa(newSize), " elements for vector \"",
+ vPtr->name, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ vPtr->size = newSize;
+ vPtr->valueArr = newArr;
+ return TCL_OK;
+ }
+
+ {
+ double *newArr;
+
+ /* Old memory was created specially (static or special allocator).
+ * Replace with dynamically allocated memory (malloc-ed). */
+
+ newArr = Blt_Calloc(newSize, sizeof(double));
+ if (newArr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't allocate ",
+ Blt_Itoa(newSize), " elements for vector \"",
+ vPtr->name, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ {
+ int used, wanted;
+
+ /* Copy the contents of the old memory into the new. */
+ used = vPtr->length;
+ wanted = newSize;
+
+ if (used > wanted) {
+ used = wanted;
+ }
+ /* Copy any previous data */
+ if (used > 0) {
+ memcpy(newArr, vPtr->valueArr, used * sizeof(double));
+ }
+ }
+
+ assert(vPtr->valueArr != NULL);
+
+ /*
+ * We're not using the old storage anymore, so free it if it's not
+ * TCL_STATIC. It's static because the user previously reset the
+ * vector with a statically allocated array (setting freeProc to
+ * TCL_STATIC).
+ */
+ if (vPtr->freeProc != TCL_STATIC) {
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*vPtr->freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ vPtr->freeProc = TCL_DYNAMIC; /* Set the type of the new storage */
+ vPtr->valueArr = newArr;
+ vPtr->size = newSize;
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_SetSize --
+ *
+ * Set the length (the number of elements currently in use) of the
+ * vector. If the new length is greater than the size (total number of
+ * slots), then the vector is grown.
+ *
+ * Results:
+ * A standard TCL result. If the reallocation is successful, TCL_OK is
+ * returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ * Memory for the array is possibly reallocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_SetLength(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ int newLength) /* Size of array in elements */
+{
+ if (vPtr->size < newLength) {
+ if (Blt_Vec_SetSize(interp, vPtr, newLength) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ vPtr->length = newLength;
+ vPtr->first = 0;
+ vPtr->last = newLength - 1;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_ChangeLength --
+ *
+ * Resizes the vector to the new size.
+ *
+ * The new size of the vector is computed by doubling the size of the
+ * vector until it fits the number of slots needed (designated by
+ * *length*).
+ *
+ * If the new size is the same as the old, simply adjust the length of
+ * the vector. Otherwise we're copying the data from one memory location
+ * to another. The trailing elements of the vector need to be reset to
+ * zero.
+ *
+ * If the storage changed memory locations, free up the old location if
+ * it was dynamically allocated.
+ *
+ * Results:
+ * A standard TCL result. If the reallocation is successful,
+ * TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ * Memory for the array is reallocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Blt_Vec_ChangeLength(
+ Tcl_Interp *interp,
+ Vector *vPtr,
+ int newLength)
+{
+ if (newLength < 0) {
+ newLength = 0;
+ }
+ if (newLength > vPtr->size) {
+ int newSize; /* Size of array in elements */
+
+ /* Compute the new size of the array. It's a multiple of
+ * DEF_ARRAY_SIZE. */
+ newSize = DEF_ARRAY_SIZE;
+ while (newSize < newLength) {
+ newSize += newSize;
+ }
+ if (newSize != vPtr->size) {
+ if (Blt_Vec_SetSize(interp, vPtr, newSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ vPtr->length = newLength;
+ vPtr->first = 0;
+ vPtr->last = newLength - 1;
+ return TCL_OK;
+
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_Reset --
+ *
+ * Resets the vector data. This is called by a client to indicate that
+ * the vector data has changed. The vector does not need to point to
+ * different memory. Any clients of the vector will be notified of the
+ * change.
+ *
+ * Results:
+ * A standard TCL result. If the new array size is invalid, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned and the new vector data is
+ * recorded.
+ *
+ * Side Effects:
+ * Any client designated callbacks will be posted. Memory may be changed
+ * for the vector array.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_Vec_Reset(
+ Vector *vPtr,
+ double *valueArr, /* Array containing the elements of the
+ * vector. If NULL, indicates to reset the
+ * vector size to the default. */
+ int length, /* The number of elements that the vector
+ * currently holds. */
+ int size, /* The maximum number of elements that the
+ * array can hold. */
+ Tcl_FreeProc *freeProc) /* Address of memory deallocation routine
+ * for the array of values. Can also be
+ * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+ if (vPtr->valueArr != valueArr) { /* New array of values resides
+ * in different memory than
+ * the current vector. */
+ if ((valueArr == NULL) || (size == 0)) {
+ /* Empty array. Set up default values */
+ valueArr = Blt_Malloc(sizeof(double) * DEF_ARRAY_SIZE);
+ size = DEF_ARRAY_SIZE;
+ if (valueArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "can't allocate ",
+ Blt_Itoa(size), " elements for vector \"",
+ vPtr->name, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ freeProc = TCL_DYNAMIC;
+ length = 0;
+ } else if (freeProc == TCL_VOLATILE) {
+ double *newArr;
+ /* Data is volatile. Make a copy of the value array. */
+ newArr = Blt_Malloc(size * sizeof(double));
+ if (newArr == NULL) {
+ Tcl_AppendResult(vPtr->interp, "can't allocate ",
+ Blt_Itoa(size), " elements for vector \"",
+ vPtr->name, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ memcpy((char *)newArr, (char *)valueArr,
+ sizeof(double) * length);
+ valueArr = newArr;
+ freeProc = TCL_DYNAMIC;
+ }
+
+ if (vPtr->freeProc != TCL_STATIC) {
+ /* Old data was dynamically allocated. Free it before attaching
+ * new data. */
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ vPtr->freeProc = freeProc;
+ vPtr->valueArr = valueArr;
+ vPtr->size = size;
+ }
+
+ vPtr->length = length;
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+Vector *
+Blt_Vec_New(VectorInterpData *dataPtr) /* Interpreter-specific data. */
+{
+ Vector *vPtr;
+
+ vPtr = Blt_AssertCalloc(1, sizeof(Vector));
+ vPtr->valueArr = Blt_Malloc(sizeof(double) * DEF_ARRAY_SIZE);
+ if (vPtr->valueArr == NULL) {
+ Blt_Free(vPtr);
+ return NULL;
+ }
+ vPtr->size = DEF_ARRAY_SIZE;
+ vPtr->freeProc = TCL_DYNAMIC;
+ vPtr->length = 0;
+ vPtr->interp = dataPtr->interp;
+ vPtr->hashPtr = NULL;
+ vPtr->chain = Blt_Chain_Create();
+ vPtr->flush = FALSE;
+ vPtr->min = vPtr->max = Blt_NaN();
+ vPtr->notifyFlags = NOTIFY_WHENIDLE;
+ vPtr->dataPtr = dataPtr;
+ return vPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_Free --
+ *
+ * Removes the memory and frees resources associated with the vector.
+ *
+ * o Removes the trace and the TCL array variable and unsets
+ * the variable.
+ * o Notifies clients of the vector that the vector is being
+ * destroyed.
+ * o Removes any clients that are left after notification.
+ * o Frees the memory (if necessary) allocated for the array.
+ * o Removes the entry from the hash table of vectors.
+ * o Frees the memory allocated for the name.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_Vec_Free(Vector *vPtr)
+{
+ Blt_ChainLink link;
+
+ if (vPtr->cmdToken != 0) {
+ DeleteCommand(vPtr);
+ }
+ if (vPtr->arrayName != NULL) {
+ UnmapVariable(vPtr);
+ }
+ vPtr->length = 0;
+
+ /* Immediately notify clients that vector is going away */
+ if (vPtr->notifyFlags & NOTIFY_PENDING) {
+ vPtr->notifyFlags &= ~NOTIFY_PENDING;
+ Tcl_CancelIdleCall(Blt_Vec_NotifyClients, vPtr);
+ }
+ vPtr->notifyFlags |= NOTIFY_DESTROYED;
+ Blt_Vec_NotifyClients(vPtr);
+
+ for (link = Blt_Chain_FirstLink(vPtr->chain); link != NULL;
+ link = Blt_Chain_NextLink(link)) {
+ VectorClient *clientPtr;
+
+ clientPtr = Blt_Chain_GetValue(link);
+ Blt_Free(clientPtr);
+ }
+ Blt_Chain_Destroy(vPtr->chain);
+ if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) {
+ if (vPtr->freeProc == TCL_DYNAMIC) {
+ Blt_Free(vPtr->valueArr);
+ } else {
+ (*vPtr->freeProc) ((char *)vPtr->valueArr);
+ }
+ }
+ if (vPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&vPtr->dataPtr->vectorTable, vPtr->hashPtr);
+ }
+#ifdef NAMESPACE_DELETE_NOTIFY
+ if (vPtr->nsPtr != NULL) {
+ Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr);
+ }
+#endif /* NAMESPACE_DELETE_NOTIFY */
+ Blt_Free(vPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorInstDeleteProc --
+ *
+ * Deletes the command associated with the vector. This is called only
+ * when the command associated with the vector is destroyed.
+ *
+ * Results:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+VectorInstDeleteProc(ClientData clientData)
+{
+ Vector *vPtr = clientData;
+
+ vPtr->cmdToken = 0;
+ Blt_Vec_Free(vPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_Vec_Create --
+ *
+ * Creates a vector structure and the following items:
+ *
+ * o TCL command
+ * o TCL array variable and establishes traces on the variable
+ * o Adds a new entry in the vector hash table
+ *
+ * Results:
+ * A pointer to the new vector structure. If an error occurred NULL is
+ * returned and an error message is left in interp->result.
+ *
+ * Side effects:
+ * A new TCL command and array variable is added to the interpreter.
+ *
+ * ----------------------------------------------------------------------
+ */
+Vector *
+Blt_Vec_Create(
+ VectorInterpData *dataPtr, /* Interpreter-specific data. */
+ const char *vecName, /* Namespace-qualified name of the vector */
+ const char *cmdName, /* Name of the TCL command mapped to
+ * the vector */
+ const char *varName, /* Name of the TCL array mapped to the
+ * vector */
+ int *isNewPtr)
+{
+ Tcl_DString dString;
+ Vector *vPtr;
+ int isNew;
+ Blt_ObjectName objName;
+ char *qualName;
+ Blt_HashEntry *hPtr;
+ Tcl_Interp *interp = dataPtr->interp;
+
+ isNew = 0;
+ vPtr = NULL;
+
+ if (!Blt_ParseObjectName(interp, vecName, &objName, 0)) {
+ return NULL;
+ }
+ Tcl_DStringInit(&dString);
+ if ((objName.name[0] == '#') && (strcmp(objName.name, "#auto") == 0)) {
+
+ do { /* Generate a unique vector name. */
+ char string[200];
+
+ sprintf_s(string, 200, "vector%d", dataPtr->nextId++);
+ objName.name = string;
+ qualName = Blt_MakeQualifiedName(&objName, &dString);
+ hPtr = Blt_FindHashEntry(&dataPtr->vectorTable, qualName);
+ } while (hPtr != NULL);
+ } else {
+ const char *p;
+
+ for (p = objName.name; *p != '\0'; p++) {
+ if (!VECTOR_CHAR(*p)) {
+ Tcl_AppendResult(interp, "bad vector name \"", objName.name,
+ "\": must contain digits, letters, underscore, or period",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ qualName = Blt_MakeQualifiedName(&objName, &dString);
+ vPtr = Blt_Vec_ParseElement((Tcl_Interp *)NULL, dataPtr, qualName,
+ NULL, NS_SEARCH_CURRENT);
+ }
+ if (vPtr == NULL) {
+ hPtr = Blt_CreateHashEntry(&dataPtr->vectorTable, qualName, &isNew);
+ vPtr = Blt_Vec_New(dataPtr);
+ vPtr->hashPtr = hPtr;
+ vPtr->nsPtr = objName.nsPtr;
+
+ vPtr->name = Blt_GetHashKey(&dataPtr->vectorTable, hPtr);
+#ifdef NAMESPACE_DELETE_NOTIFY
+ Blt_CreateNsDeleteNotify(interp, objName.nsPtr, vPtr,
+ VectorInstDeleteProc);
+#endif /* NAMESPACE_DELETE_NOTIFY */
+ Blt_SetHashValue(hPtr, vPtr);
+ }
+ if (cmdName != NULL) {
+ Tcl_CmdInfo cmdInfo;
+
+ if ((cmdName == vecName) ||
+ ((cmdName[0] == '#') && (strcmp(cmdName, "#auto")==0))) {
+ cmdName = qualName;
+ }
+ if (Tcl_GetCommandInfo(interp, (char *)cmdName, &cmdInfo)) {
+ if (vPtr != cmdInfo.objClientData) {
+ Tcl_AppendResult(interp, "command \"", cmdName,
+ "\" already exists", (char *)NULL);
+ goto error;
+ }
+ /* We get here only if the old name is the same as the new. */
+ goto checkVariable;
+ }
+ }
+ if (vPtr->cmdToken != 0) {
+ DeleteCommand(vPtr); /* Command already exists, delete old first */
+ }
+ if (cmdName != NULL) {
+ Tcl_DString dString2;
+
+ Tcl_DStringInit(&dString2);
+ if (cmdName != qualName) {
+ if (!Blt_ParseObjectName(interp, cmdName, &objName, 0)) {
+ goto error;
+ }
+ cmdName = Blt_MakeQualifiedName(&objName, &dString2);
+ }
+ vPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)cmdName,
+ Blt_Vec_InstCmd, vPtr, VectorInstDeleteProc);
+ Tcl_DStringFree(&dString2);
+ }
+ checkVariable:
+ if (varName != NULL) {
+ if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) {
+ varName = qualName;
+ }
+ if (Blt_Vec_MapVariable(interp, vPtr, varName) != TCL_OK) {
+ goto error;
+ }
+ }
+
+ Tcl_DStringFree(&dString);
+ *isNewPtr = isNew;
+ return vPtr;
+
+ error:
+ Tcl_DStringFree(&dString);
+ if (vPtr != NULL) {
+ Blt_Vec_Free(vPtr);
+ }
+ return NULL;
+}
+
+
+int
+Blt_Vec_Duplicate(Vector *destPtr, Vector *srcPtr)
+{
+ size_t nBytes;
+ size_t length;
+
+ if (destPtr == srcPtr) {
+ /* Copying the same vector. */
+ }
+ length = srcPtr->last - srcPtr->first + 1;
+ if (Blt_Vec_ChangeLength(destPtr->interp, destPtr, length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ nBytes = length * sizeof(double);
+ memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes);
+ destPtr->offset = srcPtr->offset;
+ return TCL_OK;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorNamesOp --
+ *
+ * Reports the names of all the current vectors in the interpreter.
+ *
+ * Results:
+ * A standard TCL result. interp->result will contain a list of
+ * all the names of the vector instances.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorNamesOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ VectorInterpData *dataPtr = clientData;
+ Tcl_Obj *listObjPtr;
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ if (objc == 2) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&dataPtr->vectorTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ char *name;
+
+ name = Blt_GetHashKey(&dataPtr->vectorTable, hPtr);
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(name, -1));
+ }
+ } else {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&dataPtr->vectorTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ char *name;
+ int i;
+
+ name = Blt_GetHashKey(&dataPtr->vectorTable, hPtr);
+ for (i = 2; i < objc; i++) {
+ char *pattern;
+
+ pattern = Tcl_GetString(objv[i]);
+ if (Tcl_StringMatch(name, pattern)) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(name, -1));
+ break;
+ }
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ * Creates a TCL command, and array variable representing an instance of
+ * a vector.
+ *
+ * vector a
+ * vector b(20)
+ * vector c(-5:14)
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+/*ARGSUSED*/
+static int
+VectorCreate2(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int argStart,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ VectorInterpData *dataPtr = clientData;
+ Vector *vPtr;
+ int count, i;
+ CreateSwitches switches;
+
+ /*
+ * Handle switches to the vector command and collect the vector name
+ * arguments into an array.
+ */
+ count = 0;
+ vPtr = NULL;
+ for (i = argStart; i < objc; i++) {
+ char *string;
+
+ string = Tcl_GetString(objv[i]);
+ if (string[0] == '-') {
+ break;
+ }
+ }
+ count = i - argStart;
+ if (count == 0) {
+ Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL);
+ return TCL_ERROR;
+ }
+ memset(&switches, 0, sizeof(switches));
+ if (Blt_ParseSwitches(interp, createSwitches, objc - i, objv + i,
+ &switches, BLT_SWITCH_DEFAULTS) < 0) {
+ return TCL_ERROR;
+ }
+ if (count > 1) {
+ if (switches.cmdName != NULL) {
+ Tcl_AppendResult(interp,
+ "can't specify more than one vector with \"-command\" switch",
+ (char *)NULL);
+ goto error;
+ }
+ if (switches.varName != NULL) {
+ Tcl_AppendResult(interp,
+ "can't specify more than one vector with \"-variable\" switch",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ char *leftParen, *rightParen;
+ char *string;
+ int isNew;
+ int size, first, last;
+
+ size = first = last = 0;
+ string = Tcl_GetString(objv[i + argStart]);
+ leftParen = strchr(string, '(');
+ rightParen = strchr(string, ')');
+ if (((leftParen != NULL) && (rightParen == NULL)) ||
+ ((leftParen == NULL) && (rightParen != NULL)) ||
+ (leftParen > rightParen)) {
+ Tcl_AppendResult(interp, "bad vector specification \"", string,
+ "\"", (char *)NULL);
+ goto error;
+ }
+ if (leftParen != NULL) {
+ int result;
+ char *colon;
+
+ *rightParen = '\0';
+ colon = strchr(leftParen + 1, ':');
+ if (colon != NULL) {
+
+ /* Specification is in the form vecName(first:last) */
+ *colon = '\0';
+ result = Tcl_GetInt(interp, leftParen + 1, &first);
+ if ((*(colon + 1) != '\0') && (result == TCL_OK)) {
+ result = Tcl_GetInt(interp, colon + 1, &last);
+ if (first > last) {
+ Tcl_AppendResult(interp, "bad vector range \"",
+ string, "\"", (char *)NULL);
+ result = TCL_ERROR;
+ }
+ size = (last - first) + 1;
+ }
+ *colon = ':';
+ } else {
+ /* Specification is in the form vecName(size) */
+ result = Tcl_GetInt(interp, leftParen + 1, &size);
+ }
+ *rightParen = ')';
+ if (result != TCL_OK) {
+ goto error;
+ }
+ if (size < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"", string, "\"",
+ (char *)NULL);
+ goto error;
+ }
+ }
+ if (leftParen != NULL) {
+ *leftParen = '\0';
+ }
+ /*
+ * By default, we create a TCL command by the name of the vector.
+ */
+ vPtr = Blt_Vec_Create(dataPtr, string,
+ (switches.cmdName == NULL) ? string : switches.cmdName,
+ (switches.varName == NULL) ? string : switches.varName, &isNew);
+ if (leftParen != NULL) {
+ *leftParen = '(';
+ }
+ if (vPtr == NULL) {
+ goto error;
+ }
+ vPtr->freeOnUnset = switches.watchUnset;
+ vPtr->flush = switches.flush;
+ vPtr->offset = first;
+ if (size > 0) {
+ if (Blt_Vec_ChangeLength(interp, vPtr, size) != TCL_OK) {
+ goto error;
+ }
+ }
+ if (!isNew) {
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ }
+ }
+ Blt_FreeSwitches(createSwitches, (char *)&switches, 0);
+ if (vPtr != NULL) {
+ /* Return the name of the last vector created */
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->name, -1);
+ }
+ return TCL_OK;
+ error:
+ Blt_FreeSwitches(createSwitches, (char *)&switches, 0);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ * Creates a TCL command, and array variable representing an instance of
+ * a vector.
+ *
+ * vector a
+ * vector b(20)
+ * vector c(-5:14)
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreateOp(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ return VectorCreate2(clientData, interp, 2, objc, objv);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorDestroyOp --
+ *
+ * Destroys the vector and its related TCL command and array variable (if
+ * they exist).
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ * Side effects:
+ * Deletes the vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorDestroyOp(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp, /* Not used. */
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ VectorInterpData *dataPtr = clientData;
+ Vector *vPtr;
+ int i;
+
+ for (i = 2; i < objc; i++) {
+ if (Blt_Vec_LookupName(dataPtr, Tcl_GetString(objv[i]), &vPtr)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Vec_Free(vPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorExprOp --
+ *
+ * Computes the result of the expression which may be either a scalar
+ * (single value) or vector (list of values).
+ *
+ * Results:
+ * A standard TCL result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorExprOp(
+ ClientData clientData, /* Not Used. */
+ Tcl_Interp *interp,
+ int objc, /* Not used. */
+ Tcl_Obj *const *objv)
+{
+ return Blt_ExprVector(interp, Tcl_GetString(objv[2]), (Blt_Vector *)NULL);
+}
+
+static Blt_OpSpec vectorCmdOps[] =
+{
+ {"create", 1, VectorCreateOp, 3, 0,
+ "vecName ?vecName...? ?switches...?",},
+ {"destroy", 1, VectorDestroyOp, 3, 0,
+ "vecName ?vecName...?",},
+ {"expr", 1, VectorExprOp, 3, 3, "expression",},
+ {"names", 1, VectorNamesOp, 2, 3, "?pattern?...",},
+};
+
+static int nCmdOps = sizeof(vectorCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+VectorCmd(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ VectorCmdProc *proc;
+ /*
+ * Try to replicate the old vector command's behavior:
+ */
+ if (objc > 1) {
+ char *string;
+ char c;
+ int i;
+ Blt_OpSpec *specPtr;
+
+ string = Tcl_GetString(objv[1]);
+ c = string[0];
+ for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) {
+ if ((c == specPtr->name[0]) &&
+ (strcmp(string, specPtr->name) == 0)) {
+ goto doOp;
+ }
+ }
+ /*
+ * The first argument is not an operation, so assume that its
+ * actually the name of a vector to be created
+ */
+ return VectorCreate2(clientData, interp, 1, objc, objv);
+ }
+ doOp:
+ /* Do the usual vector operation lookup now. */
+ proc = Blt_GetOpFromObj(interp, nCmdOps, vectorCmdOps, BLT_OP_ARG1,
+ objc, objv,0);
+ if (proc == NULL) {
+ return TCL_ERROR;
+ }
+ return (*proc) (clientData, interp, objc, objv);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * VectorInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the "vector" command
+ * is deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys the math and index hash tables. In addition removes
+ * the hash table managing all vector names.
+ *
+ *---------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+VectorInterpDeleteProc(
+ ClientData clientData, /* Interpreter-specific data. */
+ Tcl_Interp *interp)
+{
+ VectorInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&dataPtr->vectorTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Vector *vPtr;
+
+ vPtr = Blt_GetHashValue(hPtr);
+ vPtr->hashPtr = NULL;
+ Blt_Vec_Free(vPtr);
+ }
+ Blt_DeleteHashTable(&dataPtr->vectorTable);
+
+ /* If any user-defined math functions were installed, remove them. */
+ Blt_Vec_UninstallMathFunctions(&dataPtr->mathProcTable);
+ Blt_DeleteHashTable(&dataPtr->mathProcTable);
+
+ Blt_DeleteHashTable(&dataPtr->indexProcTable);
+ Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+VectorInterpData *
+Blt_Vec_GetInterpData(Tcl_Interp *interp)
+{
+ VectorInterpData *dataPtr;
+ Tcl_InterpDeleteProc *proc;
+
+ dataPtr = (VectorInterpData *)
+ Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_AssertMalloc(sizeof(VectorInterpData));
+ dataPtr->interp = interp;
+ dataPtr->nextId = 0;
+ Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&dataPtr->vectorTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&dataPtr->mathProcTable, BLT_STRING_KEYS);
+ Blt_InitHashTable(&dataPtr->indexProcTable, BLT_STRING_KEYS);
+ Blt_Vec_InstallMathFunctions(&dataPtr->mathProcTable);
+ Blt_Vec_InstallSpecialIndices(&dataPtr->indexProcTable);
+#ifdef HAVE_SRAND48
+ srand48(time((time_t *) NULL));
+#endif
+ }
+ return dataPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_VectorCmdInitProc --
+ *
+ * This procedure is invoked to initialize the "vector" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Creates the new command and adds a new entry into a global Tcl
+ * associative array.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+Blt_VectorCmdInitProc(Tcl_Interp *interp)
+{
+ static Blt_InitCmdSpec cmdSpec = {"vector", VectorCmd, };
+
+ cmdSpec.clientData = Blt_Vec_GetInterpData(interp);
+ return Blt_InitCmd(interp, "::blt", &cmdSpec);
+}
+
+
+
+/* C Application interface to vectors */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_CreateVector --
+ *
+ * Creates a new vector by the name and size.
+ *
+ * Results:
+ * A standard TCL result. If the new array size is invalid or a vector
+ * already exists by that name, TCL_ERROR is returned. Otherwise TCL_OK
+ * is returned and the new vector is created.
+ *
+ * Side Effects:
+ * Memory will be allocated for the new vector. A new TCL command and
+ * Tcl array variable will be created.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+int
+Blt_CreateVector2(
+ Tcl_Interp *interp,
+ const char *vecName, const char *cmdName, const char *varName,
+ int initialSize,
+ Blt_Vector **vecPtrPtr)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr;
+ int isNew;
+ char *nameCopy;
+
+ if (initialSize < 0) {
+ Tcl_AppendResult(interp, "bad vector size \"", Blt_Itoa(initialSize),
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ dataPtr = Blt_Vec_GetInterpData(interp);
+
+ nameCopy = Blt_AssertStrdup(vecName);
+ vPtr = Blt_Vec_Create(dataPtr, nameCopy, cmdName, varName, &isNew);
+ Blt_Free(nameCopy);
+
+ if (vPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (initialSize > 0) {
+ if (Blt_Vec_ChangeLength(interp, vPtr, initialSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (vecPtrPtr != NULL) {
+ *vecPtrPtr = (Blt_Vector *) vPtr;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_CreateVector(
+ Tcl_Interp *interp,
+ const char *name,
+ int size,
+ Blt_Vector **vecPtrPtr)
+{
+ return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DeleteVector --
+ *
+ * Deletes the vector of the given name. All clients with designated
+ * callback routines will be notified.
+ *
+ * Results:
+ * A standard TCL result. If no vector exists by that name, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned and vector is deleted.
+ *
+ * Side Effects:
+ * Memory will be released for the new vector. Both the TCL command and
+ * array variable will be deleted. All clients which set call back
+ * procedures will be notified.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVector(Blt_Vector *vecPtr)
+{
+ Vector *vPtr = (Vector *)vecPtr;
+
+ Blt_Vec_Free(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_DeleteVectorByName --
+ *
+ * Deletes the vector of the given name. All clients with designated
+ * callback routines will be notified.
+ *
+ * Results:
+ * A standard TCL result. If no vector exists by that name, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned and vector is deleted.
+ *
+ * Side Effects:
+ * Memory will be released for the new vector. Both the TCL command and
+ * array variable will be deleted. All clients which set call back
+ * procedures will be notified.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVectorByName(Tcl_Interp *interp, const char *name)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr;
+ char *nameCopy;
+ int result;
+
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"), the
+ * Blt_Vec_ParseElement routine will segfault when it tries to write into
+ * the string. Therefore make a writable copy and free it when we're
+ * done.
+ */
+ nameCopy = Blt_AssertStrdup(name);
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ result = Blt_Vec_LookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Vec_Free(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_VectorExists2 --
+ *
+ * Returns whether the vector associated with the client token still
+ * exists.
+ *
+ * Results:
+ * Returns 1 is the vector still exists, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_VectorExists2(Tcl_Interp *interp, const char *vecName)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_VectorExists --
+ *
+ * Returns whether the vector associated with the client token
+ * still exists.
+ *
+ * Results:
+ * Returns 1 is the vector still exists, 0 otherwise.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_VectorExists(Tcl_Interp *interp, const char *vecName)
+{
+ char *nameCopy;
+ int result;
+
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"), the
+ * Blt_VectorParseName routine will segfault when it tries to write into
+ * the string. Therefore make a writable copy and free it when we're
+ * done.
+ */
+ nameCopy = Blt_AssertStrdup(vecName);
+ result = Blt_VectorExists2(interp, nameCopy);
+ Blt_Free(nameCopy);
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetVector --
+ *
+ * Returns a pointer to the vector associated with the given name.
+ *
+ * Results:
+ * A standard TCL result. If there is no vector "name", TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned and vecPtrPtr will point to
+ * the vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetVector(Tcl_Interp *interp, const char *name, Blt_Vector **vecPtrPtr)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr;
+ char *nameCopy;
+ int result;
+
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"), the
+ * Blt_VectorParseName routine will segfault when it tries to write into
+ * the string. Therefore make a writable copy and free it when we're
+ * done.
+ */
+ nameCopy = Blt_AssertStrdup(name);
+ result = Blt_Vec_LookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Vec_UpdateRange(vPtr);
+ *vecPtrPtr = (Blt_Vector *) vPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetVectorFromObj --
+ *
+ * Returns a pointer to the vector associated with the given name.
+ *
+ * Results:
+ * A standard TCL result. If there is no vector "name", TCL_ERROR
+ * is returned. Otherwise TCL_OK is returned and vecPtrPtr will
+ * point to the vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetVectorFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ Blt_Vector **vecPtrPtr)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr;
+
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ if (Blt_Vec_LookupName(dataPtr, Tcl_GetString(objPtr), &vPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Blt_Vec_UpdateRange(vPtr);
+ *vecPtrPtr = (Blt_Vector *) vPtr;
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ * Resets the vector data. This is called by a client to indicate that
+ * the vector data has changed. The vector does not need to point to
+ * different memory. Any clients of the vector will be notified of the
+ * change.
+ *
+ * Results:
+ * A standard TCL result. If the new array size is invalid,
+ * TCL_ERROR is returned. Otherwise TCL_OK is returned and the
+ * new vector data is recorded.
+ *
+ * Side Effects:
+ * Any client designated callbacks will be posted. Memory may
+ * be changed for the vector array.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ResetVector(
+ Blt_Vector *vecPtr,
+ double *valueArr, /* Array containing the elements of the
+ * vector. If NULL, indicates to reset the
+ * vector.*/
+ int length, /* The number of elements that the vector
+ * currently holds. */
+ int size, /* The maximum number of elements that the
+ * array can hold. */
+ Tcl_FreeProc *freeProc) /* Address of memory deallocation routine
+ * for the array of values. Can also be
+ * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+ Vector *vPtr = (Vector *)vecPtr;
+
+ if (size < 0) {
+ Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL);
+ return TCL_ERROR;
+ }
+ return Blt_Vec_Reset(vPtr, valueArr, length, size, freeProc);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_ResizeVector --
+ *
+ * Changes the size of the vector. All clients with designated callback
+ * routines will be notified of the size change.
+ *
+ * Results:
+ * A standard TCL result. If no vector exists by that name, TCL_ERROR is
+ * returned. Otherwise TCL_OK is returned and vector is resized.
+ *
+ * Side Effects:
+ * Memory may be reallocated for the new vector size. All clients which
+ * set call back procedures will be notified.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_ResizeVector(Blt_Vector *vecPtr, int length)
+{
+ Vector *vPtr = (Vector *)vecPtr;
+
+ if (Blt_Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, length) != TCL_OK) {
+ Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name,
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (vPtr->flush) {
+ Blt_Vec_FlushCache(vPtr);
+ }
+ Blt_Vec_UpdateClients(vPtr);
+ return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_AllocVectorId --
+ *
+ * Creates an identifier token for an existing vector. The identifier is
+ * used by the client routines to get call backs when (and if) the vector
+ * changes.
+ *
+ * Results:
+ * A standard TCL result. If "vecName" is not associated with a vector,
+ * TCL_ERROR is returned and interp->result is filled with an error
+ * message.
+ *
+ *---------------------------------------------------------------------------
+ */
+Blt_VectorId
+Blt_AllocVectorId(Tcl_Interp *interp, const char *name)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Vector *vPtr;
+ VectorClient *clientPtr;
+ Blt_VectorId clientId;
+ int result;
+ char *nameCopy;
+
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ /*
+ * If the vector name was passed via a read-only string (e.g. "x"), the
+ * Blt_VectorParseName routine will segfault when it tries to write into
+ * the string. Therefore make a writable copy and free it when we're
+ * done.
+ */
+ nameCopy = Blt_AssertStrdup(name);
+ result = Blt_Vec_LookupName(dataPtr, nameCopy, &vPtr);
+ Blt_Free(nameCopy);
+
+ if (result != TCL_OK) {
+ return (Blt_VectorId) 0;
+ }
+ /* Allocate a new client structure */
+ clientPtr = Blt_AssertCalloc(1, sizeof(VectorClient));
+ clientPtr->magic = VECTOR_MAGIC;
+
+ /* Add the new client to the server's list of clients */
+ clientPtr->link = Blt_Chain_Append(vPtr->chain, clientPtr);
+ clientPtr->serverPtr = vPtr;
+ clientId = (Blt_VectorId) clientPtr;
+ return clientId;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_SetVectorChangedProc --
+ *
+ * Sets the routine to be called back when the vector is changed or
+ * deleted. *clientData* will be provided as an argument. If *proc* is
+ * NULL, no callback will be made.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The designated routine will be called when the vector is changed
+ * or deleted.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_SetVectorChangedProc(
+ Blt_VectorId clientId, /* Client token identifying the vector */
+ Blt_VectorChangedProc *proc,/* Address of routine to call when the contents
+ * of the vector change. If NULL, no routine
+ * will be called */
+ ClientData clientData) /* One word of information to pass along when
+ * the above routine is called */
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ return; /* Not a valid token */
+ }
+ clientPtr->clientData = clientData;
+ clientPtr->proc = proc;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_FreeVectorId --
+ *
+ * Releases the token for an existing vector. This indicates that the
+ * client is no longer interested the vector. Any previously specified
+ * callback routine will no longer be invoked when (and if) the vector
+ * changes.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Any previously specified callback routine will no longer be
+ * invoked when (and if) the vector changes.
+ *
+ *---------------------------------------------------------------------------
+ */
+void
+Blt_FreeVectorId(Blt_VectorId clientId)
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ return; /* Not a valid token */
+ }
+ if (clientPtr->serverPtr != NULL) {
+ /* Remove the client from the server's list */
+ Blt_Chain_DeleteLink(clientPtr->serverPtr->chain, clientPtr->link);
+ }
+ Blt_Free(clientPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_NameOfVectorId --
+ *
+ * Returns the name of the vector (and array variable).
+ *
+ * Results:
+ * The name of the array variable is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+const char *
+Blt_NameOfVectorId(Blt_VectorId clientId)
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) {
+ return NULL;
+ }
+ return clientPtr->serverPtr->name;
+}
+
+const char *
+Blt_NameOfVector(Blt_Vector *vecPtr) /* Vector to query. */
+{
+ Vector *vPtr = (Vector *)vecPtr;
+ return vPtr->name;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_VectorNotifyPending --
+ *
+ * Returns the name of the vector (and array variable).
+ *
+ * Results:
+ * The name of the array variable is returned.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_VectorNotifyPending(Blt_VectorId clientId)
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if ((clientPtr == NULL) || (clientPtr->magic != VECTOR_MAGIC) ||
+ (clientPtr->serverPtr == NULL)) {
+ return 0;
+ }
+ return (clientPtr->serverPtr->notifyFlags & NOTIFY_PENDING);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Blt_GetVectorById --
+ *
+ * Returns a pointer to the vector associated with the client
+ * token.
+ *
+ * Results:
+ * A standard TCL result. If the client token is not associated
+ * with a vector any longer, TCL_ERROR is returned. Otherwise,
+ * TCL_OK is returned and vecPtrPtr will point to vector.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Blt_GetVectorById(
+ Tcl_Interp *interp,
+ Blt_VectorId clientId, /* Client token identifying the vector */
+ Blt_Vector **vecPtrPtr)
+{
+ VectorClient *clientPtr = (VectorClient *)clientId;
+
+ if (clientPtr->magic != VECTOR_MAGIC) {
+ Tcl_AppendResult(interp, "bad vector token", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (clientPtr->serverPtr == NULL) {
+ Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_Vec_UpdateRange(clientPtr->serverPtr);
+ *vecPtrPtr = (Blt_Vector *) clientPtr->serverPtr;
+ return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InstallIndexProc(Tcl_Interp *interp, const char *string,
+ Blt_VectorIndexProc *procPtr)
+{
+ VectorInterpData *dataPtr; /* Interpreter-specific data. */
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ dataPtr = Blt_Vec_GetInterpData(interp);
+ hPtr = Blt_CreateHashEntry(&dataPtr->indexProcTable, string, &isNew);
+ if (procPtr == NULL) {
+ Blt_DeleteHashEntry(&dataPtr->indexProcTable, hPtr);
+ } else {
+ Blt_SetHashValue(hPtr, procPtr);
+ }
+}
+
+/* spinellia@acm.org START */
+
+
+#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr
+
+/* routine by Brenner
+ * data is the array of complex data points, perversely
+ * starting at 1
+ * nn is the number of complex points, i.e. half the length of data
+ * isign is 1 for forward, -1 for inverse
+ */
+static void
+four1(double *data, unsigned long nn, int isign)
+{
+ unsigned long n,mmax,m,j,istep,i;
+ double wtemp,wr,wpr,wpi,wi,theta;
+ double tempr,tempi;
+
+ n=nn << 1;
+ j=1;
+ for (i = 1;i<n;i+=2) {
+ if (j > i) {
+ SWAP(data[j],data[i]);
+ SWAP(data[j+1],data[i+1]);
+ }
+ m=n >> 1;
+ while (m >= 2 && j > m) {
+ j -= m;
+ m >>= 1;
+ }
+ j += m;
+ }
+ mmax=2;
+ while (n > mmax) {
+ istep=mmax << 1;
+ theta=isign*(6.28318530717959/mmax);
+ wtemp=sin(0.5*theta);
+ wpr = -2.0*wtemp*wtemp;
+ wpi=sin(theta);
+ wr=1.0;
+ wi=0.0;
+ for (m=1;m<mmax;m+=2) {
+ for (i=m;i<=n;i+=istep) {
+ j=i+mmax;
+ tempr=wr*data[j]-wi*data[j+1];
+ tempi=wr*data[j+1]+wi*data[j];
+ data[j]=data[i]-tempr;
+ data[j+1]=data[i+1]-tempi;
+ data[i] += tempr;
+ data[i+1] += tempi;
+ }
+ wr=(wtemp=wr)*wpr-wi*wpi+wr;
+ wi=wi*wpr+wtemp*wpi+wi;
+ }
+ mmax=istep;
+ }
+}
+#undef SWAP
+
+static int
+smallest_power_of_2_not_less_than(int x)
+{
+ int pow2 = 1;
+
+ while (pow2 < x){
+ pow2 <<= 1;
+ }
+ return pow2;
+}
+
+
+int
+Blt_Vec_FFT(
+ Tcl_Interp *interp, /* Interpreter to report errors to */
+ Vector *realPtr, /* If non-NULL, indicates to compute and
+ store the real values in this vector. */
+ Vector *phasesPtr, /* If non-NULL, indicates to compute
+ * and store the imaginary values in
+ * this vector. */
+ Vector *freqPtr, /* If non-NULL, indicates to compute
+ * and store the frequency values in
+ * this vector. */
+ double delta, /* */
+ int flags, /* Bit mask representing various
+ * flags: FFT_NO_constANT,
+ * FFT_SPECTRUM, and FFT_BARTLETT. */
+ Vector *srcPtr)
+{
+ int length;
+ int pow2len;
+ double *paddedData;
+ int i;
+ double Wss = 0.0;
+ /* TENTATIVE */
+ int middle = 1;
+ int noconstant;
+
+ noconstant = (flags & FFT_NO_CONSTANT) ? 1 : 0;
+
+ /* Length of the original vector. */
+ length = srcPtr->last - srcPtr->first + 1;
+ /* new length */
+ pow2len = smallest_power_of_2_not_less_than( length );
+
+ /* We do not do in-place FFTs */
+ if (realPtr == srcPtr) {
+ Tcl_AppendResult(interp, "real vector \"", realPtr->name,
+ "\" can't be the same as the source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (phasesPtr != NULL) {
+ if (phasesPtr == srcPtr) {
+ Tcl_AppendResult(interp, "imaginary vector \"", phasesPtr->name,
+ "\" can't be the same as the source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_ChangeLength(interp, phasesPtr,
+ pow2len/2-noconstant+middle) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ if (freqPtr != NULL) {
+ if (freqPtr == srcPtr) {
+ Tcl_AppendResult(interp, "frequency vector \"", freqPtr->name,
+ "\" can't be the same as the source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_ChangeLength(interp, freqPtr,
+ pow2len/2-noconstant+middle) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ /* Allocate memory zero-filled array. */
+ paddedData = Blt_Calloc(pow2len * 2, sizeof(double));
+ if (paddedData == NULL) {
+ Tcl_AppendResult(interp, "can't allocate memory for padded data",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Since we just do real transforms, only even locations will be
+ * filled with data.
+ */
+ if (flags & FFT_BARTLETT) { /* Bartlett window 1 - ( (x - N/2) / (N/2) ) */
+ double Nhalf = pow2len*0.5;
+ double Nhalf_1 = 1.0 / Nhalf;
+ double w;
+
+ for (i = 0; i < length; i++) {
+ w = 1.0 - fabs( (i-Nhalf) * Nhalf_1 );
+ Wss += w;
+ paddedData[2*i] = w * srcPtr->valueArr[i];
+ }
+ for(/*empty*/; i < pow2len; i++) {
+ w = 1.0 - fabs((i-Nhalf) * Nhalf_1);
+ Wss += w;
+ }
+ } else { /* Squared window, i.e. no data windowing. */
+ for (i = 0; i < length; i++) {
+ paddedData[2*i] = srcPtr->valueArr[i];
+ }
+ Wss = pow2len;
+ }
+
+ /* Fourier */
+ four1(paddedData-1, pow2len, 1);
+
+ /*
+ for(i=0;i<pow2len;i++){
+ printf( "(%f %f) ", paddedData[2*i], paddedData[2*i+1] );
+ }
+ */
+
+ /* the spectrum is the modulus of the transforms, scaled by 1/N^2 */
+ /* or 1/(N * Wss) for windowed data */
+ if (flags & FFT_SPECTRUM) {
+ double re, im, reS, imS;
+ double factor = 1.0 / (pow2len*Wss);
+ double *v = realPtr->valueArr;
+
+ for (i = 0 + noconstant; i < pow2len / 2; i++) {
+ re = paddedData[2*i];
+ im = paddedData[2*i+1];
+ reS = paddedData[2*pow2len-2*i-2];
+ imS = paddedData[2*pow2len-2*i-1];
+ v[i - noconstant] = factor * (
+# if 0
+ hypot( paddedData[2*i], paddedData[2*i+1] )
+ + hypot(
+ paddedData[pow2len*2-2*i-2],
+ paddedData[pow2len*2-2*i-1]
+ )
+# else
+ sqrt( re*re + im* im ) + sqrt( reS*reS + imS*imS )
+# endif
+ );
+ }
+ } else {
+ for(i = 0 + noconstant; i < pow2len / 2 + middle; i++) {
+ realPtr->valueArr[i - noconstant] = paddedData[2*i];
+ }
+ }
+ if( phasesPtr != NULL ){
+ for (i = 0 + noconstant; i < pow2len / 2 + middle; i++) {
+ phasesPtr->valueArr[i-noconstant] = paddedData[2*i+1];
+ }
+ }
+
+ /* Compute frequencies */
+ if (freqPtr != NULL) {
+ double N = pow2len;
+ double denom = 1.0 / N / delta;
+ for( i=0+noconstant; i<pow2len/2+middle; i++ ){
+ freqPtr->valueArr[i-noconstant] = ((double) i) * denom;
+ }
+ }
+
+ /* Memory is necessarily dynamic, because nobody touched it ! */
+ Blt_Free(paddedData);
+
+ realPtr->offset = 0;
+ return TCL_OK;
+}
+
+
+int
+Blt_Vec_InverseFFT(Tcl_Interp *interp, Vector *srcImagPtr, Vector *destRealPtr,
+ Vector *destImagPtr, Vector *srcPtr)
+{
+ int length;
+ int pow2len;
+ double *paddedData;
+ int i;
+ double oneOverN;
+
+ if ((destRealPtr == srcPtr) || (destImagPtr == srcPtr )){
+/* we do not do in-place FFTs */
+ return TCL_ERROR;
+ }
+ length = srcPtr->last - srcPtr->first + 1;
+
+/* minus one because of the magical middle element! */
+ pow2len = smallest_power_of_2_not_less_than( (length-1)*2 );
+ oneOverN = 1.0 / pow2len;
+
+ if (Blt_Vec_ChangeLength(interp, destRealPtr, pow2len) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Blt_Vec_ChangeLength(interp, destImagPtr, pow2len) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if( length != (srcImagPtr->last - srcImagPtr->first + 1) ){
+ Tcl_AppendResult(srcPtr->interp,
+ "the length of the imagPart vector must ",
+ "be the same as the real one", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ paddedData = Blt_AssertMalloc( pow2len*2*sizeof(double) );
+ if( paddedData == NULL ){
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "memory allocation failed", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ for(i=0;i<pow2len*2;i++) { paddedData[i] = 0.0; }
+ for(i=0;i<length-1;i++){
+ paddedData[2*i] = srcPtr->valueArr[i];
+ paddedData[2*i+1] = srcImagPtr->valueArr[i];
+ paddedData[pow2len*2 - 2*i - 2 ] = srcPtr->valueArr[i+1];
+ paddedData[pow2len*2 - 2*i - 1 ] = - srcImagPtr->valueArr[i+1];
+ }
+/* mythical middle element */
+ paddedData[(length-1)*2] = srcPtr->valueArr[length-1];
+ paddedData[(length-1)*2+1] = srcImagPtr->valueArr[length-1];
+
+/*
+for(i=0;i<pow2len;i++){
+ printf( "(%f %f) ", paddedData[2*i], paddedData[2*i+1] );
+}
+ */
+
+/* fourier */
+ four1( paddedData-1, pow2len, -1 );
+
+/* put values in their places, normalising by 1/N */
+ for(i=0;i<pow2len;i++){
+ destRealPtr->valueArr[i] = paddedData[2*i] * oneOverN;
+ destImagPtr->valueArr[i] = paddedData[2*i+1] * oneOverN;
+ }
+
+/* memory is necessarily dynamic, because nobody touched it ! */
+ Blt_Free( paddedData );
+
+ return TCL_OK;
+}
+
+
+/* spinellia@acm.org STOP */
+
+
+
+static double
+FindSplit(Point2d *points, int i, int j, int *split)
+{
+ double maxDist2;
+
+ maxDist2 = -1.0;
+ if ((i + 1) < j) {
+ int k;
+ double a, b, c;
+
+ /*
+ *
+ * dist2 P(k) = | 1 P(i).x P(i).y |
+ * | 1 P(j).x P(j).y |
+ * | 1 P(k).x P(k).y |
+ * ------------------------------------------
+ * (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
+ */
+
+ a = points[i].y - points[j].y;
+ b = points[j].x - points[i].x;
+ c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
+ for (k = (i + 1); k < j; k++) {
+ double dist2;
+
+ dist2 = (points[k].x * a) + (points[k].y * b) + c;
+ if (dist2 < 0.0) {
+ dist2 = -dist2;
+ }
+ if (dist2 > maxDist2) {
+ maxDist2 = dist2; /* Track the maximum. */
+ *split = k;
+ }
+ }
+ /* Correction for segment length---should be redone if can == 0 */
+ maxDist2 *= maxDist2 / (a * a + b * b);
+ }
+ return maxDist2;
+}
+
+
+/* Douglas-Peucker line simplification algorithm */
+int
+Blt_SimplifyLine(Point2d *inputPts, int low, int high, double tolerance,
+ int *indices)
+{
+#define StackPush(a) s++, stack[s] = (a)
+#define StackPop(a) (a) = stack[s], s--
+#define StackEmpty() (s < 0)
+#define StackTop() stack[s]
+ int *stack;
+ int split = -1;
+ double dist2, tolerance2;
+ int s = -1; /* Points to top stack item. */
+ int count;
+
+ stack = Blt_AssertMalloc(sizeof(int) * (high - low + 1));
+ StackPush(high);
+ count = 0;
+ indices[count++] = 0;
+ tolerance2 = tolerance * tolerance;
+ while (!StackEmpty()) {
+ dist2 = FindSplit(inputPts, low, StackTop(), &split);
+ if (dist2 > tolerance2) {
+ StackPush(split);
+ } else {
+ indices[count++] = StackTop();
+ StackPop(low);
+ }
+ }
+ Blt_Free(stack);
+ return count;
+}
diff --git a/src/bltVector.h b/src/bltVector.h
new file mode 100644
index 0000000..78eddc5
--- /dev/null
+++ b/src/bltVector.h
@@ -0,0 +1,137 @@
+
+/*
+ * bltVector.h --
+ *
+ * Copyright 1993-2004 George A Howlett.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _BLT_VECTOR_H
+#define _BLT_VECTOR_H
+
+typedef enum {
+ BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */
+ BLT_VECTOR_NOTIFY_DESTROY /* The vector has been destroyed and the client
+ * should no longer use its data (calling
+ * Blt_FreeVectorId) */
+} Blt_VectorNotify;
+
+typedef struct _Blt_VectorId *Blt_VectorId;
+
+typedef void (Blt_VectorChangedProc)(Tcl_Interp *interp, ClientData clientData,
+ Blt_VectorNotify notify);
+
+typedef struct {
+ double *valueArr; /* Array of values (possibly malloc-ed) */
+ int numValues; /* Number of values in the array */
+ int arraySize; /* Size of the allocated space */
+ double min, max; /* Minimum and maximum values in the vector */
+ int dirty; /* Indicates if the vector has been updated */
+ int reserved; /* Reserved for future use */
+
+} Blt_Vector;
+
+typedef double (Blt_VectorIndexProc)(Blt_Vector * vecPtr);
+
+typedef enum {
+ BLT_MATH_FUNC_SCALAR = 1, /* The function returns a single double
+ * precision value. */
+ BLT_MATH_FUNC_VECTOR /* The function processes the entire vector. */
+} Blt_MathFuncType;
+
+/*
+ * To be safe, use the macros below, rather than the fields of the
+ * structure directly.
+ *
+ * The Blt_Vector is basically an opaque type. But it's also the
+ * actual memory address of the vector itself. I wanted to make the
+ * API as unobtrusive as possible. So instead of giving you a copy of
+ * the vector, providing various functions to access and update the
+ * vector, you get your hands on the actual memory (array of doubles)
+ * shared by all the vector's clients.
+ *
+ * The trade-off for speed and convenience is safety. You can easily
+ * break things by writing into the vector when other clients are
+ * using it. Use Blt_ResetVector to get around this. At least the
+ * macros are a reminder it isn't really safe to reset the data
+ * fields, except by the API routines.
+ */
+#define Blt_VecData(v) ((v)->valueArr)
+#define Blt_VecLength(v) ((v)->numValues)
+#define Blt_VecSize(v) ((v)->arraySize)
+#define Blt_VecDirty(v) ((v)->dirty)
+
+BLT_EXTERN double Blt_VecMin(Blt_Vector *vPtr);
+BLT_EXTERN double Blt_VecMax(Blt_Vector *vPtr);
+
+BLT_EXTERN Blt_VectorId Blt_AllocVectorId(Tcl_Interp *interp,
+ const char *vecName);
+
+BLT_EXTERN void Blt_SetVectorChangedProc(Blt_VectorId clientId,
+ Blt_VectorChangedProc *proc, ClientData clientData);
+
+BLT_EXTERN void Blt_FreeVectorId(Blt_VectorId clientId);
+
+BLT_EXTERN int Blt_GetVectorById(Tcl_Interp *interp, Blt_VectorId clientId,
+ Blt_Vector **vecPtrPtr);
+
+BLT_EXTERN const char *Blt_NameOfVectorId(Blt_VectorId clientId);
+
+BLT_EXTERN const char *Blt_NameOfVector(Blt_Vector *vecPtr);
+
+BLT_EXTERN int Blt_VectorNotifyPending(Blt_VectorId clientId);
+
+BLT_EXTERN int Blt_CreateVector(Tcl_Interp *interp, const char *vecName,
+ int size, Blt_Vector ** vecPtrPtr);
+
+BLT_EXTERN int Blt_CreateVector2(Tcl_Interp *interp, const char *vecName,
+ const char *cmdName, const char *varName, int initialSize,
+ Blt_Vector **vecPtrPtr);
+
+BLT_EXTERN int Blt_GetVector(Tcl_Interp *interp, const char *vecName,
+ Blt_Vector **vecPtrPtr);
+
+BLT_EXTERN int Blt_GetVectorFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Blt_Vector **vecPtrPtr);
+
+BLT_EXTERN int Blt_VectorExists(Tcl_Interp *interp, const char *vecName);
+
+BLT_EXTERN int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, int n,
+ int arraySize, Tcl_FreeProc *freeProc);
+
+BLT_EXTERN int Blt_ResizeVector(Blt_Vector *vecPtr, int n);
+
+BLT_EXTERN int Blt_DeleteVectorByName(Tcl_Interp *interp, const char *vecName);
+
+BLT_EXTERN int Blt_DeleteVector(Blt_Vector *vecPtr);
+
+BLT_EXTERN int Blt_ExprVector(Tcl_Interp *interp, char *expr,
+ Blt_Vector *vecPtr);
+
+BLT_EXTERN void Blt_InstallIndexProc(Tcl_Interp *interp, const char *indexName,
+ Blt_VectorIndexProc * procPtr);
+
+BLT_EXTERN int Blt_VectorExists2(Tcl_Interp *interp, const char *vecName);
+
+#endif /* _BLT_VECTOR_H */