Componentes.Terceros.DevExp.../internal/x.44/1/ExpressFlowChart/Sources/dxflchrt.pas
2009-06-29 12:09:02 +00:00

4306 lines
119 KiB
ObjectPascal

{*******************************************************************}
{ }
{ Developer Express Visual Component Library }
{ ExpressFlowChart }
{ }
{ Copyright (c) 1998-2009 Developer Express Inc. }
{ ALL RIGHTS RESERVED }
{ }
{ The entire contents of this file is protected by U.S. and }
{ International Copyright Laws. Unauthorized reproduction, }
{ reverse-engineering, and distribution of all or any portion of }
{ the code contained in this file is strictly prohibited and may }
{ result in severe civil and criminal penalties and will be }
{ prosecuted to the maximum extent possible under the law. }
{ }
{ RESTRICTIONS }
{ }
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
{ LICENSED TO DISTRIBUTE THE EXPRESSFLOWCHART AND ALL ACCOMPANYING}
{ VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY. }
{ }
{ THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED }
{ FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE }
{ COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE }
{ AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT }
{ AND PERMISSION FROM DEVELOPER EXPRESS INC. }
{ }
{ CONSULT THE end USER LICENSE AGREEMENT FOR INFORMATION ON }
{ ADDITIONAL RESTRICTIONS. }
{ }
{*******************************************************************}
unit dxflchrt;
{$I cxVer.inc}
interface
uses
SysUtils, Classes, Windows, Graphics, Messages, StdCtrls, Forms,
dxLines{$IFDEF DELPHI4}, ImgList{$ENDIF}, Controls;
const
TO_HOME = $8000000;
TO_END = $7FFFFFF;
type
TdxCustomFlowChart = class;
TdxFcItem = class;
TdxFcObject = class;
TdxFcConnection = class;
TdxFcShapeType = (fcsNone, fcsRectangle, fcsEllipse, fcsRoundRect, fcsDiamond,
fcsNorthTriangle, fcsSouthTriangle, fcsEastTriangle, fcsWestTriangle,
fcsHexagon, fcsUser);
TdxFclStyle = (fclStraight, fclCurved, fclRectH, fclRectV);
TdxFcaType = (fcaNone, fcaArrow, fcaEllipse, fcaRectangle);
TdxFcHorzPos = (fchpLeft, fchpCenter, fchpRight);
TdxFcVertPos = (fcvpUp, fcvpCenter, fcvpDown);
TdxFcHitTest = set of (htNowhere, htByObject, htOnObject, htOnConnection,
htOnConLabel, htOnArrowSrc, htOnArrowDst, htOnSelPoint);
TdxFcOptions = set of (fcoCanDelete, fcoCanDrag, fcoCanSelect,
fcoMultiSelect, fcoHideSelection, fcoDelOnClick);
TdxFcDragHandler = procedure(X, Y: Integer; State: TDragState) of object;
TdxFcEvent = procedure(Sender: TdxCustomFlowChart; Item: TdxFcItem) of object;
TdxFcAllowEvent = procedure(Sender: TdxCustomFlowChart; Item: TdxFcItem; var Allow: Boolean) of object;
TdxFcEditEvent = procedure(Sender: TdxCustomFlowChart; AObject: TdxFcObject; var S: string) of object;
TdxFcDrawEvent = procedure(Sender: TdxCustomFlowChart; AObject: TdxFcObject; R: TRect) of object;
TdxFcObjData = packed record
Left: Integer;
Top: Integer;
Width: Word;
Height: Word;
Edge: Word;
Border: Word;
HTPos: TdxFcHorzPos;
VTPos: TdxFcVertPos;
HIPos: TdxFcHorzPos;
VIPos: TdxFcVertPos;
BkColor: TColor;
ShColor: TColor;
Tag: Integer;
ObjCnt: Word;
Image: Smallint;
Shape: TdxFcShapeType;
ShWidth: Byte;
ParFont: Boolean;
Transparent: Boolean;
end;
TdxFcArwData = packed record
AType: TdxFcaType;
Width: Byte;
Height: Byte;
Color: TColor;
end;
TdxFcConData = packed record
ObjSrc: Smallint;
ObjDst: Smallint;
PtCount: Word;
Color: TColor;
PtSrc: Byte;
PtDst: Byte;
Style: TdxFclStyle;
ParFont: Boolean;
ArwSrc: TdxFcArwData;
ArwDst: TdxFcArwData;
end;
TdxFcFntData = packed record
Height: Smallint;
Color: TColor;
Pitch: TFontPitch;
Style: TFontStyles;
Charset: TFontCharset;
end;
TdxFcDragData = packed record
Index: Word;
Base: TPoint;
Mobile: TPoint;
case Integer of
0: (Rgn: HRgn);
1: (Connect: TdxFcConnection);
end;
TdxFcItem = class(TPersistent)
private
FOwner: TdxCustomFlowChart;
FText: string;
FFont: TFont;
FRealFont: TFont;
FParentFont: Boolean;
FDestroying: Boolean;
FSelected: Boolean;
FRepainted: Boolean;
procedure OnFontChange(Sender: TObject);
procedure SetRealFont;
procedure SetFont(Value: TFont);
procedure SetParentFont(Value: Boolean);
procedure SetSelected(Value: Boolean);
protected
function SelList: TList; virtual; abstract;
procedure Changed;
procedure FontChanged; virtual;
procedure Invalidate; virtual; abstract;
procedure LoadFont(Stream: TStream; AIsUnicode: Boolean);
procedure SaveFont(Stream: TStream);
procedure SetText(Value: string); virtual; abstract;
procedure ScaleFont;
property RealFont: TFont read FRealFont;
public
constructor Create(AOwner: TdxCustomFlowChart);
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Destroying: Boolean read FDestroying;
property Font: TFont read FFont write SetFont;
property Owner: TdxCustomFlowChart read FOwner;
property ParentFont: Boolean read FParentFont write SetParentFont;
property Selected: Boolean read FSelected write SetSelected;
property Text: string read FText write SetText;
end;
TdxFcObject = class(TdxFcItem)
private
FConnections: TList;
FLinkedObjects: TList;
FObjects: TList;
FLeft: Integer;
FTop: Integer;
FWidth: Word;
FHeight: Word;
FEdge: Word;
FBorder: Word;
FIntRgn: HRgn;
FExtRgn: HRgn;
FPaintRgn: HRgn;
FRealLeft: Integer;
FRealTop: Integer;
FRealWidth: Integer;
FRealHeight: Integer;
FHorzTextPos: TdxFcHorzPos;
FVertTextPos: TdxFcVertPos;
FImageIndex: Smallint;
FHorzImagePos: TdxFcHorzPos;
FVertImagePos: TdxFcVertPos;
FTransparent: Boolean;
FVisible: Boolean;
FBkColor: TColor;
FShapeColor: TColor;
FRealSW: Word;
FShapeType: TdxFcShapeType;
FShapeWidth: Byte;
FShapeStyle: TPenStyle;
FData: Pointer;
FCustomData: string;
FTag: Integer;
FBkBrush: TBrush;
FShapeBrush: TBrush;
function Create1Rgn(Offset: Integer): HRgn;
function ClientRect: TRect;
function DisplayRect: TRect;
function GetLinkedObject(Index: Integer): TdxFcObject;
function GetConnection(Index: Integer): TdxFcConnection;
function GetObjectValue(Index: Integer): TdxFcObject; // renamed because of C++Builder
function GetIsUnion: Boolean;
function GetConnectionCount: Integer;
function GetLinkedObjectCount: Integer;
function GetObjectCount: Integer;
function GetPoint(const P: array of TPoint; X, Y, Cnt: Integer): Integer;
function GetZOrder: Word;
function HasEdge: Boolean;
function HasImage: Boolean;
function Opaque: Boolean;
function Quadrant(X, Y: Integer): Integer;
procedure CalculateLinkedPoints;
procedure CreateRgn;
procedure DeleteRgn;
procedure IsRepainted(Rgn: HRgn);
procedure Paint;
procedure PaintFrame;
procedure ResolveObjRefs;
procedure SelPoints(var Pts: array of TPoint);
procedure SetBkColor(Value: TColor);
procedure SetBorder(Value: Word);
procedure SetEdge(Value: Word);
procedure SetHeight(Value: Word);
procedure SetHorzImagePos(Value: TdxFcHorzPos);
procedure SetHorzTextPos(Value: TdxFcHorzPos);
procedure SetImageIndex(Value: Smallint);
procedure SetLeft(Value: Integer);
procedure SetRealBounds;
procedure SetRealSW;
procedure SetShapeColor(Value: TColor);
procedure SetShapeType(Value: TdxFcShapeType);
procedure SetShapeStyle(Value: TPenStyle);
procedure SetShapeWidth(Value: Byte);
procedure SetTransparent(Value: Boolean);
procedure SetTop(Value: Integer);
procedure SetVertImagePos(Value: TdxFcVertPos);
procedure SetVertTextPos(Value: TdxFcVertPos);
procedure SetVisible(Value: Boolean);
procedure SetWidth(Value: Word);
procedure SetZOrder(Value: Word);
procedure UpdateConnections;
procedure ZoomChanged;
protected
LinkedPoints: array[0..15] of TPoint;
function SelList: TList; override;
function UserRegion(R: TRect): HRgn; virtual;
procedure Load(Stream: TStream; AIsUnicode: Boolean); virtual;
procedure Save(Stream: TStream); virtual;
procedure Invalidate; override;
procedure SetText(Value: string); override;
procedure UserLinkedPoints; virtual;
property RealSW: Word read FRealSW;
public
constructor Create(AOwner: TdxCustomFlowChart);
destructor Destroy; override;
function GetSelPoint(X, Y: Integer): Integer;
function GetLinkedPoint(X, Y: Integer): Integer;
function HasInUnion(AObject: TdxFcObject): Boolean;
function InRect(const R: TRect): Boolean;
procedure Assign(Source: TPersistent); override;
procedure AddToUnion(AObject: TdxFcObject);
procedure RemoveFromUnion(AObject: TdxFcObject);
procedure BringToFront;
procedure ClearUnion;
procedure MakeVisible;
procedure PaintImage(R: TRect);
procedure PaintText(R: TRect);
procedure PutInFrontOf(Value: TdxFcObject);
procedure SelectUnion;
procedure SendToBack;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
property BkColor: TColor read FBkColor write SetBkColor;
property BorderStyle: Word read FBorder write SetBorder;
property CustomData: string read FCustomData write FCustomData;
property Data: Pointer read FData write FData;
property EdgeStyle: Word read FEdge write SetEdge;
property Height: Word read FHeight write SetHeight;
property HorzImagePos: TdxFcHorzPos read FHorzImagePos write SetHorzImagePos;
property HorzTextPos: TdxFcHorzPos read FHorzTextPos write SetHorzTextPos;
property ImageIndex: Smallint read FImageIndex write SetImageIndex;
property IsUnion: Boolean read GetIsUnion;
property Left: Integer read FLeft write SetLeft;
property ConnectionCount: Integer read GetConnectionCount;
property LinkedObjectCount: Integer read GetLinkedObjectCount;
property LinkedObjects[Index: Integer]: TdxFcObject read GetLinkedObject;
property Connections[Index: Integer]: TdxFcConnection read GetConnection;
property ObjectCount: Integer read GetObjectCount;
property Objects[Index: Integer]: TdxFcObject read GetObjectValue;
property RealLeft: Integer read FRealLeft;
property RealTop: Integer read FRealTop;
property RealWidth: Integer read FRealWidth;
property RealHeight: Integer read FRealHeight;
property ShapeColor: TColor read FShapeColor write SetShapeColor;
property ShapeStyle: TPenStyle read FShapeStyle write SetShapeStyle;
property ShapeType: TdxFcShapeType read FShapeType write SetShapeType;
property ShapeWidth: Byte read FShapeWidth write SetShapeWidth;
property Tag: Integer read FTag write FTag;
property Top: Integer read FTop write SetTop;
property Transparent: Boolean read FTransparent write SetTransparent;
property VertImagePos: TdxFcVertPos read FVertImagePos write SetVertImagePos;
property VertTextPos: TdxFcVertPos read FVertTextPos write SetVertTextPos;
property Visible: Boolean read FVisible write SetVisible;
property Width: Word read FWidth write SetWidth;
property ZOrder: Word read GetZOrder write SetZOrder;
end;
TdxFcConnectionArrow = class(TPersistent)
private
FOwner: TdxFcConnection;
FArrowType: TdxFcaType;
FHeight: Byte;
FWidth: Byte;
FRealHeight: Word;
FRealWidth: Word;
FColor: TColor;
FBrush: TBrush;
FPoints: array[0..3] of TPoint;
function Active: Boolean;
function DisplayRect(Ext: Boolean): TRect;
procedure ClearPoints;
procedure OffsetPoints(DX, DY: Integer);
procedure Paint;
procedure Reset;
procedure SetPoints(Index: Integer);
procedure SetRealBounds;
procedure SetArrowType(Value: TdxFcaType);
procedure SetHeight(Value: Byte);
procedure SetWidth(Value: Byte);
procedure SetColor(Value: TColor);
public
constructor Create(AOwner: TdxFcConnection);
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Owner: TdxFcConnection read FOwner;
property ArrowType: TdxFcaType read FArrowType write SetArrowType;
property Height: Byte read FHeight write SetHeight;
property Width: Byte read FWidth write SetWidth;
property Color: TColor read FColor write SetColor;
end;
(*
Point1 and Point2 properties is the linked point type.
It may have value from 0..15.
0 1 2 3 4
15 5
14 6
13 7
12 11 10 9 8
*)
TdxFcConnection = class(TdxFcItem)
private
FPoints: TList;
FRealPoints: TList;
FStyle: TdxFclStyle;
FObjectSource: TdxFcObject;
FObjectDest: TdxFcObject;
FDisplayRect: TRect;
FTextRect: TRect;
FMassCenter: TPoint;
FPointSource: Byte;
FPointDest: Byte;
FArrowSource: TdxFcConnectionArrow;
FArrowDest: TdxFcConnectionArrow;
FTransparent: Boolean;
FPen: TPen;
function DisplayRect: TRect;
function GetColor: TColor;
function GetPenStyle: TPenStyle;
function GetPenWidth: Integer;
function GetPointCount: Integer;
function GetPoint(Index: Integer): TPoint;
function GetRealPoint(Index: Integer): TPoint;
function HasPoint(X, Y: Integer): Boolean;
function IndexValid(var Index: Integer; AMax: Integer): Boolean;
function InternalGetPoint(List: TList; Index: Integer): TPoint;
function RealCount: Integer;
function RealStyle: TdxFclStyle;
function ScreenPoint(Index: Integer): TPoint;
procedure DelObj(AObj, Partneur: TdxFcObject; Index: Integer);
procedure InsObj(AObj, Partneur: TdxFcObject; Index: Integer);
procedure InternalInsertPoint(List: TList; Index: Integer; Value: TPoint);
procedure InternalPutPoint(List: TList; Index: Integer; Value: TPoint);
procedure InternalRemovePoint(List: TList; Index: Integer);
procedure InvalidateText;
procedure InvertColor;
procedure IsRepainted;
procedure NewPoint(X, Y: Integer; Handler: TdxFcDragHandler);
procedure OffsetPoints(List: TList; DX, DY: Integer);
procedure Paint(Upper: Boolean);
procedure PaintLine(DC: HDC);
procedure PlaceText;
procedure PutPoint(Index: Integer; Value: TPoint);
procedure SetArrowSource(Value: TdxFcConnectionArrow);
procedure SetArrowDest(Value: TdxFcConnectionArrow);
procedure SetColor(Value: TColor);
procedure SetDisplayRect;
procedure SetObjectPoints;
procedure SetPenStyle(Value: TPenStyle);
procedure SetPenWidth(Value: Integer);
procedure SetStyle(Value: TdxFclStyle);
procedure SetTextRect;
procedure SetTransparent(Value: Boolean);
procedure ZoomChanged;
protected
function SelList: TList; override;
procedure Load(Stream: TStream; AIsUnicode: Boolean); virtual;
procedure Save(Stream: TStream); virtual;
procedure FontChanged; override;
procedure Invalidate; override;
procedure SetText(Value: string); override;
procedure ArrowChanged(Value: TdxFcConnectionArrow);
procedure ConnectionChanged;
property RealPoints[Index: Integer]: TPoint read GetRealPoint;
public
constructor Create(AOwner: TdxCustomFlowChart);
destructor Destroy; override;
function GetNearestPoint(X, Y: Integer): Integer;
function InRect(const R: TRect): Boolean;
procedure Assign(Source: TPersistent); override;
procedure AddPoint(Pt: TPoint);
procedure InsertPoint(Index: Integer; Pt: TPoint);
procedure RemovePoint(Index: Integer);
procedure SetObjectSource(AObject: TdxFcObject; APoint: Byte);
procedure SetObjectDest(AObject: TdxFcObject; APoint: Byte);
property ArrowSource: TdxFcConnectionArrow read FArrowSource write SetArrowSource;
property ArrowDest: TdxFcConnectionArrow read FArrowDest write SetArrowDest;
property Color: TColor read GetColor write SetColor;
property ObjectSource: TdxFcObject read FObjectSource;
property ObjectDest: TdxFcObject read FObjectDest;
property PenStyle: TPenStyle read GetPenStyle write SetPenStyle;
property PenWidth: Integer read GetPenWidth write SetPenWidth; // is not saved in the stream/file
property PointCount: Integer read GetPointCount;
property Points[Index: Integer]: TPoint read GetPoint write PutPoint;
property PointSource: Byte read FPointSource;
property PointDest: Byte read FPointDest;
property Style: TdxFclStyle read FStyle write SetStyle;
property Transparent: Boolean read FTransparent write SetTransparent;
end;
TdxFcSelection = class
private
Owner: TdxCustomFlowChart;
Counts: TList;
Points: TList;
procedure AddPoint(X, Y: Integer);
procedure Clear;
procedure Paint;
public
constructor Create(AOwner: TdxCustomFlowChart);
destructor Destroy; override;
end;
TdxCustomFlowChart = class(TCustomControl)
private
FObjects: TList;
FConnections: TList;
FSelObjects: TList;
FSelConnections: TList;
FSelection: TdxFcSelection;
FLeftEdge: Integer;
FTopEdge: Integer;
FChartLeft: Integer;
FChartTop: Integer;
FChartWidth: Integer;
FChartHeight: Integer;
FLockUpdates: Integer;
FImages: TImageList;
FChangeLink: TChangeLink;
FZoom: Word;
FRealZoom: Word;
FObjectAt: TdxFcObject;
FConnectionAt: TdxFcConnection;
FHitX: Integer;
FHitY: Integer;
FDragX: Integer;
FDragY: Integer;
FDragHandler: TdxFcDragHandler;
FDragData: TdxFcDragData;
FHitTest: TdxFcHitTest;
FBorderStyle: TBorderStyle;
FOptions: TdxFcOptions;
FRepaint: Boolean;
FLoading: Boolean;
FOnChange: TdxFcEvent;
FOnCreateItem: TdxFcEvent;
FOnDeletion: TdxFcEvent;
FOnDrawObject: TdxFcDrawEvent;
FOnEdited: TdxFcEditEvent;
FOnEditing: TdxFcAllowEvent;
FOnSelected: TdxFcEvent;
FOnSelection: TdxFcAllowEvent;
function CanPaint: Boolean;
function GetConnection(Index: Integer): TdxFcConnection;
function GetObjectValue(Index: Integer): TdxFcObject; // renamed because of C++Builder
function GetConnectionCount: Integer;
function GetObjectCount: Integer;
function GetSelConnect: TdxFcConnection;
function GetSelObj: TdxFcObject;
function GetSelectedObject(Index: Integer): TdxFcObject;
function GetSelectedObjectCount: Integer;
function GetSelectedConnection(Index: Integer): TdxFcConnection;
function GetSelectedConnectionCount: Integer;
function HasSelection: Boolean;
function TmpSel: Integer;
procedure AbortDrag;
procedure AddSelectedObject(AObject: TdxFcObject);
procedure AddSelectedConnection(AConnection: TdxFcConnection);
procedure CallDragHandler(X, Y: Integer; State: TDragState);
procedure ChkDrag(Shift: TShiftState; X, Y: Integer);
procedure DragMove(X, Y: Integer; State: TDragState);
procedure DragResize(X, Y: Integer; State: TDragState);
procedure DragPoint(X, Y: Integer; State: TDragState);
procedure DragConnect(X, Y: Integer; State: TDragState);
procedure HitTest(X, Y: Integer);
procedure InitDrag(X, Y: Integer; Handler: TdxFcDragHandler);
procedure InvalidateSel;
procedure MoveObjects(DX, DY: Integer);
procedure RestoreSel(Value: Integer);
procedure ScalePoint(var P: TPoint);
procedure SetChartSizes;
procedure SetConnection(Index: Integer; Value: TdxFcConnection);
procedure SetObjectValue(Index: Integer; Value: TdxFcObject);
procedure SetBorderStyle(Value: TBorderStyle);
procedure SetImages(Value: TImageList);
procedure SetLeftEdge(Value: Integer);
procedure SetOptions(Value: TdxFcOptions);
procedure SetSelConnect(Value: TdxFcConnection);
procedure SetSelObj(Value: TdxFcObject);
procedure SetTopEdge(Value: Integer);
procedure SetZoom(Value: Word);
procedure ScrollChart(Bar, Code, Pos: Cardinal; Value, Page: Integer);
procedure OnChangeLink(Sender: TObject);
procedure CalculateRealPos;
procedure UpdateScrollRange;
procedure WMErase(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
procedure WMSize(var Msg: TWMSize); message WM_SIZE;
procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
procedure CMCtl3DChanged(var Msg: TMessage); message CM_CTL3DCHANGED;
procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
protected
function CanSelect(Item: TdxFcItem): Boolean; virtual;
function InternalCreateObject: TdxFcObject; virtual;
function InternalCreateConnection: TdxFcConnection; virtual;
procedure Changed(Item: TdxFcItem); virtual;
procedure DefaultDrawObject(AObject: TdxFcObject; R: TRect); virtual;
procedure Delete(Item: TdxFcItem); virtual;
procedure Select(Item: TdxFcItem); virtual;
procedure CreateParams(var Params: TCreateParams); override;
procedure DefineProperties(Filer: TFiler); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure Paint; override;
procedure NeedRepaint;
procedure NeedRepaintObject(AObject: TdxFcObject);
procedure WndProc(var Message: TMessage); override;
property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
property ChartHeight: Integer read FChartHeight;
property ChartWidth: Integer read FChartWidth;
property Images: TImageList read FImages write SetImages;
property Options: TdxFcOptions read FOptions write SetOptions;
property Zoom: Word read FZoom write SetZoom default 100;
property OnChange: TdxFcEvent read FOnChange write FOnChange;
property OnCreateItem: TdxFcEvent read FOnCreateItem write FOnCreateItem;
property OnDeletion: TdxFcEvent read FOnDeletion write FOnDeletion;
property OnDrawObject: TdxFcDrawEvent read FOnDrawObject write FOnDrawObject;
property OnEdited: TdxFcEditEvent read FOnEdited write FOnEdited;
property OnEditing: TdxFcAllowEvent read FOnEditing write FOnEditing;
property OnSelected: TdxFcEvent read FOnSelected write FOnSelected;
property OnSelection: TdxFcAllowEvent read FOnSelection write FOnSelection;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
function ChartPoint(X, Y: Integer): TPoint;
function CreateObject(L, T, W, H: Integer; AShape: TdxFcShapeType): TdxFcObject;
function CreateConnection(OSrc, ODst: TdxFcObject; PSrc, PDst: Byte): TdxFcConnection;
function GetConnectionAt(X, Y: Integer): TdxFcConnection;
function GetObjectAt(X, Y: Integer): TdxFcObject;
function GetHitTestAt(X, Y: Integer): TdxFcHitTest;
function SelCount: Integer;
procedure BeginUpdate;
procedure CancelUpdate;
procedure EndUpdate;
procedure Clear;
procedure ClearSelection;
procedure DeleteSelection;
procedure DeleteConnection(AConnection: TdxFcConnection);
procedure DeleteObject(AObject: TdxFcObject);
procedure Invalidate; override;
procedure LoadFromFile(const FileName: string);
procedure LoadFromStream(Stream: TStream); virtual;
procedure SaveToFile(const FileName: string);
procedure SaveToStream(Stream: TStream); virtual;
procedure SelectAll;
procedure SetLeftTop(ALeft, ATop: Integer);
property ConnectionCount: Integer read GetConnectionCount;
property Connections[Index: Integer]: TdxFcConnection read GetConnection write SetConnection;
property LeftEdge: Integer read FLeftEdge write SetLeftEdge;
property TopEdge: Integer read FTopEdge write SetTopEdge;
property ObjectCount: Integer read GetObjectCount;
property Objects[Index: Integer]: TdxFcObject read GetObjectValue write SetObjectValue;
property RealZoom: Word read FRealZoom;
property SelectedObject: TdxFcObject read GetSelObj write SetSelObj;
property SelectedConnection: TdxFcConnection read GetSelConnect write SetSelConnect;
property SelectedObjects[Index: Integer]: TdxFcObject read GetSelectedObject;
property SelectedObjectCount: Integer read GetSelectedObjectCount;
property SelectedConnections[Index: Integer]: TdxFcConnection read GetSelectedConnection;
property SelectedConnectionCount: Integer read GetSelectedConnectionCount;
end;
TdxFlowChart = class(TdxCustomFlowChart)
published
property BorderStyle;
property Images;
property Options;
property Zoom;
property OnChange;
property OnCreateItem;
property OnDeletion;
property OnDrawObject;
property OnEdited;
property OnEditing;
property OnSelected;
property OnSelection;
property Align;
property Ctl3D;
property Color;
property Enabled;
property Font;
property ParentColor default False;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property TabStop default True;
property Visible;
property OnClick;
property OnDblClick;
property OnEnter;
property OnExit;
property OnDragDrop;
property OnDragOver;
property OnStartDrag;
property OnEndDrag;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
end;
implementation
const
StreamDescriptionANSI: AnsiString = 'VER1.0A';
StreamDescriptionUNICODE: AnsiString = 'VER1.0U';
procedure Swap(var A, B: Integer);
var
C: Integer;
begin
C := A; A := B; B := C;
end;
procedure NormRect(var R: TRect);
begin
if R.Left > R.Right then Swap(R.Left, R.Right);
if R.Top > R.Bottom then Swap(R.Top, R.Bottom);
end;
procedure ExtSelRect(var R: TRect; Sel: Boolean);
begin
if Sel then InflateRect(R, 3, 3);
end;
function GetListItem(List: TList; Index: Integer): Pointer;
begin
Result := nil;
if (Index >= 0) and (Index < List.Count) then Result := List[Index];
end;
procedure InitBrush(var ABrush: TBrush; AColor: TColor);
begin
if ABrush = nil then
begin
ABrush := TBrush.Create;
ABrush.Color := AColor;
end;
end;
function ReadStr(Stream: TStream; AIsUnicode: Boolean): string;
var
L: Word;
SA: AnsiString;
SW: WideString;
begin
Stream.ReadBuffer(L, SizeOf(Word));
if AIsUnicode then
begin
SetLength(SW, L);
if L > 0 then Stream.ReadBuffer(SW[1], L * 2);
Result := SW;
end
else
begin
SetLength(SA, L);
if L > 0 then Stream.ReadBuffer(SA[1], L);
{$IFDEF DELPHI12}
Result := UTF8ToWideString(SA);
{$ELSE}
Result := SA;
{$ENDIF}
end;
end;
procedure WriteStr(Stream: TStream; const S: string);
var
L: Integer;
{$IFDEF STREAMANSIFORMAT}
SA: AnsiString;
{$ENDIF}
begin
L := Length(S);
if L > $FFFF then L := $FFFF;
Stream.WriteBuffer(L, SizeOf(Word));
if L > 0 then
begin
{$IFDEF STREAMANSIFORMAT}
{$IFDEF DELPHI12}
SA := UTF8Encode(S);
{$ELSE}
SA := S;
{$ENDIF}
Stream.WriteBuffer(SA[1], L);
{$ELSE}
Stream.WriteBuffer(S[1], L * SizeOf(Char));
{$ENDIF}
end;
end;
function QDistance(X, Y: Integer; const P: TPoint): Integer;
var
DX, DY: Integer;
begin
DX := X - P.X; DY := Y - P.Y;
Result := DX * DX + DY * DY;
end;
function AdjustRect(var R: TRect; const Bounds: TRect; HPos: TdxFcHorzPos; VPos: TdxFcVertPos): Boolean;
var
DX, DY: Integer;
begin
DX := Bounds.Right - Bounds.Left + R.Left - R.Right;
DY := Bounds.Bottom - Bounds.Top + R.Top - R.Bottom;
Result := (DX >= 0) and (DY >= 0);
if DX < 0 then Inc(R.Right, DX);
if DY < 0 then Inc(R.Bottom, DY);
if (DX < 0) or (HPos = fchpLeft) then DX := 0;
if (DY < 0) or (VPos = fcvpUp) then DY := 0;
if HPos = fchpCenter then DX := DX shr 1;
if VPos = fcvpCenter then DY := DY shr 1;
DX := DX + Bounds.Left - R.Left;
DY := DY + Bounds.Top - R.Top;
Inc(R.Left, DX); Inc(R.Right, DX);
Inc(R.Top, DY); Inc(R.Bottom, DY);
end;
{TdxFcConnectionArrow}
constructor TdxFcConnectionArrow.Create(AOwner: TdxFcConnection);
begin
FOwner := AOwner;
FColor := AOwner.Owner.Color;
end;
destructor TdxFcConnectionArrow.Destroy;
begin
FBrush.Free;
end;
procedure TdxFcConnectionArrow.ClearPoints;
begin
if ArrowType = fcaArrow then
begin
FPoints[1] := Point(0, 0);
FPoints[3] := Point(0, 0);
end;
end;
procedure TdxFcConnectionArrow.Reset;
begin
ClearPoints;
Owner.ArrowChanged(Self);
Owner.Changed;
end;
procedure TdxFcConnectionArrow.SetRealBounds;
begin
FRealWidth := MulDiv(Width, Owner.Owner.RealZoom, 100);
FRealHeight := MulDiv(Height, Owner.Owner.RealZoom, 100);
end;
procedure TdxFcConnectionArrow.SetArrowType(Value: TdxFcaType);
begin
if (FArrowType <> Value) then
begin
Owner.ArrowChanged(Self);
FArrowType := Value;
Reset;
end;
end;
procedure TdxFcConnectionArrow.SetHeight(Value: Byte);
begin
if (FHeight <> Value) then
begin
Owner.ArrowChanged(Self);
FHeight := Value;
SetRealBounds;
Reset;
end;
end;
procedure TdxFcConnectionArrow.SetWidth(Value: Byte);
begin
if (FWidth <> Value) then
begin
Owner.ArrowChanged(Self);
FWidth := Value;
SetRealBounds;
Reset;
end;
end;
procedure TdxFcConnectionArrow.SetColor(Value: TColor);
begin
if (FColor <> Value) then
begin
FColor := Value;
if FBrush <> nil then FBrush.Color := Value;
if ArrowType in [fcaRectangle, fcaEllipse] then Owner.ArrowChanged(Self);
Owner.Changed;
end;
end;
function TdxFcConnectionArrow.Active: Boolean;
begin
Result := (ArrowType <> fcaNone) and (Owner.RealCount > 1);
end;
function TdxFcConnectionArrow.DisplayRect(Ext: Boolean): TRect;
begin
if ArrowType = fcaArrow then
begin
Result.TopLeft := FPoints[1];
Result.BottomRight := FPoints[1];
ExtendRect(Result, FPoints[0]);
ExtendRect(Result, FPoints[2]);
end
else
with Result do
begin
Left := FPoints[1].X - FRealWidth shr 1;
Top := FPoints[1].Y - FRealHeight shr 1;
Right := Left + FRealWidth;
Bottom := Top + FRealHeight;
end;
ExtSelRect(Result, Ext);
end;
procedure TdxFcConnectionArrow.SetPoints(Index: Integer);
var
DX, DY, DXY, IsRect: Integer;
P1, P3: TPoint;
procedure Rotate(var P: TPoint);
var
X, Y: Integer;
begin
X := (P.X * DX - P.Y * DY) div DXY;
Y := (P.X * DY + P.Y * DX) div DXY;
P.X := X + FPoints[1].X;
P.Y := Y + FPoints[1].Y;
end;
begin
if not Active then Exit;
IsRect := Ord(Owner.Style) xor Ord(Index <> 0);
P1 := Owner.InternalGetPoint(Owner.FRealPoints, Index);
if Index = 0 then
Inc(Index)
else
Dec(Index);
P3 := Owner.InternalGetPoint(Owner.FRealPoints, Index);
if (P1.X = FPoints[1].X) and (P1.Y = FPoints[1].Y) and (P3.X = FPoints[3].X) and (P3.Y = FPoints[3].Y)
then Exit;
FPoints[1] := P1; FPoints[3] := P3;
if ArrowType = fcaArrow then
begin
DX := P3.X - P1.X; DY := P3.Y - P1.Y;
if (IsRect = 2) and (DX <> 0) then DY := 0;
if (IsRect = 3) and (DY <> 0) then DX := 0;
if (DX = 0) or (DY = 0) then
DXY := Abs(DX + DY)
else
DXY := Round(Sqrt(DX * DX + DY * DY));
if DXY = 0 then DXY := 1;
FPoints[0].X := FRealWidth; FPoints[0].Y := (FRealHeight + 1) shr 1;
FPoints[2].X := FRealWidth; FPoints[2].Y := -FPoints[0].Y;
Rotate(FPoints[0]); Rotate(FPoints[2]);
end;
end;
procedure TdxFcConnectionArrow.OffsetPoints(DX, DY: Integer);
var
I: Integer;
begin
if not Active then Exit;
for I := 0 to 3 do
begin
Inc(FPoints[I].X, DX);
Inc(FPoints[I].Y, DY);
end;
end;
procedure TdxFcConnectionArrow.Assign(Source: TPersistent);
begin
if Source is TdxFcConnectionArrow then
with TdxFcConnectionArrow(Source) do
begin
Self.Width := Width; Self.Height := Height;
Self.Color := Color; Self.ArrowType := ArrowType;
end
else
inherited Assign(Source);
end;
procedure TdxFcConnectionArrow.Paint;
var
DC: HDC;
R: TRect;
begin
if not Active then Exit;
DC := Owner.Owner.Canvas.Handle;
if ArrowType = fcaArrow then
Polyline(DC, FPoints, 3)
else
begin
R := DisplayRect(False);
InitBrush(FBrush, Color);
SelectObject(DC, FBrush.Handle);
if ArrowType = fcaRectangle
then
Rectangle(DC, R.Left, R.Top, R.Right, R.Bottom)
else
Ellipse(DC, R.Left, R.Top, R.Right, R.Bottom);
end;
end;
{TdxFcObject}
constructor TdxFcObject.Create(AOwner: TdxCustomFlowChart);
begin
inherited Create(AOwner);
FConnections := TList.Create;
FLinkedObjects := TList.Create;
FObjects := TList.Create;
FBkColor := AOwner.Color;
FVisible := True;
FImageIndex := -1;
FShapeWidth := 1;
FBorder := BF_RECT;
SetRealSW;
AOwner.FObjects.Add(Self);
end;
destructor TdxFcObject.Destroy;
var
I: Integer;
begin
Owner.Delete(Self);
DeleteRgn;
if FPaintRgn <> 0 then DeleteObject(FPaintRgn);
while ConnectionCount > 0 do
Connections[0].Free;
FConnections.Free;
FConnections := nil;
FLinkedObjects.Free;
FLinkedObjects := nil;
FObjects.Free;
FObjects := nil;
FShapeBrush.Free;
FBkBrush.Free;
Owner.FObjects.Remove(Self);
with Owner do
for I := 0 to ObjectCount - 1 do
Objects[I].FObjects.Remove(Self);
Owner.SetChartSizes;
inherited Destroy;
end;
procedure TdxFcObject.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
if (ALeft = Left) and (ATop = Top) and (AWidth = Width) and (AHeight = Height) then Exit;
FLeft := ALeft; FTop := ATop;
FWidth := Word(AWidth); FHeight := Word(AHeight);
with Owner do
begin
NeedRepaintObject(Self);
SetRealBounds;
UpdateConnections;
NeedRepaintObject(Self);
SetChartSizes;
end;
end;
function TdxFcObject.GetLinkedObject(Index: Integer): TdxFcObject;
begin
Result := TdxFcObject(GetListItem(FLinkedObjects, Index));
end;
function TdxFcObject.GetConnection(Index: Integer): TdxFcConnection;
begin
Result := TdxFcConnection(GetListItem(FConnections, Index));
end;
function TdxFcObject.GetObjectValue(Index: Integer): TdxFcObject;
begin
Result := TdxFcObject(GetListItem(FObjects, Index));
end;
function TdxFcObject.GetIsUnion: Boolean;
begin
Result := FObjects.Count > 0;
end;
function TdxFcObject.GetConnectionCount: Integer;
begin
Result := FConnections.Count;
end;
function TdxFcObject.GetLinkedObjectCount: Integer;
begin
Result := FLinkedObjects.Count;
end;
function TdxFcObject.GetObjectCount: Integer;
begin
Result := FObjects.Count;
end;
function TdxFcObject.SelList: TList;
begin
Result := Owner.FSelObjects;
end;
function TdxFcObject.HasEdge: Boolean;
begin
Result := (ShapeType = fcsRectangle) and (EdgeStyle <> 0);
end;
function TdxFcObject.HasImage: Boolean;
begin
Result := (Owner.Images <> nil) and (ImageIndex >= 0) and (ImageIndex < Owner.Images.Count);
end;
procedure TdxFcObject.SetBkColor(Value: TColor);
begin
if FBkColor <> Value then
begin
FBkColor := Value;
if FBkBrush <> nil then FBkBrush.Color := Value;
if not Transparent and (ShapeType <> fcsNone) then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetHeight(Value: Word);
begin
SetBounds(Left, Top, Width, Value);
end;
procedure TdxFcObject.SetHorzImagePos(Value: TdxFcHorzPos);
begin
if (FHorzImagePos <> Value) then
begin
FHorzImagePos := Value;
if HasImage then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetHorzTextPos(Value: TdxFcHorzPos);
begin
if (FHorzTextPos <> Value) then
begin
FHorzTextPos := Value;
if Text <> '' then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetImageIndex(Value: Smallint);
begin
if (FImageIndex <> Value) then
begin
FImageIndex := Value;
if Owner.Images <> nil then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetLeft(Value: Integer);
begin
SetBounds(Value, Top, Width, Height);
end;
procedure TdxFcObject.SetShapeColor(Value: TColor);
begin
if FShapeColor <> Value then
begin
FShapeColor := Value;
if FShapeBrush <> nil then FShapeBrush.Color := Value;
if ShapeType <> fcsNone then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetShapeType(Value: TdxFcShapeType);
begin
if FShapeType <> Value then
begin
FShapeType := Value;
CalculateLinkedPoints;
UpdateConnections;
Owner.NeedRepaintObject(Self);
end;
end;
procedure TdxFcObject.SetShapeStyle(Value: TPenStyle);
begin
if FShapeStyle <> Value then
begin
FShapeStyle := Value;
Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetShapeWidth(Value: Byte);
begin
if FShapeWidth <> Value then
begin
DeleteRgn;
FShapeWidth := Value;
SetRealSW;
if ShapeType <> fcsNone then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetText(Value: string);
begin
if FText <> Value then
begin
FText := Value;
Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetTransparent(Value: Boolean);
begin
if FTransparent <> Value then
begin
FTransparent := Value;
if ShapeType <> fcsNone then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetTop(Value: Integer);
begin
SetBounds(Left, Value, Width, Height);
end;
procedure TdxFcObject.SetVertImagePos(Value: TdxFcVertPos);
begin
if FVertImagePos <> Value then
begin
FVertImagePos := Value;
if HasImage then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetVertTextPos(Value: TdxFcVertPos);
begin
if FVertTextPos <> Value then
begin
FVertTextPos := Value;
if Text <> '' then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetVisible(Value: Boolean);
begin
if FVisible <> Value then
begin
FVisible := Value;
Owner.FHitTest := [];
Owner.NeedRepaintObject(Self);
end;
end;
procedure TdxFcObject.SetWidth(Value: Word);
begin
SetBounds(Left, Top, Value, Height);
end;
procedure TdxFcObject.SetBorder(Value: Word);
begin
if Value <> FBorder then
begin
FBorder := Value;
if (ShapeType = fcsRectangle) and (FEdge <> 0)
then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetEdge(Value: Word);
begin
if Value <> FEdge then
begin
FEdge := Value;
if ShapeType = fcsRectangle then Owner.NeedRepaintObject(Self);
Changed;
end;
end;
procedure TdxFcObject.SetZOrder(Value: Word);
begin
if Value >= Owner.ObjectCount then Value := Owner.ObjectCount - 1;
if Value <> ZOrder then
with Owner do
begin
FHitTest := [];
FObjects.Remove(Self);
FObjects.Insert(Value, Self);
NeedRepaintObject(Self);
Changed(Self);
end;
end;
procedure TdxFcObject.BringToFront;
begin
SetZOrder(Owner.ObjectCount - 1);
end;
procedure TdxFcObject.SendToBack;
begin
SetZOrder(0);
end;
procedure TdxFcObject.PutInFrontOf(Value: TdxFcObject);
var
Z: Integer;
begin
Z := Value.ZOrder;
if Z < ZOrder then Inc(Z);
SetZOrder(Z);
end;
(*
0 1 2 3 4
15 5
14 6
13 7
12 11 10 9 8
*)
procedure TdxFcObject.SetRealBounds;
begin
FRealLeft := MulDiv(Left, Owner.RealZoom, 100);
FRealTop := MulDiv(Top, Owner.RealZoom, 100);
FRealWidth := MulDiv(Width, Owner.RealZoom, 100);
FRealHeight := MulDiv(Height, Owner.RealZoom, 100);
CalculateLinkedPoints;
end;
procedure TdxFcObject.SetRealSW;
begin
FRealSW := (ShapeWidth * Owner.RealZoom + 50) div 100;
if (RealSW = 0) and (ShapeWidth <> 0) then FRealSW := 1;
end;
procedure TdxFcObject.ZoomChanged;
begin
SetRealBounds;
SetRealSW;
ScaleFont;
end;
procedure TdxFcObject.CalculateLinkedPoints;
var
qWidth, qHeight, ARight, ABottom: Integer;
DX, DY: Integer;
procedure IncP(Index, IX, IY: Integer);
begin
Inc(LinkedPoints[Index].X, IX);
Inc(LinkedPoints[Index].Y, IY);
end;
procedure Trio(I1, I2, I3: Integer);
begin
LinkedPoints[I1].X := LinkedPoints[I2].X;
LinkedPoints[I1].Y := LinkedPoints[I3].Y;
LinkedPoints[I2].Y := LinkedPoints[I1].Y;
LinkedPoints[I3].X := LinkedPoints[I1].X;
end;
procedure TrioX(I1, I2, I3, I4: Integer);
begin
LinkedPoints[I1].X := LinkedPoints[I2].X + DX;
LinkedPoints[I3].X := LinkedPoints[I4].X - DX;
end;
procedure TrioY(I1, I2, I3, I4: Integer);
begin
LinkedPoints[I1].Y := LinkedPoints[I2].Y + DY;
LinkedPoints[I3].Y := LinkedPoints[I4].Y - DY;
end;
function Scale(Value, Coef: Integer): Integer;
begin
Result := (Value * Coef + 512) shr 10;
end;
begin
DeleteRgn;
Owner.FHitTest := [];
qWidth := RealWidth div 4;
qHeight := RealHeight div 4;
ARight := RealLeft + RealWidth;
ABottom := RealTop + RealHeight;
for DX := 4 to 8 do
LinkedPoints[DX].X := ARight;
for DX := 12 to 16 do
LinkedPoints[DX and 15].X := RealLeft;
for DX := 0 to 4 do
LinkedPoints[DX].Y := RealTop;
for DX := 8 to 12 do
LinkedPoints[DX].Y := ABottom;
LinkedPoints[1].X := RealLeft + qWidth;
LinkedPoints[2].X := RealLeft + RealWidth shr 1;
LinkedPoints[3].X := ARight - qWidth;
LinkedPoints[5].Y := RealTop + qHeight;
LinkedPoints[6].Y := RealTop + RealHeight shr 1;
LinkedPoints[7].Y := ABottom - qHeight;
LinkedPoints[9].X := LinkedPoints[3].X;
LinkedPoints[10].X := LinkedPoints[2].X;
LinkedPoints[11].X := LinkedPoints[1].X;
LinkedPoints[13].Y := LinkedPoints[7].Y;
LinkedPoints[14].Y := LinkedPoints[6].Y;
LinkedPoints[15].Y := LinkedPoints[5].Y;
for DX := 4 to 8 do
Dec(LinkedPoints[DX].X);
for DX := 8 to 12 do
Dec(LinkedPoints[DX].Y);
DX := qWidth shr 1; DY := qHeight shr 1;
case ShapeType of
fcsUser: UserLinkedPoints;
fcsNorthTriangle:
begin
Trio(0, 1, 14); TrioX(13, 13, 15, 2);
Trio(4, 3, 6); TrioX(5, 2, 7, 7);
end;
fcsSouthTriangle:
begin
Trio(12, 11, 14); TrioX(15, 15, 13, 10);
Trio(8, 9, 6); TrioX(7, 10, 5, 5);
end;
fcsEastTriangle:
begin
Trio(4, 2, 5); TrioY(1, 1, 3, 6);
Trio(8, 10, 7); TrioY(9, 6, 11, 11);
end;
fcsWestTriangle:
begin
Trio(0, 2, 15); TrioY(3, 3, 1, 14);
Trio(12, 10, 13); TrioY(11, 14, 9, 9);
end;
fcsHexagon:
begin
IncP(0, DX, qHeight); IncP(4, -DX, qHeight);
IncP(8, -DX, -qHeight); IncP(12, DX, -qHeight);
IncP(13, DX, 0); IncP(15, DX, 0); IncP(5, -DX, 0); IncP(7, -DX, 0);
end;
fcsDiamond:
begin
IncP(0, qWidth, qHeight); IncP(4, -qWidth, qHeight);
IncP(8, -qWidth, -qHeight); IncP(12, qWidth, -qHeight);
IncP(1, DX, DY); IncP(3, -DX, DY); IncP(5, -DX, DY); IncP(7, -DX, -DY);
IncP(9, -DX, -DY); IncP(11, DX, -DY); IncP(13, DX, -DY); IncP(15, DX, DY);
end;
fcsRoundRect:
begin
DX := Scale(RealWidth, 75); DY := Scale(RealHeight, 75);
IncP(0, DX, DY); IncP(4, -DX, DY);
IncP(8, -DX, -DY); IncP(12, DX, -DY);
end;
fcsEllipse:
begin
DX := Scale(RealWidth, 150); DY := Scale(RealHeight, 150);
IncP(0, DX, DY); IncP(4, -DX, DY);
IncP(8, -DX, -DY); IncP(12, DX, -DY);
DX := Scale(RealWidth, 68); DY := Scale(RealHeight, 68);
IncP(13, DX, 0); IncP(15, DX, 0); IncP(5, -DX, 0); IncP(7, -DX, 0);
IncP(1, 0, DY); IncP(3, 0, DY); IncP(9, 0, -DY); IncP(11, 0, -DY);
end;
end;
end;
procedure TdxFcObject.UpdateConnections;
var
I: Integer;
begin
Changed;
for I := 0 to ConnectionCount - 1 do
with Connections[I] do
begin
ConnectionChanged;
SetObjectPoints;
SetDisplayRect;
Changed;
end;
end;
procedure TdxFcObject.Invalidate;
begin
Owner.NeedRepaintObject(Self);
end;
function TdxFcObject.DisplayRect: TRect;
begin
with Result do
begin
Left := RealLeft - Owner.LeftEdge;
Top := RealTop - Owner.TopEdge;
Right := Left + RealWidth;
Bottom := Top + RealHeight;
end;
end;
function TdxFcObject.Opaque: Boolean;
begin
Result := not Transparent and (ShapeType <> fcsNone);
end;
procedure TdxFcObject.IsRepainted(Rgn: HRgn);
var
R: TRect;
begin
FRepainted := Visible;
if not Visible then Exit;
R := DisplayRect;
ExtSelRect(R, Selected);
FRepainted := RectVisible(Owner.Canvas.Handle, R) or (csPaintCopy in Owner.ControlState); {paul}
if FRepainted and Opaque then
begin
CreateRgn;
if FPaintRgn = 0 then FPaintRgn := CreateRectRgn(0, 0, 0, 0);
CombineRgn(FPaintRgn, FExtRgn, FIntRgn, RGN_OR);
FRepainted := CombineRgn(FPaintRgn, FPaintRgn, Rgn, RGN_DIFF) <> NULLREGION;
if FRepainted then CombineRgn(Rgn, Rgn, FPaintRgn, RGN_OR);
FRepainted := FRepainted or Selected;
end;
end;
function TdxFcObject.ClientRect: TRect;
begin
Result.TopLeft := LinkedPoints[0];
Result.BottomRight := LinkedPoints[8];
case ShapeType of
fcsNorthTriangle: Result.Right := LinkedPoints[4].X;
fcsSouthTriangle: Result.Left := LinkedPoints[1].X;
fcsEastTriangle: Result.Top := LinkedPoints[15].Y;
fcsWestTriangle: Result.Bottom := LinkedPoints[7].Y;
end;
InflateRect(Result, -RealSW, -RealSW);
OffsetRect(Result, -Owner.LeftEdge, -Owner.TopEdge);
end;
function TdxFcObject.GetZOrder: Word;
begin
Result := Word(Owner.FObjects.IndexOf(Self));
end;
procedure TdxFcObject.CreateRgn;
procedure AndRgn(DX, DY: Integer);
begin
OffsetRgn(FExtRgn, DX, DY);
CombineRgn(FIntRgn, FIntRgn, FExtRgn, RGN_AND);
OffsetRgn(FExtRgn, -DX, -DY);
end;
var
W: Integer;
begin
if FIntRgn <> 0 then Exit;
if (ShapeType = fcsNone) or HasEdge then
W := 1
else
W := RealSW;
FExtRgn := Create1Rgn(0);
if ShapeType in [fcsEllipse, fcsRoundRect, fcsUser] then
FIntRgn := Create1Rgn(-W)
else
begin
FIntRgn := CreateRectRgn(0, 0, 0, 0);
CombineRgn(FIntRgn, FExtRgn, FExtRgn, RGN_COPY);
AndRgn(W, 0); AndRgn(0, W);
AndRgn(-W, 0); AndRgn(0, -W);
end;
CombineRgn(FExtRgn, FExtRgn, FIntRgn, RGN_DIFF);
end;
function TdxFcObject.Create1Rgn(Offset: Integer): HRgn;
var
NPoints: Integer;
R: TRect;
Pts: array[0..5] of TPoint;
procedure SetPolygon(const Indexes: array of Integer);
var
I: Integer;
begin
NPoints := High(Indexes) + 1;
for I := 0 to High(Indexes) do
begin
Pts[I] := LinkedPoints[Indexes[I]];
Dec(Pts[I].X, Owner.LeftEdge);
Dec(Pts[I].Y, Owner.TopEdge);
end;
end;
begin
NPoints := 0;
R := DisplayRect;
case ShapeType of
fcsDiamond:
SetPolygon([2, 6, 10, 14]);
fcsHexagon:
SetPolygon([1, 3, 6, 9, 11, 14]);
fcsNorthTriangle:
SetPolygon([2, 8, 12]);
fcsSouthTriangle:
SetPolygon([0, 4, 10]);
fcsEastTriangle:
SetPolygon([0, 6, 12]);
fcsWestTriangle:
SetPolygon([4, 8, 14]);
end;
if NPoints <> 0 then
Result := CreatePolygonRgn(Pts, NPoints, WINDING)
else
begin
InflateRect(R, Offset, Offset);
case ShapeType of
fcsUser:
Result := UserRegion(R);
fcsNone, fcsRectangle:
Result := CreateRectRgnIndirect(R);
fcsEllipse:
Result := CreateEllipticRgnIndirect(R);
fcsRoundRect:
Result := CreateRoundRectRgn(R.Left, R.Top, R.Right, R.Bottom, RealWidth shr 1, RealHeight shr 1);
else
Result := 0;
end;
end;
end;
procedure TdxFcObject.DeleteRgn;
begin
if FExtRgn <> 0 then DeleteObject(FExtRgn);
if FIntRgn <> 0 then DeleteObject(FIntRgn);
FExtRgn := 0; FIntRgn := 0;
end;
procedure TdxFcObject.SelPoints(var Pts: array of TPoint);
var
I: Integer;
begin
for I := 2 to 4 do
Pts[I].X := RealLeft + RealWidth - 1;
for I := 6 to 8 do
Pts[I and 7].X := RealLeft;
for I := 0 to 2 do
Pts[I].Y := RealTop;
for I := 4 to 6 do
Pts[I].Y := RealTop + RealHeight - 1;
Pts[1].X := RealLeft + RealWidth shr 1;
Pts[5].X := Pts[1].X;
Pts[3].Y := RealTop + RealHeight shr 1;
Pts[7].Y := Pts[3].Y;
end;
function TdxFcObject.Quadrant(X, Y: Integer): Integer;
begin
Result := Ord(X < RealLeft + RealWidth shr 1) shl 1 + Ord(Y >= RealTop + RealHeight shr 1);
if Result > 1 then
Result := Result xor 1;
end;
function TdxFcObject.GetPoint(const P: array of TPoint; X, Y, Cnt: Integer): Integer;
var
I, Cur, Min, Mask, Start: Integer;
begin
Result := 0;
Min := $7FFFFFFF; I := Cnt shr 1;
if Cnt = 2 then
Mask := 7
else
Mask := 15;
Inc(X, Owner.LeftEdge); Inc(Y, Owner.TopEdge);
Start := Quadrant(X, Y) shl I + I;
for I := Start to Start + Cnt do
begin
Cur := QDistance(X, Y, P[I and Mask]);
if Cur < Min then
begin
Min := Cur;
Result := I and Mask;
end;
end;
end;
function TdxFcObject.GetSelPoint(X, Y: Integer): Integer;
var
P: array[0..7] of TPoint;
begin
SelPoints(P);
Result := GetPoint(P, X, Y, 2);
end;
function TdxFcObject.InRect(const R: TRect): Boolean;
begin
Result := Visible;
if Result then
begin
CreateRgn;
Result := RectInRegion(FIntRgn, R);
end;
end;
function TdxFcObject.GetLinkedPoint(X, Y: Integer): Integer;
begin
Result := GetPoint(LinkedPoints, X, Y, 4);
end;
function TdxFcObject.HasInUnion(AObject: TdxFcObject): Boolean;
var
I: Integer;
begin
Result := (AObject = nil) or (AObject = Self);
if Result then Exit;
for I := 0 to ObjectCount - 1 do
begin
Result := Objects[I].HasInUnion(AObject);
if Result then Exit;
end;
end;
procedure TdxFcObject.AddToUnion(AObject: TdxFcObject);
begin
if not HasInUnion(AObject) then FObjects.Add(AObject);
end;
procedure TdxFcObject.RemoveFromUnion(AObject: TdxFcObject);
begin
FObjects.Remove(AObject);
end;
procedure TdxFcObject.ClearUnion;
begin
FObjects.Clear;
end;
procedure TdxFcObject.SelectUnion;
var
I: Integer;
begin
Selected := True;
with Owner do
if not (fcoMultiSelect in Options) then Exit;
for I := 0 to ObjectCount - 1 do
Objects[I].SelectUnion;
end;
procedure TdxFcObject.MakeVisible;
var
R: TRect;
LeftX, TopY: Integer;
begin
Visible := True;
if (RealWidth > Owner.ClientWidth) or (RealHeight > Owner.ClientHeight)
then
R := ClientRect
else
R := DisplayRect;
LeftX := R.Left; TopY := R.Top;
with Owner do
begin
if R.Right > ClientWidth then
LeftX := LeftX + ClientWidth - R.Right;
if LeftX < 0 then
LeftX := 0;
if R.Bottom > ClientHeight then
TopY := TopY + ClientHeight - R.Bottom;
if TopY < 0
then TopY := 0;
if (LeftX = R.Left) and (TopY = R.Top) then
Exit;
SetLeftTop(LeftEdge + R.Left - LeftX, TopEdge + R.Top - TopY);
end;
end;
procedure TdxFcObject.Paint;
var
R: TRect;
begin
if FRepainted then
begin
if Opaque and not (csPaintCopy in Owner.ControlState) then // Fix: by Paulthen
ExtSelectClipRgn(Owner.Canvas.Handle, FPaintRgn, RGN_OR);
if ShapeType <> fcsNone then
PaintFrame;
R := ClientRect;
if RectVisible(Owner.Canvas.Handle, R) or (csPaintCopy in Owner.ControlState) then {paul}
if Assigned(Owner.OnDrawObject) then
Owner.OnDrawObject(Owner, Self, R)
else
Owner.DefaultDrawObject(Self, R);
end;
end;
procedure TdxFcObject.PaintFrame;
var
Pt: TPoint; // by Paul
DC: HDC;
Rgn: HRgn;
R: TRect;
begin
CreateRgn;
DC := Owner.Canvas.Handle;
if not Transparent then
begin
if HasEdge then
Rgn := FPaintRgn
else
Rgn := FIntRgn;
InitBrush(FBkBrush, BkColor);
Pt := Point(0, 0); // by Paul
if HasEdge then GetWindowOrgEx(DC, Pt); // by Paul
if (Pt.X <> 0) or (Pt.Y <> 0) then // by Paul
OffsetRgn(Rgn, Pt.X, Pt.Y); // by Paul
FillRgn(DC, Rgn, FBkBrush.Handle);
if (Pt.X <> 0) or (Pt.Y <> 0) then // by Paul
OffsetRgn(Rgn, -Pt.X, -Pt.Y); // by Paul
end; // by Paul
if HasEdge then
begin
R := DisplayRect;
DrawEdge(DC, R, EdgeStyle, BorderStyle);
end
else
begin
InitBrush(FShapeBrush, ShapeColor);
FillRgn(DC, FExtRgn, FShapeBrush.Handle);
end;
end;
procedure TdxFcObject.PaintImage(R: TRect);
var
IR: TRect;
begin
if Owner.Images = nil then Exit;
IR := Rect(0, 0, Owner.Images.Width, Owner.Images.Height);
if AdjustRect(IR, R, HorzImagePos, VertImagePos)
then Owner.Images.Draw(Owner.Canvas, IR.Left, IR.Top, ImageIndex);
end;
procedure TdxFcObject.PaintText(R: TRect);
const
Aligns: array[TdxFcHorzPos] of Word = (DT_LEFT, DT_CENTER, DT_RIGHT);
var
DC: HDC;
Flags: Word;
TR: TRect;
begin
DC := Owner.Canvas.Handle; TR := R;
Flags := DT_EXPANDTABS or DT_WORDBREAK or Aligns[HorzTextPos];
SelectObject(DC, RealFont.Handle);
SetTextColor(DC, ColorToRGB(RealFont.Color));
if VertTextPos <> fcvpUp then
begin
DrawText(DC, PChar(Text), -1, TR, Flags or DT_CALCRECT);
AdjustRect(TR, R, HorzTextPos, VertTextPos);
end;
DrawText(DC, PChar(Text), -1, TR, Flags);
end;
procedure TdxFcObject.ResolveObjRefs;
var
I: Integer;
begin
for I := 0 to ObjectCount - 1 do
FObjects[I] := Owner.Objects[Integer(FObjects[I])];
end;
procedure TdxFcObject.Assign(Source: TPersistent);
begin
if Source is TdxFcObject then
with TdxFcObject(Source) do
begin
Self.Data := Data; Self.Tag := Tag;
Self.CustomData := CustomData;
Self.SetBounds(Left, Top, Width, Height);
Self.EdgeStyle := EdgeStyle; Self.BorderStyle := BorderStyle;
Self.HorzTextPos := HorzTextPos; Self.VertTextPos := VertTextPos;
Self.HorzImagePos := HorzImagePos; Self.VertImagePos := VertImagePos;
Self.BkColor := BkColor; Self.ShapeColor := ShapeColor;
Self.ImageIndex := ImageIndex; Self.Transparent := Transparent;
Self.ShapeType := ShapeType; Self.ShapeWidth := ShapeWidth;
end;
inherited Assign(Source);
end;
procedure TdxFcObject.Load(Stream: TStream; AIsUnicode: Boolean);
var
I: Integer;
ObjData: TdxFcObjData;
begin
Stream.ReadBuffer(ObjData, SizeOf(ObjData));
with ObjData do
begin
SetBounds(Left, Top, Width, Height);
EdgeStyle := Edge; BorderStyle := Border;
HorzTextPos := HTPos; VertTextPos := VTPos;
HorzImagePos := HIPos; VertImagePos := VIPos;
Self.BkColor := BkColor; ShapeColor := ShColor;
Self.Tag := Tag; ImageIndex := Image;
ShapeType := Shape; ShapeWidth := ShWidth;
ParentFont := ParFont; Self.Transparent := Transparent;
while ObjCnt > 0 do
begin
I := 0; Dec(ObjCnt);
Stream.ReadBuffer(I, SizeOf(Word));
FObjects.Add(Pointer(I));
end;
end;
LoadFont(Stream, AIsUnicode);
Text := ReadStr(Stream, AIsUnicode);
CustomData := ReadStr(Stream, AIsUnicode);
end;
procedure TdxFcObject.Save(Stream: TStream);
var
I: Integer;
W: Word;
ObjData: TdxFcObjData;
begin
with ObjData do
begin
Left := Self.Left; Top := Self.Top;
Width := Self.Width; Height := Self.Height;
Edge := EdgeStyle; Border := BorderStyle;
HTPos := HorzTextPos; VTPos := VertTextPos;
HIPos := HorzImagePos; VIPos := VertImagePos;
BkColor := Self.BkColor; ShColor := ShapeColor;
Tag := Self.Tag; Image := ImageIndex;
Shape := ShapeType; ShWidth := ShapeWidth;
ParFont := ParentFont; Transparent := Self.Transparent;
ObjCnt := Word(ObjectCount);
end;
Stream.WriteBuffer(ObjData, SizeOf(ObjData));
for I := 0 to ObjectCount - 1 do
begin
W := Objects[I].ZOrder;
Stream.WriteBuffer(W, SizeOf(W));
end;
SaveFont(Stream);
WriteStr(Stream, Text);
WriteStr(Stream, CustomData);
end;
function TdxFcObject.UserRegion(R: TRect): HRgn;
begin
Result := 0;
end;
procedure TdxFcObject.UserLinkedPoints;
begin
end;
{ TdxFcSelection }
constructor TdxFcSelection.Create(AOwner: TdxCustomFlowChart);
begin
Owner := AOwner;
Counts := TList.Create;
Points := TList.Create;
end;
destructor TdxFcSelection.Destroy;
begin
Counts.Free;
Points.Free;
end;
procedure TdxFcSelection.AddPoint(X, Y: Integer);
procedure AddOnePoint(X, Y: Integer);
begin
Points.Add(Pointer(X));
Points.Add(Pointer(Y));
end;
begin
Dec(X, Owner.LeftEdge);
Dec(Y, Owner.TopEdge);
Counts.Add(Pointer(5));
AddOnePoint(X - 2, Y - 2);
AddOnePoint(X + 2, Y - 2);
AddOnePoint(X + 2, Y + 2);
AddOnePoint(X - 2, Y + 2);
AddOnePoint(X - 2, Y - 2);
end;
procedure TdxFcSelection.Clear;
begin
Counts.Clear;
Points.Clear;
end;
procedure TdxFcSelection.Paint;
var
DC: HDC;
begin
DC := Owner.Canvas.Handle;
SelectObject(DC, GetStockObject(BLACK_PEN));
SelectObject(DC, GetStockObject(BLACK_BRUSH));
PolyPolygon(DC, Points.List^, Counts.List^, Counts.Count);
end;
{ TdxFcItem }
constructor TdxFcItem.Create(AOwner: TdxCustomFlowChart);
begin
FOwner := AOwner;
FFont := TFont.Create;
FRealFont := TFont.Create;
FFont.Assign(AOwner.Font);
SetRealFont;
FFont.OnChange := OnFontChange;
FParentFont := True;
end;
destructor TdxFcItem.Destroy;
begin
FFont.Free;
FRealFont.Free;
inherited Destroy;
end;
procedure TdxFcItem.Changed;
begin
if not Owner.FLoading then Owner.Changed(Self);
end;
procedure TdxFcItem.OnFontChange(Sender: TObject);
begin
FParentFont := False;
SetRealFont;
if Text <> '' then FontChanged;
Changed;
end;
procedure TdxFcItem.ScaleFont;
begin
RealFont.Size := MulDiv(Font.Size, Owner.RealZoom, 100);
end;
procedure TdxFcItem.SetRealFont;
begin
RealFont.Assign(Font);
ScaleFont;
end;
procedure TdxFcItem.SetFont(Value: TFont);
begin
Font.Assign(Value);
end;
procedure TdxFcItem.FontChanged;
begin
Invalidate;
end;
procedure TdxFcItem.SetParentFont(Value: Boolean);
begin
if Value <> ParentFont then
begin
if Value then Font.Assign(Owner.Font);
FParentFont := Value;
Changed;
end;
end;
procedure TdxFcItem.SetSelected(Value: Boolean);
begin
if Selected <> Value then
begin
if Value and not Owner.CanSelect(Self) then Exit;
if Selected then
begin
Invalidate;
SelList.Remove(Self);
end;
FSelected := Value;
if Selected then
begin
with Owner do
if not (fcoMultiSelect in Options) then ClearSelection;
SelList.Add(Self);
Invalidate;
end;
Owner.Select(Self);
end;
end;
procedure TdxFcItem.Assign(Source: TPersistent);
begin
if Source is TdxFcItem then
begin
Text := TdxFcItem(Source).Text;
ParentFont := TdxFcItem(Source).ParentFont;
if not ParentFont then Font := TdxFcItem(Source).Font;
end
else
inherited Assign(Source);
end;
procedure TdxFcItem.LoadFont(Stream: TStream; AIsUnicode: Boolean);
var
Data: TdxFcFntData;
FtName: string;
begin
if ParentFont then Exit;
Stream.ReadBuffer(Data, SizeOf(Data));
FtName := ReadStr(Stream, AIsUnicode);
with Font do
begin
OnChange := nil;
Height := Data.Height;
Color := Data.Color;
Pitch := Data.Pitch;
Style := Data.Style;
Charset := Data.Charset;
if FtName <> '' then Name := FtName;
OnChange := OnFontChange;
end;
SetRealFont;
end;
procedure TdxFcItem.SaveFont(Stream: TStream);
var
Data: TdxFcFntData;
FtName: string;
begin
if ParentFont then Exit;
if Font.Name = Owner.Font.Name then
FtName := ''
else
FtName := Font.Name;
with Data do
begin
Height := Font.Height;
Color := Font.Color;
Pitch := Font.Pitch;
Style := Font.Style;
Charset := Font.Charset;
end;
Stream.WriteBuffer(Data, SizeOf(Data));
WriteStr(Stream, FtName);
end;
{TdxCustomFlowChart}
constructor TdxCustomFlowChart.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle - [csCaptureMouse];
Width := 320;
Height := 200;
ParentColor := False;
TabStop := True;
FObjects := TList.Create;
FConnections := TList.Create;
FSelObjects := TList.Create;
FSelConnections := TList.Create;
FSelection := TdxFcSelection.Create(Self);
FZoom := 100;
FRealZoom := 100;
FOptions := [fcoCanDelete, fcoCanDrag, fcoCanSelect, fcoMultiSelect, fcoHideSelection, fcoDelOnClick];
FBorderStyle := bsSingle;
FChangeLink := TChangeLink.Create;
FChangeLink.OnChange := OnChangeLink;
end;
destructor TdxCustomFlowChart.Destroy;
begin
OnDeletion := nil;
OnSelected := nil;
Clear;
FObjects.Free;
FConnections.Free;
FSelObjects.Free;
FSelConnections.Free;
FSelection.Free;
FChangeLink.Free;
inherited Destroy;
end;
procedure TdxCustomFlowChart.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WindowClass.Style := Params.WindowClass.Style and not (CS_HREDRAW or CS_VREDRAW);
Params.Style := Params.Style or WS_TABSTOP or WS_HSCROLL or WS_VSCROLL;
if BorderStyle = bsSingle then
if Ctl3D then
Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE
else
Params.Style := Params.Style or WS_BORDER;
end;
procedure TdxCustomFlowChart.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FImages) then Images := nil;
end;
function TdxCustomFlowChart.GetConnection(Index: Integer): TdxFcConnection;
begin
Result := TdxFcConnection(GetListItem(FConnections, Index));
end;
function TdxCustomFlowChart.GetObjectValue(Index: Integer): TdxFcObject;
begin
Result := TdxFcObject(GetListItem(FObjects, Index));
end;
function TdxCustomFlowChart.GetConnectionCount: Integer;
begin
Result := FConnections.Count;
end;
function TdxCustomFlowChart.GetObjectCount: Integer;
begin
Result := FObjects.Count;
end;
function TdxCustomFlowChart.GetSelectedObject(Index: Integer): TdxFcObject;
begin
Result := TdxFcObject(GetListItem(FSelObjects, Index));
end;
function TdxCustomFlowChart.GetSelectedObjectCount: Integer;
begin
Result := FSelObjects.Count;
end;
function TdxCustomFlowChart.GetSelectedConnection(Index: Integer): TdxFcConnection;
begin
Result := TdxFcConnection(GetListItem(FSelConnections, Index));
end;
function TdxCustomFlowChart.GetSelectedConnectionCount: Integer;
begin
Result := FSelConnections.Count;
end;
function TdxCustomFlowChart.SelCount: Integer;
begin
Result := FSelObjects.Count + FSelConnections.Count;
end;
procedure TdxCustomFlowChart.SetBorderStyle(Value: TBorderStyle);
begin
if Value <> BorderStyle then
begin
FBorderStyle := Value;
RecreateWnd;
end;
end;
procedure TdxCustomFlowChart.SetImages(Value: TImageList);
begin
if (FImages <> Value) then
begin
if (FImages <> nil) and not (csDestroying in FImages.ComponentState) then
FImages.UnRegisterChanges(FChangeLink);
FImages := Value;
if (FImages <> nil) then
begin
FImages.RegisterChanges(FChangeLink);
FImages.FreeNotification(Self);
end;
if not (csDestroying in ComponentState) then NeedRepaint;
end;
end;
procedure TdxCustomFlowChart.SetOptions(Value: TdxFcOptions);
const
Sel: TdxFcOptions = [fcoCanSelect, fcoMultiSelect];
var
NewOpts: TdxFcOptions;
begin
if (Options <> Value) then
begin
NewOpts := (Options + Value) - (Options * Value);
FOptions := Value;
if (NewOpts * Sel <> []) and (Options * Sel <> Sel) then ClearSelection;
if (fcoHideSelection in NewOpts) and not Focused then InvalidateSel;
end;
end;
procedure TdxCustomFlowChart.SetConnection(Index: Integer; Value: TdxFcConnection);
begin
Connections[Index].Assign(Value);
end;
procedure TdxCustomFlowChart.SetObjectValue(Index: Integer; Value: TdxFcObject);
begin
Objects[Index].Assign(Value);
end;
procedure TdxCustomFlowChart.SetZoom(Value: Word);
var
W, H: Integer;
begin
if Value = Zoom then Exit;
FZoom := Value;
if Value <> 0 then
FRealZoom := Value
else
begin
FZoom := $FFFF;
W := MulDiv(FChartWidth, 100, RealZoom);
H := MulDiv(FChartHeight, 100, RealZoom);
if ClientWidth * H <= ClientHeight * W
then
FRealZoom := MulDiv(ClientWidth, 95, W)
else
FRealZoom := MulDiv(ClientHeight, 95, H);
if RealZoom > 100 then FRealZoom := 100;
if FRealZoom = 0 then FRealZoom := 1;
end;
CalculateRealPos;
end;
procedure TdxCustomFlowChart.ScalePoint(var P: TPoint);
begin
P.X := MulDiv(P.X, RealZoom, 100);
P.Y := MulDiv(P.Y, RealZoom, 100);
end;
function TdxCustomFlowChart.ChartPoint(X, Y: Integer): TPoint;
begin
Result.X := MulDiv(X + LeftEdge, 100, RealZoom);
Result.Y := MulDiv(Y + TopEdge, 100, RealZoom);
end;
procedure TdxCustomFlowChart.CalculateRealPos;
var
I: Integer;
begin
BeginUpdate;
for I := 0 to ObjectCount - 1 do
Objects[I].ZoomChanged;
for I := 0 to ConnectionCount - 1 do
Connections[I].ZoomChanged;
FRepaint := True;
EndUpdate;
end;
procedure TdxCustomFlowChart.OnChangeLink(Sender: TObject);
begin
NeedRepaint;
end;
function TdxCustomFlowChart.InternalCreateObject: TdxFcObject;
begin
Result := TdxFcObject.Create(Self);
end;
function TdxCustomFlowChart.InternalCreateConnection: TdxFcConnection;
begin
Result := TdxFcConnection.Create(Self);
end;
function TdxCustomFlowChart.CreateObject(L, T, W, H: Integer; AShape: TdxFcShapeType): TdxFcObject;
begin
Result := InternalCreateObject;
if Assigned(OnCreateItem) then OnCreateItem(Self, Result);
with Result do
begin
FShapeType := AShape;
SetBounds(L, T, W, H);
end;
end;
function TdxCustomFlowChart.CreateConnection(OSrc, ODst: TdxFcObject; PSrc, PDst: Byte): TdxFcConnection;
begin
Result := InternalCreateConnection;
if Assigned(OnCreateItem) then OnCreateItem(Self, Result);
with Result do
begin
SetObjectSource(OSrc, PSrc);
SetObjectDest(ODst, PDst);
end;
end;
procedure TdxCustomFlowChart.DeleteObject(AObject: TdxFcObject);
begin
AObject.Free;
end;
procedure TdxCustomFlowChart.DeleteConnection(AConnection: TdxFcConnection);
begin
AConnection.Free;
end;
procedure TdxCustomFlowChart.Clear;
begin
if ObjectCount + ConnectionCount = 0 then Exit;
BeginUpdate;
FSelConnections.Clear;
FSelObjects.Clear;
while ConnectionCount > 0 do
Connections[0].Free;
while ObjectCount > 0 do
Objects[0].Free;
EndUpdate;
end;
procedure TdxCustomFlowChart.ClearSelection;
begin
while SelectedConnectionCount > 0 do
SelectedConnections[0].Selected := False;
while SelectedObjectCount > 0 do
SelectedObjects[0].Selected := False;
end;
procedure TdxCustomFlowChart.DeleteSelection;
begin
while SelectedConnectionCount > 0 do
SelectedConnections[0].Free;
while SelectedObjectCount > 0 do
SelectedObjects[0].Free;
end;
procedure TdxCustomFlowChart.SelectAll;
var
I: Integer;
begin
for I := 0 to ObjectCount - 1 do
Objects[I].Selected := True;
for I := 0 to ConnectionCount - 1 do
Connections[I].Selected := True;
end;
function TdxCustomFlowChart.GetSelConnect: TdxFcConnection;
begin
if (SelectedObjectCount = 0) and (SelectedConnectionCount = 1)
then
Result := SelectedConnections[0]
else
Result := nil;
end;
function TdxCustomFlowChart.GetSelObj: TdxFcObject;
begin
if (SelectedObjectCount = 1) and (SelectedConnectionCount = 0)
then
Result := SelectedObjects[0]
else
Result := nil;
end;
procedure TdxCustomFlowChart.SetSelConnect(Value: TdxFcConnection);
begin
if Value <> SelectedConnection then
begin
ClearSelection;
if Value <> nil then Value.Selected := True;
end;
end;
procedure TdxCustomFlowChart.SetSelObj(Value: TdxFcObject);
begin
if Value <> SelectedObject then
begin
ClearSelection;
if Value <> nil then Value.Selected := True;
end;
end;
function TdxCustomFlowChart.CanPaint: Boolean;
begin
Result := (FLockUpdates = 0) and HandleAllocated;
end;
procedure TdxCustomFlowChart.NeedRepaint;
begin
FRepaint := True;
if CanPaint then Invalidate;
end;
procedure TdxCustomFlowChart.NeedRepaintObject(AObject: TdxFcObject);
var
R: TRect;
begin
FRepaint := True;
if CanPaint then
begin
R := AObject.DisplayRect;
ExtSelRect(R, AObject.Selected);
InvalidateRect(Handle, @R, True);
end;
end;
procedure TdxCustomFlowChart.BeginUpdate;
begin
if FLockUpdates = 0 then FRepaint := False;
Inc(FLockUpdates);
end;
procedure TdxCustomFlowChart.CancelUpdate;
begin
if FLockUpdates <> 0 then Dec(FLockUpdates);
end;
procedure TdxCustomFlowChart.EndUpdate;
begin
if FLockUpdates > 0 then
begin
Dec(FLockUpdates);
if FLockUpdates = 0 then
begin
if FRepaint then NeedRepaint;
SetChartSizes;
end;
end;
end;
procedure TdxCustomFlowChart.Invalidate;
begin
if FLockUpdates > 0 then
FRepaint := True
else
inherited Invalidate;
end;
procedure TdxCustomFlowChart.InvalidateSel;
var
I: Integer;
begin
for I := 0 to SelectedObjectCount - 1 do
NeedRepaintObject(SelectedObjects[I]);
for I := 0 to SelectedConnectionCount - 1 do
SelectedConnections[I].ConnectionChanged;
end;
function TdxCustomFlowChart.HasSelection: Boolean;
begin
Result := Focused or not (fcoHideSelection in Options);
end;
procedure TdxCustomFlowChart.AddSelectedObject(AObject: TdxFcObject);
var
I: Integer;
P: array[0..7] of TPoint;
begin
if not AObject.FRepainted then Exit;
AObject.SelPoints(P);
for I := 0 to 7 do
FSelection.AddPoint(P[I].X, P[I].Y);
end;
procedure TdxCustomFlowChart.AddSelectedConnection(AConnection: TdxFcConnection);
var
I: Integer;
P: TPoint;
begin
with AConnection do
begin
if not FRepainted then Exit;
for I := 0 to RealCount - 1 do
begin
P := InternalGetPoint(FRealPoints, I);
FSelection.AddPoint(P.X, P.Y);
end;
end;
end;
procedure TdxCustomFlowChart.Paint;
var
I: Integer;
ClipRgn: HRgn;
DC: hDC; // Fix: by Paul
Pt: TPoint; // Fix: by Paul
R: TRect; // Fix: by Paul
begin
FSelection.Clear;
ClipRgn := CreateRectRgn(0, 0, 0, 0);
for I := ObjectCount - 1 downto 0 do
Objects[I].IsRepainted(ClipRgn);
for I := 0 to ConnectionCount - 1 do
Connections[I].IsRepainted;
if HasSelection then
begin
for I := 0 to SelectedObjectCount - 1 do
AddSelectedObject(SelectedObjects[I]);
for I := 0 to SelectedConnectionCount - 1 do
AddSelectedConnection(SelectedConnections[I]);
end;
DC := Canvas.Handle; // Fix: by Paul
GetWindowOrgEx(DC, Pt); // Fix: by Paul
if (Pt.X <> 0) or (Pt.Y <> 0) then OffsetRgn(ClipRgn, -Pt.X, -Pt.Y); // Fix: by Paul
if not (csPaintCopy in ControlState) then // Fix: by Paul
ExtSelectClipRgn(DC, ClipRgn, RGN_DIFF);
Windows.GetClientRect(Handle, R); // Fix: by Paul
if (Pt.X <> 0) or (Pt.Y <> 0) then OffsetRect(R, Pt.X, Pt.Y); // Fix: by Paul
if not (csPaintCopy in ControlState) then // Fix: by Paul
FillRect(DC, R, Brush.Handle);
SetBkMode(DC, TRANSPARENT);
for I := 0 to ConnectionCount - 1 do
Connections[I].Paint(False);
for I := 0 to ObjectCount - 1 do
with Objects[I] do
begin
if (FPaintRgn <> 0) and (Pt.X <> 0) or (Pt.Y <> 0) then // Fix: by Paul
OffsetRgn(FPaintRgn, -Pt.X, -Pt.Y); // Fix: by Paul
Paint; // Fix: by Paul
if (FPaintRgn <> 0) and (Pt.X <> 0) or (Pt.Y <> 0) then // Fix: by Paul
OffsetRgn(FPaintRgn, Pt.X, Pt.Y); // Fix: by Paul
end; // Fix: by Paul
for I := 0 to ConnectionCount - 1 do
Connections[I].Paint(True);
if FSelection.Counts.Count > 0 then FSelection.Paint;
Windows.DeleteObject(ClipRgn);
end;
procedure TdxCustomFlowChart.DefaultDrawObject(AObject: TdxFcObject; R: TRect);
begin
with AObject do
begin
if HasImage then PaintImage(R);
if Text <> '' then PaintText(R);
end;
end;
procedure TdxCustomFlowChart.UpdateScrollRange;
var
NewX, NewY: Integer;
Info: TScrollInfo;
function SetScr(Code, Page, Min, Max: Integer): Integer;
begin
with Info do
begin
cbSize := SizeOf(Info);
fMask := SIF_ALL;
GetScrollInfo(Handle, Code, Info);
Result := nPos;
fMask := SIF_PAGE or SIF_RANGE;
nMin := Min;
nMax := Min + Max;
nPage := Page;
if (Zoom = 0) or (Zoom = $FFFF) then nPage := Max + 1;
end;
SetScrollInfo(Handle, Code, Info, True);
end;
begin
if not HandleAllocated then Exit;
NewX := SetScr(SB_HORZ, ClientWidth, FChartLeft, FChartWidth);
NewY := SetScr(SB_VERT, ClientHeight, FChartTop, FChartHeight);
SetLeftTop(NewX, NewY);
end;
procedure TdxCustomFlowChart.SetLeftTop(ALeft, ATop: Integer);
procedure Adjust(Min, Max: Integer; var Value: Integer);
begin
if Value > Max then Value := Max;
if Value < Min then Value := Min;
end;
var
I, DX, DY: Integer;
begin
Adjust(FChartLeft, FChartLeft + FChartWidth - ClientWidth, ALeft);
Adjust(FChartTop, FChartTop + FChartHeight - ClientHeight, ATop);
if (ALeft = LeftEdge) and (ATop = TopEdge) then Exit;
DX := LeftEdge - ALeft;
DY := TopEdge - ATop;
FLeftEdge := ALeft;
FTopEdge := ATop;
if not HandleAllocated then Exit;
ScrollWindow(Handle, DX, DY, nil, nil);
if DX <> 0 then SetScrollPos(Handle, SB_HORZ, ALeft, True);
if DY <> 0 then SetScrollPos(Handle, SB_VERT, ATop, True);
for I := 0 to ObjectCount - 1 do
if Objects[I].FIntRgn <> 0 then
begin
OffsetRgn(Objects[I].FExtRgn, DX, DY);
OffsetRgn(Objects[I].FIntRgn, DX, DY);
end;
end;
procedure TdxCustomFlowChart.SetChartSizes;
var
I: Integer;
OldR, NewR: TRect;
procedure SetXY(R: TRect);
begin
OffsetRect(R, LeftEdge, TopEdge);
ExtendRect(NewR, R.TopLeft);
ExtendRect(NewR, R.BottomRight);
end;
begin
if (FLockUpdates > 0) or (csDestroying in ComponentState) then Exit;
OldR := Bounds(FChartLeft, FChartTop, FChartWidth, FChartHeight);
NewR := Rect(0, 0, 0, 0);
for I := 0 to ObjectCount - 1 do
SetXY(Objects[I].DisplayRect);
for I := 0 to ConnectionCount - 1 do
SetXY(Connections[I].DisplayRect);
if not EqualRect(OldR, NewR) then
with NewR do
begin
FChartLeft := Left;
FChartTop := Top;
FChartWidth := Right - Left;
FChartHeight := Bottom - Top;
if Zoom = 0 then
begin
FZoom := RealZoom;
SetZoom(0);
end;
UpdateScrollRange;
end;
if Zoom = $FFFF then FZoom := 0;
end;
procedure TdxCustomFlowChart.SetLeftEdge(Value: Integer);
begin
if Value <> LeftEdge then SetLeftTop(Value, TopEdge);
end;
procedure TdxCustomFlowChart.SetTopEdge(Value: Integer);
begin
if Value <> TopEdge then SetLeftTop(LeftEdge, Value);
end;
procedure TdxCustomFlowChart.HitTest(X, Y: Integer);
const
Con: TdxFcHitTest = [htOnConnection, htOnConLabel, htOnArrowSrc, htOnArrowDst];
var
I, Q: Integer;
R: TRect;
P: array[0..7] of TPoint;
function OnSelPoint(Idx: Integer): Boolean;
begin
if (X >= P[Idx].X - 2) and (X <= P[Idx].X + 2) and (Y >= P[Idx].Y - 2) and (Y <= P[Idx].Y + 2)
then Include(FHitTest, htOnSelPoint);
Result := htOnSelPoint in FHitTest;
end;
function OnArrow(Arrow: TdxFcConnectionArrow): Boolean;
begin
if Arrow.ArrowType = fcaNone then
Result := False
else
Result := PtInRect(Arrow.DisplayRect(False), Point(X + LeftEdge, Y + TopEdge));
end;
function BySel(Item: TdxFcItem): Boolean;
begin
Result := Item <> nil;
if not Result then Exit;
if Item is TdxFcObject then
R := TdxFcObject(Item).DisplayRect
else
R := TdxFcConnection(Item).DisplayRect;
ExtSelRect(R, True);
Result := PtInRect(R, Point(X - LeftEdge, Y - TopEdge));
end;
begin
if (FHitTest <> []) and (X = FHitX) and (Y = FHitY) then Exit;
FHitX := X; FHitY := Y; FHitTest := [htNowhere];
FObjectAt := nil; FConnectionAt := nil;
for I := ObjectCount - 1 downto 0 do
begin
R := Objects[I].DisplayRect;
ExtSelRect(R, True);
if Objects[I].Visible and PtInRect(R, Point(X, Y)) then
begin
if FHitTest = [htNowhere] then
begin
FHitTest := [htByObject];
FObjectAt := Objects[I];
end;
Objects[I].CreateRgn;
if PtInRegion(Objects[I].FIntRgn, X, Y) then
begin
FHitTest := [htByObject, htOnObject];
FObjectAt := Objects[I];
Break;
end;
end;
end;
for I := ConnectionCount - 1 downto 0 do
begin
R := Connections[I].DisplayRect;
ExtSelRect(R, True);
if PtInRect(R, Point(X, Y)) then
begin
FConnectionAt := Connections[I];
if FHitTest = [htNowhere] then FHitTest := [];
if FConnectionAt.HasPoint(X + LeftEdge, Y + TopEdge) then Include(FHitTest, htOnConnection);
if OnArrow(FConnectionAt.ArrowSource) then Include(FHitTest, htOnArrowSrc);
if OnArrow(FConnectionAt.ArrowDest) then Include(FHitTest, htOnArrowDst);
if FConnectionAt.Text <> '' then
begin
R := FConnectionAt.FTextRect;
OffsetRect(R, -LeftEdge, -TopEdge);
if PtInRect(R, Point(X, Y)) then Include(FHitTest, htOnConLabel);
end;
if (FHitTest * Con <> []) then Break;
end;
end;
if (htOnObject in FHitTest) and (FHitTest * Con <> []) then
begin
if not FConnectionAt.Transparent then
Exclude(FHitTest, htOnObject)
else
FHitTest := FHitTest - Con;
end;
Inc(X, LeftEdge); Inc(Y, TopEdge);
if BySel(SelectedObject) then
begin
SelectedObject.SelPoints(P);
Q := SelectedObject.Quadrant(X, Y) shl 1 + 1;
for I := Q to Q + 2 do
if OnSelPoint(I and 7) then
begin
if FObjectAt <> SelectedObject then Exclude(FHitTest, htOnObject);
FObjectAt := SelectedObject;
Exit;
end;
end;
if BySel(SelectedConnection) then
for I := 0 to SelectedConnection.RealCount - 1 do
begin
with SelectedConnection do
P[0] := InternalGetPoint(FRealPoints, I);
if OnSelPoint(0) then
begin
if FConnectionAt <> SelectedConnection then FHitTest := FHitTest - Con;
Include(FHitTest, htOnConnection);
FConnectionAt := SelectedConnection;
end;
end;
if FHitTest = [] then
begin
FHitTest := [htNowhere];
FConnectionAt := nil;
end;
end;
function TdxCustomFlowChart.GetConnectionAt(X, Y: Integer): TdxFcConnection;
begin
HitTest(X, Y);
Result := FConnectionAt;
end;
function TdxCustomFlowChart.GetObjectAt(X, Y: Integer): TdxFcObject;
begin
HitTest(X, Y);
Result := FObjectAt;
end;
function TdxCustomFlowChart.GetHitTestAt(X, Y: Integer): TdxFcHitTest;
begin
HitTest(X, Y);
Result := FHitTest;
end;
procedure TdxCustomFlowChart.ScrollChart(Bar, Code, Pos: Cardinal; Value, Page: Integer);
begin
case Code of
SB_LINEDOWN: Value := Value + 16;
SB_LINEUP: Value := Value - 16;
SB_PAGEDOWN: Value := Value + Page - 16;
SB_PAGEUP: Value := Value - Page + 16;
SB_TOP: Value := TO_HOME;
SB_BOTTOM: Value := TO_END;
SB_THUMBTRACK, SB_THUMBPOSITION: Value := Pos;
end;
if Bar = SB_HORZ then
SetLeftEdge(Value)
else
SetTopEdge(Value);
end;
function TdxCustomFlowChart.TmpSel: Integer;
var
I: Integer;
Opt: TdxFcOptions;
Sel1: TdxFcEvent;
Sel2: TdxFcAllowEvent;
begin
Result := SelectedObjectCount;
if Result = 0 then Exit;
Opt := Options; FOptions := Opt + [fcoCanSelect, fcoMultiSelect];
Sel1 := OnSelected; OnSelected := nil;
Sel2 := OnSelection; OnSelection := nil;
Inc(FLockUpdates);
for I := 0 to Result - 1 do
SelectedObjects[I].SelectUnion;
Dec(FLockUpdates); FOptions := Opt;
OnSelected := Sel1; OnSelection := Sel2;
end;
procedure TdxCustomFlowChart.RestoreSel(Value: Integer);
var
I: Integer;
begin
for I := Value to SelectedObjectCount - 1 do
SelectedObjects[I].FSelected := False;
FSelObjects.Count := Value;
end;
procedure TdxCustomFlowChart.MoveObjects(DX, DY: Integer);
function IsSel(AObject: TdxFcObject): Boolean;
begin
Result := (AObject <> nil) and AObject.Selected;
end;
var
I, EndSel: Integer;
P: TPoint;
begin
if (DX or DY = 0) or (SelectedObjectCount = 0) then Exit;
P.X := DX; P.Y := DY;
ScalePoint(P);
EndSel := TmpSel;
for I := 0 to SelectedObjectCount - 1 do
begin
NeedRepaintObject(SelectedObjects[I]);
with SelectedObjects[I] do
begin
Inc(FLeft, DX);
Inc(FTop, DY);
SetRealBounds;
Changed;
end;
NeedRepaintObject(SelectedObjects[I]);
end;
for I := 0 to ConnectionCount - 1 do
with Connections[I] do
if IsSel(ObjectSource) or IsSel(ObjectDest) then
begin
ConnectionChanged;
if IsSel(ObjectSource) and IsSel(ObjectDest) then
begin
OffsetPoints(FPoints, DX, DY);
OffsetPoints(FRealPoints, P.X, P.Y);
end;
SetObjectPoints;
SetDisplayRect;
Changed;
end;
RestoreSel(EndSel);
SetChartSizes;
end;
procedure TdxCustomFlowChart.KeyDown(var Key: Word; Shift: TShiftState);
procedure Resize(DX, DY: Integer);
var
W, H: Integer;
begin
if SelectedObject = nil then Exit;
if RealZoom < 100 then
begin
DX := MulDiv(DX, 100, RealZoom);
DY := MulDiv(DY, 100, RealZoom);
end;
with SelectedObject do
begin
W := Width + DX;
H := Height + DY;
if (W > 0) and (H > 0) then SetBounds(Left, Top, W, H);
MakeVisible;
end;
end;
procedure SelNext(Mode: Integer);
var
I, DX, DY, Rate, Min: Integer; Obj: TdxFcObject;
begin
if SelectedObject = nil then Exit;
Obj := nil; Min := $20000000;
for I := 0 to ObjectCount - 1 do
begin
if Objects[I].Selected or not Objects[I].Visible then Continue;
DX := Objects[I].Left - SelectedObject.Left;
DY := Objects[I].Top - SelectedObject.Top;
if Mode > 1 then Swap(DX, DY);
if Mode and 1 <> 0 then DX := -DX;
Rate := Abs(DX) + Abs(DY) shl 3 + DX shr 2;
if Rate < Min then
begin
Min := Rate;
Obj := Objects[I];
end;
end;
if Obj <> nil then
begin
ClearSelection;
Obj.Selected := True;
if Obj.Selected then Obj.MakeVisible;
end;
end;
begin
if Assigned(FDragHandler) then Exit;
inherited KeyDown(Key, Shift);
if (Shift = [ssShift]) and (fcoCanDrag in Options) then
case Key of
VK_RIGHT: Resize(1, 0);
VK_LEFT: Resize(-1, 0);
VK_DOWN: Resize(0, 1);
VK_UP: Resize(0, -1);
VK_NEXT: Resize(1, 1);
VK_PRIOR: Resize(1, -1);
VK_HOME: Resize(-1, -1);
VK_END: Resize(-1, 1);
end;
if (Shift = [ssAlt]) and (fcoCanDrag in Options) then
case Key of
VK_RIGHT: MoveObjects(1, 0);
VK_LEFT: MoveObjects(-1, 0);
VK_DOWN: MoveObjects(0, 1);
VK_UP: MoveObjects(0, -1);
VK_NEXT: MoveObjects(1, 1);
VK_PRIOR: MoveObjects(1, -1);
VK_HOME: MoveObjects(-1, -1);
VK_END: MoveObjects(-1, 1);
end;
if Shift = [ssCtrl] then
case Key of
VK_RIGHT: LeftEdge := LeftEdge + ClientWidth - 16;
VK_LEFT: LeftEdge := LeftEdge - ClientWidth + 16;
VK_PRIOR: TopEdge := TO_HOME;
VK_NEXT: TopEdge := TO_END;
VK_HOME: SetLeftTop(TO_HOME, TO_HOME);
VK_END: SetLeftTop(TO_END, TO_END);
end;
if Shift = [] then
case Key of
VK_DELETE:
if fcoCanDelete in Options then DeleteSelection;
VK_NEXT: TopEdge := TopEdge + ClientHeight - 16;
VK_PRIOR: TopEdge := TopEdge - ClientHeight + 16;
VK_HOME: LeftEdge := TO_HOME;
VK_END: LeftEdge := TO_END;
VK_RIGHT: SelNext(0);
VK_LEFT: SelNext(1);
VK_DOWN: SelNext(2);
VK_UP: SelNext(3);
end;
end;
procedure TdxCustomFlowChart.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
SetFocus;
if not (ssDouble in Shift) then
SetCapture(Handle);
if (Button = mbLeft) and (Shift * [ssCtrl, ssAlt] = []) then
begin
FDragX := X; FDragY := Y; HitTest(X, Y);
if htOnSelPoint in FHitTest then ChkDrag(Shift, X, Y);
if Assigned(FDragHandler) then Exit;
if (htOnObject in FHitTest) and FObjectAt.Selected and not (ssShift in Shift) then
begin
FObjectAt.SelectUnion;
end else
begin
if FHitTest * [htOnObject, htOnConnection, htOnConLabel, htOnArrowSrc, htOnArrowDst] <> [] then
begin
if not (ssShift in Shift) then ClearSelection;
if htOnObject in FHitTest then
FObjectAt.Selected := not FObjectAt.Selected
else
FConnectionAt.Selected := not FConnectionAt.Selected;
end;
end;
end;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TdxCustomFlowChart.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
if GetCapture = Handle then
begin
if Assigned(FDragHandler) then
CallDragHandler(X, Y, dsDragMove)
else
begin
if Focused and (ssLeft in Shift) and (Abs(X - FDragX) + Abs(Y - FDragY) > 4) then
ChkDrag(Shift, X, Y);
end;
end;
inherited MouseMove(Shift, X, Y);
end;
procedure TdxCustomFlowChart.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if GetCapture = Handle then ReleaseCapture;
if Assigned(FDragHandler) then
begin
CallDragHandler(X, Y, dsDragLeave);
FDragHandler := nil;
ClipCursor(nil);
end;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TdxCustomFlowChart.ChkDrag(Shift: TShiftState; X, Y: Integer);
var
P: Byte;
begin
if not (fcoCanDrag in Options) or (Shift <> [ssLeft]) then Exit;
if (FObjectAt <> nil) and FObjectAt.Selected and (FHitTest * [htOnObject, htOnSelPoint] = [])
then
begin
P := FObjectAt.GetLinkedPoint(X, Y);
FDragData.Connect := CreateConnection(FObjectAt, FObjectAt, P, P);
FDragData.Index := 1;
InitDrag(X, Y, DragConnect);
Exit;
end;
if htOnSelPoint in FHitTest then
begin
if SelectedObjectCount = 1 then
InitDrag(X, Y, DragResize)
else
with FDragData do
begin
Connect := FConnectionAt;
Index := Connect.GetNearestPoint(X, Y);
if Connect.ObjectSource <> nil then Inc(Index);
if (Index = 0) or (Index = Connect.RealCount - 1)
then
InitDrag(X, Y, DragConnect)
else
InitDrag(X, Y, DragPoint);
end;
end
else
begin
if (htOnObject in FHitTest) and FObjectAt.Selected then
InitDrag(X, Y, DragMove)
else
if (htOnConnection in FHitTest) and (FConnectionAt = SelectedConnection)
then FConnectionAt.NewPoint(X, Y, DragPoint);
end;
end;
procedure TdxCustomFlowChart.InitDrag(X, Y: Integer; Handler: TdxFcDragHandler);
var
R: TRect;
begin
Update;
R := ClientRect;
Windows.ClientToScreen(Handle, R.TopLeft);
Windows.ClientToScreen(Handle, R.BottomRight);
ClipCursor(@R);
FDragHandler := Handler;
CallDragHandler(X, Y, dsDragEnter);
end;
procedure TdxCustomFlowChart.CallDragHandler(X, Y: Integer; State: TDragState);
begin
ShowCursor(False);
SetROP2(Canvas.Handle, R2_NOTXORPEN);
FDragHandler(X, Y, State);
ShowCursor(True);
end;
procedure TdxCustomFlowChart.DragMove(X, Y: Integer; State: TDragState);
var
I, EndSel: Integer; DC: HDC; SBrush: HBrush;
procedure DrawRgn;
begin
FillRgn(DC, FDragData.Rgn, SBrush);
end;
begin
DC := Canvas.Handle;
SBrush := GetStockObject(BLACK_BRUSH);
with FDragData do
case State of
dsDragEnter:
begin
EndSel := TmpSel;
Rgn := CreateRectRgn(0, 0, 0, 0);
for I := 0 to SelectedObjectCount - 1 do
begin
SelectedObjects[I].CreateRgn;
CombineRgn(Rgn, Rgn, SelectedObjects[I].FExtRgn, RGN_OR);
end;
RestoreSel(EndSel);
Base := Point(X, Y);
Mobile := Base;
DrawRgn;
end;
dsDragMove:
begin
Dec(X, Mobile.X); Dec(Y, Mobile.Y);
Inc(Mobile.X, X); Inc(Mobile.Y, Y);
DrawRgn;
OffsetRgn(Rgn, X, Y);
DrawRgn;
end;
dsDragLeave:
begin
DrawRgn;
Windows.DeleteObject(Rgn);
Base := ChartPoint(Base.X, Base.Y);
Mobile := ChartPoint(X, Y);
MoveObjects(Mobile.X - Base.X, Mobile.Y - Base.Y);
end;
end;
end;
procedure TdxCustomFlowChart.DragResize(X, Y: Integer; State: TDragState);
var
DC: HDC; R: TRect;
procedure DrawRect(Init: Boolean);
begin
if Init then
begin
SelectObject(DC, GetStockObject(BLACK_PEN));
SelectObject(DC, GetStockObject(HOLLOW_BRUSH));
end;
R.TopLeft := FDragData.Base;
R.BottomRight := FDragData.Mobile;
NormRect(R);
with R do
Rectangle(DC, Left, Top, Right, Bottom);
end;
begin
DC := Canvas.Handle;
with FDragData do
case State of
dsDragEnter:
begin
with SelectedObject do
begin
R := DisplayRect;
Index := Word(GetSelPoint(X, Y));
end;
Base := R.TopLeft;
Mobile := R.BottomRight;
if (Index < 2) or (Index > 5) then Swap(Base.X, Mobile.X);
if Index < 4 then Swap(Base.Y, Mobile.Y);
Index := Index and 3;
DrawRect(True);
end;
dsDragMove:
begin
DrawRect(True);
if Index <> 1 then Mobile.X := X;
if Index <> 3 then Mobile.Y := Y;
DrawRect(False);
end;
dsDragLeave:
begin
DrawRect(True);
if SelectedObject = nil then Exit;
R.TopLeft := ChartPoint(Base.X, Base.Y);
R.BottomRight := ChartPoint(Mobile.X, Mobile.Y);
NormRect(R);
with R do
SelectedObject.SetBounds(Left, Top, Right - Left, Bottom - Top);
end;
end;
end;
procedure TdxCustomFlowChart.DragPoint(X, Y: Integer; State: TDragState);
var
DC: HDC;
I: Integer;
Flg: Boolean;
begin
DC := Canvas.Handle;
if State = dsDragEnter then FDragData.Connect.InvertColor;
SelectObject(DC, FDragData.Connect.FPen.Handle);
with FDragData do
case State of
dsDragEnter:
with Connect do
begin
Mobile := InternalGetPoint(FRealPoints, Index);
OffsetPoints(FRealPoints, -LeftEdge, -TopEdge);
Base.X := 0;
end;
dsDragMove:
with Connect do
begin
Base.X := 1;
PaintLine(DC);
InternalPutPoint(FRealPoints, Index, Point(X, Y));
PaintLine(DC);
end;
dsDragLeave:
with Connect do
begin
PaintLine(DC);
InvertColor;
OffsetPoints(FRealPoints, LeftEdge, TopEdge);
InternalPutPoint(FRealPoints, Index, Mobile);
I := Index;
if ObjectSource <> nil then Dec(I);
Flg := (fcoDelOnClick in Options) and (Base.X = 0);
if not Flg then
begin
Base := ChartPoint(X, Y);
X := Base.X; Y := Base.Y;
Base := InternalGetPoint(FRealPoints, Index - 1);
Mobile := InternalGetPoint(FRealPoints, Index + 1);
case Style of
fclStraight: Flg := PtOnLine(liStraight, Base, 2, Screen.Width shr 9, X, Y);
fclRectH: Flg := (X = Mobile.X) or (Y = Base.Y);
fclRectV: Flg := (X = Base.X) or (Y = Mobile.Y);
end;
end;
if Flg then
RemovePoint(I)
else
Points[I] := Point(X, Y);
end;
end;
end;
procedure TdxCustomFlowChart.DragConnect(X, Y: Integer; State: TDragState);
var
DC: HDC;
I: Integer;
begin
DC := Canvas.Handle;
if State = dsDragEnter then FDragData.Connect.InvertColor;
SelectObject(DC, FDragData.Connect.FPen.Handle);
with FDragData do
case State of
dsDragEnter:
with Connect do
begin
if (Index = 0) and (ObjectSource <> nil) or (Index <> 0) and (ObjectDest <> nil) then
begin
Base := InternalGetPoint(FRealPoints, Index);
if Index = 0 then
SetObjectSource(nil, 0)
else
SetObjectDest(nil, 0);
InternalInsertPoint(FPoints, Index, Base);
InternalInsertPoint(FRealPoints, Index, Base);
end
else
ConnectionChanged;
OffsetPoints(FRealPoints, -LeftEdge, -TopEdge);
end;
dsDragMove:
with Connect do
begin
PaintLine(DC);
InternalPutPoint(FRealPoints, Index, Point(X, Y));
PaintLine(DC);
end;
dsDragLeave:
with Connect do
begin
PaintLine(DC);
InvertColor;
OffsetPoints(FRealPoints, LeftEdge, TopEdge);
HitTest(X, Y);
if FObjectAt <> nil then
begin
I := FObjectAt.GetLinkedPoint(X, Y);
InternalRemovePoint(FPoints, Index);
InternalRemovePoint(FRealPoints, Index);
if Index = 0 then
SetObjectSource(FObjectAt, I)
else
SetObjectDest(FObjectAt, I);
end
else
begin
if ObjectSource <> nil then Dec(Index);
Points[Index] := ChartPoint(X, Y);
end;
FHitTest := [];
end;
end;
end;
procedure TdxCustomFlowChart.AbortDrag;
var
P: TPoint;
begin
if Assigned(FDragHandler) then
begin
Windows.GetCursorPos(P);
Windows.ScreenToClient(Handle, P);
MouseUp(mbLeft, [], P.X, P.Y);
end;
end;
procedure TdxCustomFlowChart.LoadFromStream(Stream: TStream);
var
I: Integer;
AIsUnicode: Boolean;
B: array[0..6] of AnsiChar;
begin
BeginUpdate;
AIsUnicode := False;
if (Stream.Size - Stream.Position) > SizeOf(B) then
begin
Stream.ReadBuffer(B, SizeOf(B));
AIsUnicode := B = StreamDescriptionUNICODE;
if not AIsUnicode and (B <> StreamDescriptionANSI) then
Stream.Position := Stream.Position - SizeOf(B);
end;
FLoading := True;
try
Clear;
I := 0; Stream.ReadBuffer(I, SizeOf(Word));
while I > 0 do
begin
InternalCreateObject.Load(Stream, AIsUnicode);
Dec(I);
end;
for I := 0 to ObjectCount - 1 do
Objects[I].ResolveObjRefs;
I := 0; Stream.ReadBuffer(I, SizeOf(Word));
while I > 0 do
begin
InternalCreateConnection.Load(Stream, AIsUnicode);
Dec(I);
end;
finally
FLoading := False;
EndUpdate;
end;
end;
procedure TdxCustomFlowChart.SaveToStream(Stream: TStream);
var
I: Integer;
begin
I := ObjectCount;
{$IFNDEF STREAMANSIFORMAT}
{$IFDEF DELPHI12}
Stream.WriteBuffer(StreamDescriptionUNICODE[1], StrLen(PAnsiChar(StreamDescriptionUNICODE)));
{$ELSE}
Stream.WriteBuffer(StreamDescriptionANSI[1], Length(StreamDescriptionANSI));
{$ENDIF}
{$ENDIF}
Stream.WriteBuffer(I, SizeOf(Word));
for I := 0 to ObjectCount - 1 do
Objects[I].Save(Stream);
I := ConnectionCount;
Stream.WriteBuffer(I, SizeOf(Word));
for I := 0 to ConnectionCount - 1 do
Connections[I].Save(Stream);
end;
procedure TdxCustomFlowChart.LoadFromFile(const FileName: string);
var
S: TStream;
begin
S := TFileStream.Create(FileName, fmOpenRead);
try
LoadFromStream(S);
finally
S.Free;
end;
end;
procedure TdxCustomFlowChart.SaveToFile(const FileName: string);
var
S: TStream;
begin
S := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(S);
finally
S.Free;
end;
end;
procedure TdxCustomFlowChart.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('Items', LoadFromStream, SaveToStream, ObjectCount > 0);
end;
function TdxCustomFlowChart.CanSelect(Item: TdxFcItem): Boolean;
begin
Result := fcoCanSelect in Options;
if Assigned(OnSelection) then OnSelection(Self, Item, Result);
end;
procedure TdxCustomFlowChart.Changed(Item: TdxFcItem);
begin
if Assigned(OnChange) then OnChange(Self, Item);
end;
procedure TdxCustomFlowChart.Delete(Item: TdxFcItem);
begin
AbortDrag;
Item.Invalidate;
if Item.Selected then
begin
Item.SelList.Remove(Item);
Item.FSelected := False;
Select(Item);
end;
if Assigned(OnDeletion) then OnDeletion(Self, Item);
Item.FDestroying := True;
FHitTest := [];
end;
procedure TdxCustomFlowChart.Select(Item: TdxFcItem);
begin
if Assigned(OnSelected) then OnSelected(Self, Item);
end;
procedure TdxCustomFlowChart.WndProc(var Message: TMessage);
begin
if Assigned(FDragHandler) and (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST)
then Exit;
inherited WndProc(Message);
end;
procedure TdxCustomFlowChart.WMHScroll(var Msg: TWMHScroll);
begin
ScrollChart(SB_HORZ, Msg.ScrollCode, Msg.Pos, LeftEdge, ClientWidth);
end;
procedure TdxCustomFlowChart.WMVScroll(var Msg: TWMVScroll);
begin
ScrollChart(SB_VERT, Msg.ScrollCode, Msg.Pos, TopEdge, ClientHeight);
end;
procedure TdxCustomFlowChart.WMSetFocus(var Msg: TWMSetFocus);
begin
inherited;
if fcoHideSelection in Options then InvalidateSel;
end;
procedure TdxCustomFlowChart.WMKillFocus(var Msg: TWMKillFocus);
begin
if GetCapture = Handle then ReleaseCapture; // Fix: by Kirill
AbortDrag;
WMSetFocus(TWMSetFocus(Msg));
end;
procedure TdxCustomFlowChart.WMSize(var Msg: TWMSize);
begin
inherited;
if Msg.SizeType in [SIZE_MAXIMIZED, SIZE_RESTORED] then
begin
if (Zoom <> 0) and (Zoom <> $FFFF) then UpdateScrollRange;
if Zoom = 0 then
begin
FZoom := 100;
SetZoom(0);
end;
end;
end;
procedure TdxCustomFlowChart.WMSetCursor(var Message: TWMSetCursor);
const
Cursors: array[0..3] of TCursor = (crSizeNWSE, crSizeNS, crSizeNESW, crSizeWE);
var
pt: TPoint;
begin
GetCursorPos(pt);
pt := ScreenToClient(pt);
HitTest(pt.X, pt.Y);
if (htOnSelPoint in FHitTest) then
begin
if(FObjectAt <> nil) and (FObjectAt = SelectedObject) then
Windows.SetCursor(Screen.Cursors[Cursors[FObjectAt.GetSelPoint(pt.X, pt.Y) and 3]])
else Windows.SetCursor(Screen.Cursors[crSize]);
end else inherited;
end;
procedure TdxCustomFlowChart.WMErase(var Msg: TWMEraseBkgnd);
begin
Msg.Result := 1;
end;
procedure TdxCustomFlowChart.WMGetDlgCode(var Msg: TWMGetDlgCode);
begin
inherited;
Msg.Result := Msg.Result or DLGC_WANTARROWS;
end;
procedure TdxCustomFlowChart.WMPaint(var Msg: TWMPaint);
begin
if not Assigned(FDragHandler) then inherited;
end;
procedure TdxCustomFlowChart.WMNCHitTest(var Msg: TWMNCHitTest);
begin
DefaultHandler(Msg);
if (csDesigning in ComponentState) and not (Msg.Result in [HTHSCROLL, HTVSCROLL])
then Msg.Result := HTCLIENT;
end;
procedure TdxCustomFlowChart.CMCtl3DChanged(var Msg: TMessage);
begin
inherited;
if BorderStyle = bsSingle then RecreateWnd;
end;
procedure TdxCustomFlowChart.CMFontChanged(var Msg: TMessage);
procedure ResetFont(Item: TdxFcItem);
begin
if Item.ParentFont then
with Item do
begin
Font.Assign(Self.Font);
FParentFont := True;
end;
end;
var
I: Integer;
begin
BeginUpdate;
for I := 0 to ObjectCount - 1 do
ResetFont(Objects[I]);
for I := 0 to ConnectionCount - 1 do
ResetFont(Connections[I]);
EndUpdate;
end;
{TdxFcConnection}
constructor TdxFcConnection.Create(AOwner: TdxCustomFlowChart);
begin
inherited Create(AOwner);
FPoints := TList.Create;
FRealPoints := TList.Create;
FPen := TPen.Create;
FArrowSource := TdxFcConnectionArrow.Create(Self);
FArrowDest := TdxFcConnectionArrow.Create(Self);
AOwner.FConnections.Add(Self);
end;
destructor TdxFcConnection.Destroy;
var
Resize: Boolean;
begin
Owner.Delete(Self);
//Update linked objects for dest and source object.
Resize := not ((ObjectSource <> nil) and ObjectSource.Destroying or
(ObjectDest <> nil) and ObjectDest.Destroying);
SetObjectSource(nil, 0);
SetObjectDest(nil, 0);
FPoints.Free;
FRealPoints.Free;
FPen.Free;
FArrowSource.Free;
FArrowDest.Free;
Owner.FConnections.Remove(Self);
if Resize then Owner.SetChartSizes;
inherited Destroy;
end;
function TdxFcConnection.IndexValid(var Index: Integer; AMax: Integer): Boolean;
begin
Result := (Index >= 0) and (Index <= PointCount - AMax);
if ObjectSource <> nil then Inc(Index);
end;
function TdxFcConnection.GetPoint(Index: Integer): TPoint;
begin
if not IndexValid(Index, 1) then
Result := Point(0, 0)
else
Result := InternalGetPoint(FPoints, Index);
end;
function TdxFcConnection.GetRealPoint(Index: Integer): TPoint;
begin
if not IndexValid(Index, 1) then
Result := Point(0, 0)
else
Result := InternalGetPoint(FRealPoints, Index);
end;
function TdxFcConnection.InternalGetPoint(List: TList; Index: Integer): TPoint;
begin
Index := Index shl 1;
Result.X := Integer(List[Index]);
Result.Y := Integer(List[Index + 1]);
end;
procedure TdxFcConnection.InternalInsertPoint(List: TList; Index: Integer; Value: TPoint);
begin
Index := Index shl 1;
List.Insert(Index, Pointer(Value.X));
List.Insert(Index + 1, Pointer(Value.Y));
end;
procedure TdxFcConnection.InternalPutPoint(List: TList; Index: Integer; Value: TPoint);
begin
Index := Index shl 1;
List[Index] := Pointer(Value.X);
List[Index + 1] := Pointer(Value.Y);
end;
procedure TdxFcConnection.InternalRemovePoint(List: TList; Index: Integer);
begin
Index := Index shl 1;
List.Delete(Index);
List.Delete(Index);
end;
procedure TdxFcConnection.PutPoint(Index: Integer; Value: TPoint);
begin
if IndexValid(Index, 1) then
begin
ConnectionChanged;
InternalPutPoint(FPoints, Index, Value);
Owner.ScalePoint(Value);
InternalPutPoint(FRealPoints, Index, Value);
SetDisplayRect;
Changed;
Owner.SetChartSizes;
end;
end;
function TdxFcConnection.RealCount: Integer;
begin
Result := FRealPoints.Count shr 1;
end;
function TdxFcConnection.GetPointCount: Integer;
begin
Result := RealCount;
if ObjectSource <> nil then Dec(Result);
if ObjectDest <> nil then Dec(Result);
end;
function TdxFcConnection.SelList: TList;
begin
Result := Owner.FSelConnections;
end;
procedure TdxFcConnection.AddPoint(Pt: TPoint);
begin
InsertPoint(PointCount, Pt);
end;
procedure TdxFcConnection.InsertPoint(Index: Integer; Pt: TPoint);
begin
if IndexValid(Index, 0) then
begin
ConnectionChanged;
InternalInsertPoint(FPoints, Index, Pt);
Owner.ScalePoint(Pt);
InternalInsertPoint(FRealPoints, Index, Pt);
SetDisplayRect;
Changed;
Owner.SetChartSizes;
end;
end;
procedure TdxFcConnection.RemovePoint(Index: Integer);
begin
if IndexValid(Index, 1) then
begin
ConnectionChanged;
InternalRemovePoint(FPoints, Index);
InternalRemovePoint(FRealPoints, Index);
SetDisplayRect;
Changed;
Owner.SetChartSizes;
end;
end;
function TdxFcConnection.GetColor: TColor;
begin
Result := FPen.Color;
end;
function TdxFcConnection.GetPenStyle: TPenStyle;
begin
Result := FPen.Style;
end;
function TdxFcConnection.GetPenWidth: Integer;
begin
Result := FPen.Width;
end;
procedure TdxFcConnection.SetPenStyle(Value: TPenStyle);
begin
if (FPen.Style <> Value) then
begin
FPen.Style := Value;
ConnectionChanged;
Changed;
end;
end;
procedure TdxFcConnection.SetPenWidth(Value: Integer);
begin
if PenWidth <> Value then
begin
FPen.Width := Value;
ConnectionChanged;
Changed;
end;
end;
procedure TdxFcConnection.SetStyle(Value: TdxFclStyle);
begin
if (FStyle <> Value) then
begin
if (ArrowSource.ArrowType = fcaArrow) or (ArrowDest.ArrowType = fcaArrow)
then ConnectionChanged;
FStyle := Value;
ArrowSource.ClearPoints;
ArrowDest.ClearPoints;
SetDisplayRect;
Changed;
end;
end;
procedure TdxFcConnection.SetText(Value: string);
begin
if (FText <> Value) then
begin
if Text <> '' then InvalidateText;
FText := Value;
if Text <> '' then
begin
SetTextRect;
PlaceText;
InvalidateText;
end;
Changed;
Owner.SetChartSizes;
end;
end;
procedure TdxFcConnection.SetColor(Value: TColor);
begin
if (Color <> Value) then
begin
FPen.Color := Value;
ConnectionChanged;
Changed;
end;
end;
procedure TdxFcConnection.SetTransparent(Value: Boolean);
begin
if (FTransparent <> Value) then
begin
FTransparent := Value;
ConnectionChanged;
Changed;
end;
end;
procedure TdxFcConnection.SetArrowSource(Value: TdxFcConnectionArrow);
begin
ArrowSource.Assign(Value);
end;
procedure TdxFcConnection.SetArrowDest(Value: TdxFcConnectionArrow);
begin
ArrowDest.Assign(Value);
end;
function TdxFcConnection.DisplayRect: TRect;
begin
Result := FDisplayRect;
if Text <> '' then UnionRect(Result, Result, FTextRect);
if ArrowSource.Active then UnionRect(Result, Result, ArrowSource.DisplayRect(True));
if ArrowDest.Active then UnionRect(Result, Result, ArrowDest.DisplayRect(True));
OffsetRect(Result, -Owner.LeftEdge, -Owner.TopEdge);
end;
procedure TdxFcConnection.IsRepainted;
var
R: TRect;
begin
FRepainted := FPoints.Count > 2;
if not FRepainted then Exit;
R := DisplayRect;
ExtSelRect(R, Selected);
FRepainted := RectVisible(Owner.Canvas.Handle, R) or (csPaintCopy in Owner.ControlState); {paul}
end;
procedure TdxFcConnection.InvalidateText;
var
R: TRect;
begin
if FPoints.Count <= 2 then Exit;
Owner.FRepaint := True;
if Owner.CanPaint then
begin
R := FTextRect;
OffsetRect(R, -Owner.LeftEdge, -Owner.TopEdge);
InvalidateRect(Owner.Handle, @R, True);
end;
end;
procedure TdxFcConnection.ConnectionChanged;
var
R: TRect;
begin
if FPoints.Count <= 2 then Exit;
Owner.FRepaint := True;
ArrowSource.SetPoints(0);
ArrowDest.SetPoints(RealCount - 1);
if Owner.CanPaint then
begin
R := DisplayRect;
ExtSelRect(R, Selected);
InvalidateRect(Owner.Handle, @R, True);
end;
end;
procedure TdxFcConnection.Invalidate;
begin
ConnectionChanged;
end;
procedure TdxFcConnection.PlaceText;
var
DX, DY: Integer;
begin
with FTextRect do
begin
DX := FMassCenter.X - (Right + Left) div 2;
DY := FMassCenter.Y - (Bottom + Top) div 2;
end;
OffsetRect(FTextRect, DX, DY);
end;
procedure TdxFcConnection.SetDisplayRect;
var
I: Integer;
begin
Owner.FHitTest := [];
if Destroying or (FPoints.Count < 2) then Exit;
FDisplayRect.TopLeft := InternalGetPoint(FRealPoints, 0);
FDisplayRect.BottomRight := FDisplayRect.TopLeft;
for I := 1 to RealCount - 1 do
ExtendRect(FDisplayRect, InternalGetPoint(FRealPoints, I));
FMassCenter := LineCenter(TLineType(Style), FRealPoints.List^, RealCount);
Inc(FDisplayRect.Right); Inc(FDisplayRect.Bottom);
if Text <> '' then PlaceText;
ConnectionChanged;
end;
procedure TdxFcConnection.SetTextRect;
var
DC: HDC;
begin
Owner.FHitTest := [];
FTextRect := Rect(0, 0, 0, 0);
DC := GetDC(0);
SelectObject(DC, RealFont.Handle);
DrawText(DC, PChar(Text), -1, FTextRect, DT_CALCRECT);
ReleaseDC(0, DC);
end;
procedure TdxFcConnection.FontChanged;
begin
InvalidateText;
SetTextRect;
PlaceText;
InvalidateText;
Owner.SetChartSizes;
end;
procedure TdxFcConnection.ZoomChanged;
var
J: Integer;
P: TPoint;
begin
for J := 0 to RealCount - 1 do
begin
P := InternalGetPoint(FPoints, J);
Owner.ScalePoint(P);
InternalPutPoint(FRealPoints, J, P);
end;
ArrowSource.SetRealBounds;
ArrowDest.SetRealBounds;
SetObjectPoints;
ScaleFont;
if Text <> '' then SetTextRect;
SetDisplayRect;
end;
procedure TdxFcConnection.SetObjectPoints;
procedure SetPoint(AObj: TdxFcObject; Arrow: TdxFcConnectionArrow; PtIndex, ListIndex: Integer);
var
P: TPoint;
begin
P := AObj.LinkedPoints[PtIndex];
InternalPutPoint(FRealPoints, ListIndex, P);
Arrow.SetPoints(ListIndex);
end;
begin
if Destroying or (FPoints.Count < 2) then Exit;
if ObjectSource <> nil then SetPoint(ObjectSource, ArrowSource, FPointSource, 0);
if ObjectDest <> nil then SetPoint(ObjectDest, ArrowDest, FPointDest, RealCount - 1);
end;
procedure TdxFcConnection.DelObj(AObj, Partneur: TdxFcObject; Index: Integer);
begin
if Partneur <> nil then
begin
AObj.FLinkedObjects.Remove(Partneur);
Partneur.FLinkedObjects.Remove(AObj);
end;
AObj.FConnections.Remove(Self);
InternalRemovePoint(FPoints, Index);
InternalRemovePoint(FRealPoints, Index);
end;
procedure TdxFcConnection.InsObj(AObj, Partneur: TdxFcObject; Index: Integer);
begin
if Partneur <> nil then
begin
AObj.FLinkedObjects.Add(Partneur);
Partneur.FLinkedObjects.Add(AObj);
end;
AObj.FConnections.Add(Self);
InternalInsertPoint(FPoints, Index, Point(0, 0));
InternalInsertPoint(FRealPoints, Index, Point(0, 0));
end;
procedure TdxFcConnection.SetObjectSource(AObject: TdxFcObject; APoint: Byte);
begin
if (AObject = ObjectSource) and (APoint = FPointSource) then Exit;
ConnectionChanged;
if AObject <> ObjectSource then
begin
if ObjectSource <> nil then DelObj(ObjectSource, ObjectDest, 0);
if AObject <> nil then InsObj(AObject, ObjectDest, 0);
FObjectSource := AObject;
end;
FPointSource := APoint;
SetObjectPoints;
SetDisplayRect;
Changed;
end;
procedure TdxFcConnection.SetObjectDest(AObject: TdxFcObject; APoint: Byte);
begin
if (AObject = ObjectDest) and (APoint = FPointDest) then Exit;
ConnectionChanged;
if AObject <> ObjectDest then
begin
if ObjectDest <> nil then DelObj(ObjectDest, ObjectSource, RealCount - 1);
if AObject <> nil then InsObj(AObject, ObjectSource, RealCount);
FObjectDest := AObject;
end;
FPointDest := APoint;
SetObjectPoints;
SetDisplayRect;
Changed;
end;
function TdxFcConnection.ScreenPoint(Index: Integer): TPoint;
begin
Result := InternalGetPoint(FRealPoints, Index);
Dec(Result.X, Owner.LeftEdge);
Dec(Result.Y, Owner.TopEdge);
end;
function TdxFcConnection.HasPoint(X, Y: Integer): Boolean;
begin
Result := PtOnLine(TLineType(Style), FRealPoints.List^, RealCount, Screen.Width shr 8, X, Y);
end;
function TdxFcConnection.InRect(const R: TRect): Boolean;
var
R1: TRect;
begin
R1 := R;
OffsetRect(R1, Owner.LeftEdge, Owner.TopEdge);
Result := RectOnLine(TLineType(Style), FRealPoints.List^, RealCount, R1);
end;
function TdxFcConnection.GetNearestPoint(X, Y: Integer): Integer;
var
I, Cur, Min: Integer;
begin
Result := 0;
Min := $7FFFFFFF;
Inc(X, Owner.LeftEdge);
Inc(Y, Owner.TopEdge);
for I := 0 to RealCount - 1 do
begin
Cur := QDistance(X, Y, InternalGetPoint(FRealPoints, I));
if Cur < Min then
begin
Min := Cur;
Result := I;
end;
end;
if ObjectSource <> nil then Dec(Result);
end;
procedure TdxFcConnection.ArrowChanged(Value: TdxFcConnectionArrow);
begin
//TODO optimized the drawing
if Value = ArrowSource then Value.SetPoints(0);
if Value = ArrowDest then Value.SetPoints(RealCount - 1);
ConnectionChanged;
end;
procedure TdxFcConnection.OffsetPoints(List: TList; DX, DY: Integer);
var
I, X, Y: Integer;
begin
for I := 0 to RealCount - 1 do
begin
X := Integer(List[I shl 1]) + DX;
Y := Integer(List[I shl 1 + 1]) + DY;
List[I shl 1] := Pointer(X);
List[I shl 1 + 1] := Pointer(Y);
end;
if List = FRealPoints then
begin
ArrowSource.OffsetPoints(DX, DY);
ArrowDest.OffsetPoints(DX, DY);
end;
end;
procedure TdxFcConnection.NewPoint(X, Y: Integer; Handler: TdxFcDragHandler);
var
I: Integer;
P0, P1, P2: TPoint;
begin
I := PointIndex + 1;
if (I <= 0) or (I >= RealCount) then
begin
I := GetNearestPoint(X, Y);
if ObjectSource <> nil then Inc(I);
if I = 0 then
Inc(I)
else
if I < RealCount - 1 then
begin
P0 := ScreenPoint(I);
P1 := ScreenPoint(I - 1);
P2 := ScreenPoint(I + 1);
Dec(P0.X, X); Dec(P0.Y, Y);
Dec(P1.X, X); Dec(P1.Y, Y);
Dec(P2.X, X); Dec(P2.Y, Y);
if (P0.X * P2.X) + (P0.Y * P2.Y) < (P0.X * P1.X) + (P0.Y * P1.Y) then Inc(I);
end;
end;
P0 := Owner.ChartPoint(X, Y);
InternalInsertPoint(FPoints, I, P0);
Owner.ScalePoint(P0);
InternalInsertPoint(FRealPoints, I, P0);
Owner.FDragData.Index := I;
Owner.FDragData.Connect := Self;
ConnectionChanged;
Owner.InitDrag(X, Y, Handler);
end;
function TdxFcConnection.RealStyle: TdxFclStyle;
begin
Result := Style;
if (Result = fclCurved) and (RealCount < 3) then Result := fclStraight;
end;
procedure TdxFcConnection.InvertColor;
begin
FPen.Color := (ColorToRGB(Color) xor not ColorToRGB(Owner.Color)) and $FFFFFF;
end;
procedure TdxFcConnection.PaintLine(DC: HDC);
begin
case RealStyle of
fclStraight: Polyline(DC, FRealPoints.List^, RealCount);
fclCurved: QSpline(DC, FRealPoints.List^, RealCount);
fclRectH: RectHLine(DC, FRealPoints.List^, RealCount);
fclRectV: RectVLine(DC, FRealPoints.List^, RealCount);
end;
end;
procedure TdxFcConnection.Paint(Upper: Boolean);
var
DC: HDC;
R: TRect;
begin
if FRepainted and (Transparent xor Upper) then
begin
OffsetPoints(FRealPoints, -Owner.LeftEdge, -Owner.TopEdge);
DC := Owner.Canvas.Handle;
SelectObject(DC, FPen.Handle);
PaintLine(DC);
ArrowSource.Paint;
ArrowDest.Paint;
OffsetPoints(FRealPoints, Owner.LeftEdge, Owner.TopEdge);
if Text <> '' then
begin
R := FTextRect;
OffsetRect(R, -Owner.LeftEdge, -Owner.TopEdge);
SelectObject(DC, RealFont.Handle);
SetTextColor(DC, ColorToRGB(RealFont.Color));
DrawText(DC, PChar(Text), -1, R, DT_CENTER);
end;
end;
end;
procedure TdxFcConnection.Assign(Source: TPersistent);
var
I: Integer;
begin
if Source is TdxFcConnection then
with TdxFcConnection(Source) do
begin
if Self.PointCount > 0 then Self.RemovePoint(0);
Self.FPoints.Clear;
Self.FRealPoints.Clear;
Self.SetObjectSource(ObjectSource, PointSource);
Self.SetObjectDest(ObjectDest, PointDest);
Self.ArrowSource := ArrowSource;
Self.ArrowDest := ArrowDest;
Self.Color := Color;
Self.Transparent := Transparent;
for I := 0 to PointCount - 1 do
Self.AddPoint(Points[I]);
Self.PenStyle := PenStyle;
Self.PenWidth := PenWidth;
Self.Style := Style;
end;
inherited Assign(Source);
end;
procedure TdxFcConnection.Load(Stream: TStream; AIsUnicode: Boolean);
procedure SetArrow(Arrow: TdxFcConnectionArrow; const AData: TdxFcArwData);
begin
with Arrow do
begin
Width := AData.Width;
Height := AData.Height;
Color := AData.Color;
ArrowType := AData.AType;
end;
end;
var
P: TPoint;
ConData: TdxFcConData;
begin
Stream.ReadBuffer(ConData, SizeOf(ConData));
with ConData do
begin
Self.Style := Style;
Self.Color := Color;
ParentFont := ParFont;
Transparent := PtDst and $80 <> 0;
PenStyle := TPenStyle(PtSrc shr 4);
PtDst := PtDst and $7F; PtSrc := PtSrc and $0F;
SetObjectSource(Owner.Objects[ObjSrc], PtSrc);
SetObjectDest(Owner.Objects[ObjDst], PtDst);
while PtCount > 0 do
begin
Stream.ReadBuffer(P, SizeOf(P));
InternalInsertPoint(FPoints, PointCount + Ord(ObjectSource <> nil), P);
Owner.ScalePoint(P);
InternalInsertPoint(FRealPoints, PointCount + Ord(ObjectSource <> nil), P);
Dec(PtCount);
end;
SetArrow(ArrowSource, ArwSrc);
SetArrow(ArrowDest, ArwDst);
end;
SetDisplayRect;
LoadFont(Stream, AIsUnicode);
Text := ReadStr(Stream, AIsUnicode);
end;
procedure TdxFcConnection.Save(Stream: TStream);
procedure ReadArrow(Arrow: TdxFcConnectionArrow; var AData: TdxFcArwData);
begin
with Arrow do
begin
AData.AType := ArrowType;
AData.Width := Width;
AData.Height := Height;
AData.Color := Color;
end;
end;
var
I: Integer;
P: TPoint;
ConData: TdxFcConData;
begin
with ConData do
begin
if ObjectSource = nil then
ObjSrc := -1
else
ObjSrc := ObjectSource.ZOrder;
if ObjectDest = nil then
ObjDst := -1
else
ObjDst := ObjectDest.ZOrder;
PtCount := Word(PointCount);
Color := Self.Color;
PtSrc := FPointSource; PtDst := FPointDest;
Style := Self.Style; ParFont := ParentFont;
PtSrc := PtSrc or Ord(PenStyle) shl 4;
if Transparent then PtDst := PtDst or $80;
ReadArrow(ArrowSource, ArwSrc);
ReadArrow(ArrowDest, ArwDst);
end;
Stream.WriteBuffer(ConData, SizeOf(ConData));
for I := 0 to PointCount - 1 do
begin
P := Points[I];
Stream.WriteBuffer(P, SizeOf(P));
end;
SaveFont(Stream);
WriteStr(Stream, Text);
end;
end.