unit ceAxisFrame; {$MODE ObjFPC}{$H+} interface uses Classes, SysUtils, Graphics, Forms, Controls, ExtCtrls, ComCtrls, StdCtrls, Dialogs, Spin, TATextElements, TAChartAxis, TAGraph, ceFontFrame, cePenFrame, ceShapeBrushPenMarginsFrame, ceArrowFrame; type TChartAxisEditorPage = (aepTitle, aepLabels, aepGrid, aepLine); { TChartAxisFrame } TChartAxisFrame = class(TFrame) Bevel1: TBevel; Bevel2: TBevel; Bevel3: TBevel; Bevel4: TBevel; cbAutoMax: TCheckBox; cbAutoMin: TCheckBox; cbAxisLineVisible: TCheckBox; cbFrameVisible: TCheckBox; cbGridVisible: TCheckBox; cbInverted: TCheckBox; cbLabelsVisible: TCheckBox; cbShow: TCheckBox; cbTickColor: TColorButton; cbTitleVisible: TCheckBox; cbTitleHTML: TCheckBox; cbTitleWordwrap: TCheckBox; edLabelFormat: TEdit; gbArrow: TGroupBox; gbAxisLine: TGroupBox; gbAxisRange: TGroupBox; gbFrame: TGroupBox; gbGrid: TGroupBox; gbLabelFont: TGroupBox; gbLabels: TGroupBox; gbShapeFillBorder: TGroupBox; gbTicks: TGroupBox; gbTitleFont: TGroupBox; gbTitleShapeBrushPenMargins: TGroupBox; lblAutomatic: TLabel; lblLabelDistance: TLabel; lblLabelFormat: TLabel; lblTickInnerLength: TLabel; lblTickLength: TLabel; lblTitle: TLabel; lblTitleDistance: TLabel; mmoTitle: TMemo; PageControl: TPageControl; PanelTop: TPanel; pgGrid: TTabSheet; pgLabels: TTabSheet; pgLine: TTabSheet; pgTitle: TTabSheet; rgTitleAlignment: TRadioGroup; seLabelDistance: TSpinEdit; seMaximum: TFloatSpinEdit; seMinimum: TFloatSpinEdit; seTickInnerLength: TSpinEdit; seTickLength: TSpinEdit; seTitleDistance: TSpinEdit; Spacer: TBevel; TitleMemoPanel: TPanel; TitleParamsPanel: TPanel; procedure cbAutoMaxChange(Sender: TObject); procedure cbAutoMinChange(Sender: TObject); procedure cbAxisLineVisibleChange(Sender: TObject); procedure cbFrameVisibleChange(Sender: TObject); procedure cbGridVisibleChange(Sender: TObject); procedure cbInvertedChange(Sender: TObject); procedure cbLabelsVisibleChange(Sender: TObject); procedure cbShowChange(Sender: TObject); procedure cbTickColorColorChanged(Sender: TObject); procedure cbTitleHTMLChange(Sender: TObject); procedure cbTitleVisibleChange(Sender: TObject); procedure cbTitleWordwrapChange(Sender: TObject); procedure edLabelFormatEditingDone(Sender: TObject); procedure mmoTitleChange(Sender: TObject); procedure PageControlChanging(Sender: TObject; var AllowChange: Boolean); procedure rgTitleAlignmentClick(Sender: TObject); procedure seLabelDistanceChange(Sender: TObject); procedure seMaximumChange(Sender: TObject); procedure seMinimumChange(Sender: TObject); procedure seTickInnerLengthChange(Sender: TObject); procedure seTickLengthChange(Sender: TObject); procedure seTitleDistanceChange(Sender: TObject); private FAxis: TChartAxis; FAxisMin, FAxisMax: Double; FTitleFontFrame: TChartFontFrame; FTitleShapeBrushPenMarginsFrame: TChartShapeBrushPenMarginsFrame; FLabelFontFrame: TChartFontFrame; FGridPenFrame: TChartPenFrame; FFramePenFrame: TChartPenFrame; FAxisLinePenFrame: TChartPenFrame; FLabelShapeBrushPenMarginsFrame: TChartShapeBrushPenMarginsFrame; FArrowFrame: TChartArrowFrame; function GetAlignment(AItemIndex: Integer): TAlignment; function GetAlignmentIndex(AValue: TAlignment): Integer; function GetPage: TChartAxisEditorPage; procedure SetPage(AValue: TChartAxisEditorPage); procedure ChangedHandler(Sender: TObject); procedure LabelChangedHandler(Sender: TObject); procedure LabelFontChangedHandler(Sender: TObject); procedure LabelShapeChangedHandler(AShape: TChartLabelShape); procedure TitleShapeChangedHandler(AShape: TChartLabelShape); protected procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; {%H-}WithThemeSpace: Boolean); override; function GetChart: TChart; function GetRealAxisMax: Double; function GetRealAxisMin: Double; procedure UpdateControlState; public constructor Create(AOwner: TComponent); override; procedure Prepare(Axis: TChartAxis); function Validate(out AMsg: String; out AControl: TWinControl): Boolean; property Page: TChartAxisEditorPage read GetPage write SetPage; end; implementation {$R *.lfm} uses Math, TAChartUtils, ceUtils; constructor TChartAxisFrame.Create(AOwner: TComponent); begin inherited; FTitleFontFrame := TChartFontFrame.Create(self); FTitleFontFrame.Name := ''; FTitleFontFrame.Align := alClient; FTitleFontFrame.BorderSpacing.Around := 8; FTitleFontFrame.AutoSize := true; FTitleFontFrame.Parent := gbTitleFont; gbTitleFont.AutoSize := true; gbTitleFont.Caption := 'Font'; FTitleShapeBrushPenMarginsFrame := TChartShapeBrushPenMarginsFrame.Create(self); FTitleShapeBrushPenMarginsFrame.Name := ''; FTitleShapeBrushPenMarginsFrame.Align := alClient; FTitleShapeBrushPenMarginsFrame.BorderSpacing.Around := 8; FTitleShapeBrushPenMarginsFrame.OnShapeChange := @TitleShapeChangedHandler; FTitleShapeBrushPenMarginsFrame.AutoSize := true; FTitleShapeBrushPenMarginsFrame.Parent := gbTitleShapeBrushPenMargins; gbTitleShapeBrushPenMargins.AutoSize := true; gbTitleShapeBrushPenMargins.Caption := 'Title background'; FLabelFontFrame := TChartFontFrame.Create(self); FLabelFontFrame.Name := ''; FLabelFontFrame.Align := alClient; FLabelFontFrame.BorderSpacing.Around := 8; FLabelFontFrame.Parent := gbLabelFont; FLabelFontFrame.OnChange := @LabelFontChangedHandler; gbLabelFont.AutoSize := true; gbLabelFont.Caption := 'Label font'; FLabelShapeBrushPenMarginsFrame := TChartShapeBrushPenMarginsFrame.Create(self); FLabelShapeBrushPenMarginsFrame.Name := ''; FLabelShapeBrushPenMarginsFrame.Align := alClient; FLabelShapeBrushPenMarginsFrame.BorderSpacing.Around := 8; FLabelShapeBrushPenMarginsFrame.OnShapeChange := @LabelShapeChangedHandler; FLabelShapeBrushPenMarginsFrame.AutoSize := true; FLabelShapeBrushPenMarginsFrame.Parent := gbShapeFillBorder; gbShapeFillBorder.AutoSize := true; gbShapeFillBorder.Caption := 'Label background'; FGridPenFrame := TChartPenFrame.Create(Self); FGridPenFrame.Name := ''; FGridPenFrame.Align := alTop; FGridPenFrame.Top := 1000; FGridPenFrame.BorderSpacing.Around := 8; FGridPenFrame.Parent := gbGrid; FGridPenFrame.OnChange := @ChangedHandler; gbGrid.AutoSize := true; gbGrid.Caption := 'Grid lines'; FFramePenFrame := TChartPenFrame.Create(Self); FFramePenFrame.Name := ''; FFramePenFrame.Align := alTop; FFramePenFrame.Top := 1000; FFramePenFrame.BorderSpacing.Around := 8; FFramePenFrame.Parent := gbFrame; // FFramePenFrame.OnChange := @ChangedHandler; gbFrame.AutoSize := true; gbFrame.Caption := 'Frame'; FAxisLinePenFrame := TChartPenFrame.Create(Self); FAxisLinePenFrame.Name := ''; FAxisLinePenFrame.Align := alTop; FAxisLinePenFrame.Top := 1000; FAxisLinePenFrame.BorderSpacing.Around := 8; FAxisLinePenFrame.Parent := gbAxisLine; // FAxisLinePenFrame.OnChange := @ChangedHandler; gbAxisLine.AutoSize := true; gbAxisLine.Caption := 'Axis line'; FArrowFrame := TChartArrowFrame.Create(self); FArrowFrame.Name := ''; FArrowFrame.Align := alClient; FArrowFrame.BorderSpacing.Around := 8; FArrowFrame.AutoSize := true; FArrowFrame.Parent := gbArrow; gbArrow.AutoSize := true; gbArrow.Caption := 'Arrow'; BoldHeaders(self); TitleParamsPanel.AutoSize := true; end; procedure TChartAxisFrame.CalculatePreferredSize( var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); var w: Integer = 0; h: Integer = 0; begin gbTitleShapeBrushPenMargins.GetPreferredsize(w, h); // inc(w, FTitleShapeBrushPenMarginsFrame.BorderSpacing.Around); PreferredWidth := w*2 + // Max(w, gbTitleFont.Width) * 2 + Bevel1.Width + TitleMemoPanel.BorderSpacing.Around * 2; PreferredHeight := Max( gbTicks.Top + gbTicks.Height + gbTicks.BorderSpacing.Bottom, gbShapeFillBorder.Top + gbShapeFillBorder.Height + gbShapeFillBorder.BorderSpacing.Bottom ) + PageControl.Height - PageControl.ClientHeight + PanelTop.Height; end; procedure TChartAxisFrame.cbAutoMaxChange(Sender: TObject); begin FAxis.Range.UseMax := not cbAutoMax.Checked; seMaximum.Visible := FAxis.Range.UseMax; end; procedure TChartAxisFrame.cbAutoMinChange(Sender: TObject); begin FAxis.Range.UseMin := not cbAutoMin.Checked; seMinimum.Visible := FAxis.Range.UseMin; end; procedure TChartAxisFrame.cbAxisLineVisibleChange(Sender: TObject); begin FAxis.AxisPen.Visible := cbAxisLineVisible.Checked; UpdateControlState; end; procedure TChartAxisFrame.cbFrameVisibleChange(Sender: TObject); begin GetChart.Frame.Visible := cbFrameVisible.Checked; UpdateControlState; end; procedure TChartAxisFrame.cbGridVisibleChange(Sender: TObject); begin FAxis.Grid.Visible := cbGridVisible.Checked; UpdateControlState; end; procedure TChartAxisFrame.cbInvertedChange(Sender: TObject); begin FAxis.Inverted := cbInverted.Checked; end; procedure TChartAxisFrame.cbLabelsVisibleChange(Sender: TObject); begin FAxis.Marks{%H-}.Visible := cbLabelsVisible.Checked; UpdateControlState; end; procedure TChartAxisFrame.cbShowChange(Sender: TObject); begin FAxis.Visible := cbShow.Checked; PageControl.Visible := cbShow.checked; end; procedure TChartAxisFrame.cbTickColorColorChanged(Sender: TObject); begin FAxis.TickColor := cbTickColor.ButtonColor; end; procedure TChartAxisFrame.cbTitleHTMLChange(Sender: TObject); begin FAxis.Title.TextFormat := TEXT_FORMAT[cbTitleHTML.Checked]; end; procedure TChartAxisFrame.cbTitleVisibleChange(Sender: TObject); begin FAxis.Title.Visible := cbTitleVisible.Checked; UpdateControlState; end; procedure TChartAxisFrame.cbTitleWordwrapChange(Sender: TObject); begin FAxis.Title.Wordwrap := cbTitleWordwrap.Checked; end; procedure TChartAxisFrame.ChangedHandler(Sender: TObject); begin GetChart.Invalidate; end; procedure TChartAxisFrame.edLabelFormatEditingDone(Sender: TObject); begin try FAxis.Marks{%H-}.Format := edLabelFormat.Text; except end; end; function TChartAxisFrame.GetAlignment(AItemIndex: Integer): TAlignment; const ALIGNMENTS: array[0..2] of TAlignment = (taLeftJustify, taCenter, taRightJustify); begin Result := ALIGNMENTS[AItemIndex]; end; function TChartAxisFrame.GetAlignmentIndex(AValue: TAlignment): Integer; const ALIGNMENTS: array[TAlignment] of Integer = (0, 2, 1); begin Result := ALIGNMENTS[AValue]; end; function TChartAxisFrame.GetChart: TChart; begin Result := FAxis.Collection.Owner as TChart; end; function TChartAxisFrame.GetPage: TChartAxisEditorPage; begin Result := TChartAxisEditorPage(PageControl.ActivePageIndex); end; function TChartAxisFrame.GetRealAxisMax: Double; begin if cbAutoMax.Checked then Result := FAxisMax else Result := seMaximum.Value; end; function TChartAxisFrame.GetRealAxisMin: Double; begin if cbAutoMin.Checked then Result := FAxisMin else Result := seMinimum.Value; end; procedure TChartAxisFrame.LabelChangedHandler(Sender: TObject); begin GetChart.Invalidate; end; procedure TChartAxisFrame.LabelFontChangedHandler(Sender: TObject); begin GetChart.Invalidate; end; procedure TChartAxisFrame.LabelShapeChangedHandler(AShape: TChartLabelShape); begin FAxis.Marks{%H-}.Shape := AShape; end; procedure TChartAxisFrame.mmoTitleChange(Sender: TObject); begin FAxis.Title.Caption := mmoTitle.Lines.Text; end; procedure TChartAxisFrame.PageControlChanging(Sender: TObject; var AllowChange: Boolean); var msg: String; C: TWinControl; begin if not Validate(msg, C) then begin C.SetFocus; MessageDlg(msg, mtError, [mbOK], 0); AllowChange := false; end; end; procedure TChartAxisFrame.Prepare(Axis: TChartAxis); begin FAxis := Axis; // Page "Title" cbTitleVisible.Checked := Axis.Title.Visible; cbTitleWordwrap.Checked := Axis.Title.Wordwrap; cbTitleHTML.Checked := (Axis.Title.TextFormat = tfHTML); mmoTitle.Lines.Text := Axis.Title.Caption; with Axis.Title do begin rgTitleAlignment.ItemIndex := GetAlignmentIndex(Alignment); seTitleDistance.Value := Distance; FTitleFontFrame.Prepare(LabelFont, true); FTitleShapeBrushPenMarginsFrame.Prepare(Shape, LabelBrush, Frame, Margins); end; // Page "Labels" GetChart.GetAllSeriesAxisLimits(Axis, FAxisMin, FAxisMax); seMaximum.Value := IfThen(Axis.Range.UseMax, Axis.Range.Max, FAxisMax); seMinimum.Value := IfThen(Axis.Range.UseMin, Axis.Range.Min, FAxisMin); cbAutoMax.Checked := not Axis.Range.UseMax; cbAutoMin.Checked := not Axis.Range.UseMin; cbInverted.Checked := Axis.Inverted; seTickLength.Value := Axis.TickLength; seTickInnerLength.Value := Axis.TickInnerLength; cbTickColor.ButtonColor := Axis.TickColor; with Axis{%H-}.Marks do begin seLabelDistance.Value := Distance; cbLabelsVisible.Checked := Visible; edLabelFormat.Text := Format; FLabelFontFrame.Prepare(LabelFont, true); FLabelShapeBrushPenMarginsFrame.Prepare(Shape, LabelBrush, Frame, Margins); end; // Page "Grid" cbGridVisible.Checked := Axis.Grid.EffVisible; FGridPenFrame.Prepare(Axis.Grid); // Page "Line" cbFrameVisible.Checked := GetChart.Frame.EffVisible; FFramePenFrame.Prepare(GetChart.Frame); cbAxisLineVisible.Checked := Axis.AxisPen.EffVisible; FAxisLinePenFrame.Prepare(Axis.AxisPen); FArrowFrame.Prepare(Axis.Arrow); UpdateControlState; end; procedure TChartAxisFrame.rgTitleAlignmentClick(Sender: TObject); begin if Assigned(FAxis) then FAxis.Title.Alignment := GetAlignment(rgTitleAlignment.ItemIndex); end; procedure TChartAxisFrame.seLabelDistanceChange(Sender: TObject); begin if Assigned(FAxis) then FAxis.Marks{%H-}.Distance := seLabelDistance.Value; end; procedure TChartAxisFrame.seMaximumChange(Sender: TObject); begin if Assigned(FAxis) then begin FAxis.Range.Max := seMaximum.Value; cbAutoMax.Checked := false; end; end; procedure TChartAxisFrame.seMinimumChange(Sender: TObject); begin FAxis.Range.Min := seMinimum.Value; cbAutoMin.Checked := false; end; procedure TChartAxisFrame.SetPage(AValue: TChartAxisEditorPage); begin PageControl.ActivePageIndex := ord(AValue); end; procedure TChartAxisFrame.seTickLengthChange(Sender: TObject); begin if Assigned(FAxis) then FAxis.TickLength := seTickLength.Value; end; procedure TChartAxisFrame.seTickInnerLengthChange(Sender: TObject); begin if Assigned(FAxis) then FAxis.TickInnerLength := seTickInnerLength.Value; end; procedure TChartAxisFrame.seTitleDistanceChange(Sender: TObject); begin if Assigned(FAxis) then FAxis.Title.Distance := seTitleDistance.Value; end; procedure TChartAxisFrame.TitleShapeChangedHandler(AShape: TChartLabelShape); begin FAxis.Title.Shape := AShape; end; procedure TChartAxisFrame.UpdateControlstate; begin // title cbTitleWordwrap.Enabled := cbTitleVisible.Checked; cbTitleHTML.Enabled := cbTitleVisible.Checked; lblTitle.Enabled := cbTitleVisible.Checked; mmoTitle.Enabled := cbTitleVisible.Checked; TitleParamsPanel.Enabled := cbTitleVisible.Checked; // labels lblLabelFormat.Enabled := cbLabelsVisible.Checked; edlabelFormat.Enabled := cbLabelsVisible.Checked; lblLabelDistance.Enabled := cbLabelsVisible.Checked; seLabelDistance.Enabled := cbLabelsVisible.Checked; // grid FGridPenFrame.Enabled := cbGridVisible.Checked; // Line FAxisLinePenFrame.Enabled := cbAxisLineVisible.Checked; FFramePenFrame.Enabled := cbFrameVisible.Checked; end; function TChartAxisFrame.Validate(out AMsg: String; out AControl: TWinControl): Boolean; begin Result := false; if GetRealAxisMin >= GetRealAxisMax then begin AMsg := 'The axis minimum must be smaller than the axis maximum.'; if seMaximum.Visible then AControl := seMaximum else if seMinimum.Visible then AControl := seMinimum else AControl := cbAutoMax; exit; end; Result := true; end; end.