diff --git a/components/jvcllaz/design/JvCustomControls/images/images.txt b/components/jvcllaz/design/JvCustomControls/images/images.txt
index 88a99214b..227b8afdb 100644
--- a/components/jvcllaz/design/JvCustomControls/images/images.txt
+++ b/components/jvcllaz/design/JvCustomControls/images/images.txt
@@ -1,6 +1,7 @@
tjvtabbar.bmp
tjvmoderntabbarpainter.bmp
tjvtabbarxppainter.bmp
+tjvchart.bmp
tjvgammapanel.png
tjvgammapanel_150.png
tjvgammapanel_200.png
diff --git a/components/jvcllaz/design/JvCustomControls/images/tjvchart.bmp b/components/jvcllaz/design/JvCustomControls/images/tjvchart.bmp
new file mode 100644
index 000000000..dadad9636
Binary files /dev/null and b/components/jvcllaz/design/JvCustomControls/images/tjvchart.bmp differ
diff --git a/components/jvcllaz/design/JvCustomControls/jvcustomreg.pas b/components/jvcllaz/design/JvCustomControls/jvcustomreg.pas
index a1fe21a5a..728dcb011 100644
--- a/components/jvcllaz/design/JvCustomControls/jvcustomreg.pas
+++ b/components/jvcllaz/design/JvCustomControls/jvcustomreg.pas
@@ -22,7 +22,8 @@ uses
// JvTabBar, JvTabBarXPPainter,
JvThumbImage, JvThumbnails, JvThumbViews,
JvTimeLine, JvTMTimeLine, JvTimeLineEditor,
- JvImagesViewer, JvImageListViewer, JvOwnerDrawViewer;
+ JvImagesViewer, JvImageListViewer, JvOwnerDrawViewer,
+ JvChart;
procedure Register;
begin
@@ -33,7 +34,8 @@ begin
TJvThumbView, TJvThumbnail, TJvThumbImage,
TJvTimeLine, TJvTMTimeLine,
TJvGammaPanel,
- TJvImagesViewer, TJvImageListViewer, TJvOwnerDrawViewer
+ TJvImagesViewer, TJvImageListViewer, TJvOwnerDrawViewer,
+ TJvChart
]);
// Timeline
diff --git a/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpi b/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpi
new file mode 100644
index 000000000..1a4e7d2a3
--- /dev/null
+++ b/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpr b/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpr
new file mode 100644
index 000000000..d0ccaa902
--- /dev/null
+++ b/components/jvcllaz/examples/JvChartDemo/JvChartDemo.lpr
@@ -0,0 +1,20 @@
+program JvChartDemo;
+
+{$MODE OBJFPC}{$H+}
+
+uses
+ Interfaces,
+ Forms, printer4lazarus,
+ JvChartDemoFm in 'JvChartDemoFm.pas' {JvChartDemoForm},
+ StatsClasses in 'StatsClasses.pas';
+
+{$R *.res}
+
+
+begin
+ Application.Initialize;
+ Application.Title:='JvChartDemo';
+ Application.Scaled := true;
+ Application.CreateForm(TJvChartDemoForm, JvChartDemoForm);
+ Application.Run;
+end.
diff --git a/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.lfm b/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.lfm
new file mode 100644
index 000000000..c9c54bcff
--- /dev/null
+++ b/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.lfm
@@ -0,0 +1,526 @@
+object JvChartDemoForm: TJvChartDemoForm
+ Left = 436
+ Height = 453
+ Top = 278
+ Width = 789
+ Caption = 'JEDI JvChart Demo'
+ ClientHeight = 433
+ ClientWidth = 789
+ Color = clWindow
+ Menu = MainMenu1
+ OnCreate = FormCreate
+ OnDestroy = FormDestroy
+ OnResize = FormResize
+ Position = poScreenCenter
+ LCLVersion = '2.1.0.0'
+ object Chart: TJvChart
+ Left = 164
+ Height = 398
+ Top = 35
+ Width = 625
+ Font.Height = -19
+ Align = alClient
+ Options.XAxisValuesPerDivision = 10
+ Options.XAxisLabelAlignment = taLeftJustify
+ Options.XAxisDateTimeMode = False
+ Options.XAxisDateTimeDivision = 0
+ Options.XAxisLegendSkipBy = 2
+ Options.PenCount = 3
+ Options.XGap = 0
+ Options.XOrigin = 0
+ Options.YOrigin = 0
+ Options.YStartOffset = 42
+ Options.PrimaryYAxis.YMax = 20
+ Options.PrimaryYAxis.YMin = 0
+ Options.PrimaryYAxis.YDivisions = 20
+ Options.PrimaryYAxis.YLegendDecimalPlaces = 1
+ Options.SecondaryYAxis.YMax = 140
+ Options.SecondaryYAxis.YMin = 0
+ Options.SecondaryYAxis.YLegendDecimalPlaces = 0
+ Options.SecondaryYAxis.DefaultYLegends = 60
+ Options.MouseDragObjects = False
+ Options.Legend = clChartLegendBelow
+ Options.LegendRowCount = 1
+ Options.PenLineWidth = 2
+ Options.AxisLineWidth = 3
+ Options.XValueCount = 20
+ Options.HeaderFont.Color = clWindowText
+ Options.LegendFont.Color = clWindowText
+ Options.AxisFont.Color = clWindowText
+ Options.PaperColor = clWhite
+ Options.AxisLineColor = clBlack
+ Options.CursorColor = clBlack
+ Options.CursorStyle = psDot
+ OnChartPaint = ChartChartPaint
+ OnBeginFloatingMarkerDrag = ChartBeginFloatingMarkerDrag
+ OnEndFloatingMarkerDrag = ChartEndFloatingMarkerDrag
+ end
+ object Splitter1: TSplitter
+ Left = 159
+ Height = 398
+ Top = 35
+ Width = 5
+ end
+ object PanelTop: TPanel
+ Left = 0
+ Height = 35
+ Top = 0
+ Width = 789
+ Align = alTop
+ ClientHeight = 35
+ ClientWidth = 789
+ Font.CharSet = ANSI_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -13
+ Font.Name = 'Verdana'
+ ParentFont = False
+ TabOrder = 0
+ object ButtonBarChart: TSpeedButton
+ Left = 5
+ Height = 25
+ Hint = 'Bar Chart'
+ Top = 5
+ Width = 25
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 04000000000000010000120B0000120B00001000000000000000000000000000
+ 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333300030003
+ 0003333377737773777333333333333333333FFFFFFFFFFFFFFF770000000000
+ 0000777777777777777733039993BBB3CCC3337F737F737F737F37039993BBB3
+ CCC3377F737F737F737F33039993BBB3CCC33F7F737F737F737F77079997BBB7
+ CCC77777737773777377330399930003CCC3337F737F7773737F370399933333
+ CCC3377F737F3333737F330399933333CCC33F7F737FFFFF737F770700077777
+ CCC77777777777777377330333333333CCC3337F33333333737F370333333333
+ 0003377F33333333777333033333333333333F7FFFFFFFFFFFFF770777777777
+ 7777777777777777777733333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonBarChartClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonLine: TSpeedButton
+ Left = 58
+ Height = 25
+ Hint = 'Line Chart'
+ Top = 5
+ Width = 25
+ Down = True
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 04000000000000010000120B0000120B00001000000000000000000000000000
+ 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00337333733373
+ 3373337F3F7F3F7F3F7F33737373737373733F7F7F7F7F7F7F7F770000000000
+ 000077777777777777773303333333333333337FF333333F33333709333333C3
+ 333337773F3FF373F333330393993C3C33333F7F7F77F7F7FFFF77079797977C
+ 77777777777777777777330339339333C333337FF73373F37F33370C333C3933
+ 933337773F3737F37FF33303C3C33939C9333F7F7F7FF7F777FF7707C7C77797
+ 7C97777777777777777733033C3333333C33337F37F33333373F37033C333333
+ 33C3377F37333333337333033333333333333F7FFFFFFFFFFFFF770777777777
+ 7777777777777777777733333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonLineClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonStackedBarAve: TSpeedButton
+ Left = 345
+ Height = 25
+ Hint = 'Stacked Bars with Average'
+ Top = 7
+ Width = 25
+ Enabled = False
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 0400000000000001000000000000000000001000000000000000000000000000
+ 80000080000000808000800000008000800080800000C0C0C000808080000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333300030003
+ 0003333388838883888333333333333333333FFFFFFFFFFFFFFF880000000000
+ 000088888888888888883303999399939993338F838F838F838F38039993BBB3
+ 9993388F838F838F838F3303BBB3BBB399933F8F838F838F838F8808BBB8AAA8
+ 999888888388838883883303AAA300039993338F838F8883838F3803AAA30003
+ BBB3388F838F3333838F3303AAA30003BBB33F8F838FFFFF838F880800080008
+ BBB88888888888888388330300030003AAA3338F33333333838F380300030003
+ 0003388F33333333888333030003000300033F8FFFFFFFFFFFFF880888888888
+ 8888888888888888888833333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonStackedBarAveClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonStackedBar: TSpeedButton
+ Left = 111
+ Height = 25
+ Hint = 'Stacked Bars'
+ Top = 5
+ Width = 26
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 0400000000000001000000000000000000001000000000000000000000000000
+ 80000080000000808000800000008000800080800000C0C0C000808080000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333300030003
+ 0003333388838883888333333333333333333FFFFFFFFFFFFFFF880000000000
+ 000088888888888888883303999399939993338F838F838F838F38039993BBB3
+ BBB3388F838F838F838F3303BBB3BBB3BBB33F8F838F838F838F8808BBB8AAA8
+ BBB888888388838883883303AAA30003BBB3338F838F8883838F3803AAA33333
+ BBB3388F838F3333838F3303AAA33333BBB33F8F838FFFFF838F880800088888
+ BBB88888888888888388330333333333AAA3338F33333333838F380333333333
+ 0003388F33333333888333033333333333333F8FFFFFFFFFFFFF880888888888
+ 8888888888888888888833333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonStackedBarClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonBarAve: TSpeedButton
+ Left = 31
+ Height = 25
+ Hint = 'Bar Chart with Average Line'
+ Top = 5
+ Width = 25
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 0400000000000001000000000000000000001000000000000000000000000000
+ 80000080000000808000800000008000800080800000C0C0C000808080000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333300030003
+ 0003333388838883888333333333333333333FFFFFFFFFFFFFFF880000000000
+ 0000888888888888888833039BA39BA39BA3338F838F838F838F38039BA39BA3
+ 9BA3388F838F838F838F33039BA39BA39BA33F8F838F838F838F88089BA89BA8
+ 9BA8888883888388838833039BA390A39BA3338F838F8883838F38039BA30B03
+ 9BA3388F838F3333838F33039BA03B309BA33F8F838FFFFF838F88089B088B88
+ 0BA8888888888888838833039033333390A3338F33333333838F380393333333
+ 9BA3388F3333333388833303333333333B333F8FFFFFFFFFFFFF880888888888
+ 8B88888888888888888833333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonBarAveClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonPie: TSpeedButton
+ Left = 371
+ Height = 25
+ Hint = 'Show Pie Chart (one series only)'
+ Top = 7
+ Width = 25
+ Enabled = False
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 04000000000000010000120B0000120B00001000000000000000000000000000
+ 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
+ 3333333333FFFFF3333333333700073333333FFF3777773F3FFF00030990BB03
+ 000077737337F373777733309990BBB0333333373337F3373F3333099990BBBB
+ 033333733337F33373F337999990BBBBB73337F33337F33337F330999990BBBB
+ B03337F33337FFFFF7F3309999900000003337F33337777777F33099990A0CCC
+ C03337F3337373F337F3379990AAA0CCC733373F3733373F373333090AAAAA0C
+ 033333737333337373333330AAAAAAA033333FF73F33333733FF00330AAAAA03
+ 3000773373FFFF73377733333700073333333333377777333333333333333333
+ 3333333333333333333333333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonPieClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonMarkers: TSpeedButton
+ Left = 291
+ Height = 25
+ Hint = 'Show Markers'
+ Top = 7
+ Width = 25
+ Enabled = False
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 0400000000000001000000000000000000001000000000000000000000000000
+ 80000080000000808000800000008000800080800000C0C0C000808080000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00338333833383
+ 3383338F3F8F3F8F3F8F33838383838383833F8F8F8F8F8F8F8F880000000000
+ 000088888888888888883303333333333333338F33333333333338033393A333
+ 333A388833333333333333033C333C3333333F8FFFFFFFFFFFFF88088888988C
+ 888A88888888888888883303333333333333338F333333333333380A33A33333
+ 3333388833333333333333033333333A33333F8FFFFFFFFFFFFF880888888898
+ 889888888888888888883303933C33333333338F333333333333380333333333
+ 3A33388F3333333333333303C333333333333F8FFFFFFFFFFFFF880888888888
+ 8888888888888888888833333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonMarkersClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonDeltaAverage: TSpeedButton
+ Left = 319
+ Height = 25
+ Hint = 'Delta Average'
+ Top = 7
+ Width = 25
+ Enabled = False
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 0400000000000001000000000000000000001000000000000000000000000000
+ 80000080000000808000800000008000800080800000C0C0C000808080000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333300030003
+ 0003333388838883888333333333333333333FFFFFFFFFFFFFFF888000000000
+ 0000888888888888888833333333033333333333333F33333333333333330333
+ 33333333333F3333333333CCCCCC033333333333333F3333333388CCCCCC0333
+ 33333333333F3333333333333333033333333333333F33333333333333330AAA
+ A3333333333F33333333333333330AAAA3333333333F33333333833333330333
+ 33333333333F3333333333333333099999993333333F33333333333333330999
+ 99993333333F3333333333333333033333333FFFFFFFFFFFFFFF888000000000
+ 0000888888888888888833333333333333333333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonDeltaAverageClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object ButtonLineMarker: TSpeedButton
+ Left = 84
+ Height = 25
+ Hint = 'Line with Markers'
+ Top = 5
+ Width = 25
+ Flat = True
+ Glyph.Data = {
+ 76010000424D7601000000000000760000002800000020000000100000000100
+ 04000000000000010000120B0000120B00001000000000000000000000000000
+ 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00337333733373
+ 3373337F3F7F3F7F3F7F33737373737373733F7F7F7F7F7F7F7F770000000000
+ 000077777777777777773303333333333333337FF333333F33333709333333C3
+ 333337773F3FF373F333330393993C3C33333F7F7F77F7F7FFFF77079797977C
+ 77777777777777777777330339339333C333337FF73373F37F33370C333C3933
+ 933337773F3737F37FF33303C3C33939C9333F7F7F7FF7F777FF7707C7C79797
+ 7C97777777777777777733033C3393933C33337F37F33333373F37033C333933
+ 33C3377F3733333333733303CCC3393333C33F7FFFFFFFFFFFFF77077C777777
+ 7CCC777777777777777733333333333333C33333333333333333
+ }
+ GroupIndex = 1
+ NumGlyphs = 2
+ OnClick = ButtonLineMarkerClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ object Label1: TLabel
+ Left = 398
+ Height = 16
+ Top = 10
+ Width = 210
+ Caption = '(TODO: Fix broken chart types)'
+ Enabled = False
+ ParentColor = False
+ end
+ object SpeedButtonTestMouseOver: TSpeedButton
+ Left = 139
+ Height = 25
+ Hint = 'Floating Markers Demo (new JAN 2005!)'
+ Top = 5
+ Width = 26
+ Flat = True
+ Glyph.Data = {
+ F6000000424DF600000000000000760000002800000010000000100000000100
+ 0400000000008000000000000000000000001000000000000000000000000000
+ 8000008000000080800080000000800080008080000080808000C0C0C0000000
+ FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00D88888888888
+ 888D79FFFFFFFFFFFF98999FFFFFFFFFF99979CFFFFFFFFFCF987FFCFFFFFFFF
+ CFF87FFCFFFFFFFCFFF87FFCFFFFFFFCFFF87FFFCFFFFFCFFFF87FFFCFFFFFCF
+ FFF87FFFCFFFFFCFFFF87FFFFCFFFCFFFFF87FFFFCFFFCFFFFF87FFFFFCFFCFF
+ FFF87FFFFFC9CFFFFFF87FFFFF999FFFFFF8D77777797777777D
+ }
+ GroupIndex = 1
+ OnClick = SpeedButtonTestMouseOverClick
+ ShowHint = True
+ ParentShowHint = False
+ end
+ end
+ object ListBox1: TListBox
+ Left = 0
+ Height = 398
+ Top = 35
+ Width = 159
+ Align = alLeft
+ Color = clBtnFace
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Courier New'
+ Items.Strings = (
+ ''
+ )
+ ItemHeight = 14
+ OnClick = ListBox1Click
+ OnDblClick = ListBox1DblClick
+ ParentFont = False
+ TabOrder = 1
+ Visible = False
+ end
+ object ColorDialog1: TColorDialog
+ Color = clBlack
+ CustomColors.Strings = (
+ 'ColorA=000000'
+ 'ColorB=000080'
+ 'ColorC=008000'
+ 'ColorD=008080'
+ 'ColorE=800000'
+ 'ColorF=800080'
+ 'ColorG=808000'
+ 'ColorH=808080'
+ 'ColorI=C0C0C0'
+ 'ColorJ=0000FF'
+ 'ColorK=00FF00'
+ 'ColorL=00FFFF'
+ 'ColorM=FF0000'
+ 'ColorN=FF00FF'
+ 'ColorO=FFFF00'
+ 'ColorP=FFFFFF'
+ 'ColorQ=C0DCC0'
+ 'ColorR=F0CAA6'
+ 'ColorS=F0FBFF'
+ 'ColorT=A4A0A0'
+ )
+ left = 504
+ top = 160
+ end
+ object FontDialog1: TFontDialog
+ Font.Color = clWindowText
+ Font.Height = -13
+ Font.Name = 'System'
+ MinFontSize = 0
+ MaxFontSize = 0
+ Options = []
+ left = 592
+ top = 160
+ end
+ object MainMenu1: TMainMenu
+ left = 464
+ top = 48
+ object Demo1: TMenuItem
+ Caption = '&Demo'
+ object mnuSetHeaderFont: TMenuItem
+ Caption = 'Set Header Font...'
+ OnClick = mnuSetHeaderFontClick
+ end
+ object mnuSetAxisTitlefont: TMenuItem
+ Caption = 'Set Axis Title Font...'
+ OnClick = mnuSetAxisTitlefontClick
+ end
+ object mnuSetAxisFont: TMenuItem
+ Caption = 'Set Axis Label Font...'
+ OnClick = mnuSetAxisFontClick
+ end
+ object mnuSetLegendFont: TMenuItem
+ Caption = 'Set Legend Font...'
+ OnClick = mnuSetLegendFontClick
+ end
+ object N2: TMenuItem
+ Caption = '-'
+ end
+ object mnuScrolling: TMenuItem
+ Caption = 'Scrolling'
+ ShortCut = 16467
+ OnClick = mnuScrollingClick
+ end
+ object CopyToClipboard1: TMenuItem
+ Caption = 'Copy To Clipboard'
+ ShortCut = 16451
+ OnClick = CopyToClipboard1Click
+ end
+ object Print1: TMenuItem
+ Caption = 'Print Chart to Printer'
+ ShortCut = 16464
+ OnClick = Print1Click
+ end
+ object PrintOptions1: TMenuItem
+ Caption = '&Print Options'
+ OnClick = PrintOptions1Click
+ end
+ object N1: TMenuItem
+ Caption = '-'
+ end
+ object Generatenewrandomvalues1: TMenuItem
+ Caption = '&Generate new random values'
+ ShortCut = 16466
+ OnClick = Generatenewrandomvalues1Click
+ end
+ object ShowgapinLineChart1: TMenuItem
+ Caption = '&Show gap in Line Chart'
+ ShortCut = 16455
+ OnClick = ShowgapinLineChart1Click
+ end
+ object DateTimeAxisMode: TMenuItem
+ Caption = 'Date/Time Axis Mode'
+ OnClick = DateTimeAxisModeClick
+ end
+ object MenuNegValueTest: TMenuItem
+ AutoCheck = True
+ Caption = 'Use Positive and Negative Demo Values (-20..+20)'
+ OnClick = MenuNegValueTestClick
+ end
+ object MenuSecondaryAxisMode: TMenuItem
+ Caption = '&Plot Markers+Values using Alternate Scale (0-120%)'
+ OnClick = MenuSecondaryAxisModeClick
+ end
+ object N4: TMenuItem
+ Caption = '-'
+ end
+ object ShowDataInListbox1: TMenuItem
+ Caption = '&Show Data in listbox'
+ ShortCut = 16452
+ OnClick = ShowDataInListbox1Click
+ end
+ object LargeDataset576samples1: TMenuItem
+ Caption = '&Large Dataset (576 samples)'
+ ShortCut = 16460
+ OnClick = LargeDataset576samples1Click
+ end
+ object NewFeaturesfor20071: TMenuItem
+ Caption = 'New Features for 2007'
+ OnClick = NewFeaturesfor20071Click
+ end
+ end
+ object Help1: TMenuItem
+ Caption = '&Help'
+ object About1: TMenuItem
+ Caption = '&About JvChart Component'
+ ShortCut = 16449
+ OnClick = About1Click
+ end
+ end
+ end
+ object Timer1: TTimer
+ Enabled = False
+ Interval = 100
+ OnTimer = Timer1Timer
+ left = 344
+ top = 48
+ end
+ object PrinterSetupDialog1: TPrinterSetupDialog
+ left = 264
+ top = 160
+ end
+ object PrintDialog1: TPrintDialog
+ left = 392
+ top = 160
+ end
+end
diff --git a/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.pas b/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.pas
new file mode 100644
index 000000000..1aa656607
--- /dev/null
+++ b/components/jvcllaz/examples/JvChartDemo/jvchartdemofm.pas
@@ -0,0 +1,854 @@
+{-----------------------------------------------------------------------------
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/MPL-1.1.html
+
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
+the specific language governing rights and limitations under the License.
+
+The Original Code is: JvChartDemoFm.Pas, released on 2002-10-04.
+
+The Initial Developer of the Original Code is AABsoft and MÃ¥rten Henrichson.
+(C) 1996 AABsoft and MÃ¥rten Henrichson.
+All Rights Reserved.
+
+Contributor(s): -
+
+Last Modified: 2005-01-14
+
+ Modified March 2007 by Warren Postma - Show new JvChart 2007 Features.
+
+
+You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
+located at http://jvcl.delphi-jedi.org
+
+Description:
+ BAR/LINE Charting Component for JEDI [Updated March 2007]
+
+Known Issues:
+-----------------------------------------------------------------------------}
+
+{$MODE OBJFPC}{$H+}
+
+unit JvChartDemoFm;
+
+interface
+
+uses
+ Windows, SysUtils, Messages, Classes, Graphics, Controls,
+ Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Spin, PrintersDlgs,
+ JvChart, JvComponent, JvExControls, StatsClasses, Menus;
+
+type
+
+ { TJvChartDemoForm }
+
+ TJvChartDemoForm = class(TForm)
+ mnuSetAxisTitlefont: TMenuItem;
+ mnuSetLegendFont: TMenuItem;
+ mnuSetHeaderFont: TMenuItem;
+ PanelTop: TPanel;
+ ButtonBarChart: TSpeedButton;
+ ButtonLine: TSpeedButton;
+ ButtonStackedBarAve: TSpeedButton;
+ ButtonStackedBar: TSpeedButton;
+ ButtonBarAve: TSpeedButton;
+ ColorDialog1: TColorDialog;
+ ButtonPie: TSpeedButton;
+ FontDialog1: TFontDialog;
+ ButtonMarkers: TSpeedButton;
+ ButtonDeltaAverage: TSpeedButton;
+ Chart: TJvChart;
+ ButtonLineMarker: TSpeedButton;
+ Label1: TLabel;
+ MainMenu1: TMainMenu;
+ Demo1: TMenuItem;
+ Help1: TMenuItem;
+ About1: TMenuItem;
+ ShowgapinLineChart1: TMenuItem;
+ N1: TMenuItem;
+ Print1: TMenuItem;
+ Generatenewrandomvalues1: TMenuItem;
+ CopyToClipboard1: TMenuItem;
+ N2: TMenuItem;
+ mnuSetAxisFont: TMenuItem;
+ mnuScrolling: TMenuItem;
+ ListBox1: TListBox;
+ Splitter1: TSplitter;
+ Timer1: TTimer;
+ ShowDataInListbox1: TMenuItem;
+ N4: TMenuItem;
+ LargeDataset576samples1: TMenuItem;
+ DateTimeAxisMode: TMenuItem;
+ PrintOptions1: TMenuItem;
+ PrinterSetupDialog1: TPrinterSetupDialog;
+ PrintDialog1: TPrintDialog;
+ MenuSecondaryAxisMode: TMenuItem;
+ MenuNegValueTest: TMenuItem;
+ SpeedButtonTestMouseOver: TSpeedButton;
+ NewFeaturesfor20071: TMenuItem;
+ procedure FormResize(Sender: TObject);
+ procedure ButtonLineClick(Sender: TObject);
+ procedure ButtonBarChartClick(Sender: TObject);
+ procedure ButtonStackedBarAveClick(Sender: TObject);
+ procedure ButtonStackedBarClick(Sender: TObject);
+ procedure SpeedButton7Click(Sender: TObject);
+ procedure ButtonBarAveClick(Sender: TObject);
+ procedure SpinEdit1Change(Sender: TObject);
+ procedure Button2Click(Sender: TObject);
+ procedure ButtonPieClick(Sender: TObject);
+ procedure Button4Click(Sender: TObject);
+ procedure ButtonMarkersClick(Sender: TObject);
+ procedure ButtonDeltaAverageClick(Sender: TObject);
+ procedure ButtonLineMarkerClick(Sender: TObject);
+ procedure Panel2DblClick(Sender: TObject);
+ procedure ShowgapinLineChart1Click(Sender: TObject);
+ procedure Print1Click(Sender: TObject);
+ procedure Generatenewrandomvalues1Click(Sender: TObject);
+ procedure CopyToClipboard1Click(Sender: TObject);
+ procedure About1Click(Sender: TObject);
+ procedure mnuScrollingClick(Sender: TObject);
+ procedure Timer1Timer(Sender: TObject);
+ procedure ShowDataInListbox1Click(Sender: TObject);
+ procedure LargeDataset576samples1Click(Sender: TObject);
+ procedure DateTimeAxisModeClick(Sender: TObject);
+
+ procedure FormCreate(Sender: TObject);
+ procedure FormDestroy(Sender: TObject);
+
+ procedure PrintOptions1Click(Sender: TObject);
+ procedure MenuSecondaryAxisModeClick(Sender: TObject);
+ procedure ListBox1Click(Sender: TObject);
+ procedure ListBox1DblClick(Sender: TObject);
+
+ procedure mnuSetAxisFontClick(Sender: TObject);
+ procedure mnuSetAxisTitlefontClick(Sender: TObject);
+ procedure mnuSetHeaderFontClick(Sender: TObject);
+ procedure mnuSetLegendFontClick(Sender: TObject);
+
+ procedure MenuNegValueTestClick(Sender: TObject);
+ procedure SpeedButtonTestMouseOverClick(Sender: TObject);
+ procedure ChartEndFloatingMarkerDrag(Sender: TJvChart;
+ FloatingMarker: TJvChartFloatingMarker);
+ procedure ChartBeginFloatingMarkerDrag(Sender: TJvChart;
+ FloatingMarker: TJvChartFloatingMarker);
+ procedure ChartChartPaint(Sender: TJvChart; aCanvas: TCanvas);
+ procedure NewFeaturesfor20071Click(Sender: TObject);
+ private
+
+ // Our waveform generator uses the following as state-variables:
+ FGenerationIndex: Integer;
+ Foo, Foo1, Foo2: Integer;
+ Fhgt, Fhg0, Fhg2p: Double;
+ FStatHgt, FStatHg0: TStatArray;
+ Fdt, Fds: Double;
+ FNegValueFlag: Boolean;
+
+ protected
+ procedure _Generate;
+ procedure _StoreValue(I: integer);
+ function _QAProblemScatter: Integer;
+
+ public
+ procedure NewValues;
+ end;
+
+var
+ JvChartDemoForm: TJvChartDemoForm;
+
+implementation
+
+uses
+ LCLIntf, // OpenURL
+ LCLType, // MB_OK
+ Math; // Math:NaN handling, function isNan in D6 and higher.
+
+{$R *.lfm}
+
+{ Bogus vageuly sinusoidal signal generator }
+
+procedure TJvChartDemoForm._Generate;
+begin
+ FHgt := Abs(Random(80) + (Random(((FGenerationIndex div foo) mod foo1) * 250) * 5 + 9500));
+ FHg0 := Abs(Random(280) + Random(((FGenerationIndex div foo) mod foo2) * 650) * 5 + 1003);
+ if FNegValueFlag then
+ begin
+ if (Random(80) > 75) or (FGenerationIndex = 2) then
+ begin
+ FHg0 := -1 * FHg0; // Generate a negative value! (just to show what it looks like)
+ end;
+ end;
+ Inc(FGenerationIndex);
+end;
+
+{ Bogus random spiky-looking function to simulate a QA value,
+ which hovers within +/- 10% of perfect (100%), but
+ with relatively infrequent spiky errors }
+
+function TJvChartDemoForm._QAProblemScatter: Integer;
+var
+ n, m: Double;
+begin
+ n := Log10(Random(10000) + 1); // Random is my favourite function. How about you? -WP
+ n := n * Log10(Random(10000) + 1);
+ m := Log10(Random(10000) + 1);
+ m := m * Log10(Random(10000) + 1);
+ n := Abs(100 + n - m);
+ if (n < 0) then
+ n := 0;
+ if (n > 150) then
+ n := 150;
+
+ Result := Round(n);
+end;
+
+procedure TJvChartDemoForm._StoreValue(I: integer);
+begin
+ FStatHgt.AddValue(Fhgt);
+ FStatHg0.AddValue(Fhg0);
+
+ // PAY ATTENTION HERE, this is where we set the Chart.Data.Value[ index,pen] :=
+ // stuff which is the MOST IMPORTANT THING, unless you like blank charts:
+
+ // Set Data.Value[Pen, Series] := dataValue ...
+ Chart.Data.Value[0, I] := FStatHgt.Average / 1000;
+
+ if FHg0 < 0 then
+ begin
+ Chart.Data.Value[1, I] := FHg0 / 1000;
+ // Don't show average with negative samples or my negative demo ability goes away (averaged out!)
+ end
+ else
+ begin
+ // Test blanks on big chart, show missing data:
+ Chart.Data.Value[1, I] := FStatHg0.Average / 1000;
+ end;
+
+ Fhg2p := (FStatHgt.Average - FStatHg0.Average) / 1000;
+ if Fhg2p < 0.0 then
+ Fhg2p := 0.0;
+ Chart.Data.Value[2, I] := Fhg2p;
+
+ Fds := Fdt + (FGenerationIndex / 576);
+
+ // There are TWO ways to get an X Axis label plotted:
+ if DateTimeAxisMode.Checked then
+ Chart.Data.Timestamp[I] := Fds // X legends generated by timestamps
+ else
+ // X Legends generated by user are used by default.
+ // This would be redundant, and would be a waste of memory
+ // if Chart.Options.XAxisDateTimeMode was also set.
+ Chart.Options.XLegends.Add(FormatDateTime('hh:nn:ss', Fds));
+
+ if MenuSecondaryAxisMode.Checked then
+ begin
+ if I = 1 then
+ Chart.Data.Value[3, I] := 100
+ else
+ if (I mod 4) = 3 then
+ begin
+ //Chart.Data.Value[3,I] := 1+ ((I mod 12) * 10) // stairstep
+ //random:
+ Chart.Data.Value[3, I] := _QAProblemScatter;
+ end
+ else
+ Chart.Data.Value[3, I] := NaN; // leave some blanks.
+ end;
+end;
+
+procedure TJvChartDemoForm.NewFeaturesfor20071Click(Sender: TObject);
+var
+ vbar:TJvChartVerticalBar;
+ hbar:TJvChartHorizontalBar;
+begin
+ // Try out new features for 2007:
+
+ // Gradients.
+ Chart.Options.GradientColor := $00FDEEDB; // powder blue (baby blue) mostly white.
+ Chart.Options.GradientDirection :=grDown;
+
+
+
+ // Vertical Bar.
+ vbar := Chart.AddVerticalBar;
+ vbar.Color := $00FDDDC7;
+ vbar.Visible := true;
+ vbar.XLeft := 6;
+ vbar.XRight := 7;
+
+ // Horizontal Bar.
+ hbar := Chart.AddHorizontalBar;
+ hbar.Color := $007CCAC7;
+ hbar.Visible := true;
+ hbar.YTop := 3;
+ hbar.YBottom := 2;
+
+
+
+
+
+ // Lots of things not shown here are new in 2007:
+ // Floating Marker Count, Delete a floating marker, etc,
+ // search for 'NEW 2007' in JvChart.pas for a complete
+ // look at new things.
+
+ // last thing, always update graph:
+ Chart.PlotGraph;
+ //Chart.Refresh;
+
+
+end;
+
+procedure TJvChartDemoForm.NewValues;
+var
+ I: Integer;
+ nValueCount: Integer;
+begin
+//Chart.Options.Title := 'Click on the Chart Title to change it!';
+ Chart.Options.PenCount := 3;
+
+ Chart.ClearFloatingMarkers; // remove any previous markers.
+
+
+ ListBox1.Clear;
+ Chart.Data.Clear;
+
+ Chart.Options.PrimaryYAxis.YMax := 20;
+
+ Randomize;
+
+ Chart.Options.XAxisDateTimeMode := DateTimeAxisMode.Checked; // Use datetime timestamp labels, just Fer Instance.
+
+ if not Chart.Options.XAxisDateTimeMode then
+ Chart.Options.XAxisLegendSkipBy := 5;
+
+ if LargeDataset576samples1.Checked then
+ begin
+ // A larger bogus data set for demonstration purposes.
+ nValueCount := 576; // 2.5 minute sample period, 576 samples = 1 day.
+ foo := 5; // Used in generating our bogus data below, not really important.
+ Chart.Options.XAxisValuesPerDivision := 24;
+ // 24 samples * 150 seconds = 1 hour time divisions ( Chart.Options.XAxisValuesPerDivision := 4;
+ end
+ else
+ begin
+ // A smaller bogus data set for demonstration purposes.
+ nValueCount := 24; // 2.5 minute sample period, 24 samples =1 hour.
+ foo := 1; // Used in generating our bogus data below, not really important.
+ Chart.Options.XAxisValuesPerDivision := 4; // five divisions, 4 values per division
+ end;
+
+ //Chart.ResetGraphModule; // Clears YMax.
+ Fdt := Trunc(now - 1.0); // yesterday, midnight.
+ Foo1 := Random(5) + 2; // more randomness
+ Foo2 := Random(3) + 5; // more randomness
+ FGenerationIndex := 1;
+ for I := 0 to nValueCount - 1 do
+ begin
+ if i > 0 then
+ begin // generate random data that appears to show a sawtooth-frequency-pattern plus a lot of random noise:
+ _Generate;
+ end
+ else
+ begin
+ Fhgt := 7000; // First sample always known value, helps me troubleshoot.
+ Fhg0 := 1000;
+ end;
+
+ _StoreValue(I);
+
+ // Override stored value in special cases:
+
+ { How to make a gap in the data! }
+ if (nValueCount > 100) and ShowgapinLineChart1.Checked then
+ begin
+ if (I > 100) and (I < 130) then
+ begin
+ Chart.Data.Value[0, I] := NaN; // Use special Math.NaN const
+ Chart.Data.Value[1, I] := NaN; // Use special Math.NaN const
+ Chart.Data.Value[2, I] := NaN; // Use special Math.NaN const
+ end;
+ end;
+
+ // Just so that the last two values are visibly different, to make sure
+ // the chart handles the final vaulues correctly, we set all the final
+ // values to known amounts also:
+ if I = nValueCount - 2 then
+ begin
+ Chart.Data.Value[0, I] := 6.0; // Use special Math.NaN const
+ Chart.Data.Value[1, I] := 4.0; // Use special Math.NaN const
+ Chart.Data.Value[2, I] := 2.0; // Use special Math.NaN const
+ end
+ else
+ if I = nValueCount - 1 then
+ begin
+ Chart.Data.Value[0, I] := 3.0; // Use special Math.NaN const
+ Chart.Data.Value[1, I] := 2.0; // Use special Math.NaN const
+ Chart.Data.Value[2, I] := 1.0; // Use special Math.NaN const
+
+ end;
+
+ ListBox1.Items.Append(Chart.Data.DebugStr(I));
+ end;
+
+ with Chart.Options do
+ begin
+ Title := 'Chart Title';
+ XAxisHeader := 'Date/Time';
+ YAxisHeader := 'Readings (ng/m3)';
+
+ if FNegValueFlag then
+ begin
+ PrimaryYAxis.YMin := -20;
+ PrimaryYAxis.YMax := 20;
+ end
+ else
+ PrimaryYAxis.YMin := 0;
+ // Try out the pen styles:
+ if ChartKind = ckChartStackedBar then
+ PenStyle[0] := psClear // THIS IS HOW YOU TEMPORARILY HIDE ONE PEN!
+ else
+ PenStyle[0] := psSolid;
+ PenStyle[1] := psDash;
+ PenStyle[2] := psDot;
+
+ if MenuSecondaryAxisMode.Checked then
+ begin
+ PenCount := 4; // Add a pen for right side demo.
+ SecondaryYAxis.YMax := 140; // Example shows Q/A percentage. Experimental results
+ // results are compared to expected results, and the
+ // response percentage, is plotted from 0% to 140%
+ // of expected value.
+
+ SecondaryYAxis.YLegendDecimalPlaces := 2;
+
+ PenSecondaryAxisFlag[3] := True; // Move pen index 3 (Fourth pen) to secondary axis.
+ PenMarkerKind[3] := pmkDiamond;
+ PenValueLabels[3] := true; // Label with text.
+ PenStyle[3] := psClear; // Markers only, no lines.
+ PenColor[3] := clGray;
+ MarkerSize := 5; // Make 'em bigger.
+ end
+ else
+ begin
+ PenCount := 3;
+ MarkerSize := 3; // Make 'em little
+ end;
+
+ PenLegends.Clear;
+ PenLegends.Add('HgT');
+ PenLegends.Add('Hg0');
+ PenLegends.Add('Hg2+');
+ if MenuSecondaryAxisMode.Checked then
+ PenLegends.Add('Quality%');
+
+ PenUnit.Clear;
+ PenUnit.Add('ug/m3');
+ PenUnit.Add('ug/m3');
+ PenUnit.Add('ug/m3');
+ if MenuSecondaryAxisMode.Checked then
+ PenUnit.Add('%'); // Optional Pen in percentage scale.
+
+ //ShowLegend := TRUE;
+ Legend := clChartLegendBelow;
+
+ //ChartKind := ckChartLine;
+ end;
+// Chart.AutoFormatGraph;
+ Chart.PlotGraph;
+
+ //Chart.ResizeChartCanvas;
+end;
+
+procedure TJvChartDemoForm.FormResize(Sender: TObject);
+begin
+ if Assigned(Chart) then
+ Chart.ResizeChartCanvas;
+end;
+
+procedure TJvChartDemoForm.ButtonBarChartClick(Sender: TObject);
+begin
+ Chart.Options.ChartKind := ckChartBar;
+ NewValues;
+ //Chart.PlotGraph;
+end;
+
+procedure TJvChartDemoForm.ButtonLineClick(Sender: TObject);
+var
+ I: Integer;
+begin
+ Chart.Options.ChartKind := ckChartLine;
+ for I := 0 to Chart.Options.PenCount - 1 do
+ begin
+ Chart.Options.PenMarkerKind[I] := pmkNone;
+ end;
+
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.ButtonLineMarkerClick(Sender: TObject);
+//var
+// I:Integer;
+begin
+ Chart.Options.ChartKind := ckChartLine;
+ Chart.Options.PenMarkerKind[0] := pmkDiamond; // demonstrate both Diamond and Circle Marks.
+ Chart.Options.PenMarkerKind[1] := pmkDiamond;
+ Chart.Options.PenMarkerKind[2] := pmkCircle;
+
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.ButtonStackedBarAveClick(Sender: TObject);
+begin
+ Chart.Options.ChartKind := ckChartStackedBarAverage;
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.ButtonStackedBarClick(Sender: TObject);
+begin
+ Chart.Options.ChartKind := ckChartStackedBar;
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.SpeedButton7Click(Sender: TObject);
+begin
+// Chart.PivotData; // TODO: This causes exceptions. not sure why we want this.
+end;
+
+procedure TJvChartDemoForm.ButtonBarAveClick(Sender: TObject);
+begin
+ //Chart.ShowAsBarWithAve;
+ Chart.Options.ChartKind := ckChartBarAverage;
+
+ NewValues;
+ //Chart.Plo
+end;
+
+procedure TJvChartDemoForm.ButtonPieClick(Sender: TObject);
+begin
+ // Chart.ShowAsPie;
+ Chart.Options.ChartKind := ckChartPieChart;
+end;
+
+procedure TJvChartDemoForm.ButtonMarkersClick(Sender: TObject);
+begin
+ //Chart.ShowAsMark;
+ Chart.Options.ChartKind := ckChartMarkers;
+end;
+
+procedure TJvChartDemoForm.SpinEdit1Change(Sender: TObject);
+begin
+// Chart.Options.ColorScheme := SpinEdit1.Value;
+// Chart.PlotGraph;
+end;
+
+procedure TJvChartDemoForm.Button2Click(Sender: TObject);
+begin
+ Chart.PlotGraph;
+end;
+
+procedure TJvChartDemoForm.Button4Click(Sender: TObject);
+begin
+// Chart.AutoFormatGraph; WAP Removed. BAD CODE.
+ Chart.PlotGraph;
+end;
+
+procedure TJvChartDemoForm.ButtonDeltaAverageClick(Sender: TObject);
+begin
+ Chart.Options.ChartKind := ckChartDeltaAverage;
+end;
+
+procedure TJvChartDemoForm.FormCreate(Sender: TObject);
+begin
+ FStatHgt := TStatArray.Create(10); // Initialize for rolling average of last 10 samples.
+ FStatHg0 := TStatArray.Create(10);
+
+ if Assigned(Chart) then
+ // Chart.ShowAsLineWithMark;
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.Panel2DblClick(Sender: TObject);
+begin
+ ShellExecute(HWND(nil), 'show', 'http://jvcl.delphi-jedi.org/', nil, nil, SW_SHOW);
+end;
+
+procedure TJvChartDemoForm.ShowgapinLineChart1Click(Sender: TObject);
+begin
+ ShowgapinLineChart1.Checked := not ShowgapinLineChart1.Checked;
+ ButtonLine.Down := true;
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.Print1Click(Sender: TObject);
+begin
+ if PrintDialog1.Execute then
+ Chart.PrintGraph;
+end;
+
+procedure TJvChartDemoForm.Generatenewrandomvalues1Click(Sender: TObject);
+begin
+ if SpeedButtonTestMouseOver.Down then exit;
+
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.CopyToClipboard1Click(Sender: TObject);
+begin
+ Chart.GraphToClipboard;
+end;
+
+procedure TJvChartDemoForm.mnuSetAxisFontClick(Sender: TObject);
+begin
+ {Get the current font for the axis label texts...}
+ FontDialog1.Font.Assign(Chart.Options.AxisFont);
+
+ if FontDialog1.Execute then
+ {Set the font for the Header text...}
+ Chart.Options.AxisFont := FontDialog1.Font;
+ Chart.PlotGraph;
+// Chart.Invalidate;
+end;
+
+procedure TJvChartDemoForm.mnuSetAxisTitlefontClick(Sender: TObject);
+begin
+ {Get the current font for the axis titles...}
+ FontDialog1.Font.Assign(Chart.Font);
+
+ if FontDialog1.Execute then
+ {Set the font for the Header text...}
+ Chart.Font := FontDialog1.Font;
+ Chart.PlotGraph;
+ Chart.Invalidate;
+end;
+
+procedure TJvChartDemoForm.mnuSetHeaderFontClick(Sender: TObject);
+begin
+ {Get the current font for the header (title) text...}
+ FontDialog1.Font.Assign(Chart.Options.HeaderFont);
+
+ if FontDialog1.Execute then
+ {Set the font for the Header text...}
+ Chart.Options.HeaderFont := FontDialog1.Font;
+ Chart.PlotGraph;
+// Chart.Invalidate;
+end;
+
+procedure TJvChartDemoForm.mnuSetLegendFontClick(Sender: TObject);
+begin
+ {Get the current font for the header (title) text...}
+ FontDialog1.Font.Assign(Chart.Options.LegendFont);
+
+ if FontDialog1.Execute then
+ {Set the font for the Header text...}
+ Chart.Options.LegendFont := FontDialog1.Font;
+ Chart.PlotGraph;
+// Chart.Invalidate;
+end;
+
+procedure TJvChartDemoForm.About1Click(Sender: TObject);
+begin
+ Application.MessageBox(PChar(
+ 'JvChart comes from AABSoft Graph written by MÃ¥rten Henrichson, JVCL 3.0 ' +
+ 'version by Warren Postma. '), 'About JvChart', MB_OK);
+end;
+
+procedure TJvChartDemoForm.mnuScrollingClick(Sender: TObject);
+begin
+ if SpeedButtonTestMouseOver.Down then exit;
+
+ mnuScrolling.Checked := not mnuScrolling.Checked;
+ Timer1.Enabled := mnuScrolling.Checked;
+end;
+
+procedure TJvChartDemoForm.Timer1Timer(Sender: TObject);
+begin
+ if SpeedButtonTestMouseOver.Down then exit;
+
+ Chart.Data.Scroll;
+ _Generate;
+ _StoreValue(Chart.Data.ValueCount - 1);
+ Chart.PlotGraph;
+end;
+
+procedure TJvChartDemoForm.ShowDataInListbox1Click(Sender: TObject);
+begin
+ ShowDataInListbox1.Checked := not ShowDataInListbox1.Checked;
+ ListBox1.Visible := ShowDataInListbox1.Checked;
+
+ if not ShowDataInListbox1.Checked then
+ begin
+ Chart.CursorPosition := -1; // Invisible.
+ end;
+end;
+
+procedure TJvChartDemoForm.LargeDataset576samples1Click(Sender: TObject);
+begin
+ if SpeedButtonTestMouseOver.Down then exit;
+
+ LargeDataset576samples1.Checked := not LargeDataset576samples1.Checked;
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.DateTimeAxisModeClick(Sender: TObject);
+begin
+ DateTimeAxisMode.Checked := not DateTimeAxisMode.Checked;
+ if SpeedButtonTestMouseOver.Down then begin
+ Chart.Options.XLegends.Clear;
+ end else
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.FormDestroy(Sender: TObject);
+begin
+ FreeAndNil(FStatHgt);
+ FreeAndNil(FStatHg0);
+end;
+
+procedure TJvChartDemoForm.PrintOptions1Click(Sender: TObject);
+begin
+ PrinterSetupDialog1.Execute;
+end;
+
+procedure TJvChartDemoForm.MenuSecondaryAxisModeClick(Sender: TObject);
+begin
+ MenuSecondaryAxisMode.Checked := not MenuSecondaryAxisMode.Checked;
+
+ if MenuSecondaryAxisMode.Checked then
+ begin
+ ButtonLine.Down := true;
+ ButtonLineClick(Sender);
+ end
+ else
+ NewValues;
+end;
+
+procedure TJvChartDemoForm.ListBox1DblClick(Sender: TObject);
+begin
+ Chart.CursorPosition := ListBox1.ItemIndex; // Highlight one sample.
+end;
+
+procedure TJvChartDemoForm.ListBox1Click(Sender: TObject);
+begin
+ Chart.CursorPosition := ListBox1.ItemIndex; // Highlight one sample.
+end;
+
+procedure TJvChartDemoForm.MenuNegValueTestClick(Sender: TObject);
+begin
+ FNegValueFlag := MenuNegValueTest.Checked;
+ NewValues;
+end;
+
+{ Simple Chart Tests for finding bug }
+procedure TJvChartDemoForm.SpeedButtonTestMouseOverClick(Sender: TObject);
+var
+ i:Integer;
+ n:Double;
+ marker1,marker2,draggableCursor:TJvChartFloatingMarker;
+begin
+ ListBox1.Clear;
+ mnuScrolling.Checked := false;
+
+ Chart.Options.Title := 'Click on the Red Diamonds or the vertical gray line and Drag them!';
+ Chart.Options.ChartKind := ckChartLine;
+ Chart.Options.XLegends.Clear;
+ Chart.Options.XAxisHeader := '';
+
+ Chart.Options.MouseDragObjects := true; // NEW: MOUSE DRAG FLOATING OBJECTS!
+
+ for i := 0 to Chart.Options.PenCount - 1 do
+ begin
+ Chart.Options.PenMarkerKind[I] := pmkNone;
+ end;
+ Chart.Data.Clear;
+ Chart.Options.XValueCount := 80;
+ Chart.Data.ValueCount := 6;
+ Chart.Options.PenCount := 1;
+ for i := 0 to Chart.Options.XValueCount-1 do begin
+ n := 30-(25*((1-((i-25)/15))*(1-((i-25)/15))));
+ if n<5 then
+ n := 5 -(Sqrt((Abs(n))/15));
+ ListBox1.Items.Add( FloatToStrF(n,ffFixed,6,4));
+ Chart.Data.Value[0,i] := n;
+ end;
+ Chart.Options.PrimaryYAxis.YMax := 50;
+
+ Chart.Options.PenStyle[0] := psSolid;
+
+ // NEW: Add a floating marker:
+ marker1 := Chart.AddFloatingMarker;
+ marker1.XPosition := 13;
+ marker1.YPosition := Chart.Data.Value[0,marker1.XPosition]; // Snap to Pen 1
+ marker1.XDraggable := true; // make it mouse-moveable.
+ marker1.YDraggable := true;
+ marker1.Caption :='Start';
+ marker1.Visible := true;
+
+
+ marker2 := Chart.AddFloatingMarker;
+ marker2.XPosition := 66;
+ marker2.YPosition := Chart.Data.Value[0,marker2.XPosition]; // Snap to Pen 1
+ marker2.LineToMarker := marker1.index; // Connect with a line to marker1
+ marker2.XDraggable := true; // make it mouse-moveable.
+ marker2.YDraggable := true;
+ marker2.Caption := 'End';
+ marker2.Visible := true;
+
+ // NOTE: Do not Free marker1 or marker2.
+ // Marker objects are freed automatically
+ // by the Chart.
+
+ // a draggable cursor object:
+ draggableCursor := Chart.AddFloatingMarker;
+ draggableCursor.LineVertical := true; // Make a vertical Line
+ draggableCursor.Marker := pmkNone; // No marker. So it is a line only.
+ draggableCursor.XDraggable := true; // make it draggable.
+ draggableCursor.XPosition := 40;
+ draggableCursor.LineColor := clDkGray;
+ draggableCursor.LineStyle := psSolid;
+ draggableCursor.LineWidth := 2;
+ draggableCursor.Caption := FloatToStrF( Chart.Data.Value[0,draggableCursor.XPosition], ffFixed, 6,4 );
+ draggableCursor.Visible := true;
+
+
+
+
+
+
+ Chart.PlotGraph;
+
+
+end;
+
+procedure TJvChartDemoForm.ChartEndFloatingMarkerDrag(Sender: TJvChart;
+ FloatingMarker: TJvChartFloatingMarker);
+begin
+ // Snap to line:
+ if (FloatingMarker.Index<2) then // One of the first two markers?
+ FloatingMarker.YPosition := Chart.Data.Value[0, FloatingMarker.XPosition]
+ else // update caption
+ FloatingMarker.Caption := FloatToStrF( Chart.Data.Value[0,FloatingMarker.XPosition],ffFixed,6,4 );
+
+
+end;
+
+procedure TJvChartDemoForm.ChartBeginFloatingMarkerDrag(Sender: TJvChart;
+ FloatingMarker: TJvChartFloatingMarker);
+begin
+ if FloatingMarker.Index=2 then
+ FloatingMarker.Caption := '?';
+
+end;
+
+procedure TJvChartDemoForm.ChartChartPaint(Sender: TJvChart;
+ aCanvas: TCanvas);
+begin
+ aCanvas.Pen.Color := clRed;
+ aCanvas.Pen.Style := psSolid;
+ aCanvas.MoveTo(0,0);
+ aCanvas.LineTo(100,100);
+end;
+
+end.
diff --git a/components/jvcllaz/examples/JvChartDemo/statsclasses.pas b/components/jvcllaz/examples/JvChartDemo/statsclasses.pas
new file mode 100644
index 000000000..905955210
--- /dev/null
+++ b/components/jvcllaz/examples/JvChartDemo/statsclasses.pas
@@ -0,0 +1,268 @@
+{-----------------------------------------------------------------------------
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/MPL-1.1.html
+
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
+the specific language governing rights and limitations under the License.
+
+The Original Code is: JvChartDemoFm.Pas, released on 2002-10-04.
+
+The Initial Developer of the Original Code is AABsoft and MÃ¥rten Henrichson.
+(C) 1996 AABsoft and MÃ¥rten Henrichson.
+All Rights Reserved.
+
+Contributor(s): -
+
+Last Modified: 2004-01-07
+ Modified 2003 Warren Postma
+
+You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
+located at http://jvcl.delphi-jedi.org
+
+Description:
+ TStatArray - Statistics, Rolling average helper class.
+
+Known Issues:
+ This version is part of JvChartDemo.
+-----------------------------------------------------------------------------}
+
+{$MODE OBJFPC}{$H+}
+
+unit StatsClasses;
+
+
+interface
+
+
+type
+
+TStatArray = class
+ protected
+ //FFirst:Boolean;
+ FGrows:Boolean; // false=rolling average (circular buffer mode), true=average or sd of any number of samples (array grows without limit)
+ FValues:Array of Double;
+ FLength, // Array absolute size (may still be no data even if this is >0)
+ FIndex, // Where will the next sample be stored into the array?
+ FCount: // How many valid samples are in the array right now?
+ Integer;
+
+
+ procedure SetLen(aLength:Integer);
+
+
+ public
+ procedure AddValue(aValue:Double);
+
+ function Average:Double;
+
+ function StandardDeviation:Double;
+
+ property Grows:Boolean read FGrows write FGrows; // false=rolling average, true=average ALL samples (grows to fit)
+
+ property Length:Integer read FLength write SetLen;
+ property Count:Integer read FCount;
+ //property First:Boolean read FFirst;
+
+ procedure Reset; // Clear everything!
+
+ constructor Create; overload;
+ constructor Create(initialLength:Integer); overload;
+ destructor Destroy; override;
+end;
+
+implementation
+
+uses //Windows, // OutputDebugString
+ SysUtils, // FloatToStr
+ Math; // VCL's statistics routines. StdDev, etc.
+
+// Begin Rolling Average
+
+constructor TStatArray.Create; // overload;
+begin
+ //FFirst := true;
+ FLength := 0;
+ FIndex := 0;
+ FCount:= 0;
+ FGrows := true;
+ SetLength(FValues,0);
+end;
+
+procedure TStatArray.Reset;
+var
+ i:Integer;
+begin
+ FIndex := 0;
+ FCount := 0;
+ for i := 0 to FLength-1 do begin
+ FValues[i] := -999.0; // debug helper.
+ end;
+ //FFirst := true;
+end;
+
+constructor TStatArray.Create(initialLength:Integer); // overload;
+begin
+// FFirst := true;
+ SetLength(FValues,initialLength);
+ if (initialLength>0) then
+ FGrows := false
+ else
+ FGrows := true;
+ FLength := initialLength;
+ FIndex := 0;
+ FCount:= 0;
+end;
+
+
+destructor TStatArray.Destroy;
+begin
+ SetLength(FValues,0);
+end;
+
+
+function TStatArray.Average:Double;
+var
+ last,i:Integer;
+ sum:Double;
+begin
+ if FCount <= 0 then begin
+ result := 0;
+ end else begin
+ sum := 0;
+ if (FCount>FLength) then
+ last :=FLength-1
+ else
+ last :=FCount-1;
+ for i := 0 to last do begin
+ sum := sum + FValues[i];
+ end;
+ result := sum / (last+1);
+ end;
+end;
+
+function TStatArray.StandardDeviation:Double;
+var
+ i:Integer;
+// sum:Double;
+ TempArray:Array of Double;
+begin
+ if (FCount <= 0) then
+ result := 0
+ else if (FCount >= FLength) then begin
+ result := Math.StdDev( FValues )
+ end else begin
+ SetLength(TempArray,FCount);
+ for i := 0 to FCount-1 do begin
+ TempArray[i] := FValues[i];
+ end;
+ result := Math.StdDev( TempArray );
+ SetLength(TempArray,0);
+ end;
+end;
+
+procedure TStatArray.AddValue(aValue:Double);
+//var
+// i:Integer;
+begin
+ (*if FFirst then begin
+ FFirst := false;
+ FIndex := 0;
+ FValues[0] := aValue;
+ FCount := 1;
+ end else begin*)
+
+ // First time in we might need to create an array:
+ if FIndex>=Length then begin
+ Assert(FGrows); // Illegal condition.
+ FLength := FIndex+1;
+ SetLength( FValues,FLength); // uninitialized, as of yet.
+ end;
+
+ FValues[FIndex] := aValue;
+ Inc(FIndex);
+ Inc(FCount);
+ if (not FGrows) then begin // circular?
+ if (FIndex>=FLength) then begin
+ FIndex := 0;
+ //FCount := FLength;//FCount does not exceed FLength in wraparounds.
+ end;
+ end else begin // grow after, in doublings of size, scales better!
+ if (FIndex>=FLength) then begin
+ FLength := FLength * 2;
+ SetLength( FValues,FLength); // uninitialized, as of yet.
+{$ifdef DEBUG_ASSERTIONS}
+ Assert(FLength<20000); // Debug limit
+{$endif}
+ end;
+ end;
+
+end;
+
+procedure TStatArray.SetLen(aLength:Integer);
+begin
+ if(aLength<1) then
+ aLength := 1;
+ FLength := aLength;
+ SetLength(FValues, FLength);
+
+end;
+
+
+// End Stats
+
+
+{$ifdef UNIT_TESTS}
+procedure StatsUnitTests;
+var
+ diff:Double;
+ a1:TStatArray;
+ procedure _outs(s:String);
+ begin
+ OutputDebugString(PChar(s));
+ end;
+ procedure _outd(d:Double);
+ begin
+ OutputDebugString(PChar(FloatToStr(d)));
+ end;
+
+begin
+ _outs('StatsUnitTests begins');
+
+ a1 := TStatArray.Create(0); // Growing array.
+ a1.AddValue( 3.5 );
+ a1.AddValue( 1.5 );
+ a1.AddValue( 25.5 );
+ a1.AddValue( 100.5 );
+ _outd( a1.Average );
+
+ diff := Abs(((3.5+1.5+25.5+100.5)/4.0)-a1.Average);
+ Assert(diff<0.001);
+
+ a1.Reset;
+ Assert(Abs(a1.Average)<0.0001);
+
+ a1.AddValue( 3.5 );
+ a1.AddValue( 1.5 );
+ a1.AddValue( 25.5 );
+ a1.AddValue( 100.5 );
+ _outd( a1.Average );
+
+ diff := Abs(((3.5+1.5+25.5+100.5)/4.0)-a1.Average);
+ Assert(diff<0.001);
+
+ _outd( a1.StandardDeviation );
+ Assert(trunc(a1.StandardDeviation)=46);
+
+ _outs('StatsUnitTests ends');
+end;
+
+initialization
+ StatsUnitTests;
+
+{$endif}
+
+
+end.
diff --git a/components/jvcllaz/packages/jvcustomlazr.lpk b/components/jvcllaz/packages/jvcustomlazr.lpk
index 0d0ca45e9..dee0e68ad 100644
--- a/components/jvcllaz/packages/jvcustomlazr.lpk
+++ b/components/jvcllaz/packages/jvcustomlazr.lpk
@@ -16,7 +16,7 @@
TimeLine, OutlookBar, Thumbnail viewer, ImageViewer, ImageListViewer, OwnerDrawViewer"/>
-
+
@@ -69,6 +69,10 @@ TimeLine, OutlookBar, Thumbnail viewer, ImageViewer, ImageListViewer, OwnerDrawV
+
+
+
+
diff --git a/components/jvcllaz/resource/jvcustomreg.res b/components/jvcllaz/resource/jvcustomreg.res
index 18f1a287c..b04dbee13 100644
Binary files a/components/jvcllaz/resource/jvcustomreg.res and b/components/jvcllaz/resource/jvcustomreg.res differ
diff --git a/components/jvcllaz/run/JvCore/jvjclutils.pas b/components/jvcllaz/run/JvCore/jvjclutils.pas
index ea329accb..be48e654f 100644
--- a/components/jvcllaz/run/JvCore/jvjclutils.pas
+++ b/components/jvcllaz/run/JvCore/jvjclutils.pas
@@ -47,7 +47,7 @@ interface
uses
LCLIntf, LCLType,
- SysUtils, Classes, Graphics,
+ SysUtils, Classes, Graphics, SysConst,
JvTypes;
const
@@ -95,8 +95,18 @@ const
NullDate: TDateTime = {-693594} 0;
*)
-function JvSafeStrToFloatDef(const Str: string; Def: Extended; aDecimalSeparator: Char): Extended;
-function JvSafeStrToFloat(const Str: string; aDecimalSeparator: Char): Extended;
+ { there is a STrToIntDef provided by Delphi, but no "safe" versions of
+ StrToFloat or StrToCurr }
+ // Note: before using JvSafeStrToFloatDef, please be aware that it will ignore
+ // any character that is not a valid character for a float, which is different
+ // from what StrToFloatDef in Delphi 6 up is doing. This has been documented in Mantis
+ // issue# 2935: http://issuetracker.delphi-jedi.org/view.php?id=2935
+ // and in Mantis 4466: http://issuetracker.delphi-jedi.org/view.php?id=4466
+
+function JvSafeStrToFloatDef(const Str: string; Def: Extended;
+ aDecimalSeparator: Char = ' '): Extended; // {NOTE: default value of Space is a magic wildcard}
+
+function JvSafeStrToFloat(const Str: string; aDecimalSeparator: Char = ' '): Extended; // {NOTE: default value of Space is a magic wildcard}
(******************* NOT CONVERTED ******
function USToLocalFloatStr(const Text: string): string;
@@ -1551,6 +1561,102 @@ begin
raise EJvConvertError.CreateResFmt(ResString, Args); { will be also caught if you catch E:EConvertERror }
end;
+{ _JvSafeStrToFloat: [PRIVATE INTERNAL FUNCTION]
+ [ not to be called outside this unit, see below for public api ]
+ This is a refactored version of the internal guts of the former routine
+ StrToFloatDefIgnoreInvalidCharacters with some improvements made to decimal
+ separator handling.
+}
+function _JvSafeStrToFloat(const Str: string; aDecimalSeparator: Char; var OutValue: Extended): Boolean;
+var
+ LStr: String;
+ I: Integer;
+ CharSet: TSysCharSet;
+ LocalFormatSettings: TFormatSettings;
+begin
+ Result := false;
+ if Str = '' then
+ Exit; { hows this for a nice optimization? WPostma. }
+
+ { Locale Handling logic October 2008 supercedes former StrToFloatUS functionality. }
+ {$IFDEF RTL150_UP}
+ LocalFormatSettings.ThousandSeparator := GetLocaleChar(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, '.');
+ LocalFormatSettings.DecimalSeparator := GetLocaleChar(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, '.');
+ {$ELSE}
+ LocalFormatSettings.DecimalSeparator := DecimalSeparator;
+ {$ENDIF RTL150_UP}
+ if aDecimalSeparator = ' ' then {magic mode}
+ aDecimalSeparator := LocalFormatSettings.DecimalSeparator { default case! use system defaults! }
+ else
+ LocalFormatSettings.DecimalSeparator := aDecimalSeparator; { custom format specified! }
+
+ { Cross-codepage safety feature: Handed '1.2', a string without a comma,
+ but which is obviously a floating point number, convert it properly also.
+ This functionality is important for JvCsvDataSet and may be important in other
+ places. }
+ if (Pos(USDecimalSeparator, Str) > 0) and (Pos(aDecimalSeparator, Str) = 0) then
+ begin
+ aDecimalSeparator := USDecimalSeparator; { automatically works when US decimal values are encountered }
+ LocalFormatSettings.DecimalSeparator := aDecimalSeparator; { custom format specified! }
+ end;
+
+ LStr := '';
+ CharSet := ['0'..'9', '-', '+', 'e', 'E', AnsiChar(aDecimalSeparator)];
+
+ for I := 1 to Length(Str) do
+ if CharInSet(Str[I], CharSet) then
+ LStr := LStr + Str[I];
+
+ { the string '-' fails StrToFloat, but it can be interpreted as 0 }
+ if LStr = '-' then
+ LStr := '0';
+
+ if Length(LStr) > 0 then
+ try
+ { a string that ends in a '.' such as '12.' fails StrToFloat,
+ but as far as I am concerned, it may as well be interpreted as 12.0 }
+ if LStr[Length(LStr)] = aDecimalSeparator then
+ LStr := LStr + '0';
+
+ Result := TryStrToFloat(LStr, OutValue, LocalFormatSettings);
+ except
+ Result := False;
+ end;
+end;
+
+// JvSafeStrToFloatDef:
+//
+// Note: before using StrToFloatDef, please be aware that it will ignore
+// any character that is not a valid character for a float, which is different
+// from what the one in Delphi 6 up is doing. This has been documented in Mantis
+// issue# 2935: http://homepages.borland.com/jedi/issuetracker/view.php?id=2935
+//
+// This function was extended by WPostma, to allow specification of custom decimal
+// separators. This was required by JvCsvDataSet and may be required elsewhere in the
+// VCL wherever custom (fixed) non-current-region-settings floating point value
+// encoding must be supported. We renamed this from StrToFloatDefIgnoreInvalidCharacters
+// to JvSafeStrToFloatDef because it has multiple "floating point runtime exception safety"
+// enhancements.
+function JvSafeStrToFloatDef(const Str: string; Def: Extended;
+ aDecimalSeparator: Char = ' '): Extended;
+begin
+ { one handy dandy api expects a Default value returned instead }
+ if not _JvSafeStrToFloat(Str, aDecimalSeparator, Result) then
+ Result := Def; { failed, use default }
+end;
+
+// New routine, same as JvSafeStrToFloatDef but it will raise a conversion exception,
+// for cases when you actually want to handle an EConvertError yourself and where
+// there is no convenient or possible float value for your case.
+function JvSafeStrToFloat(const Str: string;
+ aDecimalSeparator: Char = ' '): Extended;
+begin
+ { the other handy dandy api style expects us to raise an EConvertError. }
+ if not _JvSafeStrToFloat(Str, aDecimalSeparator, Result) then
+ JvStrConvertErrorFmt(@SInvalidFloat, [Str]); {failed, raise exception }
+end;
+
+(*
function _JvSafeStrToFloat(const Str: String; aDecimalSeparator: Char; out AValue: Extended): Boolean;
var
LocalFormatSettings: TFormatSettings;
@@ -1591,6 +1697,7 @@ begin
if not _JvSafeStrToFloat(Str, aDecimalSeparator, Result) then
JvStrConvertErrorFmt(@SParInvalidFloat, [Str]); {failed, raise exception }
end;
+ *)
(******************** NOT CONVERTED ***
function StrToFloatUSDef(const Text: string; Default: Extended): Extended;
diff --git a/components/jvcllaz/run/JvCustomControls/jvchart.pas b/components/jvcllaz/run/JvCustomControls/jvchart.pas
new file mode 100644
index 000000000..8686b06ee
--- /dev/null
+++ b/components/jvcllaz/run/JvCustomControls/jvchart.pas
@@ -0,0 +1,6034 @@
+{-----------------------------------------------------------------------------
+JvChart - TJvChart Component - 2009 Public
+
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/MPL-1.1.html
+
+Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
+the specific language governing rights and limitations under the License.
+
+The Original Code is: JvChart.PAS, released on 2003-09-30.
+
+The Initial Developers of the Original Code are
+ Warren Postma (TJvChart which was originally based on TAABGraph)
+ Mårten Henrichson (TAABGraph)
+
+Contributor(s):
+ Warren Postma (warrenpstma att hotmail dott com)
+ Mårten Henrichson/AABSoft (no email known)
+
+ Contains some code which is
+ (C) 1996-1998 AABsoft and Mårten Henrichson
+
+ The rest is
+ (C) 2003-2009 Warren Postma
+ warren.postma att sympatico dott ca
+ warrenpstma att hotmail dott com
+
+Last Modified:
+
+ 2003-09-30 - (WP) - Alpha-Initial checkin of new component, into JVCL3 CVS tree.
+ 2004-02-26 - (WP) - Pre-JVCL3.0-Has been substantially jedified, also new
+ properties/events, and some renaming has occurred. See
+ cvs logs. NEW: OnChartClick event.
+ RENAME: Options.ThickLineWidth -> Options.PenLineWidth
+ RENAME: Values -> ValueIndex
+ 2004-04-10 - (WP) - Much improved Charting! Beta-Quality in most places.
+ Significant property reorganization and renaming.
+ Primary and Secondary Y (vertical) Axis support.
+ 2004-07-06 - (WP)- Added events OnYAxisClick (Left margin click),
+ OnXAxisClick (Bottom margin), OnAltYAxisClick (Right margin)
+ and OnTitleClick (Top Margin).
+
+ 2005-01-14 - (WP) - Floating Chart Markers added. Major changes to painting
+ code to allow canvas as a parameter. This is in preparation
+ for fixing up the printing code to allow printing to work
+ once again, and because the floating objects require us to
+ draw the chart into a bitmap, and then decorate the bitmap
+ dynamically with the floating objects, different
+ canvases are used to paint the bitmap, and to paint the
+ floating layer on top, thus the need for the changes.
+
+ 2005-04-15 - (WP) - Changed internal Data storage from Array of Array to
+ simple single-dimension array. This has many benefits,
+ and at least one drawback. The benefit is that much
+ larger sets of pens/data values can be accomodated.
+ The drawback is that you can't add a pen to an already displayed
+ chart without regenerating all the data points in it.
+ If you change the pen count you now MUST clear the data,
+ and re-plot the chart. Any attempt to add a pen,
+ write the new pen, and then plot without rewriting the other
+ values will cause data to be displayed in a corrupt fashion.
+ I think this obscure limitation is better to live with than
+ the many alternatives. The problem I have is that the chart
+ supports extremely large data sets, and clearing the data
+ MULTIPLE times would be a huge performance penalty in
+ any application. You can enable the OLD mode, with it's
+ limitations, by defining TJVCHART_ARRAY_OF_ARRAY.
+
+ 2007-04-26 - (WP) - Merged upstream changes.
+ - Added gradients (TJvChartGradientBar)
+ - Added new way of doing date/time markers (Options.XAxisDateTimeMode),
+ GraphXAxisLegend, and JvChart.Data properties StartDateTime, EndDateTime.
+ - Added vertical markers (AddVerticalBar)
+ - Added horizontal bars (TJvChartHorizontalBar) ClearHorizontalBars
+ - Added FloatingMarkerCount.
+ - New property in jvfloatingMarker: CaptionColor
+ - Added DeleteFloatingMarkerObj
+ - Graphical glitches/chart-display-bug-fixes.
+ 2007-04-27 - (WP) - Fixes
+ - Calls only JclMath.IsNaN, not Math.IsNaN, which doesn't
+ exist on older Delphi/BCB versions.
+ - Added CopyFloatingMarkers (thought I did that yesterday but missed it)
+
+You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
+located at http://jvcl.delphi-jedi.org
+
+-----------------------------------------------------------------------------}
+// $Id$
+
+unit JvChart;
+
+{$MODE OBJFPC}{$H+}
+{$DEFINE TJVCHART_ARRAY_OF_ARRAY}
+//{$I jvcl.inc}
+
+interface
+
+uses
+ {
+ Windows, Messages, }
+ LCLType, LCLIntf,
+ Classes, Types, Graphics, Controls, Contnrs,
+ JvComponent;
+
+const
+ JvChartVersion = 300; // ie, version 3.00
+
+ JvDefaultHintColor = TColor($00DDFBFA);
+ JvDefaultAvgLineColor = TColor($00EEDDDD);
+ JvDefaultDivisionLineColor = clLtGray; //NEW!
+ JvDefaultShadowColor = clLtGray; //NEW!
+
+ JvDefaultYLegends = 20;
+ MaxShowXValueInLegends = 10;
+
+ // Special indices to GetPenColor(Index)
+ jvChartAverageLineColorIndex = -6;
+ jvChartDivisionLineColorIndex = -5;
+ jvChartShadowColorIndex = -4;
+ jvChartAxisColorIndex = -3;
+ jvChartHintColorIndex = -2;
+ jvChartPaperColorIndex = -1;
+
+ JvChartDefaultMarkerSize = 3;
+
+type
+ { CHART TYPES }
+ TJvChartKind =
+ (
+ ckChartNone, // Blank graph.
+ ckChartLine, // default type. Line and Marker plots.
+ ckChartBar,
+ ckChartStackedBar,
+ ckChartBarAverage,
+ ckChartStackedBarAverage,
+ ckChartPieChart,
+ //ckChartLineWithMarkers, // obsolete. use ckChartLine, and set PenMarkerKind to cmDiamond.
+ ckChartMarkers,
+ ckChartDeltaAverage
+ );
+
+ // ckChartLine can have a different pen type for each pen:
+
+ TJvChartPenMarkerKind = (pmkNone, pmkDiamond, pmkCircle, pmkSquare, pmkCross);
+
+ TJvChartGradientDirection = (grNone, grUp, grDown, grLeft, grRight); // WP
+
+ TJvChartLegend = (clChartLegendNone, clChartLegendRight, clChartLegendBelow);
+
+ {$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+ TJvChartDataArray = array of array of Double;
+ {$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+ TJvChart = class;
+
+ // TJvChartFloatingMarker.Caption position enumerator:
+ TJvChartCaptionPosition =
+ (
+ cpMarker, // put right where the marker is
+ cpXAxisBottom, // put below the symbol marker, in the bottom margin
+ cpXAxisTop, // put above the symbol marker, in the top margin
+ cpTitleArea
+ );
+
+ TJvChartFloatingMarker = class(TPersistent) // was from TObject
+ private
+ FOwner: TJvChart;
+ procedure SetCaptionColor(const Value: TColor); // Which chart does it belong to?
+ protected
+ FRawXPosition: Integer; // raw pixel-based X position.
+ FRawYPosition: Integer; // raw pixel-based Y position.
+ FDragging: Boolean; // drag in progress!
+ FVisible: Boolean; // Make chart marker object visible or invisible.
+ FIndex: Integer; // Which marker is this?
+ FTag: Integer; // User assignable Integer like TComponent.Tag
+ FMarker: TJvChartPenMarkerKind; // What symbol to plot at this position?
+ FMarkerColor: TColor; // Marker color.
+ FXPosition: Integer; // Plot at same X co-ordinates as Data Sample X.
+ FYPosition: Double; // Plot at Y height as data
+ FXDraggable: Boolean; // Can marker be dragged horizontally?
+ FXDragMin: Integer; // Minimum X Position that we can drag to.
+ FXDragMax: Integer; // Maximum X Position that we can drag to.
+ FYDraggable: Boolean; // Can marker be dragged vertically?
+ //FYPositionToPen:Integer; // YPosition copied from Pen Values. (-1=disable feature, 0=first pen,1=second pen,...)
+ FLineToMarker: Integer; // If -1 then none. Otherwise, index of another marker object
+ FLineVertical: Boolean; // If true, then this object plots a vertical divider line.
+ FLineStyle: TPenStyle; // Line style (solid,dashed,etc)
+ FLineColor: TColor; // Line color.
+ FLineWidth: Integer;
+ FCaption: string; // Caption to print above the marker, or if no marker, then just this text is plotted.
+ FCaptionColor: TColor; // New 2007 - WP - Color of floating marker caption
+ FCaptionPosition: TJvChartCaptionPosition;
+ FCaptionBoxed: Boolean; // Marker caption can have a box around it to make it more readable for some uses.
+
+ //FCaptionBorderStyle:TPenStyle; // Style of border around caption, or psClear if no border.
+ //FCaptionBorderColor:TColor; //
+
+ //PROTECTED CONSTRUCTOR: Only TJvChart should create a new marker object:
+ constructor Create(Owner: TJvChart);
+
+ procedure SetCaption(ACaption: string);
+ procedure SetXPosition(XPos: Integer); // should invalidate the chart (FOwner) if changed.
+ procedure SetYPosition(YPos: Double); // should invalidate the chart (FOwner) if changed.
+
+ procedure SetVisible(AVisible: Boolean);
+ public
+ property Index: Integer read FIndex;
+
+ procedure Assign(Source: TPersistent); override; // Should be able to copy from one floating marker to another
+
+ property Marker: TJvChartPenMarkerKind read FMarker write FMarker;
+ property MarkerColor: TColor read FMarkerColor write FMarkerColor; // Marker color.
+ property Visible: Boolean read FVisible write FVisible; // Make chart marker object visible or invisible.
+ property XPosition: Integer read FXPosition write SetXPosition;
+ property YPosition: Double read FYPosition write SetYPosition;
+ property XDraggable: Boolean read FXDraggable write FXDraggable;
+ property XDragMin: Integer read FXDragMin write FXDragMin;
+ property XDragMax: Integer read FXDragMax write FXDragMax;
+ property YDraggable: Boolean read FYDraggable write FYDraggable;
+ //property YPositionToPen :Integer read FYPositionToPen write FYPositionToPen;
+ property LineToMarker: Integer read FLineToMarker write FLineToMarker;
+ property LineVertical: Boolean read FLineVertical write FLineVertical;
+ property LineStyle: TPenStyle read FLineStyle write FLineStyle;
+ property LineColor: TColor read FLineColor write FLineColor;
+ property LineWidth: Integer read FLineWidth write FLineWidth;
+ property Caption: string read FCaption write FCaption;
+ property CaptionColor: TColor read FCaptionColor write SetCaptionColor;
+ property CaptionPosition: TJvChartCaptionPosition read FCaptionPosition write FCaptionPosition;
+ property CaptionBoxed: Boolean read FCaptionBoxed write FCaptionBoxed;
+
+ property Tag: Integer read FTag write FTag; // User assignable Integer like TComponent.Tag
+
+ //property CaptionBorderStyle :TPenStyle read FCaptionBorderStyle write FCaptionBorderStyle;
+ //property CaptionBorderColor :TColor read FCaptionBorderColor write FCaptionBorderColor;
+ end;
+
+ TJvChartGradientBar = class(TObject) // NEW 2007
+ private
+ FOwner: TJvChart; // Which chart does it belongs to?
+ FVisible: Boolean;
+ //FRawRect: TRect; // raw pixel-based X position.
+ FColor: TColor;
+ FIndex: Integer;
+ FGradDirection: TJvChartGradientDirection;
+ FGradColor: TColor;
+ FPenStyle:TPenStyle; // april 2009
+ FPenColor:TColor; // april 2009
+ protected
+ FYTop, FYBottom: Double;
+ constructor Create(Owner: TJvChart);
+ procedure SetVisible(AVisible: Boolean);
+ procedure SetColor(AColor: TColor);
+ procedure SetGradientColor(AColor: TColor);
+ procedure SetGradientType(AType: TJvChartGradientDirection);
+ public
+ property Visible: boolean read FVisible write SetVisible;
+ property YTop: Double read FYTop write FYTop;
+ property YBottom: Double read FYBottom write FYBottom;
+ property Color: TColor read FColor write SetColor;
+ property GradDirection: TJvChartGradientDirection read FGradDirection write FGradDirection;
+ property GradColor: TColor read FGradColor write FGradColor;
+ property PenStyle:TPenStyle read FPenStyle write FPenStyle; // april 2009
+ property PenColor:TColor read FPenCOlor write FPenColor; // april 2009
+ end;
+
+ TJvChartHorizontalBar = class(TJvChartGradientBar) // NEW 2007
+ {
+ private
+ FYTop: Double;
+ FYBottom: Double;
+ }
+ protected
+ constructor Create(Owner: TJvChart);
+ public
+ property YTop: Double read FYTop write FYTop;
+ property YBottom: Double read FYBottom write FYBottom;
+ end;
+
+ TJvChartVerticalBar = class(TJvChartGradientBar) // NEW 2007
+ private
+ FXLeft: Integer;
+ FXRight: Integer;
+ protected
+ constructor Create(Owner: TJvChart);
+ public
+ property XLeft: Integer read FXLeft write FXLeft;
+ property XRight: Integer read FXRight write FXRight;
+ end;
+
+ { TJvChartData : Holds NxN array of Reals, Resizes automatically within preset
+ limits. Provides a functionality mix of dynamic memory use, but with
+ a memory cap, so we don't thrash the system or leak forever. -WAP.}
+ TJvChartData = class(TObject)
+ private
+ {$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+ FData: TJvChartDataArray;
+ {$ELSE}
+ FData: array of Double;
+ {$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+ FClearToValue: Double; // Typically either 0.0 or NaN
+ FTimeStamp: array of TDateTime; // Time-series as a TDateTime
+ // Dynamic array of dynamic array of Double.
+ // is empty until data is stored in them.
+ // *** Order of indexing: FData[ValueIndex,Pen] ***
+ FDataAlloc: Integer; // Last Allocated Value.
+ FValueCount: Integer; // Number of sample indices used
+ FPenCount: Integer; // can't be changed without erasing all data!
+
+ FStartDateTime: TDateTime; // needed for DateTime mode, and datetime axis labels! NEW 2007
+ FEndDateTime: TDateTime;
+ procedure SetEndDateTime(const Value: TDateTime);
+ procedure SetStartDateTime(const Value: TDateTime);
+ // needed for DateTime mode, and datetime axis labels! NEW 2007
+ protected
+ procedure Grow(Pen, ValueIndex: Integer);
+ //GetValue/SetValue resizer, also throws exception if Pen,ValueIndex is negative or just way too big.
+ function GetValue(Pen, ValueIndex: Integer): Double;
+ procedure SetValue(Pen, ValueIndex: Integer; NewValue: Double);
+ function GetTimestamp(ValueIndex: Integer): TDateTime;
+ procedure SetTimestamp(ValueIndex: Integer; AValue: TDateTime);
+ public
+ constructor Create;
+ destructor Destroy; override;
+
+ procedure PreGrow(Pen, ValueIndex: Integer); // Advanced users. Allocate a large batch of memory in advance.
+
+ function DebugStr(ValueIndex: Integer): string; // dump all pens for particular valueindex, as string.
+
+ procedure Clear; // Resets All Data to zero.
+ procedure ClearPenValues; // Clears all pen values to NaN but does not reset pen definitions etc.
+ procedure Scroll;
+ property Value[Pen, ValueIndex: Integer]: Double read GetValue write SetValue; default;
+ property Timestamp[ValueIndex: Integer]: TDateTime read GetTimestamp write SetTimestamp;
+ property ValueCount: Integer read FValueCount write FValueCount;
+ property ClearToValue: Double read FClearToValue write FClearToValue; // Typically either 0.0 or NaN. default 0.0
+
+ // NEW 2007 [for DateTimeMode XAxis Labels]
+ property StartDateTime: TDateTime read FStartDateTime write SetStartDateTime;
+ // needed for DateTime mode, and datetime axis labels! NEW 2007
+ property EndDateTime: TDateTime read FEndDateTime write SetEndDateTime;
+ // needed for DateTime mode, and datetime axis labels! NEW 2007
+ end;
+
+ TJvChartPaintEvent = procedure(Sender: TJvChart; ACanvas: TCanvas) of object;
+ TJvChartEvent = procedure(Sender: TJvChart) of object;
+ TJvChartFloatingMarkerDragEvent = procedure(Sender: TJvChart; FloatingMarker: TJvChartFloatingMarker) of object; {NEW}
+
+ TJvChartClickEvent = procedure(Sender: TJvChart;
+ Button: TMouseButton; { left/right mouse click?}
+ Shift: TShiftState; { keyboard shift state?}
+ X, Y: Integer; { mouse position}
+ ChartValueIndex: Integer; { what value in the chart? }
+ ChartPenIndex: Integer; { what pen was clicked? }
+ { User modifiable return values for custom hinting! }
+ var ShowHint, HintFirstLineBold: Boolean; HintStrs: TStrings) of object; {NEW}
+
+ TJvChartOptions = class;
+
+ { There are TWO Y Axis per graph, optionally:
+ Chart.Options.PenAxis[I] -and-
+ Chart.Options.SecondaryYAxisOne
+ The primary one is displayed along the left side, and the one
+ for the right side is only displayed if you need it.
+ Properties for each side are grouped by the
+ TJvChartYAxisOptions persistent-properties-object.
+ }
+ TJvChartYAxisOptions = class(TPersistent)
+ private
+ FOwner: TJvChartOptions;
+ FActive: Boolean; // One or more pens use this Y Axis.
+ protected
+ FYMax: Double; // Y Scale value at the top left hand side of chart.
+ FYMin: Double; // Y Scale value at the bottom left hand side of the chart (default 0)
+ FYGap: Double; // Number of values per Y scale division
+ FYGap1: Double; // Gap multiplication factor for value scaling.
+ FMarkerValueDecimals: Integer; // Decimal places on marker-values (only applies to Marker Pens with Values)
+ FYDivisions: Integer; // Number of vertical divisions in the chart. (default 10)
+ FMaxYDivisions: Integer;
+ FMinYDivisions: Integer;
+ FYLegendDecimalPlaces: Integer;
+ FYLegends: TStringList;
+ FDefaultYLegends: Integer; // Number of default Y legends.
+ FYPixelGap: Double;
+ procedure SetYMax(NewYMax: Double);
+ procedure SetYMin(NewYMin: Double);
+ // procedure SetYGap(newYgap: Double);
+ function GetYLegends: TStrings;
+ procedure SetYLegends(Value: TStrings);
+ procedure SetYDivisions(AValue: Integer);
+ public
+ constructor Create(Owner: TJvChartOptions); virtual;
+ destructor Destroy; override;
+ procedure Assign(Source:TPersistent); override;// Added Sept 2009
+ procedure Normalize;
+ procedure Clear;
+ // runtime only properties
+ property YPixelGap: Double read FYPixelGap write FYPixelGap;
+ property Active: Boolean read FActive;
+ property YGap: Double read FYGap;
+ property YGap1: Double read FYGap1; // Gap multiplication factor for value scaling.
+ property YLegends: TStrings read GetYLegends write SetYLegends; { Y Axis Legends as Strings }
+ published
+ property YMax: Double read FYMax write SetYMax;
+ property YMin: Double read FYMin write SetYMin;
+ property YDivisions: Integer read FYDivisions write SetYDivisions default 10;
+ // Number of vertical divisions in the chart
+ // YDivisions->YDivisions
+ property MaxYDivisions: Integer read FMaxYDivisions write FMaxYDivisions default 20;
+ property MinYDivisions: Integer read FMinYDivisions write FMinYDivisions default 5;
+ property MarkerValueDecimals: Integer read FMarkerValueDecimals write FMarkerValueDecimals default -1;
+ // Decimal places on marker-values (only applies to Marker Pens with Values)
+ property YLegendDecimalPlaces: Integer read FYLegendDecimalPlaces write FYLegendDecimalPlaces;
+ property DefaultYLegends: Integer read FDefaultYLegends write FDefaultYLegends default JvDefaultYLegends;
+ end;
+
+ TJvChartOptions = class(TPersistent)
+ private
+ FOwner: TJvChart;
+ {accessors}
+ function GetAverageValue(Index: Integer): Double;
+ procedure SetAverageValue(Index: Integer; AValue: Double);
+ function GetPenColor(Index: Integer): TColor;
+ procedure SetPenColor(Index: Integer; AColor: TColor);
+ function GetPenStyle(Index: Integer): TPenStyle;
+ procedure SetPenStyle(Index: Integer; APenStyle: TPenStyle);
+ function GetPenMarkerKind(Index: Integer): TJvChartPenMarkerKind;
+ procedure SetPenMarkerKind(Index: Integer; AMarkKind: TJvChartPenMarkerKind);
+ procedure SetXStartOffset(Offset: Integer);
+ function GetPenSecondaryAxisFlag(Index: Integer): Boolean;
+ procedure SetPenSecondaryAxisFlag(Index: Integer; NewValue: Boolean);
+ function GetPenValueLabels(Index: Integer): Boolean;
+ procedure SetPenValueLabels(Index: Integer; NewValue: Boolean);
+ procedure SetPenCount(Count: Integer);
+ procedure SetChartKind(AKind: TJvChartKind);
+ // TStrings<->TStringList transmogrifiers
+ function GetPenLegends: TStrings;
+ procedure SetPenLegends(Value: TStrings);
+ function GetPenUnit: TStrings;
+ procedure SetPenUnit(Value: TStrings);
+ function GetXLegends: TStrings;
+ procedure SetXLegends(Value: TStrings);
+ procedure SetHeaderFont(AFont: TFont);
+ procedure SetLegendFont(AFont: TFont);
+ procedure SetAxisFont(AFont: TFont);
+ procedure SetPaperColor(AColor: TColor);
+ procedure SetPrimaryYAxis(AssignFrom: TJvChartYAxisOptions);
+ procedure SetSecondaryYAxis(AssignFrom: TJvChartYAxisOptions);
+ // Each pen can be associated with either the primary or secondary axis,
+ // this function decides which axis to return depending on the pen configuration:
+ function GetPenAxis(Index: Integer): TJvChartYAxisOptions;
+ procedure SetXAxisDateTimeDivision(const Value: Double);
+ protected
+ FChartKind: TJvChartKind; // default JvChartLine
+ {runtime pixel spacing multipliers}
+ FXPixelGap: Double;
+ {Fonts}
+ FHeaderFont: TFont;
+ FLegendFont: TFont;
+ FAxisFont: TFont;
+ FTitle: string;
+ FNoDataMessage: string;
+ FYAxisHeader: string;
+ FYAxisDivisionMarkers: Boolean; // Do you want grid-paper look?
+ FXAxisDivisionMarkers: Boolean; // Do you want grid-paper look?
+ FXAxisHeader: string;
+ FMarkerSize:Integer;
+ FFillUnderLine : Boolean;
+ FXLegends: TStringList; // Text labels.
+ FXLegendMaxTextWidth: Integer; // runtime: display width (pixels) of widest string in FXLegends[1:X].
+ FXAxisValuesPerDivision: Integer;
+ // Number of Values (aka samples) in each vertical dotted lines that are divisision marker.
+ FXAxisLegendSkipBy: Integer; //1=print every X axis label, 2=every other, and so on. default=1
+ FXLegendHoriz: Integer; // Horizontally oriented GraphXAxisLegend ends at this X Point.
+ FXAxisLabelAlignment: TAlignment; // New: Text alignment for X axis labels. Default is left alignment.
+ FXAxisDateTimeMode: Boolean;
+ // False=use custom text labels, True=Use Date/Time Stamps as X axis labels. [REWORKED LOGIC IN 2007!]
+ FXAxisDateTimeDivision: Double; // NEW 2007 : What is the nominal date/time division (1.0=day, 1.0/24=1 hour)
+ FXAxisDateTimeFormat: string; // Usually a short date-time label, hh:nn:ss is good.
+ FDateTimeFormat: string;
+ // Usually a long date-time label, ISO standard yyyy-mm-dd hh:nn:ss is fine, as is Windows locale defaults.
+ FXValueCount: Integer;
+ // Number of pens:
+ FPenCount: Integer;
+ // Per-pen array/list properties
+ FPenColors: array of TColor;
+ FPenStyles: array of TPenStyle; // solid, dotted
+ FPenMarkerKind: array of TJvChartPenMarkerKind;
+ FPenSecondaryAxisFlag: array of Boolean; // False=Primary Y Axis, True=Secondary Y Axis.
+ FPenValueLabels: array of Boolean;
+ FPenLegends: TStringList;
+ FPenUnit: TStringList;
+ FAverageValue: array of Double; // Used in averaging chart types only.
+ FPrimaryYAxis: TJvChartYAxisOptions;
+ FSecondaryYAxis: TJvChartYAxisOptions;
+ FXGap: Double; // Number of pixels per X scale unit.
+ FXOrigin: Integer; {which value corresponds to Origin}
+ FYOrigin: Integer; // Vertical (Y) Position of the Origin point, and X axis.
+ FXStartOffset: Longint; {margin} // Horizontal (X) Position of the Origin point, and Y Axis
+ FYStartOffset: Longint; // height of the top margin above the charting area.
+ FXEnd: Longint; { From top left of control, add XEnd to find where the right margin starts }
+ FYEnd: Longint; { from top left of control, add YEnd to find where the below-the bottom margin starts }
+ { more design time }
+ FLegendWidth: Integer;
+ FLegendRowCount: Integer; // Number of lines of text in legend.
+ FAutoUpdateGraph: Boolean;
+ FMouseEdit: Boolean;
+ FMouseDragObjects: Boolean; // Can mouse drag floating objects?
+ FMouseInfo: Boolean;
+ FLegend: TJvChartLegend; // was FShowLegend, now Legend=clChartLegendRight
+ FPenLineWidth: Integer;
+ FAxisLineWidth: Integer;
+
+ //COLORS:
+ FPaperColor: TColor;
+ FDivisionLineColor: TColor; // NEW! Division line
+ FShadowColor: TColor; // NEW! Shadow color
+ FAxisLineColor: TColor; // Color of box around chart plot area.
+ FHintColor: TColor; // Hint box color
+ FAverageLineColor: TColor; // Pen color for Charts with auto-average lines.
+ FCursorColor: TColor; // Sample indicator Cursor color
+
+ FCursorStyle: TPenStyle; // Cursor style.
+ FGradientColor: TColor; // new 2007
+ FGradientDirection: TJvChartGradientDirection; // new 2007
+
+ // INTERNALS :NEW STUFF IN 2007
+ FXAxisDateTimeFirstMarker: Integer; // at XValue initial offset NEW 2007
+ FXAxisDateTimeSkipBy: Integer; // an XValue indexing multiplier NEW 2007
+ FXAxisDateTimeLines: Integer; // number of lines we're displaying NEW 2007
+
+ { event interface }
+ procedure NotifyOptionsChange;
+ public
+ constructor Create(Owner: TJvChart); virtual;
+ destructor Destroy; override;
+ procedure Assign(Source:TPersistent); override;// Warren Added Sept 2009.
+
+ { runtime properties }
+ property AverageValue[Index: Integer]: Double read GetAverageValue write SetAverageValue;
+ property PenAxis[Index: Integer]: TJvChartYAxisOptions read GetPenAxis;
+ property XLegends: TStrings read GetXLegends write SetXLegends; { X Axis Legends as Strings }
+ { plot-canvas size, depends on size of control }
+ property XEnd: Longint read FXEnd write FXEnd;
+ property YEnd: Longint read FYEnd write FYEnd;
+ {Gradient NEW 2007 }
+ property GradientColor: TColor read FGradientColor write FGradientColor; // new 2007
+ property GradientDirection: TJvChartGradientDirection read FGradientDirection write FGradientDirection; // new 2007
+
+ { pixel spacing : multipliers to scale real values into X/Y pixel amounts before plotting. CRITICALLY important. }
+ property XPixelGap: Double read FXPixelGap write FXPixelGap;
+ property XLegendMaxTextWidth: Integer read FXLegendMaxTextWidth write FXLegendMaxTextWidth;
+ { Per Pen Array/List Properties -- settable at RUNTIME only. }
+ property PenColor[Index: Integer]: TColor read GetPenColor write SetPenColor;
+ property PenStyle[Index: Integer]: TPenStyle read GetPenStyle write SetPenStyle;
+ property PenMarkerKind[Index: Integer]: TJvChartPenMarkerKind read GetPenMarkerKind write SetPenMarkerKind;
+ property PenSecondaryAxisFlag[Index: Integer]: Boolean read GetPenSecondaryAxisFlag write SetPenSecondaryAxisFlag;
+ property PenValueLabels[Index: Integer]: Boolean read GetPenValueLabels write SetPenValueLabels;
+ published
+ { design time}
+ { Per Pen Array/List Properties - settable at DESIGNTIME. Others (color/style, marker) are runtime only. }
+ property PenLegends: TStrings read GetPenLegends write SetPenLegends;
+ property PenUnit: TStrings read GetPenUnit write SetPenUnit;
+ property ChartKind: TJvChartKind read FChartKind write SetChartKind default ckChartLine;
+ property Title: string read FTitle write FTitle;
+ property NoDataMessage: string read FNoDataMessage write FNoDataMessage;
+ //NEW! NOV 2004. Optionally display this instead of fixed resource string rsNoData
+
+ { X Axis Properties }
+ property YAxisHeader: string read FYAxisHeader write FYAxisHeader;
+ property YAxisDivisionMarkers: Boolean read FYAxisDivisionMarkers write FYAxisDivisionMarkers default True;
+ // Do you want grid-paper look?
+ { X Axis Properties }
+ property XAxisDivisionMarkers: Boolean read FXAxisDivisionMarkers write FXAxisDivisionMarkers default True;
+ // Do you want grid-paper look?
+ property XAxisValuesPerDivision: Integer read FXAxisValuesPerDivision write FXAxisValuesPerDivision;
+ // Number of Values (aka samples) in each vertical dotted lines that are divisision marker.
+ property XAxisLabelAlignment: TAlignment read FXAxisLabelAlignment write FXAxisLabelAlignment;
+ // New: Text alignment for X axis labels. Default is left alignment.
+
+ property XAxisDateTimeMode: Boolean read FXAxisDateTimeMode write FXAxisDateTimeMode;
+ // REWORKED LOGIC NEW IN 2007! See GraphXAxisDivisionMarkers
+ property XAxisDateTimeDivision: Double read FXAxisDateTimeDivision write SetXAxisDateTimeDivision;
+ // NEW 2007 : What is the nominal date/time division (1.0=day, 1.0/24=1 hour)
+
+ property XAxisDateTimeFormat: string read FXAxisDateTimeFormat write FXAxisDateTimeFormat;
+ property XAxisHeader: string read FXAxisHeader write FXAxisHeader;
+ property XAxisLegendSkipBy: Integer read FXAxisLegendSkipBy write FXAxisLegendSkipBy default 1;
+ property DateTimeFormat: string read FDateTimeFormat write FDateTimeFormat;
+ // Usually a long date-time label, ISO standard yyyy-mm-dd hh:nn:ss is fine, as is Windows locale defaults.
+ property PenCount: Integer read FPenCount write SetPenCount default 1;
+ property XGap: Double read FXGap write FXGap;
+ property XOrigin: Integer read FXOrigin write FXOrigin;
+ property YOrigin: Integer read FYOrigin write FYOrigin; // Position of bottom of chart (not always the zero origin)
+ property XStartOffset: Longint read FXStartOffset write SetXStartOffset default 45;
+ property YStartOffset: Longint read FYStartOffset write FYStartOffset default 10;
+ { Y Range }
+ { plotting markers }
+ property MarkerSize: Integer read FMarkerSize write FMarkerSize default JvChartDefaultMarkerSize;
+ property FillUnderLine : Boolean read FFillUnderLine write FFillUnderLine default False;
+ { !! New: Primary (left side) Y axis, and Secondary (right side) Y Axis !!}
+ property PrimaryYAxis: TJvChartYAxisOptions read FPrimaryYAxis write SetPrimaryYAxis;
+ property SecondaryYAxis: TJvChartYAxisOptions read FSecondaryYAxis write SetSecondaryYAxis;
+ //1=print every X axis label, 2=every other, and so on. default=1
+ { vertical numeric decimal places }
+ { more design time }
+ property AutoUpdateGraph: Boolean read FAutoUpdateGraph write FAutoUpdateGraph default True;
+ property MouseEdit: Boolean read FMouseEdit write FMouseEdit default True;
+ property MouseDragObjects: Boolean read FMouseDragObjects write FMouseDragObjects;
+ // Can mouse drag floating objects?
+ property MouseInfo: Boolean read FMouseInfo write FMouseInfo default True;
+ //OLD:property ShowLegend: Boolean read FShowLegend write FShowLegend default True;
+ //CHANGEDTO:
+ property Legend: TJvChartLegend read FLegend write FLegend default clChartLegendNone;
+ property LegendRowCount: Integer read FLegendRowCount write FLegendRowCount;
+ property LegendWidth: Integer read FLegendWidth write FLegendWidth default 150;
+ property PenLineWidth: Integer read FPenLineWidth write FPenLineWidth default 1;
+ property AxisLineWidth: Integer read FAxisLineWidth write FAxisLineWidth default 2;
+ { more and more design time. these ones not sure about whether they are designtime or not.}
+ property XValueCount: Integer read FXValueCount write FXValueCount default 10;
+ {Font properties}
+ property HeaderFont: TFont read FHeaderFont write SetHeaderFont;
+ property LegendFont: TFont read FLegendFont write SetLegendFont;
+ property AxisFont: TFont read FAxisFont write SetAxisFont;
+ { Color properties}
+ property DivisionLineColor: TColor read FDivisionLineColor write FDivisionLineColor default
+ JvDefaultDivisionLineColor; // NEW! Division line
+ property ShadowColor: TColor read FShadowColor write FShadowColor default JvDefaultShadowColor; // NEW! Shadow color
+
+ property PaperColor: TColor read FPaperColor write SetPaperColor;
+ property AxisLineColor: TColor read FAxisLineColor write FAxisLineColor;
+ property HintColor: TColor read FHintColor write FHintColor default JvDefaultHintColor;
+ property AverageLineColor: TColor read FAverageLineColor write FAverageLineColor default JvDefaultAvgLineColor;
+ property CursorColor: TColor read FCursorColor write FCursorColor;
+ property CursorStyle: TPenStyle read FCursorStyle write FCursorStyle;
+ end;
+
+ TJvChart = class(TJvGraphicControl)
+ private
+ FUpdating: Boolean; // PREVENT ENDLESS EVENT LOOPING.
+ FAutoPlotDone: Boolean; // If Options.AutoUpdateGraph is set, then has paint method called PlotGraph already?
+ FPlotGraphCalled: Boolean; // Has bitmap ever been painted?
+ FInPlotGraph: Boolean; // recursion blocker.
+
+ // NEW: The component has always had a feature when you click on margin areas
+ // that the user can enter a value (Y Axis Scale, title, and X header)
+ // The right margin however didn't do anything. Now all four have a user
+ // event that can be fired. If you don't want the default editor behaviours
+ // turn off Options.MouseEdit to make it 100% user-defined what happens when
+ // the user clicks on an area of the chart.
+ FOnYAxisClick: TJvChartEvent; // Left margin (Primary Y Axis labels) click
+ FOnXAxisClick: TJvChartEvent; // Bottom margin (X Axis Header) click
+ FOnAltYAxisClick: TJvChartEvent; // Right margin click (Secondary Y Axis labels)
+ FOnTitleClick: TJvChartEvent; // Title area click (Top margin)
+ FOnChartClick: TJvChartClickEvent; // mouse click event
+ FOnBeginFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent;
+ FOnEndFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent;
+ //FOnChartPaint: TJvChartPaintEvent; // wrong event. bugfix requires removal.
+ FMouseDownShowHint: Boolean; // True=showing hint.
+ FMouseDownHintBold: Boolean; // True=first line of hint is bold.
+ FMouseDownHintStrs: TStringList;
+
+ FExtPicture:TPicture; // An external image!
+
+ { TImage stuff}
+ FPicture: TPicture; // An image drawn via GDI primitives, saveable as
+ // bitmap or WMF, or displayable to screen
+ FData: TJvChartData;
+
+ FDragFloatingMarker: TJvChartFloatingMarker; //Current object we are dragging ( nil=none )
+ FFloatingMarker: TObjectList; // NEW: collection of TJvChartFloatingMarker objects.
+ FHorizontalBars: TObjectList; // NEW 2007
+ FVerticalBars: TObjectList; // NEW 2007
+
+ FAverageData: TJvChartData;
+ FBitmap: TBitmap;
+ FOptions: TJvChartOptions; //^TOptions;
+ //Options2 : ^TOptions2; // now FData
+ PrintInSession: Boolean;
+ FStartDrag: Boolean;
+ FMouseLegend: Boolean;
+ FContainsNegative: Boolean;
+ { strColorFile: string;}// not used (ahuser)
+ FOldYOrigin: Integer;
+ FOldYGap: Double;
+ FMouseDownX: Longint;
+ FMouseDownY: Longint;
+ FMouseValue: Integer;
+ FMousePen: Integer;
+ FYFont: TFont; // Delphi Font object wrapper.
+ //NEW:
+ FXOrigin: Double; {was in TJvChart.PlotGraph}
+ FYOrigin: Double; {was in TJvChart.PlotGraph}
+ FXAxisPosition: Integer; // how far down (in Y dimension) is the X axis?
+ FOnOptionsChangeEvent: TJvChartEvent; { Component fires this event for when options change.}
+ FOnPaint: TJvChartPaintEvent; {NEW JAN 2005: Custom paint event called from TjvChart.Paint.}
+
+ FCursorPosition: Integer; // NEW: -1 means no visible cursor, 0..n means make
+ // particular value highlighted. The highlight is painted
+ // over top of the TImage, so that we can just restore the TImage
+ // without replotting the whole chart.
+ // Y Axis Vertical Font
+ FYFontHandle: HFONT; // Y AXIS VERTICAL TEXT: Vertical Font Handle (remember to DeleteObject)
+ FYLogFont: TLogFont; // Y AXIS VERTICAL TEXT: Logical Font Options Record
+ procedure MakeVerticalFont; // Call GDI calls to get the Y Axis Vertical Font handle
+ procedure MyGraphVertFont(ACanvas: TCanvas); // vertical font handle
+ procedure PaintCursor; // called from Paint iif a Cursor is visible. does NOT modify FPicture!
+ protected
+ procedure DrawFloatingMarkers;
+ procedure DrawHorizontalBars; // NEW 2007
+ procedure DrawVerticalBars; // NEW 2007
+ procedure DrawGradient; // NEW 2007
+
+ procedure DrawChartLegendBelow(ACanvas: TCanvas); {accidentally deleted during Jedi_new to Jedi_2009 branch. Restored by WP Sept 2009}
+
+ function GetFloatingMarker(Index: Integer): TJvChartFloatingMarker;
+ function GetHorizontalBar(index:integer):TJvChartHorizontalBar; // new 2009
+ function GetVerticalBar(index:integer):TJvChartVerticalBar; // new 2009
+
+ { Right Side Legend showing Pen Names, and/or Data Descriptors }
+ procedure GraphXAxisLegendMarker(ACanvas: TCanvas; MarkerKind: TJvChartPenMarkerKind; X, Y: Integer);
+ procedure GraphXAxisLegend;
+ procedure MyHeader(ACanvas: TCanvas; StrText: string);
+ procedure MyXHeader(ACanvas: TCanvas; StrText: string);
+ procedure MyYHeader(ACanvas: TCanvas; StrText: string); // NEW
+ procedure MyHeaderFont(ACanvas: TCanvas);
+ procedure MyAxisFont(ACanvas: TCanvas);
+ procedure MySmallGraphFont(ACanvas: TCanvas);
+ function MyTextHeight(ACanvas: TCanvas; StrText: string): Longint;
+ { TEXTOUT stuff }
+ procedure MyRightTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string); // RIGHT TEXT
+ procedure MyCenterTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string); // CENTER TEXT
+ procedure MyLeftTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string); // LEFT ALIGN TEXT
+
+ // Use HintColor:
+ procedure MyLeftTextOutHint(ACanvas: TCanvas; X, Y: Integer; const AText: string);
+
+ { line, curve, rectangle stuff }
+ procedure MyPenLineTo(ACanvas: TCanvas; X, Y: Integer);
+ procedure MyAxisLineTo(ACanvas: TCanvas; X, Y: Integer);
+ procedure MyRectangle(ACanvas: TCanvas; X, Y, X2, Y2: Integer);
+ procedure MyColorRectangle(ACanvas: TCanvas; Pen: Integer; X, Y, X2, Y2: Integer);
+ procedure MyPie(ACanvas: TCanvas; X1, Y1, X2, Y2, X3, Y3, X4, Y4: Longint); { pie chart segment }
+ // procedure MyArc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); { arc } // not used (ahuser)
+
+ // procedure MyEllipse(X1, Y1, X2, Y2: Integer); // not used (ahuser)
+ procedure MyDrawLine(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer); // not used (ahuser)
+ procedure MyDrawAxisMark(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer); // solid line as a tick on an axis.
+ procedure MyDrawDotLine(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer);
+
+ procedure EditXHeader;
+ procedure EditYScale;
+ procedure EditHeader;
+
+ procedure SetSolidLines(ACanvas: TCanvas);
+ procedure SetDotLines(ACanvas: TCanvas);
+
+ procedure SetLineColor(ACanvas: TCanvas; Pen: Integer);
+ procedure SetRectangleColor(ACanvas: TCanvas; Pen: Integer);
+ procedure SetFontColor(ACanvas: TCanvas; Pen: Integer);
+ procedure CountGraphAverage;
+ procedure DrawPenColorBox(ACanvas: TCanvas; NColor, W, H, X, Y: Integer);
+ { function GetDefaultColorString(nIndex: Integer): string;}// (rom) not used
+ procedure MyPiePercentage(X1, Y1, W: Longint; NPercentage: Double);
+ procedure GraphPieChart(NPen: Integer);
+ procedure GraphDeltaAverage;
+ procedure MyPieLegend(NPen: Integer);
+ procedure ShowMouseMessage(X, Y: Integer);
+ // marker symbols:
+ procedure MyPolygon(ACanvas: TCanvas; Points: array of TPoint);
+ procedure PlotCross(ACanvas: TCanvas; X, Y: Integer);
+ procedure PlotDiamond(ACanvas: TCanvas; X, Y: Integer);
+ procedure PlotFilledDiamond(ACanvas: TCanvas; X, Y: Integer);
+ procedure PlotCircle(ACanvas: TCanvas; X, Y: Integer);
+ procedure PlotSquare(ACanvas: TCanvas; X, Y: Integer);
+
+ procedure PlotMarker(ACanvas: TCanvas; MarkerKind: TJvChartPenMarkerKind; X, Y: Integer);
+ // Calls one of the Plot functions.
+
+ procedure ClearScreen;
+ // internal graphics methods
+ procedure GraphSetup; // These set up variables used for all the rest of the plotting functions
+ procedure GraphXAxis;
+ procedure GraphYAxis;
+ procedure GraphYAxisDivisionMarkers;
+ procedure GraphXAxisDivisionMarkers; // new.
+ procedure CalcYEnd; // Determine where the below-the bottom axis area starts
+
+ function GetChartCanvas(isFloating:Boolean): TCanvas; // Get Picture.Bitmap Canvas.
+ function GetChartCanvasWidth: Integer; //WP NEW 2007
+ function GetChartCanvasHeight: Integer; //WP NEW 2007
+
+ function DestRect: TRect; // from TImage
+ procedure DesignModePaint; virtual; // Invoked by Paint method when we're in design mode.
+ procedure Paint; override; // from TImage
+ procedure Resize; override; // from TControl
+ procedure Loaded; override;
+ { draw dummy data for design mode}
+ procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
+ procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
+ procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
+
+ procedure PrimaryYAxisLabels; // Put contents into Options.PrimaryYAxis.YLegends
+ procedure NotifyOptionsChange; {NEW}
+
+ procedure InternalPlotGraph; { internal version of _PlotGraph that doesn't call Invalidate. }
+
+ { internal drawing properties, valid during Paint method invocations only }
+ property XOrigin: Double read FXOrigin; {was in TJvChart.PlotGraph}
+ property YOrigin: Double read FYOrigin; {was in TJvChart.PlotGraph}
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+
+ // Get the X and Y Value for a particular Mouse location:
+ function MouseToXValue(X: Integer): Integer;
+ // convert X pixel mouse position to data index, ie Data.Values[..,].
+ function MouseToYValue(Y: Integer): Double;
+ // convert Y pixel mouse position to value in range Options.PrimaryYAxis.Min to Options.PrimaryYAxis.mAx
+
+ {General procedures for the graph...}
+ procedure ResetGraphModule; {Call this before totally new values and Pen}
+ //procedure AutoFormatGraph; {XXX BAD CODE. TO BE DELETED. MAY BE REPLACED LATER BY NEW AutoRange FUNCTION!}
+
+ procedure PlotGraph; {Update screen / draw graph to screen. calls Invalidate. Don't call from inside Paint code!}
+
+ procedure PlotPicture( picture : TPicture; fontScaling:Double );
+
+ procedure PrintGraph; {Send picture to printer; all printing done by component}
+ procedure AddGraphToOpenPrintCanvas(XStartPos, YStartPos, GraphWidth, GraphHeight: Longint);
+ {adds the graph to the "OPEN" printer canvas}
+ {printing control=outside this component; add other text etc}
+ procedure GraphToClipboard; {Puts picture on clipboard}
+ procedure ResizeChartCanvas; {Call this after screen resize and after start up}
+ procedure PivotData; { Pivot Table. Switches the x values with Pen! Resets AverageLine}
+ procedure AutoHint; // Make the automatic hint message showing all pens and their values.
+ procedure SetCursorPosition(Pos: Integer);
+ procedure DisplayBars; // NEW 2007
+
+ // FLOATING MARKERS: NEW JAN 2005. -WP
+ function AddFloatingMarker: TJvChartFloatingMarker; // NEW Jan 2005!
+
+ property FloatingMarker[Index: Integer]: TJvChartFloatingMarker read GetFloatingMarker; // NEW Jan 2005!
+ property HorizontalBar[Index:Integer]:TJvChartHorizontalBar read GetHorizontalBar; // new 2009
+ property VerticalBar[Index:Integer]:TJvChartVerticalBar read GetVerticalBar; // new 2009
+
+ procedure DeleteFloatingMarker(Index: Integer); // NEW Jan 2005!
+
+ // --NEW 2007 METHOD--
+
+ procedure DeleteFloatingMarkerObj(Marker: TJvChartFloatingMarker); // NEW 2007
+ procedure CopyFloatingMarkers(Source: TJvChart);
+ procedure ClearFloatingMarkers;
+ function FloatingMarkerCount: Integer; // NEW 2007
+
+ function AddHorizontalBar: TJvChartHorizontalBar; // NEW 2007
+ procedure ClearHorizontalBars; // NEW 2007
+ function HorizontalBarsCount: Integer; // NEW 2007
+
+ function AddVerticalBar: TJvChartVerticalBar; // NEW 2007
+ procedure ClearVerticalBars; // NEW 2007
+ function VerticalBarsCount: Integer; // NEW 2007
+
+ // -- END NEW 2007 METHOD--
+
+ property Data: TJvChartData read FData;
+ property AverageData: TJvChartData read FAverageData;
+ public
+ {runtime only helper properties}
+ { TImage-like stuff }
+ property Picture: TPicture read FPicture; // write SetPicture;
+ // NEW: Ability to highlight a particular sample by setting the Cursor position!
+ property CursorPosition: Integer read FCursorPosition write SetCursorPosition;
+ // procedure DataTests; // TESTING. WAP.
+ published
+ { Standard TControl Stuff}
+ //property Color default clWindow;
+ property Font;
+ property Align;
+ property Anchors;
+ property BorderSpacing;
+ property Constraints;
+ property OnDblClick; { TNotifyEvent from TControl }
+ property AutoSize;
+ property DragCursor;
+ property DragKind;
+ //property OnKeyDown; // Tried to add this, but it was too hard. -WP APril 2004.
+ property DragMode;
+ property Enabled;
+ property ParentShowHint;
+ property PopupMenu;
+ property ShowHint;
+ property Visible;
+ { chart options}
+ property Options: TJvChartOptions read FOptions write FOptions;
+ { chart events}
+
+ property OnChartClick: TJvChartClickEvent read FOnChartClick write FOnChartClick;
+
+ property OnChartPaint: TJvChartPaintEvent read FOnPaint write FOnPaint; // Chart paint event fixed Sept 2009
+ // After chart bitmap is painted onto control surface we can "decorate" it with owner-drawn extras.
+
+ { Drag and Drop of Floating Marker Events - NEW Jan 2005 -WP}
+ property OnBeginFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent read FOnBeginFloatingMarkerDrag write
+ FOnBeginFloatingMarkerDrag; // Drag/drop of floating markers beginning.
+ property OnEndFloatingMarkerDrag: TJvChartFloatingMarkerDragEvent read FOnEndFloatingMarkerDrag write
+ FOnEndFloatingMarkerDrag; // Drag/drop of floating markers ending.
+
+ {
+ Chart Margin Click Events - you can click on the four
+ 'margin' areas (left,right,top,bottom) around the main chart
+ area. The left and top margins have default behaviours
+ which you can disable by turning off Options.MouseEdit.
+ The other 2 margin areas are entirely up to the user to define.
+ Clicking bottom or right margins does nothing by default.
+ }
+ property OnYAxisClick: TJvChartEvent read FOnYAxisClick write FOnYAxisClick;
+ // When user clicks on Y axis, they can enter a new Y Scale value.
+ property OnXAxisClick: TJvChartEvent read FOnXAxisClick write FOnXAxisClick;
+ // Also allow user to define some optional action for clicking on the X axis.
+ property OnAltYAxisClick: TJvChartEvent read FOnAltYAxisClick write FOnAltYAxisClick;
+ // Right margin click (Secondary Y Axis labels)
+ property OnTitleClick: TJvChartEvent read FOnTitleClick write FOnTitleClick; // Top margin area (Title area) click.
+ end;
+
+
+implementation
+
+uses
+ SysUtils, Forms, Dialogs, Printers, Clipbrd,
+ Math, // uses Ceil routine, also defines IsNaN on Delphi 6 and up.
+ JvJCLUtils, // StrToFloatDef
+ JvJVCLUtils, JvResources;
+
+const
+ {$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+ CHART_SANITY_LIMIT = 60000;
+ {$ELSE}
+ CHART_SANITY_LIMIT = 12000000;
+ {$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+ // Any attempt to have more than CHART_SANITY_LIMIT elements in this
+ // graph will be treated as an internal failure on our part. This prevents
+ // ugly situations where we thrash because of excessive memory usage.
+ // Better to set this than to have the system pig out when we
+ // don't want it to. Set this very small when debugging,
+ // large when releasing component, and don't remove it unless
+ // you're absolutely sure. Increase it whenever necessary.
+ // Remember, it's a debugging tool, here on purpose to help keep you
+ // out of thrashing-virtual-memory-hell. You probably have a screen
+ // to view the chart that is a maximum of 1600x1200, so more than 1600
+ // samples will mean the data should be reduced before charting.
+
+ MAX_VALUES = 20000;
+ // Any attempt to exceed this values count will cause array size and performance problems, thus we limit it.
+ MAX_PEN = 100;
+ // Any attempt to exceed this pen count will cause array size and performance problems, thus we hardcode the pen limit to 100 pens.
+ DEFAULT_PEN_COUNT = 16; // By Default TJvChartData's internal data structures have room for up to 16 pens
+ MAX_X_LEGENDS = 50;
+ MAX_GRAPH_LEGEND_LEN = 9;
+ REALPREC = 7;
+ DEFAULT_MARKER_SIZE = 3;
+ DEFAULT_VALUE_COUNT = 100;
+ // By Default TJvChartData holds 100 values per pen. Grows autofragellisticexpialidociously. :-)
+
+// NEW 2007:
+
+ // HELPER FUNCTIONS - NEW 2007
+//-------------------Helper to draw a Gradient. Use it with TJvChartHorizontalBar, for example-------------------------
+
+procedure GradHorizontal(Canvas: TCanvas; Rect: TRect; FromColor, ToColor: TColor); // NEW 2007
+var
+ X: Integer;
+ dr, dg, DB: Extended;
+ C1, C2: TColor;
+ r1, r2, g1, g2, b1, b2: Byte;
+ R, G, B: Byte;
+ Cnt: Integer;
+ XDelta: Integer;
+begin
+ C1 := FromColor;
+ R1 := GetRValue(C1);
+ G1 := GetGValue(C1);
+ B1 := GetBValue(C1);
+
+ C2 := ToColor;
+ R2 := GetRValue(C2);
+ G2 := GetGValue(C2);
+ B2 := GetBValue(C2);
+
+ XDelta := Rect.Right - Rect.Left;
+ if XDelta <= 0 then
+ Exit;
+
+ dr := (R2 - R1) / XDelta;
+ dg := (G2 - G1) / XDelta;
+ DB := (B2 - B1) / XDelta;
+
+ Cnt := 0;
+ for X := Rect.Left to Rect.Right - 1 do
+ begin
+ R := R1 + Ceil(dr * Cnt); // uses Math.
+ G := G1 + Ceil(dg * Cnt);
+ B := B1 + Ceil(DB * Cnt);
+
+ Canvas.Pen.Color := RGB(R, G, B);
+ Canvas.MoveTo(X, Rect.Top);
+ Canvas.LineTo(X, Rect.Bottom);
+ Inc(Cnt);
+ end;
+end;
+
+procedure GradVertical(Canvas: TCanvas; Rect: TRect; FromColor, ToColor: TColor); // NEW 2007
+var
+ Y: Integer;
+ dr, dg, DB: Extended;
+ C1, C2: TColor;
+ r1, r2, g1, g2, b1, b2: Byte;
+ R, G, B: Byte;
+ Cnt: Integer;
+ YDelta: Integer;
+begin
+ C1 := FromColor;
+ R1 := GetRValue(C1);
+ G1 := GetGValue(C1);
+ B1 := GetBValue(C1);
+
+ C2 := ToColor;
+ R2 := GetRValue(C2);
+ G2 := GetGValue(C2);
+ B2 := GetBValue(C2);
+
+ YDelta := Rect.Bottom - Rect.Top;
+ if YDelta <= 0 then
+ Exit;
+ dr := (R2 - R1) / YDelta;
+ dg := (G2 - G1) / YDelta;
+ DB := (B2 - B1) / YDelta;
+
+ Cnt := 0;
+ for Y := Rect.Top to Rect.Bottom - 1 do
+ begin
+ R := R1 + Ceil(dr * Cnt);
+ G := G1 + Ceil(dg * Cnt);
+ B := B1 + Ceil(DB * Cnt);
+
+ Canvas.Pen.Color := RGB(R, G, B);
+ Canvas.MoveTo(Rect.Left, Y);
+ Canvas.LineTo(Rect.Right, Y);
+ Inc(Cnt);
+ end;
+end;
+
+//=== { TJvChartGradientBar } ================================================
+
+constructor TJvChartGradientBar.Create(Owner: TJvChart);
+begin
+ inherited Create;
+ FOwner := Owner;
+ FVisible := false;
+ FColor := clWhite;
+ FGradDirection := grNone;
+ FGradColor := FColor;
+ FPenStyle := psClear;
+ FPenColor := clNone;
+end;
+
+procedure TJvChartGradientBar.SetVisible(AVisible: Boolean);
+begin
+ if AVisible <> FVisible then
+ begin
+ FVisible := AVisible;
+ if Assigned(FOwner) and not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+procedure TJvChartGradientBar.SetColor(AColor: TColor);
+begin
+ if AColor <> FColor then
+ begin
+ FColor := AColor;
+ if Assigned(FOwner) and not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+procedure TJvChartGradientBar.SetGradientColor(AColor: TColor);
+begin
+ if AColor <> FGradColor then
+ begin
+ FGradColor := AColor;
+ if Assigned(FOwner) and not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+procedure TJvChartGradientBar.SetGradientType(AType: TJvChartGradientDirection);
+begin
+ if AType <> FGradDirection then
+ begin
+ FGradDirection := AType;
+ if Assigned(FOwner) and not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+//=== { TJvChartHorizontalBar } ==============================================
+
+constructor TJvChartHorizontalBar.Create(Owner: TJvChart);
+begin
+ inherited Create(Owner);
+ FYTop := 0;
+ FYBottom := 0;
+end;
+
+//=== { TJvChartVerticalBar } ================================================
+
+constructor TJvChartVerticalBar.Create(Owner: TJvChart);
+begin
+ inherited Create(Owner);
+ FXLeft := 0;
+ FXRight := 0;
+end;
+
+//=== {TJvChartFloatingMarker} ===============================================
+
+constructor TJvChartFloatingMarker.Create(Owner: TJvChart);
+begin
+ inherited Create;
+ FOwner := Owner;
+ FVisible := False; // NOT visible by default.
+ FIndex := -1; // not yet set.
+ FLineToMarker := -1; // Don't draw a line to connect to another marker.
+ //FYPositionToPen := -1; // Don't copy FYPosition from the pen values.
+ FMarkerColor := clRed;
+ FMarker := pmkDiamond; // default is diamond marker.
+ FLineStyle := psDot;
+ FLineColor := clBlue;
+ //FCaptionBorderStyle := psClear;
+ FXDragMin := -1; // no limit.
+ FXDragMax := -1; // no limit.
+ FRawXPosition := -1;
+ FRawYPosition := -1;
+ FLineWidth := 1;
+ //FXPosition := 0;
+ //FYPosition := 0.0;
+end;
+
+procedure TJvChartFloatingMarker.Assign(Source: TPersistent); // NEW 2007.
+var
+ Src: TJvChartFloatingMarker;
+begin
+ // don't assign FOwner, FIndex, etc.
+ //FRawXPosition {don't copy}
+ //FRawYPosition {don't copy}
+ if Source is TJvChartFloatingMarker then
+ begin
+ Src := TJvChartFloatingMarker(Source);
+
+ FCaption := Src.Caption;
+ FTag := Src.Tag;
+
+ //FYPositionToPen := Src.YPositionToPen;
+ FMarkerColor := Src.MarkerColor;
+ FMarker := Src.Marker;
+ FLineStyle := Src.LineStyle;
+ FLineColor := Src.LineColor;
+ //FCaptionBorderStyle := psClear;
+ FXDragMin := Src.XDragMin;
+ FXDragMax := Src.XDragMax;
+
+ FLineWidth := Src.LineWidth;
+ FLineToMarker := Src.LineToMarker;
+ FLineVertical := Src.LineVertical;
+
+ FCaptionColor := Src.CaptionColor;
+ FCaptionPosition := Src.CaptionPosition;
+ FCaptionBoxed := Src.CaptionBoxed;
+
+ {don't use internal property set for these:}
+ XPosition := Src.XPosition;
+ YPosition := Src.YPosition;
+ Visible := Src.Visible;
+ end;
+end;
+
+procedure TJvChartFloatingMarker.SetCaption(ACaption: string);
+begin
+ if ACaption <> FCaption then
+ begin
+ FCaption := ACaption;
+ if Assigned(FOwner) and FVisible then
+ if not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end;
+end;
+
+procedure TJvChartFloatingMarker.SetCaptionColor(const Value: TColor);
+begin
+ FCaptionColor := Value;
+end;
+
+procedure TJvChartFloatingMarker.SetXPosition(XPos: Integer); // should invalidate the chart (FOwner) if changed.
+begin
+ if XPos <> FXPosition then
+ begin
+ FXPosition := XPos;
+ if Assigned(FOwner) and FVisible then
+ if not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+procedure TJvChartFloatingMarker.SetYPosition(YPos: Double); // should invalidate the chart (FOwner) if changed.
+begin
+ if YPos <> FYPosition then
+ begin
+ FYPosition := YPos;
+ if Assigned(FOwner) and FVisible then
+ if not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+procedure TJvChartFloatingMarker.SetVisible(AVisible: Boolean);
+begin
+ if AVisible <> FVisible then
+ begin
+ FVisible := AVisible;
+ if Assigned(FOwner) then
+ if not FOwner.FUpdating then
+ FOwner.Invalidate;
+ end
+end;
+
+//=== { TJvChartData } =======================================================
+
+constructor TJvChartData.Create;
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ I: Integer;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+begin
+ inherited Create;
+ FPenCount := DEFAULT_PEN_COUNT; // Can never set less than one inside TJvChartData!
+ {$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+ // FPenCount must be valid here:
+ for I := 0 to DEFAULT_PEN_COUNT do
+ Grow(I, DEFAULT_VALUE_COUNT);
+ {$ELSE}
+ Grow(DEFAULT_PEN_COUNT, DEFAULT_VALUE_COUNT);
+ {$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+end;
+
+destructor TJvChartData.Destroy;
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ I: Integer;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+begin
+ {$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+ for I := 0 to FDataAlloc - 1 do
+ Finalize(FData[I]);
+ {$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+ Finalize(FData); // Free array.
+ inherited Destroy;
+end;
+
+function TJvChartData.GetValue(Pen, ValueIndex: Integer): Double;
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+begin
+ Assert(ValueIndex >= 0);
+ Grow(Pen, ValueIndex);
+ Result := FData[ValueIndex, Pen]; // This will raise EInvalidOP for NaN values.
+end;
+{$ELSE}
+var
+ Idx: Integer;
+begin
+ // Grow base array
+ if (Pen < 0) or (Pen >= FPenCount) then
+ Result := NaN
+ else
+ begin
+ Assert(FPenCount > 0);
+ Idx := (ValueIndex * FPenCount) + Pen;
+
+ if (Idx < 0) or (Idx > CHART_SANITY_LIMIT) then // Sanity check!
+ raise ERangeError.CreateRes(@RsEDataIndexTooLargeProbablyAnInternal);
+
+ if Idx >= Length(FData) then
+ Grow(Pen, ValueIndex);
+ Result := FData[Idx];
+ end;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+procedure TJvChartData.SetValue(Pen, ValueIndex: Integer; NewValue: Double);
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+begin
+ // Grow base array
+ Grow(Pen, ValueIndex);
+ FData[ValueIndex, Pen] := NewValue;
+ if ValueIndex >= FValueCount then
+ begin
+ Grow(Pen, ValueIndex + 1);
+ FData[ValueIndex + 1, Pen] := NewValue; // Workaround for a graphical bug. Sorry.
+ FValueCount := ValueIndex + 1;
+ end;
+end;
+{$ELSE}
+var
+ Idx: Integer;
+begin
+ Assert(FPenCount > 0);
+
+ // Grow base array
+ if (Pen < 0) or (Pen >= FPenCount) then
+ raise ERangeError.CreateRes(@RsEPenIndexInvalid);
+
+ Idx := (ValueIndex * FPenCount) + Pen;
+
+ if (Idx < 0) or (Idx > CHART_SANITY_LIMIT) then // Sanity check!
+ raise ERangeError.CreateRes(@RsEDataIndexTooLargeProbablyAnInternal);
+
+ if Idx >= Length(FData) then
+ Grow(Pen, ValueIndex);
+ FData[Idx] := NewValue;
+
+ if ValueIndex >= FValueCount then
+ FValueCount := ValueIndex + 1;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+function TJvChartData.GetTimestamp(ValueIndex: Integer): TDateTime;
+begin
+ if (ValueIndex < 0) or (ValueIndex >= Length(FTimeStamp)) then
+ Result := 0.0 // null datetime
+ else
+ Result := FTimeStamp[ValueIndex];
+end;
+
+procedure TJvChartData.SetEndDateTime(const Value: TDateTime);
+begin
+ FEndDateTime := Value;
+end;
+
+procedure TJvChartData.SetStartDateTime(const Value: TDateTime);
+begin
+ FStartDateTime := Value;
+end;
+
+procedure TJvChartData.SetTimestamp(ValueIndex: Integer; AValue: TDateTime);
+begin
+ if ValueIndex < 0 then
+ Exit;
+ if ValueIndex >= Length(FTimeStamp) then
+ SetLength(FTimeStamp, ValueIndex + 1);
+ FTimeStamp[ValueIndex] := AValue;
+end;
+
+procedure TJvChartData.Scroll;
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ I, J: Integer;
+begin
+ if FValueCount < 2 then
+ begin
+ Clear;
+ Exit;
+ end;
+ { ULTRA SLOW BUT NON-CRASHING Version }
+ for I := 0 to FValueCount - 1 do
+ begin
+ for J := 0 to Length(FData[I]) - 1 do
+ FData[I, J] := FData[I + 1, J];
+ SetTimestamp(I, GetTimestamp(I + 1));
+ end;
+ FTimeStamp[FValueCount - 1] := 0;
+ // Check we didn't Break the heap:
+end;
+{$ELSE}
+var
+ T: Integer;
+ Idx: Integer;
+begin
+ if FValueCount > FPenCount then
+ begin
+ Assert(FPenCount > 0);
+ Idx := FValueCount * FPenCount;
+
+ // Yeah, I wish:
+ //System.Move( {Source} FData[FPenCount], {Dest} FData[0], Idx-FPenCount);
+ for T := 0 to Idx - FPenCount do
+ FData[T] := FData[T + FPenCount];
+
+ for T := Idx - FPenCount to Idx - 1 do
+ begin
+ if T > Length(FData) then
+ Break;
+ FData[T] := FClearToValue;
+ end;
+ //Dec(FValueCount,FPenCount);
+ end
+ else
+ begin
+ FPenCount := 0;
+ Clear;
+ end;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+procedure TJvChartData.PreGrow(Pen, ValueIndex: Integer);
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ T: Integer;
+begin
+ if Length(FData) < ValueIndex then
+ SetLength(FData, ValueIndex);
+ for T := 0 to ValueIndex - 1 do
+ SetLength(FData[T], Pen);
+ FDataAlloc := ValueIndex;
+end;
+{$ELSE}
+begin
+ if Pen > FPenCount then
+ FPenCount := Pen;
+ Grow(Pen, ValueIndex);
+ FDataAlloc := ValueIndex;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+procedure TJvChartData.Grow(Pen, ValueIndex: Integer);
+var
+ I, J, OldLength: Integer;
+begin
+ if (Pen < 0) or (ValueIndex < 0) then
+ raise ERangeError.CreateRes(@RsEDataIndexCannotBeNegative);
+ if (Pen > CHART_SANITY_LIMIT) or (ValueIndex > CHART_SANITY_LIMIT) then
+ raise ERangeError.CreateRes(@RsEDataIndexTooLargeProbablyAnInternal);
+
+ if ValueIndex >= FDataAlloc then
+ begin
+ //--------------------------------------------------------
+ // Performance tweak: Uses more memory but makes JvChart
+ // much faster!
+ // We Double our allocation unit size
+ // until we start to get Really Huge, then grow in chunks!
+ //--------------------------------------------------------
+ if ValueIndex < 640000 then
+ FDataAlloc := ValueIndex * 2 // Double in size
+ else
+ FDataAlloc := ValueIndex + 64000;
+
+ OldLength := Length(FData);
+ SetLength(FData, FDataAlloc);
+
+ // new: If we set FClearToValue to NaN, special handling in growing arrays:
+ if IsNaN(FClearToValue) then
+ for I := OldLength to FDataAlloc - 1 do
+ for J := 0 to Length(FData[I]) - 1 do
+ FData[I][J] := FClearToValue; // XXX Debug me!
+
+ end;
+ if Pen >= Length(FData[ValueIndex]) then
+ begin
+ OldLength := Length(FData[ValueIndex]);
+ SetLength(FData[ValueIndex], Pen + 1);
+ if IsNaN(FClearToValue) then
+ begin
+ for I := OldLength to FDataAlloc - 1 do
+ begin
+ Assert(Length(FData) > ValueIndex);
+ if (Length(FData[ValueIndex]) < FDataAlloc) then
+ SetLength(FData[ValueIndex], FDataAlloc); // Safety code!
+ FData[ValueIndex][I] := FClearToValue; // XXX Debug me!
+ end;
+ end;
+ end;
+end;
+{$ELSE}
+procedure TJvChartData.Grow(Pen, ValueIndex: Integer);
+var
+ N, Idx: Integer;
+ OldLen: Integer;
+begin
+ Assert(Assigned(Self));
+ Assert(FPenCount > 0);
+ Idx := (ValueIndex + 1) * FPenCount;
+ OldLen := Length(FData);
+ if Idx >= OldLen then
+ begin
+ Idx := Idx + 1024; // Add 1024 floats (8k) headroom.
+ SetLength(FData, Idx + 1);
+ for N := OldLen to Idx do
+ FData[N] := FClearToValue;
+ end;
+ FDataAlloc := Length(FData);
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+function TJvChartData.DebugStr(ValueIndex: Integer): string; // dump all pens for particular valueindex, as string.
+var
+ S: string;
+ I: Integer;
+ LValue: Double;
+begin
+ if (ValueIndex < 0) or (ValueIndex >= FDataAlloc) then
+ Exit;
+
+ if Timestamp[ValueIndex] > 0.0 then
+ S := FormatDateTime('hh:nn:ss ', Timestamp[ValueIndex]);
+ for I := 0 to FPenCount - 1 do
+ begin
+ LValue := GetValue(I, ValueIndex);
+ if IsNaN(LValue) then
+ S := S + '-'
+ else
+ S := S + Format('%5.2f', [LValue]);
+
+ if I < FPenCount - 1 then
+ S := S + ', '
+ end;
+ Result := S;
+end;
+
+procedure TJvChartData.Clear; // Resets FValuesCount/FPenCount to zero. Zeroes everything too, just for good luck.
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ I, J: Integer;
+begin
+ for I := 0 to FDataAlloc - 1 do
+ for J := 0 to Length(FData[I]) - 1 do
+ FData[I, J] := FClearToValue;
+ FValueCount := 0;
+end;
+{$ELSE}
+var
+ I: Integer;
+begin
+ for I := 0 to Length(FData) - 1 do
+ FData[I] := FClearToValue;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+procedure TJvChartData.ClearPenValues; // Clears all pen values to NaN but does not reset pen definitions etc.
+{$IFDEF TJVCHART_ARRAY_OF_ARRAY}
+var
+ I, J: Integer;
+begin
+ for I := 0 to FDataAlloc - 1 do
+ for J := 0 to Length(FData[I]) - 1 do
+ FData[I, J] := ClearToValue; // 0.0;
+end;
+{$ELSE}
+begin
+ Clear;
+end;
+{$ENDIF TJVCHART_ARRAY_OF_ARRAY}
+
+//=== { TJvChartYAxisOptions } ===============================================
+
+constructor TJvChartYAxisOptions.Create(Owner: TJvChartOptions);
+begin
+ inherited Create;
+ FOwner := Owner;
+
+ FMarkerValueDecimals := -1; // -1 = default (automatic decimals)
+
+ FYLegends := TStringList.Create;
+ FMaxYDivisions := 20;
+ FMinYDivisions := 5;
+ FYDivisions := 10;
+ FDefaultYLegends := JvDefaultYLegends;
+end;
+
+destructor TJvChartYAxisOptions.Destroy;
+begin
+ FYLegends.Free;
+ inherited Destroy;
+end;
+
+procedure TJvChartYAxisOptions.Clear;
+begin
+ YDivisions := DefaultYLegends;
+ YLegends.Clear;
+ Normalize;
+end;
+
+
+procedure TJvChartYAxisOptions.Assign(Source:TPersistent);// Warren added sept 2009
+var
+ src:TJvChartYAxisOptions;
+begin
+ //inherited Assign(Source); //raises exception!
+ if Source is TJvChartYAxisOptions then begin
+ src := TJvChartYAxisOptions(Source);
+ FYLegends.Assign(src.YLegends);
+
+ FMarkerValueDecimals := src.MarkerValueDecimals;
+ FYDivisions := src.YDivisions;
+ FMaxYDivisions := src.MaxYDivisions;
+ FMinYDivisions := src.MinYDivisions;
+ FYLegendDecimalPlaces:= src.YLegendDecimalPlaces;
+ FDefaultYLegends := src.DefaultYLegends;
+
+ //FYGap : not copied!
+ //FYGap1 : not copied!
+ //FYPixelGap: not copied!
+ YMin := src.YMin; // always first!
+ YMax := src.YMax; // property set method calls Normalize!
+ // Always set YMax last!
+
+ end;
+end;
+
+procedure TJvChartYAxisOptions.Normalize;
+var
+ // CheckYDivisions: Integer;
+ VC: Integer;
+begin
+ if FYMax - FYMin < 0.00001 then // make sure that there is some difference here!
+ FYMax := FYMin + 10;
+
+ if (DefaultYLegends > 0) and (YDivisions = 0) then
+ YDivisions := DefaultYLegends;
+
+ // DON'T KNOW WHY WE NEEDED THIS. REMOVED IT.
+ (*
+if (YGap>0.0) then
+begin
+ CheckYDivisions := Round((YMax + (YGap - 1)) / YGap);
+ if CheckYDivisions<>YDivisions then
+ YDivisions :=CheckYDivisions;
+end;*)
+
+ VC := YDivisions;
+ if VC < 1 then
+ VC := 1;
+ FYGap := (YMax - YMin) / VC;
+ FYGap1 := ((YMax - YMin) + 1) / VC;
+
+ YPixelGap := (FOwner.YEnd - 1) / VC; // Vertical Pixels Per Value Division counter.
+
+ (*CheckYDivisions := Round(((YMax-YMin) + (YGap - 1)) / YGap);
+ if CheckYDivisions <> YDivisions then
+ YDivisions := CheckYDivisions; *)
+
+ //---------------------------------------------------------------------
+ // Here's the normalization section:
+ // !!!The 10 and 20 here should be properties settable by the user!!!
+ //---------------------------------------------------------------------
+ if YDivisions < MinYDivisions then
+ begin
+ YDivisions := MinYDivisions;
+ FYGap := (YMax - YMin) / YDivisions;
+ end
+ else
+ if YDivisions > MaxYDivisions then
+ begin
+ YDivisions := MaxYDivisions;
+ FYGap := (YMax - YMin) / YDivisions;
+ end;
+end;
+
+procedure TJvChartYAxisOptions.SetYMin(NewYMin: Double);
+begin
+ if IsNaN(NewYMin) then
+ Exit;
+
+ try
+ if NewYMin = FYMin then
+ Exit;
+ except
+ {$IFDEF DEBUGINFO_ON}
+ OutputDebugString('TJvChartYAxisOptions.SetYMin-WTF?');
+ {$ENDIF DEBUGINFO_ON}
+ Exit;
+ end;
+
+ FYMin := NewYMin;
+
+ if not Assigned(FOwner) then
+ Exit;
+ if not Assigned(FOwner.FOwner) then
+ Exit;
+ if csLoading in FOwner.FOwner.ComponentState then
+ Exit;
+
+ // Rework other values around new YMin:
+ Normalize;
+ FOwner.NotifyOptionsChange;
+ {NEW: Auto-Regenerate Y Axis Labels}
+ if Assigned(FYLegends) then
+ if FYLegends.Count > 0 then
+ begin
+ FYLegends.Clear;
+ FOwner.FOwner.PrimaryYAxisLabels;
+ end;
+end;
+
+procedure TJvChartYAxisOptions.SetYMax(NewYMax: Double);
+begin
+ if IsNaN(NewYMax) then
+ Exit;
+
+ if NewYMax = FYMax then
+ Exit;
+
+ FYMax := NewYMax;
+
+ if not Assigned(FOwner) then
+ Exit;
+ if not Assigned(FOwner.FOwner) then
+ Exit;
+ if csLoading in FOwner.FOwner.ComponentState then
+ Exit;
+
+ // Rework other values around new YMax:
+ Normalize;
+ FOwner.NotifyOptionsChange;
+
+ {NEW: Auto-Regenerate Y Axis Labels}
+ if Assigned(FYLegends) then
+ if FYLegends.Count > 0 then
+ begin
+ FYLegends.Clear;
+ FOwner.FOwner.PrimaryYAxisLabels;
+ end;
+end;
+
+(*procedure TJvChartYAxisOptions.SetYGap(newYgap: Double);
+begin
+ if (FYGap < 5.0) and (YMax>100) then
+ begin
+ OutputDebugString('Bug');
+ end;
+
+ FYGap := newYGap;
+ // TODO: Fire event, and cause a refresh, recalculate other
+ // dependant fields that are calculated from the YGap.
+ FOwner.NotifyOptionsChange; // Fire event before we auto-format graph. Allows some customization to occur here.
+end;
+ *)
+
+function TJvChartYAxisOptions.GetYLegends: TStrings;
+begin
+ Result := TStrings(FYLegends);
+end;
+
+procedure TJvChartYAxisOptions.SetYLegends(Value: TStrings);
+begin
+ FYLegends.Assign(Value);
+ if Assigned(FOwner) then
+ FOwner.NotifyOptionsChange; // Fire event before we auto-format graph. Allows some customization to occur here.
+end;
+
+procedure TJvChartYAxisOptions.SetYDivisions(AValue: Integer);
+begin
+ FYDivisions := AValue;
+
+ if not Assigned(FOwner) then
+ Exit;
+ if not Assigned(FOwner.FOwner) then
+ Exit;
+ if csLoading in FOwner.FOwner.ComponentState then
+ Exit;
+
+ // Rework other values around new YMax:
+ Normalize;
+ FOwner.NotifyOptionsChange;
+end;
+
+//=== { TJvChartOptions } ====================================================
+
+constructor TJvChartOptions.Create(Owner: TJvChart);
+begin
+ inherited Create;
+ FOwner := Owner;
+
+ FAutoUpdateGraph := True;
+
+ FPrimaryYAxis := TJvChartYAxisOptions.Create(Self);
+ FSecondaryYAxis := TJvChartYAxisOptions.Create(Self);
+
+ FXAxisDivisionMarkers := True; //default property.
+ FYAxisDivisionMarkers := True; //default property.
+
+ SetLength(FPenColors, 12);
+ FPenColors[0] := clLime;
+ FPenColors[1] := clRed;
+ FPenColors[2] := clBlue;
+ FPenColors[3] := clYellow;
+ FPenColors[4] := clMaroon;
+ FPenColors[5] := clGreen;
+ FPenColors[6] := clOlive;
+ FPenColors[7] := clNavy;
+ FPenColors[8] := clPurple;
+ FPenColors[9] := clTeal;
+ FPenColors[10] := clFuchsia;
+ FPenColors[11] := clAqua;
+
+ FChartKind := ckChartLine;
+
+ FPenCount := 1;
+
+ FLegend := clChartLegendNone; //default Legend is None.
+
+ // Create TStringList property objects
+ FXLegends := TStringList.Create;
+ FPenLegends := TStringList.Create;
+ FPenUnit := TStringList.Create;
+ // dynamic array setup
+ SetLength(FAverageValue, DEFAULT_VALUE_COUNT);
+
+ // Defaults for Graph Options:
+
+ FMarkerSize := JvChartDefaultMarkerSize;
+ FXStartOffset := 45; {DEFAULT}
+ FYStartOffset := 10;
+ FTitle := '';
+ // FXAxisHeader := 'X';
+ // FYAxisHeader := 'Y';
+
+ FPaperColor := clWhite;
+ FAxisLineColor := clBlack;
+ FAverageLineColor := JvDefaultAvgLineColor;
+ FDivisionLineColor := JvDefaultDivisionLineColor; // NEW!
+ FShadowColor := JvDefaultShadowColor; //NEW!
+
+ FHeaderFont := TFont.Create;
+ FLegendFont := TFont.Create;
+ FAxisFont := TFont.Create;
+
+ //FShowLegend := True;
+ FMouseEdit := True;
+ FMouseInfo := True;
+ FLegendWidth := 150;
+ FPenLineWidth := 1;
+ FAxisLineWidth := 3;
+
+ FXValueCount := 10;
+
+ FXAxisLegendSkipBy := 1;
+ FXLegendHoriz := 0;
+
+ FHintColor := JvDefaultHintColor;
+end;
+
+destructor TJvChartOptions.Destroy;
+begin
+ FreeAndNil(FPrimaryYAxis); //memory leak fix SEPT 21, 2004.WAP.
+ FreeAndNil(FSecondaryYAxis); //memory leak fix SEPT 21, 2004. WAP.
+
+ FreeAndNil(FXLegends);
+ FreeAndNil(FPenLegends);
+ FreeAndNil(FPenUnit);
+
+ FreeAndNil(FHeaderFont);
+ FreeAndNil(FLegendFont);
+ FreeAndNil(FAxisFont);
+
+ inherited Destroy;
+end;
+
+
+procedure TJvChartOptions.Assign(Source: TPersistent); // Warren added sept 2009
+var
+ src:TJvChartOptions;
+ t:Integer;
+begin
+// inherited Assign(Source); {raises exception!}
+ if (Source is TJvChartOptions) then begin
+ src := Source as TJvChartOptions;
+
+
+ FLegend := src.Legend;//: TJvChartLegend;
+
+ FHeaderFont.Assign(src.HeaderFont);
+ FLegendFont.Assign(src.LegendFont);
+ FAxisFont.Assign(src.AxisFont);
+ FPenLegends.Assign(src.PenLegends);
+ FPenUnit.Assign(src.PenUnit);
+ FXLegends.Assign(src.XLegends);
+
+ FChartKind := src.ChartKind;
+ FTitle := src.Title;
+ FNoDataMessage := src.NoDataMessage;
+ FYAxisHeader := src.YAxisHeader;
+ FYAxisDivisionMarkers := src.YAxisDivisionMarkers;
+ FXAxisDivisionMarkers := src.XAxisDivisionMarkers;
+ FXAxisHeader := src.XAxisHeader;
+
+ FXLegendMaxTextWidth := src.XLegendMaxTextWidth;
+ FXAxisValuesPerDivision:= src.XAxisValuesPerDivision;
+ FXAxisLegendSkipBy := src.XAxisLegendSkipBy;
+ FXLegendHoriz := src.FXLegendHoriz;
+ FXAxisLabelAlignment := src.XAxisLabelAlignment;
+ FXAxisDateTimeMode := src.XAxisDateTimeMode;
+ FXAxisDateTimeFormat := src.XAxisDateTimeFormat;
+ FXAxisDateTimeDivision := src.XAxisDateTimeDivision;
+ FDateTimeFormat := src.DateTimeFormat;
+ FXValueCount := src.XValueCount;
+ FPenCount := src.PenCount;
+
+ // Array copies
+ SetLength(FPenColors, Length(src.FPenColors));
+ for t := 0 to Length(FPenColors)-1 do
+ FPenColors[t] := src.FPenColors[t];
+
+ SetLength(FPenStyles, Length(src.FPenStyles));
+ for t := 0 to Length(FPenStyles)-1 do
+ FPenStyles[t] := src.FPenStyles[t];
+
+ SetLength(FPenMarkerKind, Length(src.FPenMarkerKind));
+ for t := 0 to Length(FPenMarkerKind)-1 do
+ FPenMarkerKind[t] := src.FPenMarkerKind[t];
+
+ SetLength(FPenSecondaryAxisFlag, Length(src.FPenSecondaryAxisFlag));
+ for t := 0 to Length(FPenSecondaryAxisFlag)-1 do
+ FPenSecondaryAxisFlag[t] := src.FPenSecondaryAxisFlag[t];
+
+ SetLength(FPenValueLabels, Length(src.FPenValueLabels));
+ for t := 0 to Length(FPenValueLabels)-1 do
+ FPenValueLabels[t] := src.FPenValueLabels[t];
+
+ //SetLength(FAverageValue,Length(src.FAverageValue));
+ // no copy of averages!
+
+ FXOrigin := src.XOrigin;
+ FYOrigin := src.YOrigin;
+ FXStartOffset := src.XStartOffset;
+ FYStartOffset := src.YStartOffset;
+ FXEnd := src.XEnd;
+ FYEnd := src.YEnd;
+ FMarkerSize := src.MarkerSize;
+ { more design time }
+ FLegendWidth := src.LegendWidth;
+ FLegendRowCount := src.LegendRowCount;
+ FAutoUpdateGraph := src.AutoUpdateGraph;
+ FMouseEdit := src.MouseEdit;
+ FMouseDragObjects := src.MouseDragObjects;
+ FMouseInfo := src.MouseInfo;
+
+ FPenLineWidth := src.PenLineWidth;
+ FAxisLineWidth := src.AxisLineWidth;
+
+ //COLORS:
+ FPaperColor := src.PaperColor;
+ FDivisionLineColor := src.DivisionLineColor;
+ FShadowColor := src.ShadowColor;
+ FAxisLineColor := src.AxisLineColor;
+ FHintColor := src.HintColor;
+ FAverageLineColor := src.AverageLineColor;
+ FCursorColor := src.CursorColor;
+
+ FCursorStyle := src.CursorStyle;
+ FGradientColor := src.GradientColor;
+ FGradientDirection := src.GradientDirection;
+
+ // more internal dynamically calculated stuff:
+ //FXAxisDateTimeFirstMarker not copied
+ //FXAxisDateTimeSkipBy not copied
+ //FXAxisDateTimeLines not copied
+ //FXGap,FYGap: not copied.
+
+
+
+ // Second last:
+ FSecondaryYAxis.Assign(src.SecondaryYAxis);
+
+ // Last!
+ FPrimaryYAxis.Assign(src.PrimaryYAxis);
+
+ // re-plot chart:
+ NotifyOptionsChange;
+ end;
+
+end;
+procedure TJvChartOptions.NotifyOptionsChange;
+begin
+ if Assigned(FOwner) then
+ FOwner.NotifyOptionsChange;
+end;
+
+// Each pen can be associated with either the primary or secondary axis,
+// this function decides which axis to return depending on the pen configuration:
+
+function TJvChartOptions.GetPenAxis(Index: Integer): TJvChartYAxisOptions;
+begin
+ if (Index < 0) or (Index >= Length(FPenSecondaryAxisFlag)) then
+ Result := FPrimaryYAxis // default
+ else
+ if FPenSecondaryAxisFlag[Index] then
+ Result := FSecondaryYAxis // alternate!
+ else
+ Result := FPrimaryYAxis; // default
+end;
+
+procedure TJvChartOptions.SetChartKind(AKind: TJvChartKind);
+begin
+ if AKind <> FChartKind then
+ FChartKind := AKind;
+end;
+
+function TJvChartOptions.GetPenMarkerKind(Index: Integer): TJvChartPenMarkerKind;
+begin
+ if (Index >= 0) and (Index < Length(FPenMarkerKind)) then
+ Result := FPenMarkerKind[Index]
+ else
+ Result := pmkNone;
+end;
+
+procedure TJvChartOptions.SetPenMarkerKind(Index: Integer; AMarkKind: TJvChartPenMarkerKind);
+begin
+ if Index >= 0 then
+ begin
+ if Index >= Length(FPenMarkerKind) then
+ SetLength(FPenMarkerKind, Index + 1);
+ FPenMarkerKind[Index] := AMarkKind;
+ end;
+end;
+
+function TJvChartOptions.GetPenColor(Index: Integer): TColor;
+begin
+ // Don't check for out of range values, since we use that on purpose in this
+ // function. Okay, ugly, but it works. -WP.
+ case Index of
+ jvChartAverageLineColorIndex:
+ Result := FAverageLineColor;
+ jvChartDivisionLineColorIndex: // horizontal and vertical division line color
+ Result := FDivisionLineColor;
+ jvChartShadowColorIndex: // legend shadow (light gray)
+ Result := FShadowColor;
+ jvChartAxisColorIndex:
+ Result := FAxisLineColor; // get property.
+ jvChartHintColorIndex:
+ Result := FHintColor; // Get property.
+ jvChartPaperColorIndex:
+ Result := FPaperColor; // Get property.
+ else
+ if Index < jvChartAverageLineColorIndex then
+ Result := clBtnFace
+ else
+ if Index >= 0 then
+ Result := FPenColors[Index]
+ else
+ Result := clNone; // I hope clNone is a good unknown value (ahuser). {{Good enough. -WP.}}
+ end;
+end;
+
+procedure TJvChartOptions.SetPenColor(Index: Integer; AColor: TColor);
+begin
+ if (Index < 0) or (Index >= MAX_PEN) then
+ raise ERangeError.CreateRes(@RsEChartOptionsPenCountPenCountOutOf);
+
+ if Index >= Length(FPenColors) then
+ SetLength(FPenColors, Index + 1);
+ FPenColors[Index] := AColor;
+end;
+
+procedure TJvChartOptions.SetPenStyle(Index: Integer; APenStyle: TPenStyle);
+begin
+ if (Index < 0) or (Index >= MAX_PEN) then
+ raise ERangeError.CreateRes(@RsEChartOptionsPenCountPenCountOutOf);
+
+ if Index >= Length(FPenStyles) then
+ SetLength(FPenStyles, Index + 1);
+ FPenStyles[Index] := APenStyle;
+end;
+
+function TJvChartOptions.GetPenStyle(Index: Integer): TPenStyle;
+begin
+ if (Index >= 0) and (Index < Length(FPenStyles)) then
+ Result := FPenStyles[Index]
+ else
+ Result := psSolid;
+end;
+
+function TJvChartOptions.GetAverageValue(Index: Integer): Double;
+begin
+ if Index < 0 then
+ raise ERangeError.CreateRes(@RsEGetAverageValueIndexNegative);
+ if Index >= Length(FAverageValue) then
+ Result := 0.0
+ else
+ Result := FAverageValue[Index];
+end;
+
+procedure TJvChartOptions.SetAverageValue(Index: Integer; AValue: Double);
+begin
+ if Index < 0 then
+ raise ERangeError.CreateRes(@RsESetAverageValueIndexNegative);
+ if Index >= Length(FAverageValue) then
+ SetLength(FAverageValue, Index + 1);
+ FAverageValue[Index] := AValue;
+end;
+
+function TJvChartOptions.GetPenSecondaryAxisFlag(Index: Integer): Boolean;
+begin
+ if (Index < 0) or (Index >= Length(FPenSecondaryAxisFlag)) then
+ Result := False
+ else
+ Result := FPenSecondaryAxisFlag[Index];
+end;
+
+procedure TJvChartOptions.SetPenSecondaryAxisFlag(Index: Integer; NewValue: Boolean);
+begin
+ if (Index < 0) or (Index >= MAX_PEN) then
+ raise ERangeError.CreateRes(@RsEChartOptionsPenCountPenCountOutOf);
+ if Index >= Length(FPenSecondaryAxisFlag) then
+ SetLength(FPenSecondaryAxisFlag, Index + 1);
+ FPenSecondaryAxisFlag[Index] := NewValue;
+end;
+
+function TJvChartOptions.GetPenValueLabels(Index: Integer): Boolean;
+begin
+ if (Index < 0) or (Index >= Length(FPenValueLabels)) then
+ Result := False
+ else
+ Result := FPenValueLabels[Index];
+end;
+
+procedure TJvChartOptions.SetPenValueLabels(Index: Integer; NewValue: Boolean);
+begin
+ if (Index < 0) or (Index >= MAX_PEN) then
+ raise ERangeError.CreateRes(@RsEChartOptionsPenCountPenCountOutOf);
+
+ if Index >= Length(FPenValueLabels) then
+ SetLength(FPenValueLabels, Index + 1);
+ FPenValueLabels[Index] := NewValue;
+end;
+
+procedure TJvChartOptions.SetPenCount(Count: Integer);
+begin
+ if (Count < 0) or (Count >= MAX_PEN) then
+ raise ERangeError.CreateRes(@RsEChartOptionsPenCountPenCountOutOf);
+ FPenCount := Count;
+ SetLength(FPenSecondaryAxisFlag, FPenCount + 1);
+ // notify data object:
+ NotifyOptionsChange;
+end;
+
+function TJvChartOptions.GetPenLegends: TStrings;
+begin
+ Result := TStrings(FPenLegends);
+end;
+
+procedure TJvChartOptions.SetPenLegends(Value: TStrings);
+begin
+ FPenLegends.Assign(Value);
+end;
+
+function TJvChartOptions.GetPenUnit: TStrings;
+begin
+ Result := TStrings(FPenUnit);
+end;
+
+procedure TJvChartOptions.SetPenUnit(Value: TStrings);
+begin
+ FPenUnit.Assign(Value);
+end;
+
+function TJvChartOptions.GetXLegends: TStrings;
+begin
+ Result := TStrings(FXLegends);
+end;
+
+procedure TJvChartOptions.SetXAxisDateTimeDivision(const Value: Double);
+begin
+ FXAxisDateTimeDivision := Value;
+end;
+
+procedure TJvChartOptions.SetXLegends(Value: TStrings);
+begin
+ FXLegends.Assign(Value);
+end;
+
+procedure TJvChartOptions.SetHeaderFont(AFont: TFont);
+begin
+ FHeaderFont.Assign(AFont);
+end;
+
+procedure TJvChartOptions.SetLegendFont(AFont: TFont);
+begin
+ FLegendFont.Assign(AFont);
+end;
+
+procedure TJvChartOptions.SetAxisFont(AFont: TFont);
+begin
+ FAxisFont.Assign(AFont);
+end;
+
+procedure TJvChartOptions.SetPrimaryYAxis(AssignFrom: TJvChartYAxisOptions);
+begin
+ FPrimaryYAxis.Assign(AssignFrom);
+end;
+
+procedure TJvChartOptions.SetSecondaryYAxis(AssignFrom: TJvChartYAxisOptions);
+begin
+ FSecondaryYAxis.Assign(AssignFrom);
+end;
+
+procedure TJvChartOptions.SetPaperColor(AColor: TColor);
+begin
+ if AColor <> FPaperColor then
+ begin
+ FPaperColor := AColor;
+ if Assigned(FOwner) then
+ FOwner.Invalidate;
+ end;
+end;
+
+procedure TJvChartOptions.SetXStartOffset(Offset: Integer);
+begin
+ //if not PrintInSession then
+ // if (Offset < 10) or (Offset > (FOwner.Width div 2)) then
+ // raise ERangeError.CreateRes(@RsEChartOptionsXStartOffsetValueOutO);
+ FXStartOffset := Offset;
+end;
+
+//=== { TJvChart } ===========================================================
+
+{ GRAPH }
+{**************************************************************************}
+{ call this function : NEVER! }
+{**************************************************************************}
+
+constructor TJvChart.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner); {by TImage...}
+
+ ControlStyle := ControlStyle + [csOpaque];
+ // XXX FLICKER REDUCTION: Set ControlStyle properly. -WP. APRIL 2004.
+
+ FPicture := TPicture.Create;
+
+ FCursorPosition := -1; // Invisible until CursorPosition is set >=0 to make it visible.
+
+ FMouseDownHintStrs := TStringList.Create;
+
+ { logical font used for rotating text to show vertical labels }
+
+ FData := TJvChartData.Create;
+ FAverageData := TJvChartData.Create;
+
+ FFloatingMarker := TObjectList.Create; // NEW: collection of TJvChartFloatingMarker objects.
+ FFloatingMarker.OwnsObjects := True;
+
+ FHorizontalBars := TObjectList.Create; // NEW: collection of TJvChartFloatingMarker objects.
+ FHorizontalBars.OwnsObjects := True;
+
+ FVerticalBars := TObjectList.Create; // NEW: collection of TJvChartFloatingMarker objects.
+ FVerticalBars.OwnsObjects := True;
+
+ FOptions := TJvChartOptions.Create(Self);
+ CalcYEnd;
+
+ PrintInSession := False;
+
+ FOldYGap := 1;
+ FOldYOrigin := 0;
+ FStartDrag := False;
+ FMouseLegend := False;
+ FContainsNegative := False;
+ FMouseValue := 0;
+ FMousePen := 0;
+
+ {Set default values for component fields...}
+
+ if csDesigning in ComponentState then
+ begin
+ // default height and width
+ if not Assigned(Parent) then
+ begin
+ Width := 300;
+ Height := 300;
+ end;
+ end;
+end;
+
+{**************************************************************************}
+{ call this function : NEVER! }
+{**************************************************************************}
+
+destructor TJvChart.Destroy;
+begin
+ {Add code for destroying my own data...here}
+ FBitmap.Free;
+ if Ord(FYFontHandle) <> 0 then
+ DeleteObject(FYFontHandle); // vertical font object
+ FreeAndNil(FYFont);
+
+ FreeAndNil(FPicture);
+ FreeAndNil(FAverageData);
+ FreeAndNil(FOptions);
+ FreeAndNil(FData);
+
+ FreeAndNil(FFloatingMarker); // Destroy collection of TJvChartFloatingMarker objects. Destroys contained objects also.
+ FreeAndNil(FHorizontalBars); // NEW 2007
+ FreeAndNil(FVerticalBars); // NEW 2007
+
+ FreeAndNil(FMouseDownHintStrs); //new.
+
+ inherited Destroy;
+end;
+
+{Paint helper}
+
+function TJvChart.DestRect: TRect;
+begin
+ Result.Left := 0;
+ Result.Top := 0;
+ Result.Right := GetChartCanvasWidth;
+ Result.Bottom := GetChartCanvasHeight;
+end;
+
+procedure TJvChart.Loaded;
+begin
+ inherited Loaded;
+ ResizeChartCanvas;
+end;
+
+procedure TJvChart.Resize;
+begin
+ inherited Resize;
+ ResizeChartCanvas;
+ // Invalidate already happens in ResizeChartCanvas.
+end;
+
+{ PAINT }
+
+procedure TJvChart.DesignModePaint;
+var
+ DesignStr: string;
+ TW, TH: Integer;
+ LCanvas: TCanvas;
+begin
+ LCanvas := GetChartCanvas(false);
+
+ LCanvas.Brush.Color := Options.PaperColor;
+ LCanvas.Rectangle(0, 0, Width, Height);
+
+ DesignStr := ClassName + RsChartDesigntimeLabel;
+
+ if Options.PrimaryYAxis.YMin >= Options.PrimaryYAxis.YMax then
+ begin
+ if Options.PrimaryYAxis.YMax > 0 then
+ Options.PrimaryYAxis.YMin := 0.0;
+ end;
+
+ if (Abs(Options.PrimaryYAxis.YMax) < 0.000001) and (Abs(Options.PrimaryYAxis.YMin) < 0.000001) then
+ Options.PrimaryYAxis.YMax := 10.0; // Reasonable non-zero default, so that charting works!
+
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+ GraphSetup;
+
+ DrawGradient;
+ DisplayBars;
+
+ PrimaryYAxisLabels;
+ GraphXAxis;
+ GraphXAxisDivisionMarkers;
+ GraphYAxis;
+ GraphYAxisDivisionMarkers;
+
+ { designtime component label }
+ TW := LCanvas.TextWidth(DesignStr);
+ TH := LCanvas.TextHeight(DesignStr);
+
+ LCanvas.Brush.Color := Options.PaperColor;
+ LCanvas.Pen.Color := Color;
+
+ //ACanvas.Pen.Style := psDot;
+ //ACanvas.Rectangle( (width div 2) - (TW div 2), (height div 2) - (TH div 2), TW, TH);
+ if (TW < Width) and (TH < Height) then
+ LCanvas.TextOut((Width div 2) - (TW div 2), (Height div 2) - (TH div 2), DesignStr);
+end;
+
+procedure TJvChart.Paint; { based on TImage.Paint }
+begin
+ if csDesigning in ComponentState then
+ begin
+ DesignModePaint;
+ Exit;
+ end;
+
+ if Options.AutoUpdateGraph and not FAutoPlotDone then
+ begin
+ FAutoPlotDone := True;
+ PlotGraph; // Makes sure something is visible in the TPicture.
+ end;
+
+ Assert(Assigned(FPicture));
+ //inherited ACanvas.Lock;
+ inherited Canvas.StretchDraw(DestRect, Picture.Graphic);
+
+ // New: Draw custom moveable markers on TOP of base data pen layer:
+ DrawFloatingMarkers;
+
+ // Draw cursor (vertical dotted line) if present:
+ if (FCursorPosition >= 0) and (FCursorPosition <= Options.XValueCount) then
+ PaintCursor;
+
+ // Allow end-user to custom paint on the Chart chanvas:
+ if Assigned(FOnPaint) then
+ FOnPaint(Self, Canvas);
+end;
+
+// Draw an oscilliscope-like cursor over the place where the current sample is in the chart.
+// This is very handy when you want to associate your table, grid, or other data source,
+// with the chart, and highlight one row in the chart.
+
+procedure TJvChart.PaintCursor;
+var
+ X: Integer;
+ XPixelGap: Double;
+begin
+ with inherited Canvas do
+ begin
+ Pen.Color := Options.CursorColor;
+ Pen.Style := Options.CursorStyle;
+
+ XPixelGap := ((Options.XEnd - 2) - Options.XStartOffset) / (Options.XValueCount - 1);
+
+ X := Round(Options.XStartOffset + XPixelGap * FCursorPosition);
+
+ // Vertical line along X position:
+ MoveTo(X, Options.YStartOffset);
+ LineTo(X, FXAxisPosition - 1);
+ end;
+end;
+
+{device independent functions... no checking for printer / screen needed}
+
+{**************************************************************************}
+{ call this function : }
+{ a) before setting totally new values to the graph }
+{ b) note that any custom strings in the PrimaryYAxis.Legends or }
+{ SecondaryYAxis.Legends are CLEARED by this function. }
+{**************************************************************************}
+
+procedure TJvChart.ResetGraphModule;
+begin
+ Data.Clear;
+
+ FPlotGraphCalled := False;
+ FContainsNegative := False;
+ Options.Title := '';
+ Options.PenCount := 1;
+ Options.XValueCount := 0;
+
+ Options.PrimaryYAxis.Clear;
+ Options.SecondaryYAxis.Clear;
+
+ Options.XOrigin := 0;
+ Options.YOrigin := 0;
+ Options.XGap := 1;
+
+ Options.PenLegends.Clear;
+
+ (* for I := 0 to MAX_VALUES-1 do
+ begin
+ Options.AverageValue[I] := 0;
+ end; *)
+
+ Data.Clear;
+ AverageData.Clear;
+
+ Options.XLegends.Clear;
+end;
+
+procedure TJvChart.PrimaryYAxisLabels;
+var
+ I, J: Integer;
+ YDivision: Double;
+ FormatStr, YDivisionStr, PrevYDivisionStr: string;
+ // left hand side, vertically ascending labels for scale of Y values.
+ Decimals: Integer;
+ Unique: Boolean;
+begin
+ Decimals := Options.PrimaryYAxis.YLegendDecimalPlaces;
+ Unique := False; { Add Decimals until we get unique values }
+ while not Unique do
+ begin
+ Unique := True;
+ PrevYDivisionStr := '';
+ Options.PrimaryYAxis.YLegends.Clear;
+ FormatStr := '0.0';
+ for J := 2 to Decimals do
+ FormatStr := FormatStr + '0';
+ for I := 0 to Options.PrimaryYAxis.YDivisions do // NOTE! Don't make this YDivisions-1 That'd be bad! !!!!
+ begin
+ YDivision := Options.PrimaryYAxis.YMin + (I * Options.PrimaryYAxis.YGap);
+ if Decimals <= 0 then
+ YDivisionStr := IntToStr(Round(YDivision)) // Whole Numbers Only.
+ else
+ YDivisionStr := FormatFloat(FormatStr, YDivision); // Variable Decimals
+ if (PrevYDivisionStr = YDivisionStr) and (Decimals < 5) then
+ begin
+ Inc(Decimals);
+ Unique := False; // Force repeat
+ Break; // Exit for loop.
+ end;
+ Options.PrimaryYAxis.YLegends.Add(YDivisionStr);
+ PrevYDivisionStr := YDivisionStr;
+ end;
+ end;
+end;
+
+{ Setup Graph Formatting Properties
+
+ *** AutoFormatGraph CONSIDERED HARMFUL. REMOVED. ***
+ This procedure does nothing helpful, and will be removed from CVS soon.
+ What it *does* do is wildly screw up plotting of graphs with negative
+ values in it.
+ -Wpostma.
+}
+
+(* XXXX BAD CODE. TO BE DELETED SOON. Wpostma.
+procedure TJvChart.AutoFormatGraph;
+var
+ V, NYMax, NYMin: Double;
+// NPen: Longint;
+ I, J: Integer;
+// calcYGap: Double; // not used (ahuser)
+ ATextWidth, SkipBy, MaxFit: Integer;
+begin
+ // nMaxXValue := 0;
+ // NPen := 0;
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+
+ {Set graph type according to component property}
+
+ if Options.PrimaryYAxis.YMax <= Options.PrimaryYAxis.YMin then
+ begin
+ {Analyse graph for max and min values...}
+ NYMax := Low(Integer);
+ NYMin := High(Integer);
+ for I := 0 to Data.ValueCount - 1 do
+ begin
+ for J := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenAxis[J] <> Options.PrimaryYAxis then
+ Continue; // XXX !!! AUTO SCALING ONLY ON PRIMARY AXIS, FOR NOW. !!!
+
+ V := FData.Value[J, I];
+
+ if IsNaN(V) then
+ Continue;
+ if NYMin > V then
+ NYMin := V;
+ //if (I>nMaxXValue) and (FData.Value[J,I]<>0) then
+ //nMaxXValue := I;
+ //if (J>NPen) and (FData.Value[J,I]<>0) then
+ // NPen := J;
+ if NYMax < FData.Value[J, I] then
+ NYMax := FData.Value[J, I];
+ end;
+ if (NYMin > 0) and (Options.PrimaryYAxis.YMin = 0) then
+ NYMin := 0;
+ end;
+ // Round up YMax so it's got some zeros after it:
+ if NYMax > 5000 then
+ NYMax := Trunc(Trunc(NYMax + 499) / 500) * 500
+ else
+ if NYMax > 1000 then
+ NYMax := Trunc(Trunc(NYMax + 99) / 100) * 100
+ else
+ if NYMax > 10 then
+ NYMax := Trunc(Trunc(NYMax + 9) / 10) * 10;
+
+ // And now the really bad hack:
+ Options.PrimaryYAxis.SetYMax(0);
+ Options.PrimaryYAxis.SetYMax(NYMax);
+ end
+ else
+ begin
+ // !!!!!!!!!!!!! WARNING WARNING WARNING !!!!!!!!!!!!!!!!!!!!
+ // The following line has been commented out because it triggers
+ // a warning because NYMax is not used anywhere after the
+ // setting of its value
+ //NYMax := Options.PrimaryYAxis.YMax;
+
+ NYMin := Options.PrimaryYAxis.YMin;
+ end;
+
+ // And some negative handling crap.
+ FContainsNegative := False;
+ if NYMin < 0 then
+ begin
+ FContainsNegative := True;
+
+// if Options.PrimaryYAxis.DefaultYLegends>0 then
+// Options.PrimaryYAxis.Normalize
+// else
+// Options.PrimaryYAxis.YGap := 1;
+
+// if Options.PrimaryYAxis.YGap <= 0 then {* XXX WORKAROUND A BUG. Better to have bad looking data than divide by zero exceptions. XXX *}
+// Options.PrimaryYAxis.YGap := 0.00001;
+
+ Options.ChartKind := ckChartLine;
+ Options.YOrigin := Round(-NYMin / Options.PrimaryYAxis.YGap);
+ end;
+
+ if Options.PrimaryYAxis.YDivisions = 0 then
+ Options.PrimaryYAxis.YDivisions := 1;
+
+ //Options.PenCount := NPen;
+ if Options.XValueCount < Data.ValueCount then
+ Options.XValueCount := Data.ValueCount;
+
+//XXX if Options.PrimaryYAxis.YDivisions < 3 then
+// Options.PrimaryYAxis.YDivisions := 3; // some labels
+
+ // Primary Y Axis Labels. This version only supports 0,1,2 decimal places.
+ PrimaryYAxisLabels;
+
+ // XXX TODO: Draw secondary Y Axis labels, if enabled!
+
+ // if we put too many labels on the bottom X axis, they crowd or overlap,
+ // so this prevents that:
+
+ for I := 0 to Options.XLegends.Count - 1 do
+ begin
+ ATextWidth := ChartCanvas.TextWidth(Options.XLegends[I]) + 10;
+
+ if ATextWidth > Options.XLegendMaxTextWidth then
+ Options.XLegendMaxTextWidth := ATextWidth;
+ end;
+ if Options.XLegendMaxTextWidth < 20 then
+ Options.XLegendMaxTextWidth := 20;
+
+ MaxFit := ((Width - (Options.XStartOffset * 2)) div
+ (Options.XLegendMaxTextWidth + (Options.XLegendMaxTextWidth div 4)));
+ if MaxFit < 1 then
+ MaxFit := 1;
+
+ SkipBy := Data.ValueCount div MaxFit;
+ if SkipBy < 1 then
+ SkipBy := 1;
+ //if SkipBy > Options.XAxisLegendSkipBy then
+ Options.XAxisLegendSkipBy := SkipBy;
+
+ // Now do the graphing.
+ CountGraphAverage;
+
+ PlotGraph;
+end;
+ XXX BAD CODE. READ WARNING ABOVE.
+*)
+
+procedure TJvChart.CountGraphAverage;
+var
+ I, J: Integer;
+begin
+ if Options.ChartKind = ckChartLine then
+ Exit; // no average needed.
+
+ for I := 0 to Data.ValueCount - 1 do
+ begin
+ Options.AverageValue[I] := 0;
+ for J := 0 to MAX_PEN - 1 do
+ Options.AverageValue[I] := Options.AverageValue[I] + FData.Value[J, I];
+ if Options.PenCount = 0 then
+ Options.AverageValue[I] := 0
+ else
+ Options.AverageValue[I] := Options.AverageValue[I] / Options.PenCount;
+ end;
+end;
+
+// These set up variables used for all the rest of the plotting functions.
+
+procedure TJvChart.GraphSetup;
+var
+ X1, X2, Y1, Y2, PYVC, VC: Integer;
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas(false);
+
+ ACanvas.Brush.Style := bsSolid;
+ if FData.ValueCount > 0 then
+ Options.XValueCount := FData.ValueCount;
+
+ { Get X value count }
+ VC := Options.XValueCount;
+ if VC < 1 then
+ VC := 1;
+
+ { Get Y value count. First normalize. }
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+ PYVC := Options.PrimaryYAxis.YDivisions;
+ if PYVC < 1 then
+ PYVC := 1;
+
+ Options.XPixelGap := ((Options.XEnd - 1) - Options.XStartOffset) / VC;
+
+ FXOrigin := Options.XStartOffset + Options.XPixelGap * (Options.XOrigin);
+ FYOrigin := Options.YStartOffset + Round(Options.PrimaryYAxis.YPixelGap * PYVC);
+
+ ACanvas.Brush.Style := bsClear;
+
+ { NEW: Box around entire chart area. }
+ if Options.AxisLineWidth <> 0 then
+ begin
+ X1 := Round(XOrigin);
+ X2 := Round(Options.XStartOffset + Options.XPixelGap * VC);
+ Y1 := Options.YStartOffset - 1;
+ Y2 := Round(YOrigin) + 1; // was YTempOrigin
+
+ if Y2 > Height then
+ begin
+ // I suspect that the value of YPixelGap is too large in some cases.
+ Options.PrimaryYAxis.Normalize;
+ //OutputDebugString( PChar('Y2 is bogus. PYVC='+IntToStr(PYVC)) );
+ end;
+ MyRectangle(ACanvas, X1, Y1, X2, Y2);
+ end;
+
+ ACanvas.Brush.Style := bsSolid;
+end;
+
+// internal methods
+
+procedure TJvChart.GraphYAxis;
+var
+ ACanvas: TCanvas;
+begin
+ if Options.AxisLineWidth = 0 then
+ Exit;
+
+ ACanvas := GetChartCanvas(false);
+ ACanvas.Pen.Style := psSolid;
+ ACanvas.Pen.Color := Options.AxisLineColor;
+ ACanvas.MoveTo(Round(XOrigin), Options.YStartOffset);
+ MyAxisLineTo(ACanvas, Round(XOrigin),
+ Round((Options.YStartOffset - 1) +
+ Options.PrimaryYAxis.YPixelGap * (Options.PrimaryYAxis.YDivisions)));
+end;
+
+// internal methods
+
+procedure TJvChart.GraphXAxis;
+var
+ LCanvas: TCanvas;
+begin
+ if Options.AxisLineWidth = 0 then
+ Exit;
+
+ LCanvas := GetChartCanvas(false);
+ LCanvas.Pen.Style := psSolid;
+ LCanvas.Pen.Color := Options.AxisLineColor;
+ LCanvas.Pen.Width := Options.AxisLineWidth; // was missing. Added Feb 2005. -WPostma.
+ FXAxisPosition := Options.YStartOffset + Round(Options.PrimaryYAxis.YPixelGap * (Options.PrimaryYAxis.YDivisions));
+
+ {Draw X-axis}
+ LCanvas.MoveTo(Options.XStartOffset, FXAxisPosition);
+ MyAxisLineTo(LCanvas, Round(Options.XStartOffset + Options.XPixelGap * Options.XValueCount), FXAxisPosition);
+end;
+
+procedure TJvChart.GraphXAxisDivisionMarkers; // new.
+var
+ I, X: Integer;
+ Lines: Integer;
+ LCanvas: TCanvas;
+ // these are used only in special XAxisDateTimeMode:
+ TimePerXValue: Double;
+ ElapsedTime: Double;
+begin
+ if not Enabled then
+ Exit;
+ if Options.XValueCount <= 0 then // NOT VISIBLE WHEN NO VALUES TO SHOW. NEW 2007
+ Exit;
+ if Options.XStartOffset <= 0 then // NOT VISIBLE WHEN NO ROOM TO SHOW IT. NEW 2007
+ Exit;
+
+ LCanvas := GetChartCanvas(false);
+
+ if not Options.XAxisDivisionMarkers then
+ Exit;
+ if Options.XAxisValuesPerDivision <= 0 then
+ Exit;
+
+ //XAxisDateTimeMode: [NEW 2007]
+ // Make charts with XAxis divisions synchronized
+ // to some regular time division such as hourly periods.
+ //
+ // new mode! when looking at date/time charts
+ // it's useful to be able to force the divisions to be
+ // shown at hourly intervals, or if you're looking at a month of data
+ // perhaps you might want to plot a division marker at midnight
+ // or at weekly intervals.
+ //
+ if (Options.XAxisDateTimeMode) and
+ (Options.XAxisDateTimeDivision > 0.000000001) and
+ (FData.EndDateTime > FData.StartDateTime) then
+ begin
+
+ // How much time goes by in this chart? ( 1.0 = one day)
+ ElapsedTime := FData.EndDateTime - FData.StartDateTime;
+
+ // How far apart the bars are spaced is determined by
+ // XAxisDateTimeDivision.
+ // if we plot one day of values, and we want a marker every
+ // hour, we want XAxisDateTimeDivision=(1.0/24).
+
+ // Given the elapsed time in this chart, how many divisions
+ // should we be showing?
+ Options.FXAxisDateTimeLines := Round(ElapsedTime / Options.XAxisDateTimeDivision);
+ if (Options.FXAxisDateTimeLines < 0) or (Options.FXAxisDateTimeLines > 10000) then // sanity check!
+ Exit;
+
+ // this value is to help us figure out how much time goes by
+ // for each time we go from one X value to the next one.
+ TimePerXValue := ElapsedTime / Options.XValueCount;
+
+ // figure out how many divisions to move over for firstMarker
+ // given TimePerXValue (1.0=one day) and StartDateTime and
+ // XAxisDateTimeDivision.
+ Options.FXAxisDateTimeFirstMarker := 0;
+ // If XAxisDateTimeDivion=1.0, and TimePerXValue=0.25, then
+ // we want a division marker for every 4th value
+ Options.FXaxisDateTimeSkipBy := Round(Options.XAxisDateTimeDivision / TimePerXValue);
+
+ for I := 0 to Options.FXAxisDateTimeLines - 1 do
+ begin
+ X := Round(Options.XStartOffset + (Options.XPixelGap * I * Options.FXaxisDateTimeSkipBy)) +
+ Options.FXAxisDateTimeFirstMarker;
+ if X > Options.XEnd then
+ Break;
+ // don't draw dotted line right at X Axis.
+ if X <> Options.XStartOffset then
+ begin
+ LCanvas.Pen.Color := Options.GetPenColor(jvChartDivisionLineColorIndex);
+ MyDrawDotLine(LCanvas, X, Options.YStartOffset + 1, X, FXAxisPosition - 1);
+ end;
+ end;
+
+ // Note: datetime labels aren't drawn yet, they are drawn later,
+ // see local procedure XAxisDateTimeModeLabels2 inside
+ // GraphXAxisLegend, for the printing of the datetime labels!
+
+ Exit; // done!
+ end; // END OF NEW CODE IN 2007 FOR THIS METHOD. -WP-
+
+ Lines := (((Options.XValueCount + (Options.XAxisValuesPerDivision div 2)) div Options.XAxisValuesPerDivision)) - 1;
+
+ for I := 1 to Lines do
+ begin
+ X := Round(Options.XStartOffset + Options.XPixelGap * I * Options.XAxisValuesPerDivision);
+ LCanvas.Pen.Color := Options.GetPenColor(jvChartDivisionLineColorIndex);
+ MyDrawDotLine(LCanvas, X, Options.YStartOffset + 1, X, FXAxisPosition - 1);
+ end;
+end;
+
+procedure TJvChart.GraphYAxisDivisionMarkers;
+var
+ I, Y: Integer;
+ LCanvas: TCanvas;
+begin
+ Assert(Assigned(Self));
+ Assert(Assigned(Options));
+ Assert(Assigned(Options.PrimaryYAxis));
+ Assert(Options.PrimaryYAxis.YPixelGap > 0);
+ LCanvas := GetChartCanvas(false);
+
+ LCanvas.Font := Options.AxisFont;
+
+ for I := 0 to Options.PrimaryYAxis.YDivisions do
+ begin
+ Y := Round(YOrigin - (Options.PrimaryYAxis.YPixelGap * ((I) - Options.YOrigin)));
+
+ if I < Options.PrimaryYAxis.YLegends.Count then
+ MyRightTextOut(LCanvas, Round(XOrigin - 3), Y, Options.PrimaryYAxis.YLegends[I]);
+
+ Y := Round(YOrigin - (Options.PrimaryYAxis.YPixelGap * ((I) - Options.YOrigin)));
+ if (I > 0) and (I < (Options.PrimaryYAxis.YDivisions)) and Options.YAxisDivisionMarkers then
+ begin
+ LCanvas.Pen.Color := Options.GetPenColor(jvChartDivisionLineColorIndex);
+ MyDrawDotLine(LCanvas, Options.XStartOffset, Y,
+ Round(Options.XStartOffset + Options.XPixelGap * Options.XValueCount) - 1, Y);
+ end;
+ if I > 0 then
+ if Options.PrimaryYAxis.YPixelGap > 20 then
+ begin // more than 20 pixels per major division?
+ LCanvas.Pen.Color := Options.GetPenColor(jvChartAxisColorIndex);
+
+ Y := Round(Y + (Options.PrimaryYAxis.YPixelGap / 2));
+ MyDrawAxisMark(LCanvas, Options.XStartOffset, Y,
+ Options.XStartOffset - 4, // Tick at halfway between major marks.
+ Y);
+ end;
+ end;
+end;
+
+procedure TJvChart.PlotMarker(ACanvas: TCanvas; MarkerKind: TJvChartPenMarkerKind; X, Y: Integer);
+begin
+ // Note: each drawing function below uses chart
+ // Options.MarkerSize property to determine the
+ // size of the markers! Future Idea: More flexible marker sizing
+ // might be useful especially in the case of floating markers.
+ case MarkerKind of
+ pmkDiamond:
+ PlotFilledDiamond(ACanvas, X, Y);
+ pmkCircle:
+ begin
+ ACanvas.Brush.Style := bsClear;
+ PlotCircle(ACanvas, X, Y);
+ ACanvas.Brush.Style := bsSolid;
+ end;
+ pmkSquare:
+ begin
+ ACanvas.Brush.Style := bsClear;
+ PlotSquare(ACanvas, X, Y);
+ ACanvas.Brush.Style := bsSolid;
+ end;
+ pmkCross:
+ PlotCross(ACanvas, X, Y);
+ end;
+end;
+
+
+{ PlotPicture:
+ New helper method helps us to print a prettier JvChart or save to disk,
+ with higher resolution and larger fonts than the ones we can show on
+ the on-screen form.
+ }
+procedure TJvChart.PlotPicture(picture:TPicture; fontScaling:Double);
+var
+// oldfontsize:Integer;
+ oldhdrfontsize:Integer;
+ oldlgdfontsize:Integer;
+ oldaxisfontsize:Integer;
+ oldystartoffset:Integer;
+ oldxstartoffset:Integer;
+begin
+ if picture.Graphic=nil then begin
+ raise Exception.Create('JvChart.PlotToPicture: You must initialize picture.Graphic.Bitmap first');
+ end;
+
+ if not (picture.Graphic is TBitmap) then begin
+ raise Exception.Create('JvChart.PlotToPicture: picture.Graphic.Bitmap must be type TBitmap.');
+ end;
+
+
+ //oldfontsize := Self.Font.Size;
+ //Self.Font.Size := Self.Font.Size * fontScaling;
+
+ oldystartoffset := Options.YStartOffset;
+ Options.YStartOffset := Round(Options.YStartOffset * fontScaling);
+
+ oldxstartoffset := Options.XStartOffset;
+ Options.XStartOffset := Round(Options.XStartOffset * fontScaling);
+
+
+ oldhdrfontsize := Options.HeaderFont.Size;
+ Options.HeaderFont.Size := Round(Options.HeaderFont.Size * fontScaling);
+
+ oldlgdfontsize := Options.LegendFont.Size;
+ Options.LegendFont.Size := Round(Options.LegendFont.Size * fontScaling);
+
+ oldaxisfontsize := Options.AxisFont.Size;
+ Options.AxisFont.Size := Round(Options.AxisFont.Size * fontScaling);
+
+ FExtPicture := picture;
+ try
+ ResizeChartCanvas; // Recovery. This shouldn't happen.
+ PlotGraph;
+ DrawFloatingMarkers;
+
+ finally
+ FExtPicture := nil;
+
+ Options.YStartOffset := oldystartoffset;
+ Options.XStartOffset := oldxstartoffset;
+ Options.HeaderFont.Size := oldhdrfontsize;
+ Options.LegendFont.Size := oldlgdfontsize;
+ Options.AxisFont.Size := oldaxisfontsize;
+
+ end;
+ ResizeChartCanvas; // reset everything.
+ Invalidate; // repaint.
+
+end;
+
+{**************************************************************************}
+{ call this function : }
+{ a) you want to show the graph stored in memory }
+{ b) you have changed single graph value (call AutoFormatGraph if all new)}
+{ c) you have changed the settings of the graph and if you do not use }
+{ FAutoUpdateGraph option }
+{**************************************************************************}
+
+procedure TJvChart.PlotGraph;
+begin
+ Assert(Assigned(Options));
+
+ // Sanity check on YEnd/XEnd:
+ if (Options.YEnd <= 0) or (Options.XEnd <= 0) or
+ (Options.YEnd > Height) or (Options.XEnd > Width) then
+ begin
+ FInPlotGraph := True; // recursion blocker.
+ ResizeChartCanvas; // Recovery. This shouldn't happen.
+ FInPlotGraph := False;
+ end;
+
+ InternalPlotGraph;
+ Invalidate; // Force repaint.
+end;
+
+procedure TJvChart.InternalPlotGraph;
+var
+ ACanvas: TCanvas;
+ nStackGap: Integer;
+ n100Sum: Double;
+ // nOldY: Longint;
+ YOldOrigin: Integer;
+ nMaxTextHeight: Integer;
+ // Rectangle plotting:
+ X, Y, X2, Y2: Integer;
+
+ //aWidth:Integer;
+ //aHeight:Integer;
+ { Here be lots of local functions }
+
+ { Draw symbol markers and text labels on a chart... }
+
+ procedure PlotGraphChartMarkers;
+ var
+ TW, TH, VC, I, J: Integer;
+ PenAxisOpt: TJvChartYAxisOptions;
+ V: Double;
+ MaxV, MinV: array of Double;
+ LineXPixelGap: Double;
+ LastX, LastY: Integer;
+ MinIndex, MaxIndex: array of Integer;
+ Decimals: Integer;
+ begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ ACanvas.Brush.Color := Options.PaperColor;
+ ACanvas.Pen.Style := psSolid;
+ ACanvas.Pen.Color := Options.AxisLineColor;
+ ACanvas.Brush.Style := bsSolid;
+
+ VC := Options.XValueCount;
+ LastX := Round(XOrigin);
+ LastY := 0;
+
+ if VC < 2 then
+ VC := 2;
+ LineXPixelGap := ((Options.XEnd - 2) - Options.XStartOffset) / (VC - 1);
+
+ SetLength(MaxV, Options.PenCount);
+ SetLength(MinV, Options.PenCount);
+ SetLength(MinIndex, Options.PenCount);
+ SetLength(MaxIndex, Options.PenCount);
+
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenMarkerKind[I] = pmkNone then
+ Continue;
+ PenAxisOpt := Options.PenAxis[I]; // Get whether this pen is plotted using the lefthand or righthand Y axis.
+ MaxV[I] := PenAxisOpt.YMin;
+ MinV[I] := PenAxisOpt.YMax;
+ MinIndex[I] := -1;
+ MaxIndex[I] := -1;
+
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ V := FData.Value[I, J];
+ if IsNaN(V) then
+ Continue;
+ //MaxFlag := False;
+ //MinFlag := False;
+ if V > MaxV[I] then
+ begin
+ MaxV[I] := V;
+ MaxIndex[I] := J;
+ end;
+
+ if V < MinV[I] then
+ begin
+ MinV[I] := V;
+ MinIndex[I] := J;
+ end;
+
+ // Calculate Marker position:
+ X := Round(XOrigin + J * LineXPixelGap);
+
+ //old:Y := Round(YOrigin - ((V / PenAxisOpt.YGap1) * PenAxisOpt.YPixelGap));
+ Y := Round(YOrigin - (((V - PenAxisOpt.YMin) / PenAxisOpt.YGap) * PenAxisOpt.YPixelGap));
+ SetLineColor(ACanvas, I);
+ if Y < Options.YStartOffset then
+ Y := Options.YStartOffset; // constrain Y to stay on chart.
+
+ (*
+ if MinFlag or MaxFlag then // local min/max markers!
+ ACanvas.Pen.Width := 2
+ else
+ ACanvas.Pen.Width := 1;
+ *)
+
+ // Now plot the right kind of marker:
+ PlotMarker(ACanvas, Options.PenMarkerKind[I], X, Y);
+ end;
+ end;
+
+ { Now plot labels After all the markers. Looks nicer than doing
+ it all together }
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if not Options.PenValueLabels[I] then
+ Continue;
+ PenAxisOpt := Options.PenAxis[I]; // Get whether this pen is plotted using the lefthand or righthand Y axis.
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ V := FData.Value[I, J];
+ if IsNaN(V) then
+ Continue;
+ // Calculate Marker position:
+ X := Round(XOrigin + J * LineXPixelGap);
+ Y := Round(YOrigin - ((V / PenAxisOpt.YGap1) * PenAxisOpt.YPixelGap));
+ if Y < (Options.YStartOffset + 10) then
+ Y := (Options.YStartOffset + 10); // constrain Y to stay on chart.
+
+ // Format with fixed number of decimal places (avoid screen clutter)
+ Decimals := Options.PenAxis[I].MarkerValueDecimals;
+ if Decimals < 0 then // auto
+ if V < 100.0 then
+ Decimals := 1 // handy automatic percentage mode.
+ else
+ Decimals := 0;
+ Text := FloatToStrF(V, ffFixed, 16, Decimals);
+
+ if Options.PenUnit.Count >= I then
+ Text := Text + Options.PenUnit[I];
+
+ TW := ACanvas.TextWidth(Text);
+ TH := ACanvas.TextHeight(Text);
+
+ if Options.GetPenValueLabels(I) and
+ ((X > (LastX + (TW div 2))) or // Show if it's not going to collide
+ ((Abs(Y - LastY) > (TH * 2)) and
+ (X > LastX)) or
+ ((J = MinIndex[I]) or (J = MaxIndex[I]))) then // Always show max/mins
+ begin
+ // TODO: EVENT FOR END-USER-CUSTOMIZED OR FORMATTED LABELS
+ //if Assigned(FOnGetValueLabel) then
+ // FOnGetValueLabel(Sender, {Pen}I, {Sample#}J, {Value}V, {var}Text );
+ if Length(Text) > 0 then
+ begin
+ Dec(Y, 2);
+ // nifty little bit to draw a box around min/max values.
+ if (J = MinIndex[I]) or (J = MaxIndex[I]) then
+ begin
+ ACanvas.Pen.Style := psClear; //was psDot
+ ACanvas.Brush.Color := Options.PaperColor; //was HintColor
+ MyPolygon(ACanvas, [Point(X - ((TW div 2) + 2), Y - (TH + Options.MarkerSize + 2)),
+ Point(X - ((TW div 2) + 2), Y - Options.MarkerSize),
+ Point(X + (TW div 2) + 2, Y - Options.MarkerSize),
+ Point(X + (TW div 2) + 2, Y - (TH + Options.MarkerSize + 2))]);
+ ACanvas.Pen.Style := psSolid;
+ end;
+
+ if Y >= Options.YStartOffset + 20 then
+ begin
+ ACanvas.Brush.Style := bsSolid;
+ MyCenterTextOut(ACanvas, X + 1, (Y - (Options.MarkerSize + TH)) - 1, Text);
+ ACanvas.Brush.Color := Options.PaperColor;
+ LastX := X + TW;
+ LastY := Y;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ procedure PlotGraphStackedBar;
+ var
+ I, J: Integer;
+ begin
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ YOldOrigin := 0;
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenStyle[I] <> psClear then
+ begin
+ if Options.XPixelGap < 3.0 then
+ ACanvas.Pen.Color := Options.PenColor[I]; // greek-out the borders
+ MyColorRectangle(ACanvas, I,
+ Round((XOrigin + J * Options.XPixelGap) + (Options.XPixelGap / 6)),
+ Round(YOrigin - YOldOrigin),
+ Round(XOrigin + (J + 1) * Options.XPixelGap - nStackGap),
+ Round((YOrigin - YOldOrigin) -
+ ((FData.Value[I, J] / Options.PenAxis[I].YGap) * Options.PrimaryYAxis.YPixelGap)));
+ YOldOrigin := Round(YOldOrigin +
+ ((FData.Value[I, J] / Options.PenAxis[I].YGap) * Options.PrimaryYAxis.YPixelGap));
+ end;
+ end;
+ end;
+ end;
+
+ procedure PlotGraphBar;
+ var
+ I, J, N: Integer;
+ BarCount: Double;
+ V, BarGap: Double;
+ YTempOrigin: Integer;
+
+ function BarXPosition(Index: Integer): Integer;
+ begin
+ Result := Round(XOrigin + (Index * BarGap));
+ end;
+
+ begin
+ YTempOrigin := Options.YStartOffset + Round(Options.PrimaryYAxis.YPixelGap * (Options.PrimaryYAxis.YDivisions));
+
+ BarCount := Options.PenCount * Options.XValueCount;
+ BarGap := (((Options.XEnd - 1) - Options.XStartOffset) / BarCount);
+
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenAxis[I].YGap = 0 then
+ Continue; // Can't plot this one.
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ N := (J * Options.PenCount) + I; // Which Bar Number!?
+ // Plot a rectangle for each Bar in our bar chart...
+ X := BarXPosition(N) + 1;
+ // Make a space between groups, 4 pixels per XValue Index:
+ //Dec(X,4);
+ //Inc(X, 2*J);
+ Y := YTempOrigin;
+// Assert(Y < aHeight);
+ Assert(Y > 0);
+ Assert(X > 0);
+ //if (X>=aWidth) then
+ // OutputDebugString('foo!');
+// Assert(X < aWidth);
+ X2 := BarXPosition(N + 1) - 3;
+ // Make a space between groups, 4 pixels per XValue Index:
+ //Dec(X2,4);
+ //Inc(X2, 2*J);
+ V := FData.Value[I, J];
+ if IsNaN(V) then
+ Continue;
+ Y2 := Round(YOrigin - ((V / Options.PenAxis[I].YGap) * Options.PrimaryYAxis.YPixelGap));
+ //Assert(Y2 < aHeight);
+ if Y2 < 0 then
+ Y2 := -1; //clip extreme negatives.
+ if Y2 >= Y then
+ Y2 := Y - 1;
+ Assert(Y2 < Y);
+ Assert(X2 > 0);
+ //if (X2X);
+ if Options.PenCount > 1 then
+ if X2 > X then
+ Dec(X2); // Additional 1 pixel gap
+ if Options.PenStyle[I] <> psClear then
+ begin
+ if (X2 - X) < 4 then // don't draw black line around bar if it is a very narrow bar.
+ ACanvas.Pen.Style := psClear
+ else
+ ACanvas.Pen.Style := Options.PenStyle[I];
+ MyColorRectangle(ACanvas, I, X, Y, X2, Y2);
+ end;
+ end;
+ end;
+ {add average line for the type...}
+ if Options.ChartKind = ckChartBarAverage then
+ begin
+ SetLineColor(ACanvas, jvChartAverageLineColorIndex);
+ ACanvas.MoveTo(Round(XOrigin + 1 * Options.XPixelGap),
+ Round(YOrigin - ((Options.AverageValue[1] / Options.PrimaryYAxis.YGap) * Options.PrimaryYAxis.YPixelGap)));
+ for J := 0 to Options.XValueCount do
+ MyPenLineTo(ACanvas, Round(XOrigin + J * Options.XPixelGap),
+ Round(YOrigin - ((Options.AverageValue[J] / Options.PrimaryYAxis.YGap) * Options.PrimaryYAxis.YPixelGap)));
+ SetLineColor(ACanvas, jvChartAxisColorIndex);
+ end;
+ // NEW: Add markers to bar chart:
+ PlotGraphChartMarkers;
+ end;
+
+ // Keep Y in visible chart range:
+
+ function GraphConstrainedLineY(Pen, Sample: Integer): Double;
+ var
+ V: Double;
+ PenAxisOpt: TJvChartYAxisOptions;
+ begin
+ V := FData.Value[Pen, Sample];
+ PenAxisOpt := Options.PenAxis[Pen];
+ if IsNaN(V) then
+ begin
+ Result := NaN; // blank placeholder value in chart!
+ Exit;
+ end;
+ if PenAxisOpt.YGap < 0.0000001 then
+ begin
+ Result := 0.0; // can't chart! YGap is near zero, zero, or negative.
+ Exit;
+ end;
+ Result := YOrigin - (((V - PenAxisOpt.YMin) / PenAxisOpt.YGap) * PenAxisOpt.YPixelGap);
+ if Result >= YOrigin - 1 then
+ Result := Round(YOrigin) - 1 // hit the top of the chart
+ else
+ if Result < Options.YStartOffset - 2 then
+ Result := Options.YStartOffset - 2; // Not quite good enough, but better than before.
+ end;
+
+ function GetUnderLineFillColor(const A, B: TColor) : TColor;
+ const
+ Lerp = 0.85; // 0-1, where 0 is fully A and 1 is fully B. This value seems good
+ var
+ AR, AG, AB, BR, BG, BB: Byte;
+ begin
+ AR := GetRValue(A);
+ AG := GetGValue(A);
+ AB := GetBValue(A);
+ BR := GetRValue(B);
+ BG := GetGValue(B);
+ BB := GetBValue(B);
+
+ Result := RGB(
+ Round(AR + Lerp * (BR - AR)),
+ Round(AG + Lerp * (BG - AG)),
+ Round(AB + Lerp * (BB - AB))
+ );
+ end;
+
+ procedure PlotGraphChartLine;
+ var
+ I, I2, J, Y1: Integer;
+ V, LineXPixelGap: Double;
+ NanFlag: Boolean;
+ VC: Integer;
+ // PenAxisOpt: TJvChartYAxisOptions;
+ begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ VC := Options.XValueCount;
+ if VC < 2 then
+ VC := 2;
+ LineXPixelGap := ((Options.XEnd - 2) - Options.XStartOffset) / (VC - 1);
+
+ ACanvas.Pen.Style := psSolid;
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ // PenAxisOpt := Options.PenAxis[I];
+ // No line types?
+ if Options.PenStyle[I] = psClear then
+ Continue;
+ SetLineColor(ACanvas, I);
+ J := 0;
+ V := GraphConstrainedLineY(I, J);
+ NanFlag := IsNaN(V);
+ if not NanFlag then
+ begin
+ Y := Round(V);
+ ACanvas.MoveTo(Round(XOrigin), Y);
+ end;
+
+ for J := 1 to Options.XValueCount - 1 do
+ begin
+ V := GraphConstrainedLineY(I, J);
+ if IsNaN(V) then
+ begin
+ NanFlag := True; // skip.
+ ACanvas.MoveTo(Round(XOrigin + J * LineXPixelGap), 200); //DEBUG!
+ end
+ else
+ begin
+ if NanFlag then
+ begin // resume, valid value.
+ NanFlag := False;
+ Y := Round(V);
+ // pick up the pen and slide forward
+ ACanvas.MoveTo(Round(XOrigin + J * LineXPixelGap), Y);
+ end
+ else
+ begin
+ Y := Round(V);
+ ACanvas.Pen.Style := Options.PenStyle[I];
+ if I > 0 then
+ begin
+ for I2 := 0 to I - 1 do
+ begin
+ V := GraphConstrainedLineY(I2, J);
+ if IsNaN(V) then
+ Continue;
+ Y1 := Round(V);
+ if Y1 = Y then
+ begin
+ Dec(Y); // Prevent line-overlap. Show dotted line above other line.
+ if ACanvas.Pen.Style = psSolid then
+ ACanvas.Pen.Style := psDot;
+ end;
+ end;
+ end;
+ MyPenLineTo(ACanvas, Round(XOrigin + J * LineXPixelGap), Y);
+ //OldV := V; // keep track of last valid value, for handling gaps.
+ end;
+ end;
+ end;
+ end;
+ PlotGraphChartMarkers;
+ // MARKERS:
+ SetLineColor(ACanvas, jvChartAxisColorIndex);
+ end;
+
+ procedure PlotGraphChartLineFill;
+ var
+ I, I2, J: Integer;
+ V, LineXPixelGap: Double;
+ NanFlag: Boolean;
+ VC: Integer;
+ FillPoints : array of TPoint;
+ FillPolyIndex : Integer;
+ begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ SetLength(FillPoints, 0); // Not set to 0 by compiler
+ try
+ VC := Options.XValueCount;
+ if VC < 2 then
+ VC := 2;
+ LineXPixelGap := ((Options.XEnd - 2) - Options.XStartOffset) / (VC - 1);
+
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ // No line types?
+ if Options.PenStyle[I] = psClear then
+ Continue;
+
+ SetLength(FillPoints, FData.ValueCount + 2 {start and end points});
+ FillPolyIndex := 0;
+
+ J := 0;
+ V := GraphConstrainedLineY(I, J);
+ NanFlag := IsNaN(V);
+ if not NanFlag then
+ begin
+ Y := Round(V);
+ FillPoints[FillPolyIndex] := Point(Round(XOrigin)+1, Round(YOrigin)); // start at origin
+ Inc(FillPolyIndex);
+ FillPoints[FillPolyIndex] := Point(Round(XOrigin)+1, Y); // add first point
+ Inc(FillPolyIndex);
+ end;
+
+ for J := 1 to Options.XValueCount - 1 do
+ begin
+ V := GraphConstrainedLineY(I, J);
+ if IsNaN(V) then
+ begin
+ NanFlag := True; // skip.
+ FillPoints[FillPolyIndex] := Point(Round(XOrigin + J * LineXPixelGap), Round(YOrigin)); // !!! DEBUG
+ Inc(FillPolyIndex);
+ end
+ else
+ begin
+ if NanFlag then
+ begin // resume, valid value.
+ NanFlag := False;
+ Y := Round(V);
+ // pick up the pen and slide forward
+ FillPoints[FillPolyIndex] := Point(Round(XOrigin + J * LineXPixelGap), Y);
+ Inc(FillPolyIndex);
+ end
+ else
+ begin
+ Y := Round(V);
+ if I > 0 then
+ begin
+ for I2 := 0 to I - 1 do
+ begin
+ V := GraphConstrainedLineY(I2, J);
+ if IsNaN(V) then
+ Continue;
+ end;
+ end;
+
+ FillPoints[FillPolyIndex] := Point(Round(XOrigin + J * LineXPixelGap), Y); // add next point
+ Inc(FillPolyIndex);
+ end;
+ end;
+ end;
+
+ // Add a final point which is the same as the last, but at the bottom
+ if FillPolyIndex > 0 then begin
+ FillPoints[FillPolyIndex] := Point(FillPoints[FillPolyIndex-1].X, Round(YOrigin));
+ Inc(FillPolyIndex);
+ SetLength(FillPoints, FillPolyIndex); // Trim to the actual usage
+
+ // First draw the fill
+ ACanvas.Pen.Style := psClear;
+ ACanvas.Brush.Color := GetUnderLineFillColor(Options.PenColor[I],
+ Options.PenColor[jvChartPaperColorIndex]);
+ ACanvas.Brush.Style := bsSolid;
+ ACanvas.Polygon(FillPoints);
+ end;
+ end;
+ finally
+ SetLength(FillPoints, 0);
+ end;
+ end;
+
+ procedure PlotGraphStackedBarAverage;
+ var
+ I, J: Integer;
+ begin
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ n100Sum := 0;
+ for I := 0 to Options.PenCount - 1 do
+ n100Sum := n100Sum + FData.Value[I, J];
+
+ for I := 0 to Options.PenCount - 1 do
+ if n100Sum <> 0 then
+ AverageData.Value[I, J] := (FData.Value[I, J] / n100Sum) * 100
+ else
+ AverageData.Value[I, J] := 0;
+ end;
+
+ for J := 0 to Options.XValueCount - 1 do
+ begin
+ YOldOrigin := 0;
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if I = Options.PenCount then {last one; draw it always to the top line}
+ MyColorRectangle(ACanvas, I,
+ Round(XOrigin + J * Options.XPixelGap + (Options.XPixelGap / 2)),
+ Round(YOrigin - YOldOrigin),
+ Round(XOrigin + (J + 1) * Options.XPixelGap + (Options.XPixelGap / 2) - nStackGap),
+ Options.YStartOffset)
+ else
+ begin
+ MyColorRectangle(ACanvas, I,
+ Round(XOrigin + J * Options.XPixelGap + (Options.XPixelGap / 2)),
+ Round(YOrigin - YOldOrigin),
+ Round(XOrigin + (J + 1) * Options.XPixelGap + (Options.XPixelGap / 2) - nStackGap),
+ Round((YOrigin - YOldOrigin) -
+ ((AverageData.Value[I, J] / Options.PenAxis[I].YGap) * Options.PrimaryYAxis.YPixelGap)));
+ YOldOrigin := YOldOrigin + Round((AverageData.Value[I, J] / Options.PenAxis[I].YGap) *
+ Options.PrimaryYAxis.YPixelGap);
+ end;
+ end;
+ end;
+ end;
+
+ procedure CheckYAxisFlags;
+ var
+ I: Integer;
+ begin
+ Options.PrimaryYAxis.FActive := True;
+ Options.SecondaryYAxis.FActive := False;
+ for I := 0 to Options.PenCount - 1 do
+ if Options.PenSecondaryAxisFlag[I] then
+ begin
+ Options.SecondaryYAxis.FActive := True;
+ Break;
+ end;
+ end;
+
+begin { Enough local functions for ya? -WP }
+ ACanvas := GetChartCanvas(false);
+ //aWidth := GetChartCanvasWidth;
+ //aHeight:= GetChartCanvasHeight;
+
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(ACanvas.Pen));
+
+ FPlotGraphCalled := True;
+
+ // refuse to refresh under these conditions:
+ if not (Enabled and Visible) then
+ Exit;
+ if csDesigning in ComponentState then
+ begin
+ if not Assigned(Self.Parent) then
+ Exit;
+ if Self.Name = '' then
+ Exit;
+ end;
+
+ // safety before we paint.
+ Assert(Assigned(Self));
+ Assert(Assigned(Data));
+ Assert(Assigned(Options));
+ Assert(Assigned(Options.PrimaryYAxis));
+ Assert(Assigned(Options.SecondaryYAxis));
+ Assert(Assigned(AverageData));
+
+ // NEW: Primary Y axis is always shown, but secondary is only shown
+ // if a pen is set up to plot on the secondary Y Axis scale.
+ CheckYAxisFlags;
+
+ ClearScreen;
+
+ if Options.XValueCount > MAX_VALUES then
+ Options.XValueCount := MAX_VALUES;
+ if Options.PenCount > MAX_PEN then
+ Options.PenCount := MAX_PEN;
+ (*
+ if Options.PrimaryYAxis.YGap = 0 then
+ Options.PrimaryYAxis.YGap := 1;
+ if Options.SecondaryYAxis.YGap = 0 then
+ Options.SecondaryYAxis.YGap := 1;
+ *)
+
+ PrimaryYAxisLabels; // Make sure there are Y Axis labels!
+
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ { Resize Header area according to HeaderFont size }
+ if (not PrintInSession) and (Length(Options.XAxisHeader) > 0) then
+ begin
+ MyHeaderFont(ACanvas);
+ // nOldY := Options.YStartOffset;
+ nMaxTextHeight := CanvasMaxTextHeight(ACanvas) + 8;
+ // Bump bottom margins if the fonts don't fit!
+ if Options.YStartOffset < 2 * nMaxTextHeight then
+ begin
+ Options.YStartOffset := nMaxTextHeight * 2;
+ //Options.YEnd := Options.YEnd + (nOldY - Options.YStartOffset);
+ CalcYEnd;
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+ end;
+ end;
+
+ if (Options.ChartKind = ckChartStackedBar) or
+ (Options.ChartKind = ckChartStackedBarAverage) then
+ begin
+ FOldYOrigin := Options.YOrigin;
+ Options.YOrigin := 0;
+ end
+ else
+ FOldYOrigin := Options.YOrigin;
+ if Options.ChartKind = ckChartStackedBarAverage then
+ FOldYGap := Options.PrimaryYAxis.YGap
+ else
+ FOldYGap := Options.PrimaryYAxis.YGap;
+
+ { This effects only graph type: JvChartStackedBar(_100) }
+ nStackGap := 1;
+ if Options.XEnd > 200 then
+ nStackGap := 3;
+
+ MyAxisFont(ACanvas);
+
+ YOldOrigin := Trunc(YOrigin);
+
+ {Draw header and other stuff...}
+ GraphSetup;
+
+ // If FData.ValueCount is zero we can preview the visual appearance of the chart,
+ // but there is no plotting of pens that can occur, so stop now...
+ if FData.ValueCount = 0 then // EMPTY!
+ begin
+ { draw a blank chart. Component shouldn't just be a white square, it makes
+ users think we're broken, when we're not, they just haven't given us any
+ data.}
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+ GraphSetup;
+
+ GraphXAxis;
+ GraphXAxisDivisionMarkers;
+ GraphYAxis;
+ GraphYAxisDivisionMarkers;
+
+ ACanvas.Font.Color := clRed;
+ if Length(Options.NoDataMessage) = 0 then
+ MyLeftTextOut(ACanvas, Round(XOrigin), Round(YOrigin) + 4, RsNoData)
+ else
+ MyLeftTextOut(ACanvas, Round(XOrigin), Round(YOrigin) + 4, Options.NoDataMessage); // NEW! NOV 2004. WP.
+
+ Exit;
+ end;
+
+ DrawGradient; // NEW 2007
+ DisplayBars; // NEW 2007
+
+ {Y Axis}
+ GraphYAxis;
+ GraphYAxisDivisionMarkers; // dotted lines making graph-paper across graph
+
+ {X Axis}
+ GraphXAxis;
+ GraphXAxisDivisionMarkers; // new.
+
+ {X-axis legends...}
+ GraphXAxisLegend;
+
+ {Main Header}
+ if Options.Title <> '' then
+ MyHeader(ACanvas, Options.Title);
+
+ {X axis header}
+ if Options.XAxisHeader <> '' then
+ MyXHeader(ACanvas, Options.XAxisHeader);
+
+ {Create the actual graph...}
+ case Options.ChartKind of
+ ckChartBar, ckChartBarAverage:
+ PlotGraphBar;
+ ckChartStackedBar:
+ PlotGraphStackedBar;
+ ckChartLine: //, ckChartLineWithMarkers:
+ begin
+ if Options.FillUnderLine then
+ PlotGraphChartLineFill; // Do this before drawing lines, so lines are always visible
+ PlotGraphChartLine;
+ end;
+ ckChartMarkers:
+ PlotGraphChartMarkers;
+ ckChartStackedBarAverage:
+ PlotGraphStackedBarAverage;
+ ckChartPieChart:
+ GraphPieChart(1); { special types}
+ ckChartDeltaAverage:
+ GraphDeltaAverage; { special types}
+ end;
+ {Y axis header}
+ MyYHeader(ACanvas, Options.YAxisHeader); // vertical text out on Y axis
+end;
+
+
+
+procedure TJvChart.DrawChartLegendBelow(ACanvas: TCanvas); {accidentally deleted during Jedi_new to Jedi_2009 branch. Restored by WP June 2009}
+var
+ I,Y,nTextHeight:Integer;
+ BoxWidth:Integer;
+ LLabel:String;
+begin
+
+ if (Options.Legend <> clChartLegendBelow) then exit;
+
+ if (Options.YStartOffset<=0) or (Options.XStartOffset<=0) then exit;
+
+ // space-saving pen-legend below chart
+ MySmallGraphFont(ACanvas);
+
+ {10 % extra space for line height}
+ nTextHeight := Round(CanvasMaxTextHeight(ACanvas) * 1.01);
+
+ //BoxHeight := nTextHeight - 2;
+
+ Options.FXLegendHoriz := Options.XStartOffset;
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenStyle[I] = psClear then
+ if Options.GetPenMarkerKind(I) = pmkNone then
+ Continue; // Skip invisible pens.
+
+ Y := Options.YStartOffset + Options.YEnd + (nTextHeight div 2);
+
+ // If chart has X legends:
+ if (Options.XLegends.Count > 0) or Options.XAxisDateTimeMode then
+ Y := Y + nTextHeight;
+
+ if Options.PenStyle[I] = psClear then
+ begin
+ // For markers, draw marker:
+ ACanvas.Pen.Color := Options.GetPenColor(I);
+ GraphXAxisLegendMarker(ACanvas, Options.GetPenMarkerKind(I),
+ Options.FXLegendHoriz, (Y + 8) - (Options.MarkerSize div 2));
+ BoxWidth := Options.MarkerSize + 2;
+ end
+ else
+ begin
+ // For lines, draw a pen color box:
+ BoxWidth := ACanvas.TextWidth('X') * 2 - 2;
+ DrawPenColorBox(ACanvas, I, {pen#}
+ BoxWidth - 2, {width}
+ nTextHeight - 2, {height}
+ Options.FXLegendHoriz, {X=}
+ Y + 4); {Y=}
+ end;
+
+ //SetFontColor(ACanvas, jvChartAxisColorIndex); XXX
+ ACanvas.Font.Color := Options.AxisFont.Color;
+
+ // Draw the Pen Legend (WAP :add unit to legend. )
+ if Options.PenLegends.Count > I then
+ LLabel := Options.PenLegends[I]
+ else
+ LLabel := IntToStr(I + 1);
+
+ // Put units in pen legends
+ //
+ // if ( Options.PenUnit.Count > I )
+ // and ( Length( Options.PenUnit[I] ) > 0 ) then
+ // myLabel := myLabel + ' ('+Options.PenUnit[I]+')';
+
+
+ MyLeftTextOut(ACanvas, Options.FXLegendHoriz + BoxWidth + 3, Y, LLabel);
+ Inc(Options.FXLegendHoriz, BoxWidth + ACanvas.TextWidth( LLabel) + 14);
+ //Inc(VisiblePenCount);
+ //end;
+ end;
+end;{procedure}
+
+
+
+
+procedure TJvChart.GraphXAxisLegendMarker(ACanvas: TCanvas; MarkerKind: TJvChartPenMarkerKind; X, Y: Integer);
+begin
+ case MarkerKind of
+ pmkDiamond:
+ PlotFilledDiamond(ACanvas, X, Y);
+ pmkCircle:
+ PlotCircle(ACanvas, X, Y);
+ pmkSquare:
+ PlotSquare(ACanvas, X, Y);
+ pmkCross:
+ PlotCross(ACanvas, X, Y);
+ end;
+end;
+
+procedure TJvChart.GraphXAxisLegend; // reworked in 2007.
+var
+ I: Integer;
+ Timestamp: TDateTime;
+ TimestampStr: string;
+ XOverlap: Integer;
+ ACanvas: TCanvas;
+
+ { draw x axis text at various alignments:}
+ function LeftXAxisText: Boolean;
+ begin
+ Result := True;
+ // Don't exceed right margin - causes some undesirable clipping. removed. -wpostma.
+ {if I < Options.XLegends.Count then
+ if ACanvas.TextWidth(Options.XLegends[I]) + Options.FXLegendHoriz > (Width XEnd+10) then begin
+ Result := False;
+ Exit;
+ end;}
+
+ // Label X axis above or below?
+ if FContainsNegative then
+ begin
+ if I < Options.XLegends.Count then
+ begin // fix exception. June 23, 2004- WPostma.
+ if Options.FXLegendHoriz < XOverlap then
+ Exit; // would overlap, don't draw it.
+ MyLeftTextOut(ACanvas, Options.FXLegendHoriz, Options.YEnd + 3, Options.XLegends[I]);
+ XOverlap := Options.FXLegendHoriz + ACanvas.TextWidth(Options.XLegends[I]);
+ end;
+ end
+ else
+ if I < Options.XLegends.Count then
+ begin
+ if Options.FXLegendHoriz < XOverlap then
+ Exit; // would overlap, don't draw it.
+ MyLeftTextOut(ACanvas, Options.FXLegendHoriz,
+ {bottom:}FXAxisPosition + Options.AxisLineWidth {top: Round(YTempOrigin - Options.PrimaryYAxis.YPixelGap)},
+ Options.XLegends[I]);
+ XOverlap := Options.FXLegendHoriz + ACanvas.TextWidth(Options.XLegends[I]);
+ end
+ else
+ Result := False;
+ end;
+
+ function RightXAxisText: Boolean;
+ begin
+ Result := True;
+ // Label X axis above or below?
+ if FContainsNegative then
+ begin
+ if I < Options.XLegends.Count then // fix exception. June 23, 2004- WPostma.
+ MyRightTextOut(ACanvas, Options.FXLegendHoriz, Options.YEnd + 3, Options.XLegends[I])
+ end
+ else
+ if I < Options.XLegends.Count then
+ MyRightTextOut(ACanvas, Options.FXLegendHoriz,
+ {bottom:}FXAxisPosition + Options.AxisLineWidth {top: Round(YTempOrigin - Options.PrimaryYAxis.YPixelGap)},
+ Options.XLegends[I])
+ else
+ Result := False;
+ end;
+
+ function CenterXAxisText: Boolean;
+ begin
+ Result := True;
+ // Label X axis above or below?
+ if FContainsNegative then
+ begin
+ if I < Options.XLegends.Count then // fix exception. June 23, 2004- WPostma.
+ MyCenterTextOut(ACanvas, Options.FXLegendHoriz, Options.YEnd + 3, Options.XLegends[I])
+ end
+ else
+ if I < Options.XLegends.Count then
+ MyCenterTextOut(ACanvas, Options.FXLegendHoriz,
+ {bottom:}FXAxisPosition + Options.AxisLineWidth {top: Round(YTempOrigin - Options.PrimaryYAxis.YPixelGap)},
+ Options.XLegends[I])
+ else
+ Result := False;
+ end;
+
+ procedure XAxisDateTimeModeLabels1; // Classic mode [REFACTORED 2007]
+ var
+ L: Integer;
+ begin
+ // classic JvChart XAxisDateTime mode labels painting code.
+
+ // if not Options.XAxisDivisionMarkers then Exit;
+ if Options.XAxisValuesPerDivision <= 0 then
+ Exit;
+ if Options.XStartOffset <= 0 then
+ Exit;
+
+ for L := 1 to Options.XValueCount div Options.XAxisValuesPerDivision - 1 do
+ begin
+ Options.FXLegendHoriz := Round(Options.XStartOffset + Options.XPixelGap * L * Options.XAxisValuesPerDivision);
+
+ Timestamp := FData.Timestamp[L * Options.XAxisValuesPerDivision - 1];
+ if Timestamp < 0.0000001 then
+ Continue;
+
+ if Length(Options.FXAxisDateTimeFormat) = 0 then // not specified, means use Locale defaults
+ TimestampStr := TimeToStr(Timestamp)
+ else
+ TimestampStr := FormatDateTime(Options.FXAxisDateTimeFormat, Timestamp);
+
+ // Check if writing this label would collide with previous label, if not, plot it
+ if (Options.FXLegendHoriz - (ACanvas.TextWidth(TimestampStr) div 2)) > XOverlap then
+ begin
+ MyCenterTextOut(ACanvas, Options.FXLegendHoriz,
+ {bottom:}FXAxisPosition + Options.AxisLineWidth
+ {top: Round(YTempOrigin - Options.PrimaryYAxis.YPixelGap)},
+ TimestampStr);
+
+ // draw a ticky-boo (technical term used by scientists the world over)
+ // so that we can see where on the chart the X axis datetime is pointing to.
+ ACanvas.Pen.Width := 1;
+ ACanvas.MoveTo(Options.FXLegendHoriz, FXAxisPosition);
+ ACanvas.LineTo(Options.FXLegendHoriz, FXAxisPosition + Options.AxisLineWidth + 2);
+
+ XOverlap := Options.FXLegendHoriz + ACanvas.TextWidth(TimestampStr);
+ end;
+ end;
+
+ end;
+
+ //XAxisDateTimeModeLabels2: [NEW 2007]
+ // make text labels line up with new division line drawing code
+ // in GraphXAxisDivisionMarkers:
+
+ procedure XAxisDateTimeModeLabels2; // [NEW 2007]
+ var
+ L: Integer;
+ X: Integer;
+ DivPixels: Integer;
+ TextWidth: Integer;
+ Modn: Integer;
+ begin
+ Assert(Options.FXAxisDateTimeSkipBy > 0);
+
+ DivPixels := Round(Options.XPixelGap * Options.FXaxisDateTimeSkipBy);
+
+ for L := 0 to FOptions.FXAxisDateTimeLines - 1 do
+ begin
+ Timestamp := FData.Timestamp[(L * Options.FXaxisDateTimeSkipBy)] + Options.FXAxisDateTimeFirstMarker;
+
+ if Timestamp < 0.0000001 then
+ Continue;
+
+ if Length(Options.FXAxisDateTimeFormat) = 0 then // not specified, means use Locale defaults
+ TimestampStr := TimeToStr(Timestamp)
+ else
+ TimestampStr := FormatDateTime(Options.FXAxisDateTimeFormat, Timestamp);
+
+ TextWidth := ACanvas.TextWidth(TimeStampStr);
+ if DivPixels > 0 then
+ begin
+ Modn := Trunc(TextWidth / DivPixels) + 1;
+ if Modn > 1 then
+ begin
+ if (L mod Modn) <> 0 then
+ Continue; // skip labels if they are too densely spaced.
+ end;
+ end;
+
+ X := Round(Options.XStartOffset + (Options.XPixelGap * L * Options.FXaxisDateTimeSkipBy)) +
+ Options.FXAxisDateTimeFirstMarker;
+ if X > Options.XEnd then
+ Break;
+ if X = Options.XStartOffset then
+ Continue; // don't draw dotted line right at X Axis.
+
+ MyCenterTextOut(ACanvas, X,
+ {bottom:}FXAxisPosition + Options.AxisLineWidth,
+ TimestampStr);
+
+ ACanvas.Pen.Color := Options.GetPenColor(jvChartDivisionLineColorIndex);
+ MyDrawDotLine(ACanvas, X, Options.YStartOffset + 1, X, FXAxisPosition - 1);
+ end;
+ end;
+
+ procedure DefaultXAxisLegendMode;
+ var
+ count:Integer;
+ K:Integer;
+ begin
+ {default X axis legend mode: use text legends}
+ if Options.FXAxisLegendSkipBy < 1 then
+ Options.FXAxisLegendSkipBy := 1;
+
+ Count := (Options.XValueCount + (Options.FXAxisLegendSkipBy - 1)) div Options.FXAxisLegendSkipBy;
+ // Skip the first (Index 0) Axis Label, for visual reasons.
+ for K := 0 to Count - 1 do
+ begin
+ I := K * Options.FXAxisLegendSkipBy;
+ //Options.FXLegendHoriz := Round(Options.XStartOffset + (Options.XPixelGap * I));
+ Options.FXLegendHoriz := Round(Options.XStartOffset + Options.XPixelGap * I );
+
+ case Options.FXAxisLabelAlignment of
+ taLeftJustify:
+ if not leftXAxisText then break;
+ taRightJustify:
+ if not rightXAxisText then break;
+ taCenter:
+ if not centerXAxisText then break;
+ end;
+ end; {for K}
+ end; {default mode}
+
+begin
+ {X-LEGEND: ...}
+ if (Options.XStartOffset = 0) and (Options.YStartOffset = 0) then
+ Exit;
+ ACanvas := GetChartCanvas(false);
+
+ XOverlap := 0; // XAxis Label Overlap protection checking variable.
+
+ {Count how many characters to show in the separate legend}
+
+ SetLineColor(ACanvas, jvChartAxisColorIndex);
+ MyAxisFont(ACanvas);
+
+ { datetime mode for X axis legends : follow the time division markers }
+ if Options.XAxisDateTimeMode then
+ begin { if DateTime mode then legends are painted where the division markers are painted }
+ if (Data.EndDateTime > Data.StartDateTime) and (Options.XAxisDateTimeDivision > 0.00001) then
+ XAxisDateTimeModeLabels2 // new mode! align division markers to even hour/day/etc boundaries!
+ else
+ XAxisDateTimeModeLabels1; // classic mode! let the labels displayed be any old time.
+ end else
+ if Options.XValueCount > 0 then // is there data to plot?
+ begin
+ DefaultXAxisLegendMode;
+ end;
+ DrawChartLegendBelow(ACanvas);
+
+
+end;
+
+procedure TJvChart.DrawPenColorBox(ACanvas: TCanvas; NColor, W, H, X, Y: Integer);
+begin
+ MyColorRectangle(ACanvas, NColor, X, Y, X + W, Y + H);
+ SetRectangleColor(ACanvas, jvChartPaperColorIndex);
+end;
+
+{**************************************************************************}
+{ call this function : }
+{ a) when you want to print the graph to Windows default printer }
+{**************************************************************************}
+
+procedure TJvChart.PrintGraph;
+var
+ nXEnd, nYEnd: Longint;
+ nXStart, nYStart: Longint;
+ nLegendWidth: Longint;
+begin
+ {Save display values...}
+ nXEnd := Options.XEnd;
+ nYEnd := Options.YEnd;
+ nXStart := Options.XStartOffset;
+ nYStart := Options.YStartOffset;
+ nLegendWidth := Options.LegendWidth;
+ {Calculate new values for printer....}
+ Options.LegendWidth := Round((Options.LegendWidth / (nXEnd + Options.LegendWidth)) * Printer.PageWidth);
+ Options.XStartOffset := Round(Printer.PageWidth * 0.08); {8%}
+ Options.YStartOffset := Round(Printer.PageHeight * 0.1); {10%}
+ Options.XEnd := Round(Printer.PageWidth - (1.2 * Options.LegendWidth)) - Options.XStartOffset;
+ Options.YEnd := Round(Printer.PageHeight * 0.75);
+ if Options.YEnd > Options.XEnd then
+ Options.YEnd := Options.XEnd;
+ {Begin printing...}
+ PrintInSession := True;
+ Printer.BeginDoc;
+ PlotGraph; {Here it goes!}
+ Printer.EndDoc;
+ PrintInSession := False;
+ {Restore display values...}
+ Options.XStartOffset := nXStart; {margin}
+ Options.YStartOffset := nYStart;
+ Options.XEnd := nXEnd;
+ Options.YEnd := nYEnd;
+ Options.LegendWidth := nLegendWidth;
+end;
+
+{**************************************************************************}
+{ call this function : }
+{ a) when you want to print the graph to Windows default printer }
+{ AND you add something else on the same paper. This function }
+{ will just add the chart to the OPEN printer canvas at given position }
+{**************************************************************************}
+
+// (rom) XStartPos, YStartPos unused
+
+procedure TJvChart.AddGraphToOpenPrintCanvas(XStartPos, YStartPos, GraphWidth, GraphHeight: Longint);
+var
+ nXEnd, nYEnd: Longint;
+ nXStart, nYStart: Longint;
+ nLegendWidth: Longint;
+begin
+ {Save display values...}
+ nXEnd := Options.XEnd;
+ nYEnd := Options.YEnd;
+ nXStart := Options.XStartOffset;
+ nYStart := Options.YStartOffset;
+ nLegendWidth := Options.LegendWidth;
+ {Set new values for printing the graph at EXISTING print canvas....}
+ Options.LegendWidth := Round((Options.LegendWidth / (nXEnd + Options.LegendWidth)) * GraphWidth);
+ Options.XStartOffset := Round(GraphWidth * 0.08); {8%}
+ Options.YStartOffset := Round(GraphHeight * 0.1); {10%}
+ Options.XEnd := Round(GraphWidth - (1.2 * Options.LegendWidth)) - Options.XStartOffset;
+ Options.YEnd := Round(GraphHeight * 0.75);
+ {Begin printing...NOTICE BeginDoc And EndDoc must be done OUTSIDE this procedure call}
+ PrintInSession := True;
+ PlotGraph; {Here it goes!}
+ PrintInSession := False;
+ {Restore display values...}
+ Options.XStartOffset := nXStart; {margin}
+ Options.YStartOffset := nYStart;
+ Options.XEnd := nXEnd;
+ Options.YEnd := nYEnd;
+ Options.LegendWidth := nLegendWidth;
+end;
+
+{NEW}
+{ when the user clicks the chart and changes the axis, we need a notification
+ so we can save the new settings. }
+
+procedure TJvChart.NotifyOptionsChange;
+begin
+ if FUpdating then
+ Exit;
+ if csDesigning in ComponentState then
+ begin
+ Invalidate;
+ Exit;
+ end;
+ if csLoading in ComponentState then
+ Exit; // Component properties being set at runtime.
+
+ if Options.PenCount <> Self.Data.FPenCount then
+ begin
+ if Options.PenCount > 0 then { never set Data.FPenCount to zero internally! }
+ Data.FPenCount := Options.PenCount;
+ end;
+
+ // Event fire:
+ if Assigned(FOnOptionsChangeEvent) then
+ FOnOptionsChangeEvent(Self);
+ if Options.AutoUpdateGraph then
+ begin
+ FAutoPlotDone := False; // Next paint will also call PlotGraph
+ Invalidate;
+ end;
+end;
+
+{ Warren implemented TImage related code directly into TJvChart, to remove TImage as base class.}
+// (rom) simplified by returning the Printer ACanvas when printing
+
+function TJvChart.GetChartCanvas(isFloating:Boolean): TCanvas;
+var
+ Bitmap: TBitmap;
+begin
+ { designtime - draw directly to screen }
+ if csDesigning in ComponentState then
+ begin
+ Result := Self.Canvas;
+ Assert(Assigned(Result));
+ Assert(Assigned(Result.Brush));
+ Exit;
+ end;
+
+ // external picture mode?
+ if Assigned(FExtPicture) and Assigned(FExtPicture.Graphic) then begin
+ if FExtPicture.Graphic is TBitmap then begin
+ result := TBitmap(FExtPicture.Graphic).Canvas;
+ exit;
+ end else begin
+ raise EInvalidOperation.Create(RsEUnableToGetCanvas);
+ end;
+ end;
+
+
+ { printer canvas }
+ if PrintInSession then
+ begin
+ Result := Printer.Canvas;
+ Assert(Assigned(Result));
+ Assert(Assigned(Result.Brush));
+ Exit;
+ end;
+
+ { Floating marker draw but not external picture mode:}
+ if isFloating then begin
+ Result := Self.Canvas;
+ exit;
+ end;
+
+
+ { FPicture.Graphic -bitmap canvas - normal display method. }
+ if FPicture.Graphic = nil then
+ begin
+ Bitmap := TBitmap.Create;
+ try
+ Bitmap.Width := Width;
+ Bitmap.Height := Height;
+ FPicture.Graphic := Bitmap;
+ finally
+ Bitmap.Free;
+ end;
+ end;
+ if FPicture.Graphic is TBitmap then
+ begin
+ Result := TBitmap(FPicture.Graphic).Canvas;
+ Assert(Assigned(Result));
+ Assert(Assigned(Result.Brush));
+ end
+ else
+ raise EInvalidOperation.CreateRes(@RsEUnableToGetCanvas);
+end;
+
+function TJvChart.GetChartCanvasWidth: Integer; // WP NEW 2007
+begin
+ { designtime - draw directly to screen }
+ if csDesigning in ComponentState then
+ begin
+ Result := Width;
+ Exit;
+ end;
+ if Assigned(FExtPicture) then begin
+ result := FExtPicture.Graphic.Width;
+ exit;
+ end;
+ if PrintInSession then
+ begin
+ Result := Printer.PageWidth;
+ Exit;
+ end;
+ if Assigned(FPicture) then
+ Result := FPicture.Width
+ else
+ Result := Width;
+end;
+
+function TJvChart.GetChartCanvasHeight: Integer; // WP NEW 2007
+begin
+ { designtime - draw directly to screen }
+ if csDesigning in ComponentState then
+ begin
+ Result := Self.Height;
+ Exit;
+ end;
+ if Assigned(FExtPicture) then begin
+ result := FExtPicture.Graphic.Height;
+ exit;
+ end;
+
+ { printer canvas }
+ if PrintInSession then
+ begin
+ Result := Printer.PageHeight;
+ Exit;
+ end;
+
+ { FPicture.Graphic -bitmap canvas - normal display method. }
+ if Assigned(FPicture) then
+ Result := FPicture.Height
+ else
+ Result := Self.Height;
+end;
+
+procedure TJvChart.CalcYEnd;
+var
+ aHeight:Integer;
+begin
+ if Assigned(FExtPicture) then begin
+ aHeight := FExtPicture.Bitmap.Height;
+ end else begin
+ if not Assigned(FBitmap) then exit;
+ aHeight := FBitmap.Height;
+ end;
+
+ Options.YEnd := aHeight - 2 * Options.YStartOffset; {canvas size, excluding margin}
+end;
+{**************************************************************************}
+{ call this function : }
+{ a) when you resize the canvas for the AABsoftGraph }
+{ b) at program startup before drawing the first graph }
+{**************************************************************************}
+
+// ResizeChartCanvas/PlotGraph endless recursion loop fixed. --WP
+
+procedure TJvChart.ResizeChartCanvas;
+var
+ awidth:Integer;
+begin
+ {Add code for my own data...here}
+ if Assigned(FExtPicture) then begin
+ awidth := FExtPicture.Graphic.Width;
+ end else begin
+ if not Assigned(FBitmap) then
+ begin
+ FBitmap := TBitmap.Create;
+ FBitmap.Height := Height;
+ FBitmap.Width := Width;
+ FPicture.Graphic := FBitmap;
+ end
+ else
+ begin
+ FBitmap.Width := Width;
+ FBitmap.Height := Height;
+ FPicture.Graphic := FBitmap;
+ end;
+ awidth := Width;
+ end;
+
+ CalcYEnd; // YEnd depends on YStartOffset.
+
+ if Options.Legend = clChartLegendRight then
+ Options.XEnd := Round(((awidth - 2) - 1.5 * Options.XStartOffset) - Options.LegendWidth)
+ else
+ Options.XEnd := Round((awidth - 2) - 0.5 * Options.XStartOffset);
+
+ if Options.XEnd < 10 then
+ Options.XEnd := 10;
+ if Options.YEnd < 10 then
+ Options.YEnd := 10;
+
+ if not Assigned(Data) then
+ Exit; //safety.
+ // if Data.ValueCount = 0 then
+ // Exit; // no use, there's no data yet.
+
+ Options.PrimaryYAxis.Normalize;
+ Options.SecondaryYAxis.Normalize;
+
+ if (not FInPlotGraph) and Visible {and (Data.ValueCount>0)} then // endless recursion protection.
+ if Options.AutoUpdateGraph or FPlotGraphCalled then
+ begin
+ FInPlotGraph := True; // recursion blocker.
+ InternalPlotGraph; { must not call Invalidate here, causes exceptions in some cases. }
+ FInPlotGraph := False;
+ end;
+end;
+
+{This procedure is called when user clicks on the main header}
+
+procedure TJvChart.EditHeader;
+var
+ StrString: string;
+begin
+ StrString := Options.Title;
+ if InputQuery(RsGraphHeader, Format(RsCurrentHeaders, [Options.Title]), StrString) then
+ Options.Title := StrString;
+ InternalPlotGraph;
+ Invalidate;
+ if Assigned(FOnTitleClick) then
+ FOnTitleClick(Self);
+end;
+
+{This procedure is called when user clicks on the X-axis header}
+
+procedure TJvChart.EditXHeader;
+var
+ StrString: string;
+begin
+ StrString := Options.XAxisHeader;
+ if InputQuery(RsGraphHeader, Format(RsXAxisHeaders, [Options.XAxisHeader]), StrString) then
+ Options.XAxisHeader := StrString;
+ InternalPlotGraph;
+ Invalidate;
+end;
+
+procedure TJvChart.EditYScale;
+var
+ StrString: string;
+begin
+ StrString := FloatToStr(Options.PrimaryYAxis.YMax);
+
+ // NOTE: StrToFloatDefIgnoreInvalidCharacters now called JvSafeStrToFloatDef:
+
+ if InputQuery(RsGraphScale, Format(RsYAxisScales, [FloatToStr(Options.PrimaryYAxis.YMax)]), StrString) then
+ Options.PrimaryYAxis.YMax := JvSafeStrToFloatDef(StrString, Options.PrimaryYAxis.YMax)
+ else
+ Exit;
+
+ // Fire event so the application can save this new Options.PrimaryYAxis.YMax value
+ if Assigned(FOnYAxisClick) then
+ FOnYAxisClick(Self);
+
+ //XXX AutoFormatGraph; BAD CODE REMOVED. Wpostma. Call PlotGraph instead.
+ InternalPlotGraph;
+ Invalidate;
+end;
+
+// NEW: X Axis Header has to move to make room if there is a horizontal
+// X axis legend:
+
+procedure TJvChart.MyXHeader(ACanvas: TCanvas; StrText: string);
+var
+ X, Y, H: Integer;
+begin
+ H := ACanvas.TextHeight(StrText);
+ MyAxisFont(ACanvas);
+ Y := Options.YStartOffset + Options.YEnd + Round(1.6 * H);
+ if Options.Legend = clChartLegendBelow then
+ begin
+ { left aligned X Axis Title, right after the legend itself}
+ X := Options.FXLegendHoriz + 32;
+ MyLeftTextOut(ACanvas, X, Y, StrText);
+ end
+ else
+ begin
+ X := Options.XStartOffset + (Options.XEnd div 2);
+ MyCenterTextOut(ACanvas, X, Y, StrText);
+ end;
+end;
+
+procedure TJvChart.MyYHeader(ACanvas: TCanvas; StrText: string);
+var
+ {ht,}WD, Vert, Horiz: Integer; // not used (ahuser)
+begin
+ if Length(StrText) = 0 then
+ Exit;
+ ACanvas.Brush.Color := Color;
+ { !!warning: uses Win32 only font-handle stuff!!}
+ MyGraphVertFont(ACanvas); // Select Vertical Font Output.
+ if Options.XStartOffset > 10 then
+ begin
+ {ht := MyTextHeight(StrText); }// not used (ahuser)
+ WD := ACanvas.TextWidth(StrText);
+ // Kindof a fudge, but we'll work out something better later... :-) -WAP.
+ Vert := Options.YStartOffset * 2 + Height div 2 - WD div 2;
+ if Vert < 0 then
+ Vert := 0;
+ Horiz := 2;
+ // NOTE: Because of the logical font selected, this time TextOut goes vertical.
+ // If this doesn't go vertical, it may be because the font selection above failed.
+ MyLeftTextOut(ACanvas, Horiz, Vert, StrText);
+ end;
+ MyAxisFont(ACanvas);
+ // Self.MyLeftTextOut(Horiz, Vert+50, '*');
+end;
+
+{***************************************************************************}
+{ MOUSE FUNCTIONS AND PROCEDURES }
+{***************************************************************************}
+
+function TJvChart.MouseToXValue(X: Integer): Integer;
+var
+ XPixelGap: Double;
+begin
+ XPixelGap := ((Options.XEnd - Options.XStartOffset) / Options.XValueCount);
+ if XPixelGap > 0.001 then
+ begin
+ Result := Round(((X - 1) - Options.XStartOffset) / (XPixelGap));
+ if (Result >= Data.ValueCount - 1) then
+ Result := Data.ValueCount - 1
+ else
+ if Result < 0 then
+ Result := 0;
+ end
+ else
+ Result := 0; // can't figure it out.
+end;
+
+function TJvChart.MouseToYValue(Y: Integer): Double;
+begin
+ with FOptions.PrimaryYAxis do
+ begin
+ //Y = (YOrigin - (((Result - YMin) / YGap) * YPixelGap))
+
+ Result := -1 * (((Y / YPixelGap) * YGap) - ((YOrigin / YPixelGap) * YGap) - YMin);
+
+ if Result < YMin then
+ Result := YMin
+ else
+ if Result > YMax then
+ Result := YMax;
+ end;
+end;
+
+procedure TJvChart.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+begin
+ Screen.Cursor := crDefault;
+ inherited MouseUp(Button, Shift, X, Y);
+
+ if Options.MouseDragObjects then
+ begin
+ if Assigned(FDragFloatingMarker) then
+ begin
+ FDragFloatingMarker.FDragging := False;
+ // Solve for X Position etc.
+ FDragFloatingMarker.XPosition := MouseToXValue(X);
+
+ FDragFloatingMarker.YPosition := MouseToYValue(Y);
+ //OutputDebugString(PChar( 'End Mouse Drag Floating Object at '+InTToStr(FDragFloatingMarker.XPosition)+','+FloatToStr(FDragFloatingMarker.YPosition)) );
+ if Assigned(FOnEndFloatingMarkerDrag) then
+ FOnEndFloatingMarkerDrag(Self, FDragFloatingMarker);
+
+ FDragFloatingMarker := nil;
+ Invalidate;
+ Exit;
+ end;
+ end;
+
+ if FStartDrag then
+ begin
+ Options.LegendWidth := Options.LegendWidth + (FMouseDownX - X);
+ Options.XEnd := Options.XEnd - (FMouseDownX - X);
+ InternalPlotGraph;
+ end;
+ if FMouseLegend then
+ begin
+ InternalPlotGraph;
+ FMouseLegend := False;
+ end;
+ FStartDrag := False;
+ Invalidate;
+end;
+
+procedure TJvChart.MouseMove(Shift: TShiftState; X, Y: Integer); //override;
+begin
+ inherited MouseMove(Shift, X, Y);
+ if Assigned(FDragFloatingMarker) then
+ begin
+ if FDragFloatingMarker.XDraggable then
+ begin
+ if X < Options.XStartOffset then
+ X := Options.XStartOffset;
+ if X > Options.XEnd then
+ X := Options.XEnd;
+ FDragFloatingMarker.FRawXPosition := X;
+ end;
+ if FDragFloatingMarker.YDraggable then
+ if Y > FXAxisPosition then
+ Y := FXAxisPosition;
+
+ if Y < Options.YStartOffset then
+ Y := Options.YStartOffset;
+
+ FDragFloatingMarker.FRawYPosition := Y;
+
+ Self.Invalidate; // Repaint control LATER ! .. like a PostMessage(WM_PAINT)
+ //Self.Repaint; // much more CPU intensive, but smoother.
+ end;
+
+end;
+
+procedure TJvChart.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+var
+ XPixelGap: Double;
+ I: Integer;
+ // YPixelGap: Double; // not used (ahuser)
+ Marker: TJvChartFloatingMarker;
+begin
+ inherited MouseDown(Button, Shift, X, Y);
+
+ if Options.MouseDragObjects then
+ begin
+ if not Assigned(FDragFloatingMarker) then
+ begin
+ for I := 0 to FFloatingMarker.Count - 1 do
+ begin
+ Marker := GetFloatingMarker(I);
+ if not Marker.Visible then
+ Continue;
+ if not (Marker.XDraggable or Marker.YDraggable) then
+ Continue;
+ if (Abs(X - Marker.FRawXPosition) < (Options.MarkerSize * 2)) and
+ ((Marker.Marker = pmkNone) or (Abs(Y - Marker.FRawYPosition) < Options.MarkerSize * 2)) then
+ begin
+ FDragFloatingMarker := Marker;
+ FDragFloatingMarker.FDragging := True;
+ //OutputDebugString('Begin Mouse Drag Floating Object');
+ if Assigned(FOnBeginFloatingMarkerDrag) then
+ FOnBeginFloatingMarkerDrag(Self, FDragFloatingMarker);
+ Exit;
+ end;
+ end;
+ end;
+
+ end;
+
+ if Options.MouseEdit then
+ begin
+ if X < Options.XStartOffset then
+ begin
+ EditYScale;
+ Exit;
+ end
+ else
+ // New: Don't let end user mess with title, if we
+ // provide our own way to set the title or title options,
+ // however, if Options.MouseEdit is on, they can still set the
+ // scale via mouse clicking.
+ if (Y < Options.YStartOffset) and not Assigned(FOnTitleClick) then
+ begin
+ EditHeader;
+ Exit;
+ end;
+
+ if (Y > Options.YStartOffset + Options.YEnd) and not Assigned(FOnXAxisClick) then
+ begin
+ EditXHeader;
+ Exit;
+ end;
+ end;
+
+ if X < Options.XStartOffset then
+ begin
+ // Just fire the Y axis clicked event, don't popup the editor
+ if Assigned(FOnYAxisClick) then
+ begin
+ FOnYAxisClick(Self);
+ Exit;
+ end;
+ end
+ else
+ if Y < Options.YStartOffset then
+ if Assigned(FOnTitleClick) then
+ FOnTitleClick(Self);
+ // New: Click on bottom area of chart (X Axis) can be defined by
+ // user of the component to do something.
+ if Assigned(FOnXAxisClick) then
+ if (Y > Options.YEnd) and (X > Options.XStartOffset) then
+ begin
+ FOnXAxisClick(Self);
+ Exit;
+ end;
+ if Assigned(FOnAltYAxisClick) then
+ if (Y < Options.YEnd) and (X > Options.XEnd) then
+ FOnAltYAxisClick(Self);
+
+ if Options.MouseInfo then
+ begin
+ FStartDrag := False;
+ FMouseDownX := X;
+ FMouseDownY := Y;
+ if (Y > Options.YStartOffset) and
+ (Y < Options.YStartOffset + Options.YEnd) and
+ (X > Options.XStartOffset) and
+ (X < Options.XStartOffset + Options.XEnd + 10) then
+ begin
+ {Legend resize...}
+ if X > (Options.XStartOffset + Options.XEnd) - 5 then
+ begin
+ FStartDrag := True;
+ Screen.Cursor := crSizeWE;
+ end;
+ {Inside the actual graph...}
+ if (X <= (Options.XStartOffset + Options.XEnd) - 5) and
+ (Options.ChartKind <> ckChartPieChart) and
+ (Options.ChartKind <> ckChartDeltaAverage) then
+ begin
+ XPixelGap := ((Options.XEnd - Options.XStartOffset) / (Options.XValueCount + 1));
+ //if XPixelGap <1 then
+ // XPixelGal := 1;
+ if XPixelGap > 0.001 then
+ FMouseValue := Round((X - Options.XStartOffset) / (XPixelGap))
+ else
+ FMouseValue := 0; // can't figure it out.
+
+ case Options.ChartKind of
+ ckChartBar, ckChartBarAverage:
+ if Options.PenCount = 1 then {check for Pen count}
+ FMousePen := Round(((X + (XPixelGap / 2)) -
+ (Options.XStartOffset +
+ Options.XOrigin * XPixelGap +
+ XPixelGap * FMouseValue)) /
+ Round(XPixelGap / (Options.PenCount + 0.1)) + 0.1)
+ else
+ FMousePen := Round(((X + (XPixelGap / 2)) -
+ (Options.XStartOffset +
+ Options.XOrigin * XPixelGap +
+ XPixelGap * FMouseValue)) /
+ Round(XPixelGap / (Options.PenCount + 0.5)) + 0.5);
+ ckChartStackedBar, ckChartLine, ckChartStackedBarAverage:
+ FMousePen := 0;
+ end;
+ if (FMouseValue > Options.XValueCount) or (FMouseValue < 0) then
+ FMouseValue := 0;
+ if FMousePen > Options.PrimaryYAxis.YDivisions then
+ FMousePen := 0;
+
+ // New: Allow user to do custom hints, or else do other things
+ // when a chart is clicked.
+ if Assigned(FOnChartClick) then
+ begin
+ FMouseDownShowHint := False;
+ FMouseDownHintBold := False;
+ // This event can handle chart clicks on data area only.
+ if X <= Options.XEnd then
+ FOnChartClick(Self, Button, Shift, X, Y, FMouseValue,
+ FMousePen, FMouseDownShowHint,
+ FMouseDownHintBold, FMouseDownHintStrs);
+ end
+ else
+ begin
+ if Button = mbLeft then
+ begin
+ FMouseDownShowHint := True;
+ FMouseDownHintBold := True;
+ AutoHint;
+ end
+ else
+ FMouseDownShowHint := False; { don't show }
+ end;
+
+ if (FMouseDownHintStrs.Count > 0) and FMouseDownShowHint then
+ ShowMouseMessage(X, Y);
+ end;
+ end;
+ end;
+end;
+
+procedure TJvChart.SetCursorPosition(Pos: Integer);
+begin
+ FCursorPosition := Pos;
+ Invalidate; // repaint!
+end;
+
+procedure TJvChart.DisplayBars; // NEW 2007!
+begin
+ DrawHorizontalBars;
+ DrawVerticalBars;
+end;
+
+{ make list of 'PenName=Value' strings for each pen.. }
+
+procedure TJvChart.AutoHint; // Make the automatic hint message showing all pens and their values.
+var
+ I: Integer;
+ Str: string;
+ Val: Double;
+begin
+ FMouseDownHintStrs.Clear;
+
+ if Options.XAxisDateTimeMode then
+ begin
+ if Length(Options.DateTimeFormat) = 0 then
+ Str := DateTimeToStr(FData.GetTimestamp(FMouseValue))
+ else
+ Str := FormatDateTime(Options.DateTimeFormat, FData.GetTimestamp(FMouseValue));
+
+ FMouseDownHintStrs.Add(Str);
+ end
+ else
+ if Options.XLegends.Count > FMouseValue then
+ FMouseDownHintStrs.Add(Options.XLegends[FMouseValue]);
+
+ for I := 0 to Options.PenCount - 1 do
+ begin
+ if Options.PenLegends.Count <= I then
+ Break; // Exception fixed. WP
+ Str := Options.PenLegends[I];
+ Val := FData.Value[I, FMouseValue];
+ if Length(Str) = 0 then
+ Str := IntToStr(I + 1);
+ Str := Str + ' : ';
+ if IsNaN(Val) then
+ Str := Str + RsNA
+ else
+ begin
+ Str := Str + FloatToStrF(Val, ffFixed, REALPREC, 3);
+ if Options.PenUnit.Count > I then
+ Str := Str + ' ' + Options.PenUnit[I];
+ end;
+
+ FMouseDownHintStrs.Add(Str);
+ {$IFDEF DEBUGINFO_ON}
+ OutputDebugString(PChar('TJvChart.AutoHint: ' + Str));
+ {$ENDIF DEBUGINFO_ON}
+ end;
+end;
+
+(* orphaned
+
+ {We will show some Double values...}
+if FMousePen = 0 then
+begin
+{show all values in the Pen...}
+nLineCount := Options.PenCount;
+nHeight := nLineH * (nLineCount + 2);
+if Options.XLegends.Count > FMouseValue then
+strMessage1 := Options.XLegends[FMouseValue]
+else
+strMessage1 := '';
+strMessage2 := '-';
+if nWidth < ChartCanvas.TextWidth(strMessage1) then
+nWidth := ChartCanvas.TextWidth(strMessage1);
+end
+else
+begin
+nLineCount := 1;
+nHeight := nLineH * (nLineCount + 2);
+strMessage1 := Options.XLegends[FMouseValue];
+if nWidth < ChartCanvas.TextWidth(strMessage1) then
+nWidth := ChartCanvas.TextWidth(strMessage1);
+if FMousePen > 0 then
+strMessage2 := Options.PenLegends[FMousePen];
+if ChartCanvas.TextWidth(strMessage2) > nWidth then
+nWidth := ChartCanvas.TextWidth(strMessage2);
+strMessage3 := FloatToStrF(FData.Value[FMousePen, FMouseValue], ffFixed, REALPREC, 3);
+end;
+
+*)
+
+{ ShowMouseMessage can invoke an OnChartClick event, and/or
+shows hint boxes, etc. }
+
+procedure TJvChart.ShowMouseMessage(X, Y: Integer);
+var
+ nWidth: Integer;
+ nHeight: Integer;
+ nLineH: Integer;
+ nLineCount: Integer;
+ I: Integer;
+ StrWidth, StrHeight: Integer;
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas({floating}true);
+ ACanvas.Font.Color := Font.Color; // March 2004 Fixed.
+
+ // scan and set nWidth,nLineH
+ nWidth := 100; // minimum 100 pixel hint box width.
+ nLineH := 8; // minimum 8 pixel line height for hints.
+ nLineCount := FMouseDownHintStrs.Count;
+
+ for I := 0 to nLineCount - 1 do
+ begin
+ StrWidth := ACanvas.TextWidth(FMouseDownHintStrs[I]);
+ if StrWidth > nWidth then
+ nWidth := StrWidth;
+ StrHeight := ACanvas.TextHeight(FMouseDownHintStrs[I]);
+ if StrHeight > nLineH then
+ nLineH := StrHeight;
+ end;
+
+ // bump height of text in hint box,
+ // leaving a little extra pixel space between rows.
+ nLineH := Round(nLineH * 1.07) + 1;
+
+ {RsNoValuesHere}
+ if FMouseDownHintStrs.Count = 0 then
+ begin
+ StrWidth := ACanvas.TextWidth(RsNoValuesHere);
+ if StrWidth > nWidth then
+ nWidth := StrWidth;
+ MyColorRectangle(ACanvas, jvChartHintColorIndex, X + 3, Y + 3, X + nWidth + 3 + 5, Y + nLineH + 3);
+ MyColorRectangle(ACanvas, jvChartPaperColorIndex, X, Y, X + nWidth + 5, Y + nLineH);
+ ACanvas.Font.Color := Self.Font.Color;
+ MyLeftTextOutHint(ACanvas, X + 2, Y, RsNoValuesHere);
+ FMouseLegend := True;
+ Exit;
+ end;
+
+ // Get hint box height/width, size to contents:
+ nWidth := nWidth + 25;
+ nHeight := nLineH * nLineCount + 8;
+
+ // keep hint from clipping at bottom and right.
+ if (Y + nHeight) > Self.Height then
+ Y := (Self.Height - nHeight);
+ if (X + nWidth) > Self.Width then
+ X := (Self.Width - nWidth);
+
+ // Draw hint box:
+ MyColorRectangle(ACanvas, jvChartPaperColorIndex, X + 3, Y + 3, X + nWidth + 3, Y + nHeight + 3);
+ MyColorRectangle(ACanvas, jvChartHintColorIndex, X, Y, X + nWidth, Y + nHeight);
+
+ //MyLeftTextOut( ACanvas, X + 3, Y + 3, 'Foo');
+
+ // Draw text inside the hint box:
+ ACanvas.Font.Color := Self.Font.Color;
+ //ACanvas.Font.Style :=
+
+ if FMouseDownHintBold then
+ ACanvas.Font.Style := [fsBold];
+
+ for I := 0 to nLineCount - 1 do
+ begin
+ if (I = 1) and FMouseDownHintBold then
+ ACanvas.Font.Style := [];
+ MyLeftTextOutHint(ACanvas, X + 2, 4 + Y + (I * nLineH), FMouseDownHintStrs[I]); // draw text for each line.
+ end;
+
+ FMouseLegend := True;
+
+ //Invalidate; //removed to solve painting glitch.
+ //ResizeChartCanvas; // removed to solve painting glitch.
+end;
+
+{***************************************************************************}
+{ PIE FUNCTIONS AND PROCEDURES }
+{***************************************************************************}
+
+procedure TJvChart.GraphPieChart(NPen: Integer);
+var
+ nSize: Integer;
+ I: Integer;
+ nLast: Integer;
+ nXExtra: Integer;
+ nSum: Double;
+ n100Sum: Double;
+ nP: Double;
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas(false);
+ ClearScreen;
+
+ {Main Header}
+ MyHeader(ACanvas, Options.Title);
+ MyPieLegend(NPen);
+ if Options.XEnd < Options.YEnd then
+ begin
+ nSize := Options.XEnd;
+ nXExtra := 0;
+ end
+ else
+ begin
+ nSize := Options.YEnd;
+ nXExtra := Round((Options.XEnd - Options.YEnd) / 2);
+ end;
+ {Count total sum...}
+ n100Sum := 0;
+ for I := 1 to MAX_VALUES do
+ n100Sum := n100Sum + FData.Value[NPen, I];
+ {Show background pie....}
+ SetRectangleColor(ACanvas, jvChartAxisColorIndex); {black...}
+ MyPiePercentage(Options.XStartOffset + nXExtra + 2,
+ Options.YStartOffset + 2, nSize, 100);
+ {Show pie if not zero...}
+ if n100Sum <> 0 then
+ begin
+ nSum := n100Sum;
+ nLast := Options.XValueCount + 1;
+ if nLast > MAX_VALUES then
+ nLast := MAX_VALUES;
+ for I := nLast downto 2 do
+ begin
+ nSum := nSum - FData.Value[NPen, I];
+ nP := 100 * (nSum / n100Sum);
+ SetRectangleColor(ACanvas, I - 1);
+ MyPiePercentage(Options.XStartOffset + nXExtra,
+ Options.YStartOffset, nSize, nP);
+ end;
+ end;
+end;
+
+procedure TJvChart.MyPiePercentage(X1, Y1, W: Longint; NPercentage: Double);
+var
+ nOriginX, nOriginY: Longint;
+ nGrade: Double;
+ nStartGrade: Double;
+ X, Y: Double;
+ nLen: Double;
+begin
+ nOriginX := Round((W - 1.01) / 2) + X1;
+ nOriginY := Round((W - 1.01) / 2) + Y1;
+ nGrade := (NPercentage / 100) * 2 * Pi;
+ nStartGrade := (2 / 8) * 2 * Pi;
+ nLen := Round((W - 1) / 2);
+ X := Cos(nStartGrade + nGrade) * nLen;
+ Y := Sin(nStartGrade + nGrade) * nLen;
+ MyPie(GetChartCanvas(false), X1, Y1, X1 + W, Y1 + W,
+ nOriginX, Y1, nOriginX + Round(X), nOriginY - Round(Y));
+end;
+
+procedure TJvChart.MyPieLegend(NPen: Integer);
+var
+ I: Integer;
+ nTextHeight: Longint;
+ {nChars: Integer;}// not used (ahuser)
+ XLegendStr: string;
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas(false);
+ {Count how many characters to show in the separate legend}
+ {nChars := Round(Options.LegendWidth / ChartCanvas.TextWidth('1'));}// not used (ahuser)
+ {Decrease the value due to the color box shown}
+ {if (nChars>4) then nChars := nChars-4;}// not used (ahuser)
+
+ MySmallGraphFont(ACanvas);
+ nTextHeight := Round(CanvasMaxTextHeight(ACanvas) * 1.2);
+
+ // Pie Chart Right Side Legend.
+ if Options.Legend = clChartLegendRight then
+ begin
+ MyColorRectangle(ACanvas, 0,
+ Options.XStartOffset + Options.XEnd + 6,
+ Options.YStartOffset + 1 * nTextHeight + 6 + 4,
+ Options.XStartOffset + Options.XEnd + Options.LegendWidth + 6,
+ Options.YStartOffset + (Options.XValueCount + 1) * nTextHeight + 6 + 4);
+ MyColorRectangle(ACanvas, jvChartPaperColorIndex,
+ Options.XStartOffset + Options.XEnd + 3,
+ Options.YStartOffset + 1 * nTextHeight + 3 + 4,
+ Options.XStartOffset + Options.XEnd + Options.LegendWidth + 3,
+ Options.YStartOffset + (Options.XValueCount + 1) * nTextHeight + 3 + 4);
+ for I := 1 to Options.XValueCount do
+ begin
+ DrawPenColorBox(ACanvas, I, ACanvas.TextWidth('12') - 2, nTextHeight - 4,
+ Options.XStartOffset + Options.XEnd + 7,
+ Options.YStartOffset + I * nTextHeight + 9);
+ SetFontColor(ACanvas, jvChartAxisColorIndex);
+ if I - 1 < Options.XLegends.Count then
+ XLegendStr := Options.XLegends[I - 1]
+ else
+ XLegendStr := IntToStr(I);
+ MyLeftTextOut(ACanvas, Options.XStartOffset + Options.XEnd + 7 + ACanvas.TextWidth('12'),
+ Options.YStartOffset + I * nTextHeight + 7,
+ XLegendStr);
+ end;
+ end;
+end;
+
+// Used in line charting as a Marker kind:
+
+procedure TJvChart.PlotSquare(ACanvas: TCanvas; X, Y: Integer);
+begin
+ MyPolygon(ACanvas, [Point(X - Options.MarkerSize, Y - Options.MarkerSize),
+ Point(X + Options.MarkerSize, Y - Options.MarkerSize),
+ Point(X + Options.MarkerSize, Y + Options.MarkerSize),
+ Point(X - Options.MarkerSize, Y + Options.MarkerSize)]);
+end;
+
+// Used in line charting as a Marker kind:
+
+procedure TJvChart.PlotDiamond(ACanvas: TCanvas; X, Y: Integer);
+begin
+ MyPolygon(ACanvas, [Point(X, Y - Options.MarkerSize),
+ Point(X + Options.MarkerSize, Y),
+ Point(X, Y + Options.MarkerSize),
+ Point(X - Options.MarkerSize, Y)]);
+end;
+
+procedure TJvChart.PlotFilledDiamond(ACanvas: TCanvas; X, Y: Integer);
+begin
+ with ACanvas.Brush do
+ begin
+ Style := bsSolid;
+ Color := ACanvas.Pen.Color;
+ PlotDiamond(ACanvas, X, Y);
+ Style := bsClear;
+ end;
+end;
+
+// Used in line charting as a Marker kind:
+
+procedure TJvChart.PlotCircle(ACanvas: TCanvas; X, Y: Integer);
+begin
+ ACanvas.Pen.Style := psSolid;
+ ACanvas.Ellipse(X - Options.MarkerSize,
+ Y - Options.MarkerSize,
+ X + Options.MarkerSize,
+ Y + Options.MarkerSize); // Marker Circle radius 3.
+end;
+
+// Used in line charting as a Marker kind:
+
+procedure TJvChart.PlotCross(ACanvas: TCanvas; X, Y: Integer);
+begin
+ MyDrawLine(ACanvas, X - Options.MarkerSize, Y, X + Options.MarkerSize, Y);
+ MyDrawLine(ACanvas, X, Y - Options.MarkerSize, X, Y + Options.MarkerSize);
+end;
+
+procedure TJvChart.ClearScreen;
+var
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas(false);
+ {Clear screen}
+ SetLineColor(ACanvas, jvChartPaperColorIndex);
+ // Fishy:
+ MyColorRectangle(ACanvas, jvChartPaperColorIndex, 0, 0,
+ // XXX The point here is to exceed the edges, wipe it all, thus the 3* and 5* multipliers.
+ 3 * Options.XStartOffset + Options.XEnd + Options.LegendWidth,
+ 5 * Options.YStartOffset + Options.YEnd);
+ SetRectangleColor(ACanvas, jvChartAxisColorIndex);
+ SetLineColor(ACanvas, jvChartAxisColorIndex);
+end;
+
+{NEW chart type!!!}
+
+procedure TJvChart.GraphDeltaAverage;
+var
+ XPixelGap: Longint;
+ YPixelGap: Longint;
+ lXOrigin: Longint;
+ lYOrigin: Longint;
+ I, J: Longint;
+ TempYOrigin: Longint;
+ ACanvas: TCanvas;
+begin
+ ACanvas := GetChartCanvas(false);
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ {new type of chart...}
+ ClearScreen;
+
+ {Check graph values and correct if wrong. Actually not needed if there are no bugs}
+// if (Options.PrimaryYAxis.YDivisions>MAX_Y_LEGENDS) then
+// Options.PrimaryYAxis.YDivisions := MAX_Y_LEGENDS;
+ if Options.PrimaryYAxis.YDivisions = 0 then
+ Options.PrimaryYAxis.YDivisions := 1;
+ if Options.XValueCount > MAX_VALUES then
+ Options.XValueCount := MAX_VALUES;
+ if Options.XValueCount = 0 then
+ Options.XValueCount := 1;
+ if Options.PenCount > MAX_PEN then
+ Options.PenCount := MAX_PEN;
+ // if Options.PrimaryYAxis.YGap = 0 then
+ // Options.PrimaryYAxis.YGap := 1;
+
+ XPixelGap := Round((Options.YEnd - Options.YStartOffset) /
+ (Options.XValueCount));
+ YPixelGap := Round((Options.XEnd - Options.XStartOffset) /
+ (Options.PrimaryYAxis.YDivisions + 1)); // SPECIALIZED.
+
+ TempYOrigin := Options.YOrigin;
+ Options.YOrigin := Options.PrimaryYAxis.YDivisions div 2;
+
+ lYOrigin := Options.XStartOffset + (YPixelGap * Options.YOrigin);
+ lXOrigin := Options.YStartOffset;
+
+ {Create texts for Y-axis}
+// Options.PrimaryYAxis.YLegends.Clear;
+// for I := 0 to MAX_Y_LEGENDS-1 do
+ // Options.PrimaryYAxis.YLegends.Add( IntToStr(Round(((I-1)-Options.YOrigin)*Options.PrimaryYAxis.YGap)) );
+
+ {Y-axis legends and lines...}
+ MyAxisFont(ACanvas);
+ for I := 1 to Options.PrimaryYAxis.YDivisions + 1 do
+ begin
+ if I >= Options.PrimaryYAxis.YLegends.Count then
+ Exit;
+ MyLeftTextOut(ACanvas,
+ lYOrigin + (YPixelGap * ((I - 1) - Options.YOrigin)),
+ lXOrigin + XPixelGap * Options.XValueCount + 2,
+ Options.PrimaryYAxis.YLegends[I]);
+ MyDrawDotLine(ACanvas,
+ lYOrigin - (YPixelGap * ((I - 1) - Options.YOrigin)),
+ lXOrigin,
+ lYOrigin - (YPixelGap * ((I - 1) - Options.YOrigin)),
+ lXOrigin + (XPixelGap * (Options.XValueCount)));
+ end;
+
+ {Draw Y-axis}
+ ACanvas.MoveTo(Options.XStartOffset, lXOrigin);
+ MyAxisLineTo(ACanvas, Options.XEnd, lXOrigin);
+ {Draw second Y-axis}
+ ACanvas.MoveTo(Options.XStartOffset, lXOrigin + XPixelGap * Options.XValueCount + 1);
+ MyAxisLineTo(ACanvas, Options.XEnd, lXOrigin + XPixelGap * Options.XValueCount + 1);
+ {Draw X-axis}
+ ACanvas.MoveTo(lYOrigin, lXOrigin);
+ MyAxisLineTo(ACanvas, lYOrigin, lXOrigin + XPixelGap * Options.XValueCount + 1);
+
+ {X-axis legends...}
+ GraphXAxisLegend;
+
+ {Main Header}
+ MyHeader(ACanvas, Options.Title);
+
+ {X axis header}
+ MyXHeader(ACanvas, Options.XAxisHeader);
+
+ // Now draw the delta average...
+ for I := 0 to Options.PenCount - 1 do
+ for J := 0 to Options.XValueCount - 1 do
+ if Options.PenCount = 1 then
+ MyColorRectangle(ACanvas, I,
+ lYOrigin,
+ lXOrigin + J * XPixelGap + (I) * Round(XPixelGap / (Options.PenCount + 0.1)) - XPixelGap,
+ lYOrigin + Round(((FData.Value[I, J] - Options.AverageValue[J]) /
+ Options.PrimaryYAxis.YGap) * YPixelGap),
+ lXOrigin + J * XPixelGap + (I + 1) * Round(XPixelGap / (Options.PenCount + 0.1)) - XPixelGap)
+ else
+ MyColorRectangle(ACanvas, I,
+ lYOrigin,
+ lXOrigin + J * XPixelGap + (I) * Round(XPixelGap / (Options.PenCount + 0.5)) - XPixelGap,
+ lYOrigin + Round(((FData.Value[I, J] - Options.AverageValue[J]) /
+ Options.PrimaryYAxis.YGap) * YPixelGap),
+ lXOrigin + J * XPixelGap + (I + 1) * Round(XPixelGap / (Options.PenCount + 0.5)) - XPixelGap);
+ Options.YOrigin := TempYOrigin;
+end;
+
+{****************************************************************************}
+{ Device dependent functions for the rest of this module...check for printer }
+{ or check for metafile output! }
+{****************************************************************************}
+
+
+{ !!warning: uses Win32 only font-handle stuff!!}
+procedure TJvChart.MakeVerticalFont;
+begin
+ if Ord(FYFontHandle) <> 0 then
+ DeleteObject(FYFontHandle); // delete old object
+ // Clear the contents of FLogFont
+ FillChar(FYLogFont, SizeOf(TLogFont), 0);
+ // Set the TLOGFONT's fields - Win32 Logical Font Details.
+ with FYLogFont do
+ begin
+ lfHeight := Abs(Font.Height) + 2;
+ lfWidth := 0; //Font.Width;
+ lfEscapement := 900; // 90 degree vertical rotation
+ lfOrientation := 900; // not used.
+ lfWeight := FW_BOLD; //FW_HEAVY; // bold, etc
+ lfItalic := 1; // no
+ lfUnderline := 0; // no
+ lfStrikeOut := 0; // no
+ lfCharSet := ANSI_CHARSET; // or DEFAULT_CHARSET
+ lfOutPrecision := OUT_TT_ONLY_PRECIS; //Require TrueType!
+ // OUT_DEFAULT_PRECIS;
+ // OUT_STRING_PRECIS, OUT_CHARACTER_PRECIS, OUT_STROKE_PRECIS,
+ // OUT_TT_PRECIS, OUT_DEVICE_PRECIS, OUT_RASTER_PRECIS,
+ // OUT_TT_ONLY_PRECIS
+
+ lfClipPrecision := CLIP_DEFAULT_PRECIS;
+ lfQuality := DEFAULT_QUALITY;
+ lfPitchAndFamily := DEFAULT_PITCH or FF_DONTCARE;
+ StrPCopy(lfFaceName, Font.Name);
+ end;
+
+ // Retrieve the requested font
+ FYFontHandle := CreateFontIndirect(FYLogFont);
+ Assert(Ord(FYFontHandle) <> 0);
+ // Assign to the Font.Handle
+ //Font.Handle := FYFont; // XXX DEBUG
+ //pbxFont.Refresh;
+ FYFont := TFont.Create;
+ FYFont.Assign(Font);
+ FYFont.Color := Options.AxisFont.Color;
+ FYFont.Handle := FYFontHandle;
+end;
+
+
+procedure TJvChart.MyHeader(ACanvas: TCanvas; StrText: string);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ MyHeaderFont(ACanvas);
+ MyCenterTextOut(ACanvas, Options.XStartOffset + Round(Options.XEnd / 2),
+ (Options.YStartOffset div 2) - (MyTextHeight(ACanvas, StrText) div 2),
+ StrText);
+ MyAxisFont(ACanvas);
+end;
+
+procedure TJvChart.MySmallGraphFont(ACanvas: TCanvas);
+begin
+ ACanvas.Brush.Color := Options.PaperColor; // was hard coded to clWhite.
+ ACanvas.Font.Assign(Options.LegendFont);
+end;
+
+procedure TJvChart.MyAxisFont(ACanvas: TCanvas);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(ACanvas.Font));
+ Assert(Assigned(Options));
+ ACanvas.Brush.Color := Options.PaperColor; // was hard coded to clWhite.
+ ACanvas.Font.Assign(Options.AxisFont);
+end;
+
+
+{ !!warning: uses Win32 only font-handle stuff!!}
+procedure TJvChart.MyGraphVertFont(ACanvas: TCanvas);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+
+ if Ord(FYFontHandle) = 0 then
+ MakeVerticalFont;
+ ACanvas.Font.Assign(FYFont); //Handle := FYFontHnd;
+ if not PrintInSession then
+ Assert(ACanvas.Font.Handle = FYFontHandle);
+end;
+
+
+procedure TJvChart.MyHeaderFont(ACanvas: TCanvas);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(Options));
+ ACanvas.Brush.Color := Options.PaperColor; //was clWhite;
+ ACanvas.Font.Assign(Options.HeaderFont);
+end;
+
+procedure TJvChart.MyPenLineTo(ACanvas: TCanvas; X, Y: Integer);
+begin
+ ACanvas.Pen.Width := Options.PenLineWidth;
+ ACanvas.LineTo(X, Y);
+ ACanvas.Pen.Width := 1;
+end;
+
+procedure TJvChart.MyAxisLineTo(ACanvas: TCanvas; X, Y: Integer);
+begin
+ if Options.AxisLineWidth = 0 then
+ Exit;
+
+ ACanvas.Pen.Width := Options.AxisLineWidth;
+ ACanvas.LineTo(X, Y);
+ ACanvas.Pen.Width := 1;
+end;
+
+function TJvChart.MyTextHeight(ACanvas: TCanvas; StrText: string): Longint;
+begin
+ Result := ACanvas.TextHeight(StrText);
+end;
+
+{ Text Left Aligned to X,Y boundary }
+
+procedure TJvChart.MyLeftTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Brush.Color := Options.PaperColor; // non default paper color.
+ ACanvas.TextOut(X, Y + 1, AText);
+end;
+
+procedure TJvChart.MyLeftTextOutHint(ACanvas: TCanvas; X, Y: Integer; const AText: string);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Brush.Color := Options.HintColor;
+ ACanvas.TextOut(X, Y + 1, AText);
+end;
+
+procedure TJvChart.MyCenterTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Brush.Color := Options.PaperColor; // non default paper color.
+ ACanvas.TextOut(X - Round(ACanvas.TextWidth(AText) / 2), Y + 1, AText);
+end;
+
+procedure TJvChart.MyRightTextOut(ACanvas: TCanvas; X, Y: Integer; const AText: string);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Brush.Color := Options.PaperColor; // non default paper color.
+ ACanvas.TextOut(
+ X - ACanvas.TextWidth(AText),
+ Y - Round(ACanvas.TextHeight(AText) / 2),
+ AText
+ );
+end;
+
+procedure TJvChart.MyRectangle(ACanvas: TCanvas; X, Y, X2, Y2: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Rectangle(X, Y, X2, Y2);
+end;
+
+(*Procedure TJvChart.MyShadowRectangle(Pen : Integer; X, Y, X2, Y2: Integer);
+begin
+ SetRectangleColor(Shadow);
+ ACanvas.Rectangle(X, Y, X2, Y2);
+end;*)
+
+procedure TJvChart.MyColorRectangle(ACanvas: TCanvas; Pen: Integer; X, Y, X2, Y2: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ SetRectangleColor(ACanvas, Pen);
+ //OutputDebugString(PChar('MyColorRectangle X='+IntToStr(X)+' Y='+IntToStr(Y)+ ' X2='+IntToStr(X2)+ ' Y2='+IntToStr(Y2) ));
+ ACanvas.Rectangle(X, Y, X2, Y2);
+end;
+
+procedure TJvChart.MyPie(ACanvas: TCanvas; X1, Y1, X2, Y2, X3, Y3, X4, Y4: Longint);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4);
+end;
+
+{Procedure TJvChart.MyArc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
+begin
+ ACanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4);
+end;}// not used (ahuser)
+
+procedure TJvChart.MyPolygon(ACanvas: TCanvas; Points: array of TPoint);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Polygon(Points);
+end;
+
+{Procedure TJvChart.MyEllipse(X1, Y1, X2, Y2: Integer);
+begin
+ ACanvas.Ellipse(X1, Y1, X2, Y2);
+end;}
+
+procedure TJvChart.MyDrawLine(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.MoveTo(X1, Y1);
+ ACanvas.LineTo(X2, Y2);
+end;
+
+procedure TJvChart.MyDrawAxisMark(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ SetSolidLines(ACanvas);
+ ACanvas.Pen.Width := 1; // always width 1
+ ACanvas.MoveTo(X1, Y1);
+ ACanvas.LineTo(X2, Y2);
+end;
+
+procedure TJvChart.MyDrawDotLine(ACanvas: TCanvas; X1, Y1, X2, Y2: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ SetDotLines(ACanvas);
+ ACanvas.MoveTo(X1, Y1);
+ ACanvas.LineTo(X2, Y2);
+ SetSolidLines(ACanvas);
+end;
+
+{ (rom) not used
+function TJvChart.GetDefaultColorString(nIndex: Integer): string;
+begin
+ if nIndex <= 10 then
+ case nIndex of
+ -2:
+ Result := 'clWhite'; // MouseDownBox
+ -1:
+ Result := 'clWhite';
+ 0:
+ Result := 'clBlack';
+ 1:
+ Result := 'clLime';
+ 2:
+ Result := 'clBlue';
+ 3:
+ Result := 'clRed';
+ 4:
+ Result := 'clGreen';
+ 5:
+ Result := 'clMaroon';
+ 6:
+ Result := 'clOlive';
+ 7:
+ Result := 'clSilver';
+ 8:
+ Result := 'clTeal';
+ 9:
+ Result := 'clBlack';
+ 10:
+ Result := 'clAqua';
+ end
+ else
+ Result := '$00888888';
+end;
+}
+
+procedure TJvChart.SetFontColor(ACanvas: TCanvas; Pen: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Font.Color := Options.PenColor[Pen];
+end;
+
+procedure TJvChart.SetRectangleColor(ACanvas: TCanvas; Pen: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ ACanvas.Brush.Color := Options.PenColor[Pen];
+end;
+
+procedure TJvChart.SetLineColor(ACanvas: TCanvas; Pen: Integer);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(ACanvas.Pen));
+ ACanvas.Pen.Color := Options.PenColor[Pen];
+end;
+
+procedure TJvChart.SetDotLines(ACanvas: TCanvas);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(ACanvas.Pen));
+ ACanvas.Pen.Style := psDot;
+end;
+
+procedure TJvChart.SetSolidLines(ACanvas: TCanvas);
+begin
+ Assert(Assigned(ACanvas));
+ Assert(Assigned(ACanvas.Brush));
+ Assert(Assigned(ACanvas.Pen));
+ ACanvas.Pen.Style := psSolid;
+end;
+
+procedure TJvChart.GraphToClipboard;
+begin
+ {This works with bitmaps at least...how to do it as a metafile?}
+ Clipboard.Assign(FPicture);
+end;
+
+{ PivotData: Pivot Data in Table. Formerly ChangeXValuesWithPen }
+
+procedure TJvChart.PivotData;
+var
+ I, J: Integer;
+ PenCount, XValueCount: Integer;
+ TempData: TJvChartData;
+ TempStrings: TStringList;
+begin
+ TempData := TJvChartData.Create;
+ PenCount := Options.PenCount;
+ XValueCount := Options.XValueCount;
+ try
+ { Move data to temp }
+ for I := 0 to PenCount - 1 do
+ for J := 0 to XValueCount - 1 do
+ TempData.Value[I, J] := FData.Value[I, J];
+ FData.Clear;
+ { copy back, pivot X/Y axis }
+ for I := 0 to PenCount - 1 do
+ for J := 0 to XValueCount - 1 do
+ TempData.Value[I, J] := FData.Value[J, I];
+
+ {swap labels}
+ TempStrings := Options.FXLegends;
+ Options.FXLegends := Options.FPenLegends;
+ Options.FPenLegends := TempStrings;
+
+ Options.XValueCount := PenCount;
+ Options.PenCount := XValueCount;
+
+ {recalc average}
+ CountGraphAverage;
+ InternalPlotGraph;
+ Invalidate;
+ finally
+ TempData.Free;
+ end;
+end;
+
+{FLOATING MARKERS: new Jan 2005 by WP }
+
+procedure TJvChart.DrawFloatingMarkers; { called from TJvChart.Paint! }
+var
+ Marker, Marker2: TJvChartFloatingMarker;
+ LineXPixelGap: Double;
+ CaptionYPosition, TextWidth, TextHeight, VC, I: Integer;
+ ACanvas: TCanvas;
+begin
+ if csDesigning in ComponentState then
+ Exit;
+ if FFloatingMarker.Count = 0 then
+ Exit;
+ ACanvas := Self.GetChartCanvas({isFloating}true);
+
+ VC := Options.XValueCount;
+ if (VC < 2) then
+ VC := 2;
+ LineXPixelGap := ((Options.XEnd - 2) - Options.XStartOffset) / (VC - 1);
+
+ {-- First loop through all and update their Raw X and Y Positions --}
+ for I := 0 to FFloatingMarker.Count - 1 do
+ begin
+ Marker := GetFloatingMarker(I);
+ if not Marker.Visible then
+ Continue;
+ if (Marker.XPosition < 0) or (Marker.XPosition >= VC) then
+ Continue; // out of visible X range.
+
+ // if following a pen, get the updated pen value:
+ //if Marker.YPositionToPen>=0 then begin
+ // Marker.YPosition := Self.Data.Value[Marker.YPositionToPen, Marker.XPosition];
+ //end;
+
+ // find Raw X,Y co-ordinates:
+ if not Marker.FDragging then
+ begin
+ with FOptions.PrimaryYAxis do
+ Marker.FRawYPosition := Trunc((YOrigin - (((Marker.YPosition - YMin) / YGap) * YPixelGap)));
+ Marker.FRawXPosition := Round(XOrigin + Marker.XPosition * LineXPixelGap);
+ end;
+ end;
+
+ {-- Now draw any connecting lines or vertical lines --}
+ for I := 0 to FFloatingMarker.Count - 1 do
+ begin
+ Marker := GetFloatingMarker(I);
+ if not Marker.Visible then
+ Continue;
+ if (Marker.XPosition < 0) or (Marker.XPosition >= VC) then
+ Continue; // out of visible X range.
+
+ // Draw connecting (rubberband) line:
+ if (Marker.LineToMarker >= 0) and (Marker.FLineStyle <> psClear) then
+ begin
+ Marker2 := GetFloatingMarker(Marker.LineToMarker);
+ ACanvas.Pen.Style := Marker.FLineStyle;
+ ACanvas.Pen.Color := Marker.FLineColor;
+ ACanvas.Pen.Width := Marker.FLineWidth;
+ ACanvas.MoveTo(Marker.FRawXPosition, Marker.FRawYPosition);
+ ACanvas.LineTo(Marker2.FRawXPosition, Marker2.FRawYPosition);
+ end
+ else
+ if Marker.FLineVertical then
+ begin
+ // Vertical line along X position:
+ ACanvas.Pen.Style := Marker.FLineStyle;
+ ACanvas.Pen.Color := Marker.FLineColor;
+ ACanvas.Pen.Width := Marker.FLineWidth;
+ ACanvas.MoveTo(Marker.FRawXPosition, Options.YStartOffset);
+ ACanvas.LineTo(Marker.FRawXPosition, FXAxisPosition - 1);
+ end;
+ end;
+
+ {-- Now draw the markers themselves, we draw them LAST so they are ON TOP. --}
+ MySmallGraphFont(ACanvas);
+ for I := 0 to FFloatingMarker.Count - 1 do
+ begin
+ Marker := GetFloatingMarker(I);
+ if not Marker.Visible then
+ Continue;
+ if (Marker.XPosition < 0) or (Marker.XPosition >= VC) then
+ Continue; // out of visible X range.
+ if Marker.Marker <> pmkNone then
+ begin
+ // Draw Marker:
+ ACanvas.Pen.Color := Marker.FMarkerColor;
+ PlotMarker(ACanvas, Marker.Marker, Marker.FRawXPosition, Marker.FRawYPosition);
+ end;
+
+ if Marker.Caption <> '' then
+ begin
+ TextHeight := ACanvas.TextHeight(Marker.Caption);
+
+ CaptionYPosition := 0; // not used.
+ case Marker.CaptionPosition of
+ cpMarker:
+ CaptionYPosition := Marker.FRawYPosition - Round(TextHeight * 1.4);
+ cpXAxisBottom:
+ CaptionYPosition := Options.YStartOffset + Options.YEnd + Round(TextHeight * 1.4);
+ cpXAxisTop:
+ CaptionYPosition := Trunc(XOrigin - Round(TextHeight * 1.4));
+ cpTitleArea:
+ CaptionYPosition := (Options.YStartOffset div 2) - (TextHeight div 2);
+ end;
+
+ if Marker.CaptionBoxed then
+ begin
+ TextWidth := ACanvas.TextWidth(Marker.Caption) + 10;
+
+ ACanvas.Pen.Color := Marker.LineColor;
+ ACanvas.Pen.Width := 1;
+ ACanvas.Pen.Style := Marker.LineStyle;
+ MyRectangle(ACanvas,
+ Marker.FRawXPosition - TextWidth div 2,
+ CaptionYPosition,
+ Marker.FRawXPosition + TextWidth div 2,
+ CaptionYPosition + TextHeight + TextHeight div 4);
+ ACanvas.Pen.Style := psSolid;
+ //MySmallGraphFont(ACanvas); <-redundant.
+ //MyCenterTextOut(ACanvas, Marker.FRawXPosition, Options.FYStartOffset + Round(TextHeight / 4),
+ // Marker.Caption);
+ end;
+
+ MyCenterTextOut(ACanvas, Marker.FRawXPosition, CaptionYPosition, Marker.Caption);
+ end;
+ end;
+end;
+
+function TJvChart.AddFloatingMarker: TJvChartFloatingMarker;
+begin
+ Assert(Assigned(FFloatingMarker));
+ Result := TJvChartFloatingMarker.Create(Self);
+ Result.FIndex := FFloatingMarker.Count;
+ FFloatingMarker.Add(Result);
+end;
+
+procedure TJvChart.DeleteFloatingMarker(Index: Integer);
+var
+ I: Integer;
+begin
+ Assert(Assigned(FFloatingMarker));
+ if Assigned(FDragFloatingMarker) then
+ FDragFloatingMarker := nil;
+
+ FFloatingMarker.Delete(Index);
+ for I := Index to FFloatingMarker.Count - 1 do
+ with GetFloatingMarker(I) do
+ begin
+ FIndex := I; // update index.
+ if LineToMarker = Index then
+ LineToMarker := -1 // Disconnected now.
+ else
+ if LineToMarker > Index then
+ LineToMarker := LineToMarker - 1; // Index changed.
+ end;
+ Invalidate;
+end;
+
+procedure TJvChart.DeleteFloatingMarkerObj(Marker: TJvChartFloatingMarker); // NEW 2007
+var
+ I: Integer;
+begin
+ for I := 0 to FFloatingMarker.Count - 1 do
+ begin
+ if TJvChartFloatingMarker(FFloatingMarker[I]) = Marker then
+ begin
+ DeletefloatingMarker(I);
+ Exit;
+ end;
+ end;
+end;
+
+procedure TJvChart.CopyFloatingMarkers(Source: TJvChart);
+var
+ I: Integer;
+ NewMarker: TJvChartFloatingMarker;
+begin
+ ClearFloatingMarkers;
+ for I := 0 to Source.FloatingMarkerCount - 1 do
+ begin
+ NewMarker := Self.AddFloatingMarker;
+ NewMarker.Assign(Source.GetFloatingMarker(I));
+ end;
+ Invalidate; // repaint!
+end;
+
+procedure TJvChart.ClearFloatingMarkers;
+begin
+ if Assigned(FDragFloatingMarker) then
+ FDragFloatingMarker := nil;
+ FFloatingMarker.Clear;
+end;
+
+function TJvChart.GetFloatingMarker(Index: Integer): TJvChartFloatingMarker;
+begin
+ Assert(Assigned(FFloatingMarker));
+ Result := TJvChartFloatingMarker(FFloatingMarker[Index]);
+end;
+
+function TJvChart.GetHorizontalBar(index: integer): TJvChartHorizontalBar;
+begin
+ // new 2009
+ Assert(Assigned(FHorizontalBars));
+ Result := TJvChartHorizontalBar(FHorizontalBars[Index]);
+
+end;
+
+function TJvChart.GetVerticalBar(index: integer): TJvChartVerticalBar;
+begin
+// new 2009
+ Assert(Assigned(FVerticalBars));
+ Result := TJvChartVerticalBar(FVerticalBars[Index]);
+end;
+
+function TJvChart.FloatingMarkerCount: Integer;
+begin
+ Assert(Assigned(FFloatingMarker));
+ Result := FFloatingMarker.Count;
+end;
+
+// NEW HORIZONTAL BAR AND VERTICAL BAR AND GRADIENT PAINTING METHODS (2007) - W.Postma.
+
+procedure TJvChart.DrawGradient; // new 2007
+var
+ ACanvas: TCanvas;
+ RawRect: TRect;
+ VC: Integer;
+begin
+ if csDesigning in ComponentState then
+ Exit;
+ if (Options.FGradientDirection = grNone) or (Options.PaperColor = Options.FGradientColor) then
+ Exit;
+ ACanvas := GetChartCanvas(false);
+ VC := Options.XValueCount;
+ if VC < 1 then
+ VC := 1;
+ RawRect.Top := FOptions.YStartOffset;
+ RawRect.Bottom := Trunc(YOrigin);
+ RawRect.Left := Round(XOrigin);
+ RawRect.Right := Round(Options.XStartOffset + Options.XPixelGap * VC) - 1;
+ case Options.FGradientDirection of
+ //grNone:
+ // ;
+ grUp:
+ GradVertical(ACanvas, RawRect, Options.FGradientColor, Options.PaperColor);
+ grDown:
+ GradVertical(ACanvas, RawRect, Options.PaperColor, Options.FGradientColor);
+ grLeft:
+ GradHorizontal(ACanvas, RawRect, Options.PaperColor, Options.FGradientColor);
+ grRight:
+ GradHorizontal(ACanvas, RawRect, Options.FGradientColor, Options.PaperColor);
+ end;
+end;
+
+{ Gradient bars - indicators on background of various vertical subranges }
+
+procedure TJvChart.DrawHorizontalBars; // new 2007
+var
+ HB: TJvChartHorizontalBar;
+ J: Integer;
+ ACanvas: TCanvas;
+ VC: Integer;
+ RawRect: TRect;
+ procedure CalcRawRect; // new april 2009
+begin
+ with FOptions.PrimaryYAxis do
+ if (YGap <> 0) then
+ begin
+ if IsNaN(HB.FYTop) then
+ RawRect.Top := FOptions.YStartOffset
+ else
+ begin
+ RawRect.Top := Trunc((YOrigin - (((HB.FYTop - YMin) / YGap) * YPixelGap)));
+ if RawRect.Top < 0 then
+ RawRect.Top := FOptions.YStartOffset;
+ end;
+
+ if IsNaN(HB.FYBottom) then
+ RawRect.Bottom := Trunc(YOrigin)
+ else
+ begin
+ RawRect.Bottom := Trunc((YOrigin - (((HB.FYBottom - YMin) / YGap) * YPixelGap)));
+ if (RawRect.Bottom < 0) or (RawRect.Bottom > YOrigin) then
+ RawRect.Bottom := Trunc(YOrigin);
+ end;
+
+ RawRect.Left := Round(XOrigin);
+ RawRect.Right := Round(Options.XStartOffset + Options.XPixelGap * VC) - 1;
+ end;
+ end;
+begin
+ if csDesigning in ComponentState then
+ Exit;
+ if FHorizontalBars.Count = 0 then
+ Exit;
+ ACanvas := GetChartCanvas(false);
+ VC := Options.XValueCount;
+ if VC < 1 then
+ VC := 1;
+
+ for J := 0 to FHorizontalBars.Count - 1 do
+ begin
+ HB := TJvChartHorizontalBar(FHorizontalBars[J]);
+ if not HB.FVisible then
+ Continue;
+
+ CalcRawRect;
+
+ ACanvas.Brush.Color := HB.FColor;
+ ACanvas.Brush.Style := bsSolid;
+ ACanvas.FillRect(RawRect);
+ if HB.FColor <> HB.FGradColor then
+ case HB.FGradDirection of
+ //grNone:
+ // ;
+ grUp:
+ GradVertical(ACanvas, RawRect, HB.FGradColor, HB.FColor);
+ grDown:
+ GradVertical(ACanvas, RawRect, HB.FColor, HB.FGradColor);
+ end;
+ end;
+
+ {now draw outlines }
+ // new april 2009
+ for J := 0 to FHorizontalBars.Count - 1 do
+ begin
+ HB := TJvChartHorizontalBar(FHorizontalBars[J]);
+ if not HB.FVisible then
+ Continue;
+ if HB.PenStyle<>psClear then begin
+ CalcRawRect;
+ ACanvas.Brush.Style := bsClear;
+ ACanvas.Pen.Style := HB.PenStyle;
+ ACAnvas.Pen.Color := HB.PenColor;
+ ACanvas.Rectangle(RawRect);
+ end;
+ end;
+
+
+end;
+
+procedure TJvChart.DrawVerticalBars; // new 2007
+var
+ VB: TJvChartVerticalBar;
+ J: Integer;
+ ACanvas: TCanvas;
+ VC: Integer;
+ RawRect: TRect;
+ procedure CalcRawRect;
+ begin
+ RawRect.Top := FOptions.YStartOffset;
+ RawRect.Bottom := Trunc(YOrigin);
+ RawRect.Left := Round(Options.XStartOffset + Options.XPixelGap * VB.FXLeft);
+ if RawRect.Left <= 0 then
+ RawRect.Left := Round(XOrigin);
+ RawRect.Right := Round(Options.XStartOffset + Options.XPixelGap * VB.FXRight);
+ VC := Round(Options.XStartOffset + Options.XPixelGap * Options.XValueCount);
+ if RawRect.Right > VC then
+ RawRect.Right := VC;
+ end;
+
+begin
+ if csDesigning in ComponentState then
+ Exit;
+ if FVerticalBars.Count = 0 then
+ Exit;
+ ACanvas := GetChartCanvas(false);
+ {VC :=Options.XValueCount;
+ if VC<1 then VC:=1;}
+
+ for J := 0 to FVerticalBars.Count - 1 do
+ begin
+ VB := TJvChartVerticalBar(FVerticalBars[J]);
+ if not VB.FVisible then
+ Continue;
+
+ CalcRawRect;
+
+ ACanvas.Brush.Color := VB.FColor;
+ ACanvas.Brush.Style := bsSolid;
+ ACanvas.FillRect(RawRect);
+ if VB.FColor <> VB.FGradColor then
+ case VB.FGradDirection of
+ //grNone:
+ // ;
+ grUp:
+ GradVertical(ACanvas, RawRect, VB.FGradColor, VB.FColor);
+ grDown:
+ GradVertical(ACanvas, RawRect, VB.FColor, VB.FGradColor);
+ grLeft:
+ GradHorizontal(ACanvas, RawRect, VB.FColor, VB.FGradColor);
+ grRight:
+ GradHorizontal(ACanvas, RawRect, VB.FGradColor, VB.FColor);
+ end;
+ end;
+
+
+ {now draw outlines. these are done last so we can properly paint overlaps }
+ // new april 2009
+ for J := 0 to FVerticalBars.Count - 1 do
+ begin
+ VB := TJvChartVerticalBar(FVerticalBars[J]);
+ if not VB.FVisible then
+ Continue;
+ if VB.PenStyle<>psClear then begin
+ CalcRawRect;
+ ACanvas.Brush.Style := bsClear;
+ ACanvas.Pen.Style := VB.PenStyle;
+ ACAnvas.Pen.Color := VB.PenColor;
+ ACanvas.Rectangle(RawRect);
+ end;
+ end;
+
+end;
+
+function TJvChart.AddHorizontalBar: TJvChartHorizontalBar; // NEW 2007
+begin
+ Assert(Assigned(FHorizontalBars));
+ Result := TJvChartHorizontalBar.Create(Self);
+ Result.FIndex := FHorizontalBars.Count;
+ FHorizontalBars.Add(Result);
+end;
+
+function TJvChart.AddVerticalBar: TJvChartVerticalBar; // NEW 2007
+begin
+ Assert(Assigned(FVerticalBars));
+ Result := TJvChartVerticalBar.Create(Self);
+ Result.FIndex := FVerticalBars.Count;
+ FVerticalBars.Add(Result);
+end;
+
+procedure TJvChart.ClearHorizontalBars; // NEW 2007
+begin
+ FHorizontalBars.Clear;
+end;
+
+procedure TJvChart.ClearVerticalBars; // NEW 2007
+begin
+ FVerticalBars.Clear;
+end;
+
+function TJvChart.HorizontalBarsCount: Integer; // NEW 2007
+begin
+ Result := FHorizontalBars.Count;
+end;
+
+function TJvChart.VerticalBarsCount: Integer; // NEW 2007
+begin
+ Result := FVerticalBars.Count;
+end;
+
+end.