{********************************************************************} { } { Developer Express Visual Component Library } { ExpressScheduler } { } { Copyright (c) 2003-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 EXPRESSSCHEDULER 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 cxSchedulerGanttView; {$I cxVer.inc} interface uses Types, Forms, Windows, SysUtils, StdCtrls, Classes, Math, Controls, Graphics, cxScrollBar, cxLookAndFeelPainters, cxSchedulerCustomControls, cxSchedulerStorage, cxSchedulerCustomResourceView, cxSchedulerUtils, cxSchedulerStrs, cxDateUtils, cxClasses, cxDrawTextUtils, cxSchedulerTimeGridView, cxGraphics, cxGeometry, cxControls; const // hittest constants htcLink = $15; htcExpandButton = $17; type TcxSchedulerGanttView = class; TcxSchedulerGanttViewController = class; TcxSchedulerGanttViewViewInfo = class; TcxSchedulerGanttViewEventStyle = (esDefault, esProgress); { TcxSchedulerGanttViewEventLayoutBuilder } TcxSchedulerGanttViewEventLayoutBuilder = class(TcxSchedulerEventLayoutBuilder) public procedure Calculate; override; end; { TcxSchedulerGanttViewLinkViewInfo } TcxSchedulerGanttViewLinkLines = array of TRect; TcxSchedulerGanttViewLinkViewInfo = class(TcxSchedulerCustomViewInfoItem) private FArrowDirection: TcxArrowDirection; FArrowRect: TRect; FClipRect: TRect; FEventItemLink: TcxSchedulerEventItemLink; FFinishEventViewInfo: TcxSchedulerEventCellViewInfo; FHint: string; FLines: TcxSchedulerGanttViewLinkLines; FStartEventViewInfo: TcxSchedulerEventCellViewInfo; FViewInfo: TcxSchedulerGanttViewViewInfo; function GetArrowDirection(const AArrowStart, AArrowFinish: TPoint): TcxArrowDirection; function GetFinishPoint(const AStartRect, AFinishRect: TRect): TPoint; function GetIsHot: Boolean; function GetRelation: TcxSchedulerEventRelation; function GetStartPoint(const AStartRect: TRect): TPoint; protected function GetHitTest(AHitTest: TcxSchedulerCustomResourceViewHitTest; var ABreak: Boolean): Boolean; override; procedure AfterCustomDraw(ACanvas: TcxCanvas); override; procedure BeforeCustomDraw(ACanvas: TcxCanvas); override; procedure Calculate(AStartRect: TRect; AFinishRect: TRect); virtual; procedure CalculateArrowRect(const AArrowStart, AArrowFinish: TPoint); virtual; procedure CalculateBounds; virtual; procedure SetClipRegion(ACanvas: TcxCanvas); property FinishEventViewInfo: TcxSchedulerEventCellViewInfo read FFinishEventViewInfo; property StartEventViewInfo: TcxSchedulerEventCellViewInfo read FStartEventViewInfo; property ClipRect: TRect read FClipRect; public procedure Initialize(AViewInfo: TcxSchedulerGanttViewViewInfo; AStartEventViewInfo, AFinishEventViewInfo: TcxSchedulerEventCellViewInfo; AEventItemLink: TcxSchedulerEventItemLink); virtual; procedure Draw(ACanvas: TcxCanvas); override; property ArrowDirection: TcxArrowDirection read FArrowDirection; property ArrowRect: TRect read FArrowRect; property EventItemLink: TcxSchedulerEventItemLink read FEventItemLink; property Hint: string read FHint; property IsHot: Boolean read GetIsHot; property Lines: TcxSchedulerGanttViewLinkLines read FLines; property Relation: TcxSchedulerEventRelation read GetRelation; property ViewInfo: TcxSchedulerGanttViewViewInfo read FViewInfo; end; { TcxSchedulerGanttViewExpandButtonViewInfo } TcxSchedulerGanttViewExpandButtonViewInfo = class(TcxSchedulerCustomViewInfoItem) private FEvent: TcxSchedulerControlEvent; FExpanded: Boolean; protected procedure DoClick; procedure InitHitTest(AHitTest: TcxSchedulerCustomResourceViewHitTest); override; public procedure Draw(ACanvas: TcxCanvas); override; property Event: TcxSchedulerControlEvent read FEvent; property Expanded: Boolean read FExpanded; end; { TcxSchedulerGanttViewTotalProgressViewInfo } TcxSchedulerGanttViewTotalProgressViewInfo = class private FContentArea: TRect; FDrawAsPolyline: Boolean; FLineColor: TColor; FPoints: array of TPoint; FProgressNodes: array of TPoint; FTimeLineRect: TRect; FViewInfo: TcxSchedulerGanttViewViewInfo; function FindUncompliteEventBeforeLine(const ALine: TRect; AResource: TcxSchedulerStorageResourceItem; var AViewInfo: TcxSchedulerEventCellViewInfo): Boolean; function GetTimeLineApproximateRect: TRect; function GetTimeLineRect: TRect; function GetUncompliteEventPoint(const ALine: TRect; var P: TPoint; AResource: TcxSchedulerStorageResourceItem): Boolean; public constructor Create(AViewInfo: TcxSchedulerGanttViewViewInfo); virtual; procedure Calculate; virtual; procedure CalculatePolyline; procedure Draw(ACanvas: TcxCanvas); procedure DrawProgressNodes(ACanvas: TcxCanvas); property ContentArea: TRect read FContentArea; property DrawAsPolyline: Boolean read FDrawAsPolyline; property LineColor: TColor read FLineColor; property TimeLineRect: TRect read FTimeLineRect; property ViewInfo: TcxSchedulerGanttViewViewInfo read FViewInfo; end; { TcxSchedulerGanttViewViewInfo } TcxSchedulerGanttViewViewInfo = class(TcxSchedulerTimeGridViewViewInfo) private FExpandButtons: TcxSchedulerViewInfoCellList; FHotLink: TcxSchedulerGanttViewLinkViewInfo; FLinks: TcxSchedulerViewInfoCellList; FTotalProgressViewInfo: TcxSchedulerGanttViewTotalProgressViewInfo; FVisibleEventEnumerationList: TcxObjectList; FVisibleEventWithoutResourceCount: Integer; function GetGanttView: TcxSchedulerGanttView; function GetIsHotTrackEnabled: Boolean; function GetLinkViewParams: TcxViewParams; function GetVisibleEventEnumeration(AIndex: Integer): TcxSchedulerResourceViewInfo; procedure SetHotLink(AHotLink: TcxSchedulerGanttViewLinkViewInfo); protected procedure AddEventForCalculation(ABuilder: TcxSchedulerEventLayoutBuilder; AEvent: TcxSchedulerControlEvent; AResourceIndex: Integer); override; procedure CalculateExpandButtonViewInfos; procedure CheckEventVisibility(ACell: TcxSchedulerEventCellViewInfo; AIndex: Integer); override; procedure Clear; override; function CreateEventViewData(AEvent: TcxSchedulerControlEvent; const ABounds: TRect; const AStart: TDateTime; const AFinish: TDateTime; AResource: TcxSchedulerResourceViewInfo): TcxSchedulerEventViewData; override; function CreateLayoutBuilder: TcxSchedulerEventLayoutBuilder; override; function CreateLinkItemViewInfo(AEventViewInfo: TcxSchedulerEventCellViewInfo; AEventItemLink: TcxSchedulerEventItemLink; out ALinkItem: TcxSchedulerGanttViewLinkViewInfo): Boolean; procedure DoCalculate; override; function FindEventViewInfoBySource(AEvent: TcxSchedulerEvent; const ADate: TDateTime; AResource: TcxSchedulerStorageResourceItem; var AViewInfo: TcxSchedulerEventCellViewInfo): Boolean; function GetEventBounds(AEvent: TcxSchedulerControlEvent; AResource: TcxSchedulerStorageResourceItem; out AEventRect: TRect): Boolean; function GetEventForResourceCount(AResourceIndex: Integer; out AResourceID: Variant): Integer; override; function GetEventWithoutResourceCount: Integer; override; function GetIndentBetweenLines: Integer; override; function GetNeedShowCurrentTime: Boolean; override; function HasVisibleEvents: Boolean; override; function IsEventCollapsed(AEvent: TcxSchedulerEvent; AHidden: Boolean): Boolean; function IsTaskLinksExpanded(ATaskLinks: TcxSchedulerEventLinks): Boolean; function NavigationButtonOffset(AKind: TcxSchedulerContentNavigationButtonKind; AResourceIndex: Integer): Integer; override; // Links procedure CreateLinks; virtual; procedure RecreateLinks; virtual; property ExpandButtons: TcxSchedulerViewInfoCellList read FExpandButtons; public constructor Create(AOwner: TcxSchedulerSubControl); override; destructor Destroy; override; procedure CalculateHitTest(AHitTest: TcxSchedulerCustomResourceViewHitTest); override; property HotLink: TcxSchedulerGanttViewLinkViewInfo read FHotLink write SetHotLink; property Links: TcxSchedulerViewInfoCellList read FLinks; property LinkViewParams: TcxViewParams read GetLinkViewParams; property TotalProgressViewInfo: TcxSchedulerGanttViewTotalProgressViewInfo read FTotalProgressViewInfo; property View: TcxSchedulerGanttView read GetGanttView; property VisibleEventEnumerationList[AIndex: Integer]: TcxSchedulerResourceViewInfo read GetVisibleEventEnumeration; end; { TcxSchedulerGanttViewPainter } TcxSchedulerGanttViewPainter = class(TcxSchedulerTimeGridViewPainter) private function GetView: TcxSchedulerGanttView; function GetViewInfo: TcxSchedulerGanttViewViewInfo; procedure DoDrawExpandButton(AItem: TcxSchedulerCustomViewInfoItem; var ADone: Boolean); procedure DoDrawLinkItem(AItem: TcxSchedulerCustomViewInfoItem; var ADone: Boolean); public procedure Paint; override; property View: TcxSchedulerGanttView read GetView; property ViewInfo: TcxSchedulerGanttViewViewInfo read GetViewInfo; end; { TcxSchedulerGanttViewHitTest } TcxSchedulerGanttViewHitTest = class(TcxSchedulerTimeGridViewHitTest) private function GetHitAtEventDraggingArea: Boolean; function GetHitExpandButton: TcxSchedulerGanttViewExpandButtonViewInfo; function GetHitLink: TcxSchedulerGanttViewLinkViewInfo; function GetView: TcxSchedulerGanttView; protected FCalcEventDraggingArea: Boolean; FEventDraggingArea: TRect; property View: TcxSchedulerGanttView read GetView; property HitExpandButton: TcxSchedulerGanttViewExpandButtonViewInfo read GetHitExpandButton; property HitLink: TcxSchedulerGanttViewLinkViewInfo read GetHitLink; public property HitAtEventDraggingArea: Boolean read GetHitAtEventDraggingArea; property HitAtExpandButton: Boolean index htcExpandButton read GetBitState; property HitAtLink: Boolean index htcLink read GetBitState; end; { TcxGanttDragEventHelper } TcxSchedulerEventDragMode = (sedmNone, sedmMoveTo, sedmTaskLinkTo); TcxSchedulerGanttDragObject = class(TcxSchedulerDragObject) private FDragGroupedTaskLinks: Boolean; function GetController: TcxSchedulerGanttViewController; protected function GetDragCursor(Accepted: Boolean; X, Y: Integer): TCursor; override; property Controller: TcxSchedulerGanttViewController read GetController; public constructor Create(AControl: TControl); override; property DragGroupedTaskLinks: Boolean read FDragGroupedTaskLinks write FDragGroupedTaskLinks; end; TcxGanttDragEventHelper = class(TcxTimeGridDragEventHelper) private function GetController: TcxSchedulerGanttViewController; function GetDragObject: TcxSchedulerGanttDragObject; function GetHitTest: TcxSchedulerGanttViewHitTest; function GetViewInfo: TcxSchedulerGanttViewViewInfo; protected procedure ApplyChanges; override; procedure ApplyTaskLinks; virtual; function CanDragTo(ADeltaTime: TDateTime): Boolean; virtual; procedure DragOver(const P: TPoint; State: TDragState; var Accepted: Boolean); override; procedure InternalApplyTaskLink(AEvent: TcxSchedulerControlEvent); virtual; function IsAtOrigin: Boolean; override; procedure PrepareGroupedTaskLinks; virtual; procedure UpdateViewClonesTime; override; property Controller: TcxSchedulerGanttViewController read GetController; property DragObject: TcxSchedulerGanttDragObject read GetDragObject; property HitTest: TcxSchedulerGanttViewHitTest read GetHitTest; property ViewInfo: TcxSchedulerGanttViewViewInfo read GetViewInfo; end; { TcxGanttEventSizing } TcxGanttEventSizing = class(TcxTimeGridEventSizing) protected function CanResize: Boolean; virtual; procedure DragOver(const P: TPoint; State: TDragState; var Accepted: Boolean); override; function IsValidTime: Boolean; override; end; { TcxSchedulerGanttViewController } TcxSchedulerGanttViewController = class(TcxSchedulerTimeGridViewController) private FEventDragMode: TcxSchedulerEventDragMode; FTimerScrollBarKind: TScrollBarKind; FDragEventClipRect: TRect; procedure CalcDragEventClipRect; function GetDragEventHelper: TcxGanttDragEventHelper; function GetEventHintData(var AHint: string): Boolean; function GetHitTest: TcxSchedulerGanttViewHitTest; function GetLinkHintData(var AHint: string): Boolean; function GetView: TcxSchedulerGanttView; function GetViewInfo: TcxSchedulerGanttViewViewInfo; procedure InternalPaintDraggingLink(const AStart, AFinish: TPoint); procedure SetEventDragMode(AValue: TcxSchedulerEventDragMode); protected FPrevDragPoint: TPoint; FStartDragPoint: TPoint; procedure CheckScrolling(const APos: TPoint); override; procedure CheckTaskLinkDrawOnScroll(AScrollBarKind: TScrollBarKind; AScrollCode: TScrollCode); virtual; function ConsiderHiddenEvents: Boolean; override; function CreateDragEventHelper: TcxDragEventHelper; override; function CreateResizeEventHelper: TcxEventSizingHelper; override; procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure EndDrag(Target: TObject; X, Y: Integer); override; procedure EraseDraggingLink; virtual; function GetDragEventClipRect: TRect; virtual; procedure InitTimer(AllowStart: Boolean; AScrollCode: TScrollCode); override; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure OnTimer(Sender: TObject); override; procedure PaintDraggingLink(const P: TPoint); virtual; procedure Scrolling(AScrollCode: TScrollCode; var AScrollPos: Integer); override; function ShowHint: Boolean; override; procedure StartDrag(var DragObject: TDragObject); override; procedure UpdateDraggingLink(const P: TPoint); virtual; procedure UpdateEventDragMode; property DragEventHelper: TcxGanttDragEventHelper read GetDragEventHelper; property EventDragMode: TcxSchedulerEventDragMode read FEventDragMode write SetEventDragMode; property HitTest: TcxSchedulerGanttViewHitTest read GetHitTest; property View: TcxSchedulerGanttView read GetView; property ViewInfo: TcxSchedulerGanttViewViewInfo read GetViewInfo; end; { TcxSchedulerGanttView } TcxSchedulerShowTaskDependencyEditorEvent = procedure (Sender: TcxCustomScheduler; ALink: TcxSchedulerEventItemLink; var AHandled: Boolean) of object; TcxSchedulerStartLinkEventEvent = procedure (Sender: TcxCustomScheduler; AEvent: TcxSchedulerControlEvent; var AAllow: Boolean) of object; TcxSchedulerEndLinkEventEvent = procedure (Sender: TcxCustomScheduler; ALinkEvent: TcxSchedulerControlEvent; var AAllow: Boolean) of object; TcxSchedulerGanttView = class(TcxSchedulerTimeGridView) private FEventsStyle: TcxSchedulerGanttViewEventStyle; FLinkLineColor: TColor; FShowExpandButtons: Boolean; FShowTotalProgressLine: Boolean; FTotalProgressLineColor: TColor; FOnEndLinkEvent: TcxSchedulerEndLinkEventEvent; FOnShowTaskDependencyEditor: TcxSchedulerShowTaskDependencyEditorEvent; FOnStartLinkEvent: TcxSchedulerStartLinkEventEvent; function GetController: TcxSchedulerGanttViewController; function GetHitTest: TcxSchedulerGanttViewHitTest; function GetViewInfo: TcxSchedulerGanttViewViewInfo; procedure SetEventsStyle(AValue: TcxSchedulerGanttViewEventStyle); procedure SetLinkLineColor(AColor: TColor); procedure SetShowExpandButtons(AValue: Boolean); procedure SetShowTotalProgressLine(AValue: Boolean); procedure SetTotalProgressLineColor(AValue: TColor); protected function CheckEventsVisibility: Boolean; override; function CreateController: TcxSchedulerSubControlController; override; function CreateHitTest: TcxSchedulerSubControlHitTest; override; function CreatePainter: TcxSchedulerSubControlPainter; override; function CreateViewInfo: TcxSchedulerSubControlViewInfo; override; function DoEndLinkEvent(ALink: TcxSchedulerControlEvent): Boolean; virtual; function DoShowTaskDependencyEditor(ALink: TcxSchedulerEventItemLink): Boolean; virtual; function DoStartLinkEvent(AEvent: TcxSchedulerControlEvent): Boolean; virtual; function GetDragObjectClass: TDragControlObjectClass; override; function GetEventHintText(AEvent: TcxSchedulerControlEvent): string; override; function IsInplaceEditingEnabled: Boolean; override; function ShowTaskComplete: Boolean; override; procedure ShowTaskDependencyEditor(ALink: TcxSchedulerEventItemLink); virtual; procedure ValidateContentPopupMenuItems(var AItems: TcxSchedulerContentPopupMenuItems); override; property Controller: TcxSchedulerGanttViewController read GetController; property EventDetailInfo; property ViewInfo: TcxSchedulerGanttViewViewInfo read GetViewInfo; public constructor Create(AOwner: TcxCustomScheduler); override; property HitTest: TcxSchedulerGanttViewHitTest read GetHitTest; published property EventsStyle: TcxSchedulerGanttViewEventStyle read FEventsStyle write SetEventsStyle default esDefault; property LinkLineColor: TColor read FLinkLineColor write SetLinkLineColor default clNavy; property ShowExpandButtons: Boolean read FShowExpandButtons write SetShowExpandButtons default False; property ShowTotalProgressLine: Boolean read FShowTotalProgressLine write SetShowTotalProgressLine default False; property TotalProgressLineColor: TColor read FTotalProgressLineColor write SetTotalProgressLineColor default clRed; property OnEndLinkEvent: TcxSchedulerEndLinkEventEvent read FOnEndLinkEvent write FOnEndLinkEvent; property OnShowTaskDependencyEditor: TcxSchedulerShowTaskDependencyEditorEvent read FOnShowTaskDependencyEditor write FOnShowTaskDependencyEditor; property OnStartLinkEvent: TcxSchedulerStartLinkEventEvent read FOnStartLinkEvent write FOnStartLinkEvent; end; implementation uses cxLibraryConsts, cxSchedulerDialogs, cxVariants; const LinkLineOffset = 5; ProgressNodeRadius = 4; type TcxCustomSchedulerAccess = class(TcxCustomScheduler); TcxSchedulerEventAccess = class(TcxSchedulerEvent); TcxSchedulerControlEventAccess = class(TcxSchedulerControlEvent); TcxSchedulerEventCellViewInfoAccess = class(TcxSchedulerEventCellViewInfo); TcxSchedulerHintControllerAccess = class(TcxSchedulerHintController); TcxSchedulerTimeGridResourceScrollAccess = class(TcxSchedulerTimeGridResourceScroll); TcxSchedulerCustomDateNavigatorAccess = class(TcxSchedulerCustomDateNavigator); function cxGanttViewCompareEventPlaces(AEventPlace1: TcxSchedulerEventPlace; AEventPlace2: TcxSchedulerEventPlace): Integer; function CheckClone(AEvent1, AEvent2: TcxSchedulerControlEvent; var ASign: Integer): Boolean; begin if AEvent1.IsClone and (AEvent1.Source = AEvent2) then ASign := 1 else if AEvent2.IsClone and (AEvent2.Source = AEvent1) then ASign := -1 else ASign := 0; Result := ASign <> 0; end; var AEvent1: TcxSchedulerControlEvent; AEvent2: TcxSchedulerControlEvent; ATaskIndex1, ATaskIndex2: Integer; begin Result := Integer(AEventPlace1.Resource) - Integer(AEventPlace2.Resource); if Result = 0 then begin AEvent1 := TcxSchedulerControlEvent(AEventPlace1.Event); AEvent2 := TcxSchedulerControlEvent(AEventPlace2.Event); if AEvent1.Pattern <> nil then ATaskIndex1 := TcxSchedulerControlEvent(AEvent1.Pattern).TaskIndex else ATaskIndex1 := AEvent1.TaskIndex; if AEvent2.Pattern <> nil then ATaskIndex2 := TcxSchedulerControlEvent(AEvent2.Pattern).TaskIndex else ATaskIndex2 := AEvent2.TaskIndex; Result := ATaskIndex1 - ATaskIndex2; if Result <> 0 then begin if ATaskIndex1 = -1 then Result := 1; if ATaskIndex2 = -1 then Result := -1; end else if not CheckClone(AEvent1, AEvent2, Result) and (ATaskIndex1 = -1) then begin Result := AEvent1.RecordIndex - AEvent2.RecordIndex; if Result <> 0 then begin if AEvent1.RecordIndex < 0 then Result := 1; if AEvent2.RecordIndex < 0 then Result := -1; end; end; end; end; { TcxSchedulerGanttView } constructor TcxSchedulerGanttView.Create(AOwner: TcxCustomScheduler); begin inherited Create(AOwner); FLinkLineColor := clNavy; FTotalProgressLineColor := clRed; end; function TcxSchedulerGanttView.CheckEventsVisibility: Boolean; begin Result := False; end; function TcxSchedulerGanttView.CreateController: TcxSchedulerSubControlController; begin Result := TcxSchedulerGanttViewController.Create(Self); end; function TcxSchedulerGanttView.CreateHitTest: TcxSchedulerSubControlHitTest; begin Result := TcxSchedulerGanttViewHitTest.Create(Self); end; function TcxSchedulerGanttView.CreatePainter: TcxSchedulerSubControlPainter; begin Result := TcxSchedulerGanttViewPainter.Create(Self); end; function TcxSchedulerGanttView.CreateViewInfo: TcxSchedulerSubControlViewInfo; begin Result := TcxSchedulerGanttViewViewInfo.Create(Self); end; function TcxSchedulerGanttView.DoEndLinkEvent(ALink: TcxSchedulerControlEvent): Boolean; begin Result := True; if Assigned(FOnEndLinkEvent) then FOnEndLinkEvent(Scheduler, ALink, Result); end; function TcxSchedulerGanttView.DoShowTaskDependencyEditor( ALink: TcxSchedulerEventItemLink): Boolean; begin Result := False; if Assigned(OnShowTaskDependencyEditor) then OnShowTaskDependencyEditor(Scheduler, ALink, Result); end; function TcxSchedulerGanttView.DoStartLinkEvent(AEvent: TcxSchedulerControlEvent): Boolean; begin Result := True; if Assigned(FOnStartLinkEvent) then FOnStartLinkEvent(Scheduler, AEvent, Result); end; function TcxSchedulerGanttView.GetDragObjectClass: TDragControlObjectClass; begin Result := TcxSchedulerGanttDragObject; end; function TcxSchedulerGanttView.GetEventHintText( AEvent: TcxSchedulerControlEvent): string; begin Result := Format(cxGetResourceString(@scxGanttEventHint), [AEvent.Caption, AEvent.TaskComplete, cxDateToStr(AEvent.Start), cxDateToStr(AEvent.Finish)]); end; function TcxSchedulerGanttView.IsInplaceEditingEnabled: Boolean; begin Result := EventsStyle <> esProgress; end; function TcxSchedulerGanttView.ShowTaskComplete: Boolean; begin Result := True; end; procedure TcxSchedulerGanttView.ShowTaskDependencyEditor( ALink: TcxSchedulerEventItemLink); begin if not DoShowTaskDependencyEditor(ALink) then cxShowTaskDependencyEditor(ALink, GetSchedulerLookAndFeel(True)); end; procedure TcxSchedulerGanttView.ValidateContentPopupMenuItems( var AItems: TcxSchedulerContentPopupMenuItems); begin if not (Scales.MinorUnit in [suHour, suDay]) then Exclude(AItems, cpmiToday); AItems := AItems - [cpmiGoToDate, cpmiGoToThisDay]; end; function TcxSchedulerGanttView.GetController: TcxSchedulerGanttViewController; begin Result := TcxSchedulerGanttViewController(inherited Controller); end; function TcxSchedulerGanttView.GetHitTest: TcxSchedulerGanttViewHitTest; begin Result := TcxSchedulerGanttViewHitTest(inherited HitTest); end; function TcxSchedulerGanttView.GetViewInfo: TcxSchedulerGanttViewViewInfo; begin Result := TcxSchedulerGanttViewViewInfo(inherited ViewInfo); end; procedure TcxSchedulerGanttView.SetEventsStyle(AValue: TcxSchedulerGanttViewEventStyle); begin if AValue <> EventsStyle then begin FEventsStyle := AValue; Changed; end; end; procedure TcxSchedulerGanttView.SetLinkLineColor(AColor: TColor); begin if AColor <> LinkLineColor then begin FLinkLineColor := AColor; Changed; end; end; procedure TcxSchedulerGanttView.SetShowExpandButtons(AValue: Boolean); begin if AValue <> ShowExpandButtons then begin FShowExpandButtons := AValue; Changed; end; end; procedure TcxSchedulerGanttView.SetShowTotalProgressLine(AValue: Boolean); begin if ShowTotalProgressLine <> AValue then begin FShowTotalProgressLine := AValue; Changed; end; end; procedure TcxSchedulerGanttView.SetTotalProgressLineColor(AValue: TColor); begin if TotalProgressLineColor <> AValue then begin FTotalProgressLineColor := AValue; Changed; end; end; { TcxSchedulerGanttViewLinkItemViewInfo } procedure TcxSchedulerGanttViewLinkViewInfo.Initialize( AViewInfo: TcxSchedulerGanttViewViewInfo; AStartEventViewInfo, AFinishEventViewInfo: TcxSchedulerEventCellViewInfo; AEventItemLink: TcxSchedulerEventItemLink); begin FViewInfo := AViewInfo; FFinishEventViewInfo := AFinishEventViewInfo; FStartEventViewInfo := AStartEventViewInfo; FEventItemLink := AEventItemLink; FHint := Format(cxGetResourceString(@scxLinkHint), [ cxGetResourceString(sEventRelations[Integer(Relation)]), cxGetResourceString(sEventRelationsShort[Integer(Relation)]), StartEventViewInfo.Caption, FinishEventViewInfo.Caption]); Calculate(AStartEventViewInfo.Bounds, AFinishEventViewInfo.Bounds); CalculateBounds; end; procedure TcxSchedulerGanttViewLinkViewInfo.AfterCustomDraw(ACanvas: TcxCanvas); begin // nothing to do end; procedure TcxSchedulerGanttViewLinkViewInfo.BeforeCustomDraw(ACanvas: TcxCanvas); begin ACanvas.Brush.Style := bsSolid; ACanvas.SetBrushColor(ViewParams.Color); ACanvas.Pen.Color := ViewParams.Color; end; procedure TcxSchedulerGanttViewLinkViewInfo.Calculate( AStartRect: TRect; AFinishRect: TRect); function GetValidRect(const P1, P2: TPoint): TRect; begin if P1.X = P2.X then Result := Rect(P1.X, Min(P1.Y, P2.Y), P1.X + 1, Max(P1.Y, P2.Y) + 1) else Result := Rect(Min(P1.X, P2.X), P2.Y, Max(P1.X, P2.X) + 1, P2.Y + 1); end; function GetLineXOffset(const R, R1: TRect): Integer; begin Result := Min(Abs(R.Right - R1.Left), Abs(R1.Right - R.Left)); if Result > LinkLineOffset then Result := LinkLineOffset; end; procedure AddLine(const P1, P2: TPoint; var ALineNumber: Integer); begin if ALineNumber >= Length(FLines) then SetLength(FLines, ALineNumber + 1); FLines[ALineNumber] := GetValidRect(P1, P2); Inc(ALineNumber); end; procedure InitializePoints(var AStart, AFinish: TPoint; var ALineNumber: Integer); var AOffset: Integer; P: TPoint; R: TRect; begin R := AStartRect; P := GetStartPoint(AStartRect); AOffset := GetLineXOffset(AStartRect, AFinishRect); InflateRect(AStartRect, AOffset, AOffset); AStart := GetStartPoint(AStartRect); AddLine(P, AStart, ALineNumber); P := GetFinishPoint(R, AFinishRect); if (Relation = trFinishToStart) and (AFinishRect.Left >= R.Right) then InflateRect(AFinishRect, 0, 2 * LinkLineOffset) else InflateRect(AFinishRect, 2 * LinkLineOffset, 0); AFinish := GetFinishPoint(R, AFinishRect); AddLine(AFinish, P, ALineNumber); CalculateArrowRect(AFinish, P); end; function GetNextPoint(var APoint: TPoint; AFinishPoint: TPoint; AXOffset: Integer; var ALineNumber: Integer): Boolean; var P: TPoint; R: TRect; begin Result := True; P := APoint; P.X := APoint.X + AXOffset; if cxRectIntersect(R, AStartRect, GetValidRect(APoint, P)) and (R.Right - R.Left > 1) then begin P := APoint; Result := P.Y <> AFinishPoint.Y; if Relation in [trFinishToFinish, trStartToStart] then P.Y := AFinishPoint.Y else if AFinishPoint.Y > P.Y then P.Y := AStartRect.Bottom + 1 else P.Y := AStartRect.Top - 1; end; AddLine(APoint, P, ALineNumber); APoint := P; end; var AFinishPoint: TPoint; ALineNumber: Integer; P: TPoint; begin ALineNumber := 0; SetLength(FLines, 10); InitializePoints(P, AFinishPoint, ALineNumber); while GetNextPoint(P, AFinishPoint, AFinishPoint.X - P.X, ALineNumber) and (P.X <> AFinishPoint.X) do ; AddLine(P, AFinishPoint, ALineNumber); SetLength(FLines, ALineNumber); end; procedure TcxSchedulerGanttViewLinkViewInfo.CalculateArrowRect( const AArrowStart, AArrowFinish: TPoint); begin FArrowDirection := GetArrowDirection(AArrowStart, AArrowFinish); case ArrowDirection of adUp: FArrowRect := Rect(AArrowStart.X - LinkLineOffset + 1, AArrowFinish.Y, AArrowStart.X + LinkLineOffset, AArrowStart.Y - LinkLineOffset); adDown: FArrowRect := Rect(AArrowStart.X - LinkLineOffset + 1, AArrowStart.Y + LinkLineOffset, AArrowStart.X + LinkLineOffset, AArrowFinish.Y); adLeft: FArrowRect := Rect(AArrowFinish.X, AArrowStart.Y - LinkLineOffset, AArrowStart.X - LinkLineOffset, AArrowFinish.Y + LinkLineOffset); adRight: FArrowRect := Rect(AArrowStart.X + LinkLineOffset, AArrowFinish.Y - LinkLineOffset, AArrowFinish.X, AArrowFinish.Y + LinkLineOffset); end; end; procedure TcxSchedulerGanttViewLinkViewInfo.CalculateBounds; var R: TRect; K, I: Integer; ALines: TcxSchedulerGanttViewLinkLines; begin FClipRect := ViewInfo.GetEventClipRect(StartEventViewInfo); K := 0; SetLength(ALines, Length(FLines)); for I := 0 to High(Lines) do if cxRectIntersect(R, FLines[I], FClipRect) then begin ALines[K] := R; Inc(K); end; SetLength(ALines, K); FLines := ALines; R := ArrowRect; for I := 0 to High(Lines) do R := cxRectUnion(R, Lines[I]); InflateRect(R, 3, 3); // todo: valid redraw after hot link FBounds := R; end; function TcxSchedulerGanttViewLinkViewInfo.GetArrowDirection( const AArrowStart, AArrowFinish: TPoint): TcxArrowDirection; begin if AArrowStart.X = AArrowFinish.X then if AArrowStart.Y > AArrowFinish.Y then Result := adUp else Result := adDown else if AArrowStart.X > AArrowFinish.X then Result := adLeft else Result := adRight; end; function TcxSchedulerGanttViewLinkViewInfo.GetFinishPoint( const AStartRect, AFinishRect: TRect): TPoint; begin Result.Y := (AFinishRect.Bottom + AFinishRect.Top) div 2; case Relation of trFinishToStart, trStartToStart: begin Result.X := AFinishRect.Left; if (Relation = trFinishToStart) and (AStartRect.Right <= AFinishRect.Left) then if AStartRect.Top >= Result.Y then Result.Y := AFinishRect.Bottom else Result.Y := AFinishRect.Top; end; trFinishToFinish, trStartToFinish: Result.X := AFinishRect.Right; end; end; function TcxSchedulerGanttViewLinkViewInfo.GetHitTest( AHitTest: TcxSchedulerCustomResourceViewHitTest; var ABreak: Boolean): Boolean; var I: Integer; begin Result := False; for I := 0 to Length(FLines) - 1 do begin Result := PtInRect(cxRectInflate(FLines[I], 1, 1), AHitTest.HitPoint); if Result then begin with TcxSchedulerGanttViewHitTest(AHitTest) do begin SetBitState(htcLink, True); FHitObject := Self; ABreak := True; end; Break; end; end; end; function TcxSchedulerGanttViewLinkViewInfo.GetIsHot: Boolean; begin Result := ViewInfo.HotLink = Self; end; function TcxSchedulerGanttViewLinkViewInfo.GetStartPoint( const AStartRect: TRect): TPoint; begin Result.Y := (AStartRect.Bottom + AStartRect.Top) div 2; Result.X := AStartRect.Right; if Relation in [trStartToStart, trStartToFinish] then Result.X := AStartRect.Left; end; function TcxSchedulerGanttViewLinkViewInfo.GetRelation: TcxSchedulerEventRelation; begin Result := EventItemLink.Relation; end; procedure TcxSchedulerGanttViewLinkViewInfo.SetClipRegion(ACanvas: TcxCanvas); var I: Integer; begin ACanvas.SetClipRegion(TcxRegion.Create(ClipRect), roSet); for I := 0 to ViewInfo.NavigationButtons.Count - 1 do with ViewInfo.NavigationButtons.Items[I] do ACanvas.SetClipRegion(TcxRegion.Create(Bounds), roSubtract); ACanvas.ExcludeClipRect(StartEventViewInfo.Bounds); ACanvas.ExcludeClipRect(FinishEventViewInfo.Bounds); end; procedure TcxSchedulerGanttViewLinkViewInfo.Draw(ACanvas: TcxCanvas); function CheckRect(const R: TRect): TRect; begin Result := R; if IsHot then InflateRect(Result, 1, 1); end; var AArrowPoints: TcxArrowPoints; I: Integer; begin ACanvas.SaveClipRegion; try SetClipRegion(ACanvas); TcxCustomLookAndFeelPainter.CalculateArrowPoints(CheckRect(ArrowRect), AArrowPoints, ArrowDirection, True, LinkLineOffset + Byte(IsHot) * 2); ACanvas.Polygon(AArrowPoints); for I := 0 to Length(FLines) - 1 do ACanvas.FillRect(CheckRect(FLines[I])); finally ACanvas.RestoreClipRegion; end; end; { TcxSchedulerGanttViewPainter } procedure TcxSchedulerGanttViewPainter.DoDrawExpandButton( AItem: TcxSchedulerCustomViewInfoItem; var ADone: Boolean); begin // nothing to do end; procedure TcxSchedulerGanttViewPainter.DoDrawLinkItem( AItem: TcxSchedulerCustomViewInfoItem; var ADone: Boolean); begin // nothing to do end; function TcxSchedulerGanttViewPainter.GetView: TcxSchedulerGanttView; begin Result := inherited View as TcxSchedulerGanttView; end; function TcxSchedulerGanttViewPainter.GetViewInfo: TcxSchedulerGanttViewViewInfo; begin Result := TcxSchedulerGanttViewViewInfo(inherited ViewInfo); end; procedure TcxSchedulerGanttViewPainter.Paint; begin inherited Paint; ViewInfo.Links.Draw(Canvas, DoDrawLinkItem); ViewInfo.ExpandButtons.Draw(Canvas, DoDrawExpandButton); if View.ShowTotalProgressLine then ViewInfo.TotalProgressViewInfo.Draw(Canvas); View.Controller.EraseDraggingLink; end; { TcxSchedulerGanttViewHitTest } function TcxSchedulerGanttViewHitTest.GetHitAtEventDraggingArea: Boolean; function GetEventDraggingArea: TRect; var ADragEvent: TcxSchedulerControlEvent; R: TRect; begin Result := cxNullRect; ADragEvent := View.Controller.DragEvent; if (View.Controller.DragEventHelper = nil) or (ADragEvent = nil) or (ADragEvent.Source = nil) or (View = nil) or (View.ViewInfo = nil) then Exit; if View.ViewInfo.GetEventBounds(ADragEvent, Resource, R) then begin Result := View.ViewInfo.Bounds; Result.Top := R.Top; Result.Bottom := R.Bottom; FCalcEventDraggingArea := True; end; end; begin if not FCalcEventDraggingArea then FEventDraggingArea := GetEventDraggingArea; Result := PtInRect(FEventDraggingArea, HitPoint); end; function TcxSchedulerGanttViewHitTest.GetHitExpandButton: TcxSchedulerGanttViewExpandButtonViewInfo; begin if FHitObject is TcxSchedulerGanttViewExpandButtonViewInfo then Result := TcxSchedulerGanttViewExpandButtonViewInfo(FHitObject) else Result := nil end; function TcxSchedulerGanttViewHitTest.GetHitLink: TcxSchedulerGanttViewLinkViewInfo; begin if FHitObject is TcxSchedulerGanttViewLinkViewInfo then Result := TcxSchedulerGanttViewLinkViewInfo(FHitObject) else Result := nil; end; function TcxSchedulerGanttViewHitTest.GetView: TcxSchedulerGanttView; begin Result := TcxSchedulerGanttView(inherited Owner); end; { TcxSchedulerGanttDragObject } constructor TcxSchedulerGanttDragObject.Create(AControl: TControl); begin inherited Create(AControl); DragGroupedTaskLinks := True; end; function TcxSchedulerGanttDragObject.GetDragCursor(Accepted: Boolean; X, Y: Integer): TCursor; const ACursors: array [Boolean] of TCursor = (crNoDrop, crTaskLink); var ACursor: Boolean; begin if Controller.EventDragMode <> sedmTaskLinkTo then Result := inherited GetDragCursor(Accepted, X, Y) else begin ACursor := Controller.DragEvent <> nil; ACursor := ACursor and (((Controller.HitTest.Resource <> nil) and (Scheduler.Storage.ResourceCount > 0) and Controller.DragEvent.IsResourceEvent(Controller.HitTest.Resource, True)) or (Scheduler.Storage.ResourceCount = 0)); ACursor := ACursor and ((Controller.HitTest.Event = nil) or (Controller.DragEvent.Source.CanLink(Controller.HitTest.Event.Source) and Controller.DragEvent.Source.CanLink(Controller.HitTest.Event))); Result := ACursors[ACursor]; end; end; function TcxSchedulerGanttDragObject.GetController: TcxSchedulerGanttViewController; begin Result := TcxSchedulerGanttViewController(DragEventHelper.Controller); end; { TcxGanttDragEventHelper } procedure TcxGanttDragEventHelper.ApplyChanges; begin if Controller.EventDragMode <> sedmTaskLinkTo then inherited ApplyChanges else ApplyTaskLinks; end; procedure TcxGanttDragEventHelper.ApplyTaskLinks; var I: Integer; begin for I := 0 to Clones.Count - 1 do InternalApplyTaskLink(TcxSchedulerControlEvent(TcxSchedulerControlEvent(Clones[I]).Source)); end; function TcxGanttDragEventHelper.CanDragTo(ADeltaTime: TDateTime): Boolean; function EventInClones(AEvent: TcxSchedulerEvent): TcxSchedulerControlEvent; var I: Integer; begin Result := nil; for I := 0 to Clones.Count - 1 do begin if AEvent = TcxSchedulerControlEvent(Clones[I].Source).Source then begin Result := Clones[I]; Break; end; end; end; function CanMoving(AClone: TcxSchedulerControlEvent): Boolean; var AStartTime, AFinishTime: TDateTime; ANewTime: TDateTime; I: Integer; ARelation: TcxSchedulerEventRelation; begin Result := (AClone.TaskStatus = tsComplete) or (AClone.TaskLinkOwners.Count = 0); if not Result then begin ANewTime := AClone.Source.Start - ADeltaTime; for I := 0 to AClone.TaskLinkOwners.Count - 1 do begin AStartTime := MinDateTime; AFinishTime := MaxDateTime; if not AClone.GetTaskLinkOwnerRelation(AClone.TaskLinkOwners[I], ARelation) then Continue; TcxSchedulerEventAccess(AClone).GetValidTaskTimeRangeByRelation(ARelation, AClone.TaskLinkOwners[I], AStartTime, AFinishTime); if EventInClones(AClone.TaskLinkOwners[I]) <> nil then begin if AStartTime <> MinDateTime then AStartTime := AStartTime - ADeltaTime; if AFinishTime <> MaxDateTime then AFinishTime := AFinishTime - ADeltaTime; end; Result := (AStartTime <= ANewTime) and (AFinishTime >= (ANewTime + AClone.Duration)); if not Result then Break; end; end; end; var I: Integer; begin Result := True; for I := 0 to Clones.Count - 1 do begin Result := Result and CanMoving(Clones[I]); if not Result then Break; end; end; procedure TcxGanttDragEventHelper.DragOver(const P: TPoint; State: TDragState; var Accepted: Boolean); var ADeltaTime: TDateTime; begin if Controller.EventDragMode <> sedmTaskLinkTo then inherited DragOver(P, State, Accepted); case Destination of dodView: ADeltaTime := Controller.StartDragHitTime - HitTest.Time; dodDateNavigator: ADeltaTime := Controller.StartDragHitTime - TcxSchedulerCustomDateNavigatorAccess(DateNavigator).HitTest.Time; else ADeltaTime := MinDateTime; end; Accepted := (Accepted or HitTest.HitAtExpandButton or HitTest.HitAtLink) and (CanDragTo(ADeltaTime) or (Controller.EventDragMode = sedmTaskLinkTo)); end; procedure TcxGanttDragEventHelper.InternalApplyTaskLink(AEvent: TcxSchedulerControlEvent); function CreateCustom(APattern: TcxSchedulerEvent; ADate: TDateTime): TcxSchedulerEvent; begin Result := APattern.GetOccurrence(ADate); if Result <> nil then begin Result.EventType := etCustom; Result.Post; end; end; var AHitEvent, ADragEvent: TcxSchedulerEvent; AHitEventPattern, ADragEventPattern: TcxSchedulerEvent; AHitEventDate, ADragEventDate: TDateTime; ADragEventType: TcxEventType; begin if (HitTest.Event <> nil) and (HitTest.Event.Source <> nil) and not HitTest.Event.IsClone and AEvent.CanLink(HitTest.Event) and AEvent.CanLink(HitTest.Event.Source) and (Scheduler.Storage <> nil) and (HitTest.Event <> AEvent) and Controller.View.DoEndLinkEvent(HitTest.Event) then begin Scheduler.Storage.BeginUpdate; try AHitEvent := HitTest.Event; AHitEventDate := AHitEvent.Start; AHitEventPattern := AHitEvent.Pattern; ADragEvent := AEvent; ADragEventType := AEvent.EventType; ADragEventDate := AEvent.Start; ADragEventPattern := ADragEvent.Pattern; if AHitEvent.EventType = etOccurrence then AHitEvent := CreateCustom(AHitEventPattern, AHitEventDate) else AHitEvent := TcxSchedulerControlEvent(AHitEvent).Source; if ADragEventType = etOccurrence then ADragEvent := CreateCustom(ADragEventPattern, ADragEventDate); if ADragEvent <> nil then ADragEvent.TaskLinks.Add(AHitEvent, trFinishToStart); finally Scheduler.Storage.EndUpdate; end; end; end; function TcxGanttDragEventHelper.IsAtOrigin: Boolean; begin Result := inherited IsAtOrigin and (Controller.EventDragMode <> sedmTaskLinkTo); end; procedure TcxGanttDragEventHelper.PrepareGroupedTaskLinks; function GetControlEvent(ASource: TcxSchedulerEvent): TcxSchedulerControlEvent; var I: Integer; begin Result := nil; for I := 0 to Events.Count - 1 do if Events[I].Source = ASource then begin Result := Events[I]; Break; end; end; procedure AddClones(ALinksHeaderEvent: TcxSchedulerEvent); var I: Integer; AEvent: TcxSchedulerControlEvent; begin for I := 0 to ALinksHeaderEvent.TaskLinks.Count - 1 do begin if (ALinksHeaderEvent.TaskLinks[I].Link = nil) or (ALinksHeaderEvent.TaskLinks[I].Link.TaskLinkOwners.Count > 1) then Continue; AEvent := GetControlEvent(ALinksHeaderEvent.TaskLinks[I].Link); if AEvent = nil then Continue; Events.Selection.Add(AEvent, [ssCtrl]); AddClones(ALinksHeaderEvent.TaskLinks[I].Link); end; end; var I: Integer; begin if not DragObject.DragGroupedTaskLinks then Exit; for I := 0 to DragObject.DragEvents.Count - 1 do if not ViewInfo.IsTaskLinksExpanded(DragObject.DragEvents[I].TaskLinks) then AddClones(DragObject.DragEvents[I]); Events.CreateClones; end; procedure TcxGanttDragEventHelper.UpdateViewClonesTime; var I: Integer; ADelta: TDateTime; begin ADelta := Controller.StartDragHitTime - HitTest.Time; if not CanDragTo(ADelta) then Exit; for I := 0 to Clones.Count - 1 do with TcxSchedulerControlEventAccess(Clones[I]) do InternalMoveTo(Source.Start - ADelta); CorrectAllDayEventProperty; end; function TcxGanttDragEventHelper.GetController: TcxSchedulerGanttViewController; begin Result := TcxSchedulerGanttViewController(inherited Controller); end; function TcxGanttDragEventHelper.GetDragObject: TcxSchedulerGanttDragObject; begin Result := TcxSchedulerGanttDragObject(inherited DragObject); end; function TcxGanttDragEventHelper.GetHitTest: TcxSchedulerGanttViewHitTest; begin Result := TcxSchedulerGanttViewHitTest(inherited HitTest); end; function TcxGanttDragEventHelper.GetViewInfo: TcxSchedulerGanttViewViewInfo; begin Result := TcxSchedulerGanttViewViewInfo(inherited ViewInfo); end; { TcxGanttEventSizing } function TcxGanttEventSizing.CanResize: Boolean; var AStartTime, AFinishTime: TDateTime; begin TcxSchedulerControlEvent(Event).GetValidTaskTimeRange(AStartTime, AFinishTime); Result := (HitTest.Time >= AStartTime) and (HitTest.Time < AFinishTime); end; procedure TcxGanttEventSizing.DragOver(const P: TPoint; State: TDragState; var Accepted: Boolean); begin inherited DragOver(P, State, Accepted); FHasConflicts := not Accepted; end; function TcxGanttEventSizing.IsValidTime: Boolean; begin Result := inherited IsValidTime and CanResize; end; { TcxSchedulerGanttViewController } procedure TcxSchedulerGanttViewController.CheckScrolling(const APos: TPoint); var ACanScroll: Boolean; AScrollCode: TScrollCode; R: TRect; begin if DragEvent = nil then begin inherited CheckScrolling(APos); Exit; end; R := GetDragEventClipRect; FPos := APos; ACanScroll := True; AScrollCode := TScrollCode(Timer.Tag); if APos.X < (R.Left + cxScrollZoneSize) then begin AScrollCode := scLineUp; R.Right := R.Left + cxScrollZoneSize; FTimerScrollBarKind := sbHorizontal; end else begin if APos.X >= (R.Right - cxScrollZoneSize) then begin AScrollCode := scLineDown; R.Left := R.Right - cxScrollZoneSize; FTimerScrollBarKind := sbHorizontal; end else begin if APos.Y >= (R.Bottom - cxScrollZoneSize) then begin AScrollCode := scLineDown; R.Top := R.Bottom - cxScrollZoneSize; FTimerScrollBarKind := sbVertical; end else begin if APos.Y < (R.Top + cxScrollZoneSize) then begin AScrollCode := scLineUp; R.Bottom := R.Top + cxScrollZoneSize; FTimerScrollBarKind := sbVertical; end else ACanScroll := False; end; end; end; ACanScroll := ACanScroll and cxRectPtIn(R, APos); if (ACanScroll <> Timer.Enabled) or (Integer(AScrollCode) <> Timer.Tag) then InitTimer(ACanScroll, AScrollCode); end; procedure TcxSchedulerGanttViewController.CheckTaskLinkDrawOnScroll(AScrollBarKind: TScrollBarKind; AScrollCode: TScrollCode); var ADelta: Integer; begin EraseDraggingLink; if EventDragMode = sedmTaskLinkTo then begin ADelta := 0; case AScrollCode of scLineDown, scPageDown: ADelta := -1; scLineUp, scPageUp: ADelta := 1; end; if ADelta <> 0 then begin case AScrollBarKind of sbHorizontal: begin ADelta := ADelta * ViewInfo.FColumnWidth; Inc(FStartDragPoint.X, ADelta); end; sbVertical: begin ADelta := ADelta * (ViewInfo.FEventRowHeight); Inc(FStartDragPoint.Y, ADelta); end; end; end; end; PaintDraggingLink(HitTest.HitPoint); end; function TcxSchedulerGanttViewController.ConsiderHiddenEvents: Boolean; begin Result := False; end; function TcxSchedulerGanttViewController.CreateDragEventHelper: TcxDragEventHelper; begin Result := TcxGanttDragEventHelper.Create(Scheduler); end; function TcxSchedulerGanttViewController.CreateResizeEventHelper: TcxEventSizingHelper; begin Result := TcxGanttEventSizing.Create(Scheduler); end; procedure TcxSchedulerGanttViewController.DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var ADragMode: TcxSchedulerEventDragMode; begin ADragMode := EventDragMode; if Assigned(DragEventHelper) then begin EraseDraggingLink; UpdateEventDragMode; end; inherited DragOver(Source, X, Y, State, Accept); if ADragMode = sedmTaskLinkTo then PaintDraggingLink(HitTest.HitPoint); end; procedure TcxSchedulerGanttViewController.EndDrag(Target: TObject; X, Y: Integer); begin inherited EndDrag(Target, X, Y); EraseDraggingLink; EventDragMode := sedmNone; HitTest.FCalcEventDraggingArea := False; HitTest.FEventDraggingArea := cxNullRect; end; procedure TcxSchedulerGanttViewController.EraseDraggingLink; begin if EventDragMode <> sedmTaskLinkTo then Exit; InternalPaintDraggingLink(FStartDragPoint, FPrevDragPoint); FPrevDragPoint := FStartDragPoint; end; function TcxSchedulerGanttViewController.GetDragEventClipRect: TRect; begin Result := FDragEventClipRect; end; function TcxSchedulerGanttViewController.GetLinkHintData(var AHint: string): Boolean; begin Result := HitTest.HitAtLink and (HitTest.HitLink <> nil); if Result then AHint := HitTest.HitLink.Hint; end; procedure TcxSchedulerGanttViewController.InitTimer(AllowStart: Boolean; AScrollCode: TScrollCode); begin inherited InitTimer(AllowStart, AScrollCode); if not AllowStart then FPrevDragPoint := FStartDragPoint; end; procedure TcxSchedulerGanttViewController.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FStartDragPoint := HitTest.HitPoint; if (Button = mbLeft) and HitTest.HitAtExpandButton then HitTest.HitExpandButton.DoClick else if (Button = mbLeft) and HitTest.HitAtLink then View.ShowTaskDependencyEditor(HitTest.HitLink.FEventItemLink) else inherited MouseDown(Button, Shift, X, Y); end; procedure TcxSchedulerGanttViewController.OnTimer(Sender: TObject); begin inherited OnTimer(Sender); FPrevDragPoint := FStartDragPoint; end; procedure TcxSchedulerGanttViewController.PaintDraggingLink(const P: TPoint); begin if EventDragMode <> sedmTaskLinkTo then Exit; InternalPaintDraggingLink(FStartDragPoint, P); FPrevDragPoint := P; end; procedure TcxSchedulerGanttViewController.Scrolling(AScrollCode: TScrollCode; var AScrollPos: Integer); var ADelta: Integer; AResourceIndex: Integer; ALastScrollPos: Integer; begin if FTimerScrollBarKind = sbHorizontal then begin CheckTaskLinkDrawOnScroll(FTimerScrollBarKind, AScrollCode); View.Scroll(FTimerScrollBarKind, AScrollCode, AScrollPos); end else begin if DragEvent = nil then Exit; ADelta := 0; case AScrollCode of scLineUp: ADelta := -1; scLineDown: ADelta := 1; end; if ViewInfo.ResourceCount = 0 then AResourceIndex := 0 else AResourceIndex := DragEvent.GetResourceItem.Index; if AResourceIndex >= 0 then begin ALastScrollPos := AScrollPos; AScrollPos := -TcxSchedulerTimeGridResourceScrollAccess(ViewInfo.ResourceViewShift[AResourceIndex]).FShift + ADelta; if (AScrollPos >= 0) and (AScrollPos <= ViewInfo.GetResourceScrollBarMax(AResourceIndex)) then begin CheckTaskLinkDrawOnScroll(FTimerScrollBarKind, AScrollCode); ViewInfo.DoResourceVerticalScroll(ViewInfo.ResourceViewShift[AResourceIndex].ScrollBar, AScrollCode, AScrollPos); end else begin EraseDraggingLink; AScrollPos := ALastScrollPos; end; end; end; end; function TcxSchedulerGanttViewController.ShowHint: Boolean; var AHintText: string; begin Result := GetLinkHintData(AHintText) or GetEventHintData(AHintText); if Result then begin if not SameText(TcxSchedulerHintControllerAccess(HintController).FHintText, AHintText) then InternalHideHint; InternalShowHint(GetMouseCursorPos, AHintText); end else Result := inherited ShowHint; end; procedure TcxSchedulerGanttViewController.StartDrag(var DragObject: TDragObject); begin FEventDragMode := sedmNone; FPrevDragPoint := FStartDragPoint; HitTest.FCalcEventDraggingArea := False; HitTest.FEventDraggingArea := cxNullRect; CalcDragEventClipRect; inherited StartDrag(DragObject); DragEventHelper.PrepareGroupedTaskLinks; end; procedure TcxSchedulerGanttViewController.UpdateDraggingLink(const P: TPoint); begin EraseDraggingLink; PaintDraggingLink(P); end; procedure TcxSchedulerGanttViewController.UpdateEventDragMode; begin if (EventDragMode = sedmNone) and not DragEventHelper.IsAtOrigin then EventDragMode := sedmMoveTo; if (EventDragMode <> sedmTaskLinkTo) and not IsCopyDragDrop and not HitTest.HitAtEventDraggingArea and ((EventDragMode = sedmNone) or (HitTest.HitAtEvent and (HitTest.Event.Source <> DragEvent))) then EventDragMode := sedmTaskLinkTo; end; procedure TcxSchedulerGanttViewController.CalcDragEventClipRect; var AViewInfo: TcxSchedulerEventCellViewInfo; begin if (DragEvent <> nil) and ViewInfo.FindEventViewInfoBySource(DragEvent.Source, DateOf(DragEvent.Start), HitTest.Resource, AViewInfo) then FDragEventClipRect := ViewInfo.GetEventClipRect(AViewInfo) else FDragEventClipRect := cxEmptyRect; end; function TcxSchedulerGanttViewController.GetDragEventHelper: TcxGanttDragEventHelper; begin Result := TcxGanttDragEventHelper(inherited DragEventHelper); end; function TcxSchedulerGanttViewController.GetEventHintData(var AHint: string): Boolean; var AEvent: TcxSchedulerControlEvent; begin Result := (EventDragMode = sedmNone) and HitTest.HitAtEvent; if Result then begin AEvent := HitTest.Event; AHint := View.GetEventHintText(AEvent); TcxCustomSchedulerAccess(Scheduler).GetEventUserHintText(AEvent, AHint); end; end; function TcxSchedulerGanttViewController.GetHitTest: TcxSchedulerGanttViewHitTest; begin Result := View.HitTest; end; function TcxSchedulerGanttViewController.GetView: TcxSchedulerGanttView; begin Result := TcxSchedulerGanttView(inherited View); end; function TcxSchedulerGanttViewController.GetViewInfo: TcxSchedulerGanttViewViewInfo; begin Result := View.ViewInfo; end; procedure TcxSchedulerGanttViewController.InternalPaintDraggingLink(const AStart, AFinish: TPoint); begin if (AStart.X = AFinish.X) and (AStart.Y = AFinish.Y) then Exit; with View.GetControlCanvas do begin SaveClipRegion; try SetClipRegion(TcxRegion.Create(GetDragEventClipRect), roSet); Pen.Mode := pmNotXor; Pen.Style := psSolid; Pen.Color := View.LinkLineColor; MoveTo(AStart.X, AStart.Y); LineTo(AFinish.X, AFinish.Y); finally RestoreClipRegion; end; end; end; procedure TcxSchedulerGanttViewController.SetEventDragMode(AValue: TcxSchedulerEventDragMode); var I: Integer; begin if AValue = FEventDragMode then Exit; if AValue = sedmTaskLinkTo then begin if not View.DoStartLinkEvent(DragEvent) then Exit; for I := 0 to DragEventHelper.Clones.Count -1 do with DragEventHelper.Clones[I] do begin MoveTo(Source.Start); ResourceID := Source.ResourceID; end; View.Refresh; FPrevDragPoint := FStartDragPoint; end; FEventDragMode := AValue; end; { TcxSchedulerGanttViewEventLayoutBuilder } procedure TcxSchedulerGanttViewEventLayoutBuilder.Calculate; function FindStartOfRecurrence(AEventPlace: TcxSchedulerEventPlace; ACheckEventPlaceCount: Integer; out AIndex: Integer): Boolean; var AMin: Integer; APlace: TcxSchedulerEventPlace; I: Integer; begin Result := False; if AEventPlace.Event.Pattern <> nil then begin AMin := MAXINT; for I := 0 to ACheckEventPlaceCount - 1 do begin APlace := EventPlaces[I]; if (APlace.Resource = AEventPlace.Resource) and (APlace.Event.Pattern = AEventPlace.Event.Pattern) and (APlace.Event.RecurrenceIndex < AMin) and (((I > 0) and (APlace.LineStart >= 0)) or (I = 0)) then begin AMin := APlace.Event.RecurrenceIndex; Result := APlace <> AEventPlace; if Result then AIndex := APlace.LineStart; end; end; end; end; procedure CheckEventPlaceResource(AEventPlace: TcxSchedulerEventPlace; var ALastResource, APlaceIndex: Integer); begin if ALastResource <> Integer(AEventPlace.Resource) then begin ALastResource := Integer(AEventPlace.Resource); APlaceIndex := 0; end; end; function GetTaskIndex(AEventPlace: TcxSchedulerEventPlace; ACheckEventPlaceCount: Integer; var APlaceIndex: Integer): Integer; begin if not FindStartOfRecurrence(AEventPlace, ACheckEventPlaceCount, APlaceIndex) then APlaceIndex := Max(AEventPlace.Event.TaskIndex, APlaceIndex); Result := APlaceIndex; Inc(APlaceIndex); end; var AEventPlace: TcxSchedulerEventPlace; APlaceIndex: Integer; ALastResource: Integer; I: Integer; begin APlaceIndex := 0; ALastResource := -1; EventPlacesList.Sort(TListSortCompare(@cxGanttViewCompareEventPlaces)); for I := 0 to EventPlaceCount - 1 do begin AEventPlace := EventPlaces[I]; CheckEventPlaceResource(AEventPlace, ALastResource, APlaceIndex); AEventPlace.LineStart := GetTaskIndex(AEventPlace, I + 1, APlaceIndex); AEventPlace.LineFinish := AEventPlace.LineStart; end; end; { TcxSchedulerGanttViewExpandButtonViewInfo } procedure TcxSchedulerGanttViewExpandButtonViewInfo.Draw(ACanvas: TcxCanvas); begin Painter.DrawExpandButton(ACanvas, Bounds, Expanded); end; procedure TcxSchedulerGanttViewExpandButtonViewInfo.DoClick; begin with Event.TaskLinks do Expanded := not Expanded; end; procedure TcxSchedulerGanttViewExpandButtonViewInfo.InitHitTest( AHitTest: TcxSchedulerCustomResourceViewHitTest); begin inherited InitHitTest(AHitTest); TcxSchedulerGanttViewHitTest(AHitTest).SetBitState(htcExpandButton, True); end; { TcxSchedulerGanttViewTotalProgressViewInfo } constructor TcxSchedulerGanttViewTotalProgressViewInfo.Create( AViewInfo: TcxSchedulerGanttViewViewInfo); begin FViewInfo := AViewInfo; end; procedure TcxSchedulerGanttViewTotalProgressViewInfo.Calculate; begin FDrawAsPolyline := ViewInfo.View.EventsStyle = esProgress; FLineColor := ViewInfo.View.TotalProgressLineColor; FTimeLineRect := GetTimeLineRect; FContentArea := ViewInfo.Bounds; Inc(FContentArea.Left, ViewInfo.ResourceHeaderWidth); if DrawAsPolyline then CalculatePolyline; end; procedure TcxSchedulerGanttViewTotalProgressViewInfo.CalculatePolyline; var ANodeIndex: Integer; APointIndex: Integer; AVisibleLineCount: Integer; I: Integer; R: TRect; procedure AddPoint(X, Y: Integer); begin FPoints[APointIndex] := Point(X, Y); Inc(APointIndex); end; procedure AddPointsByResource(const ALine, ABounds: TRect; AResource: TcxSchedulerStorageResourceItem); var APoint: TPoint; ATop: Integer; begin ATop := ABounds.Top; while ATop < ABounds.Bottom do begin AddPoint(ALine.Left, ATop); Inc(ATop, ViewInfo.IndentBetweenLines); if GetUncompliteEventPoint(Rect(ALine.Left, ATop, ALine.Right, ATop + ViewInfo.ContentLineHeight), APoint, AResource) then begin AddPoint(ALine.Left, ATop); AddPoint(APoint.X, ATop + ViewInfo.ContentLineHeight div 2); FProgressNodes[ANodeIndex] := Point(APoint.X, ATop + ViewInfo.ContentLineHeight div 2); Inc(ANodeIndex); end; Inc(ATop, ViewInfo.ContentLineHeight); end; AddPoint(ALine.Left, ABounds.Bottom); end; begin APointIndex := 0; ANodeIndex := 0; AVisibleLineCount := (1 + cxRectHeight(ViewInfo.Bounds) div ViewInfo.FEventRowHeight); SetLength(FPoints, 3 * AVisibleLineCount); SetLength(FProgressNodes, AVisibleLineCount); AddPoint(TimeLineRect.Left, TimeLineRect.Top); if ViewInfo.ResourceCount = 0 then begin R := ViewInfo.Bounds; R.Top := ViewInfo.ScalesHeight; AddPointsByResource(TimeLineRect, R, nil); end else for I := 0 to ViewInfo.ResourceCount - 1 do begin AddPointsByResource(TimeLineRect, ViewInfo.ResourceHeaderCells.Items[I + 1].Bounds, ViewInfo.Resources[I].ResourceItem); end; SetLength(FPoints, APointIndex); SetLength(FProgressNodes, ANodeIndex); end; procedure TcxSchedulerGanttViewTotalProgressViewInfo.Draw(ACanvas: TcxCanvas); var I: Integer; begin ACanvas.SaveClipRegion; try ACanvas.SetClipRegion(TcxRegion.Create(ContentArea), roSet); for I := 0 to ViewInfo.NavigationButtons.Count - 1 do with ViewInfo.NavigationButtons.Items[I] do ACanvas.SetClipRegion(TcxRegion.Create(Bounds), roSubtract); for I := 0 to ViewInfo.GroupSeparatorCells.Count - 1 do ACanvas.ExcludeClipRect(ViewInfo.GroupSeparatorCells.Items[I].Bounds); if DrawAsPolyline then begin ACanvas.Pen.Color := LineColor; ACanvas.Polyline(FPoints); DrawProgressNodes(ACanvas); end else ACanvas.FillRect(TimeLineRect, LineColor); finally ACanvas.RestoreClipRegion; end; end; procedure TcxSchedulerGanttViewTotalProgressViewInfo.DrawProgressNodes( ACanvas: TcxCanvas); var I: Integer; R: TRect; begin ACanvas.Brush.Color := LineColor; for I := 0 to High(FProgressNodes) do begin R.TopLeft := FProgressNodes[I]; R.BottomRight := FProgressNodes[I]; InflateRect(R, ProgressNodeRadius, ProgressNodeRadius); ACanvas.Canvas.Ellipse(R); end; end; function TcxSchedulerGanttViewTotalProgressViewInfo.FindUncompliteEventBeforeLine( const ALine: TRect; AResource: TcxSchedulerStorageResourceItem; var AViewInfo: TcxSchedulerEventCellViewInfo): Boolean; var AEventViewInfo: TcxSchedulerEventCellViewInfo; I: Integer; R: TRect; begin Result := False; for I := 0 to ViewInfo.EventCells.Count - 1 do begin AEventViewInfo := TcxSchedulerEventCellViewInfo(ViewInfo.EventCells.Items[I]); R := AEventViewInfo.Bounds; if (AResource = nil) or (AResource = AEventViewInfo.ResourceItem) then if (R.Left <= ALine.Left) and (R.Top >= ALine.Top) and (R.Bottom <= ALine.Bottom) then if (AEventViewInfo.Event.TaskComplete < 100) and not AEventViewInfo.Hidden then begin AViewInfo := AEventViewInfo; Result := True; Break; // todo: need find nearest event (not first!) end; end; end; function TcxSchedulerGanttViewTotalProgressViewInfo.GetTimeLineApproximateRect: TRect; var AVisibleStart: TDateTime; begin AVisibleStart := ViewInfo.View.VisibleStart; if Now > AVisibleStart then begin Result := ViewInfo.Bounds; Inc(Result.Left, ViewInfo.ResourceHeaderWidth); Inc(Result.Left, Trunc((Now - AVisibleStart) * (Result.Right - Result.Left) / (ViewInfo.FLastVisibleTime - AVisibleStart))); Result.Right := Result.Left; end else Result := cxEmptyRect; end; function TcxSchedulerGanttViewTotalProgressViewInfo.GetTimeLineRect: TRect; var ACell: TcxSchedulerTimeGridSelectionBarCell; AItem: TcxSchedulerCustomViewInfoItem; I: Integer; begin Result := cxEmptyRect; for I := 0 to ViewInfo.TimeLineCells.Count - 1 do begin AItem := ViewInfo.TimeLineCells.Items[I]; if AItem is TcxSchedulerTimeGridSelectionBarCell then begin ACell := TcxSchedulerTimeGridSelectionBarCell(AItem); if ACell.IsCurrentTimeCell then begin Result := ACell.TimeLineRect; Break; end; end; end; if IsRectEmpty(Result) then Result := GetTimeLineApproximateRect; end; function TcxSchedulerGanttViewTotalProgressViewInfo.GetUncompliteEventPoint( const ALine: TRect; var P: TPoint; AResource: TcxSchedulerStorageResourceItem): Boolean; var AEventViewInfo: TcxSchedulerEventCellViewInfo; R: TRect; begin Result := FindUncompliteEventBeforeLine(ALine, AResource, AEventViewInfo); if Result then begin R := cxRectContent(AEventViewInfo.Bounds, ViewInfo.View.ExternalPainter.Painter.SchedulerEventProgressOffsets); P := Point(Min(ALine.Left, R.Left + Trunc((R.Right - R.Left) * AEventViewInfo.Event.TaskComplete / 100)), (ALine.Top + ALine.Bottom) div 2); end; end; { TcxSchedulerGanttViewViewInfo } constructor TcxSchedulerGanttViewViewInfo.Create(AOwner: TcxSchedulerSubControl); begin inherited Create(AOwner); FExpandButtons := TcxSchedulerViewInfoCellList.Create; FLinks := TcxSchedulerViewInfoCellList.Create; FTotalProgressViewInfo := TcxSchedulerGanttViewTotalProgressViewInfo.Create(Self); FVisibleEventEnumerationList := TcxObjectList.Create; FCells.Add(FExpandButtons); FCells.Add(FLinks); end; destructor TcxSchedulerGanttViewViewInfo.Destroy; begin FreeAndNil(FTotalProgressViewInfo); FreeAndNil(FVisibleEventEnumerationList); inherited Destroy; end; procedure TcxSchedulerGanttViewViewInfo.AddEventForCalculation( ABuilder: TcxSchedulerEventLayoutBuilder; AEvent: TcxSchedulerControlEvent; AResourceIndex: Integer); begin if not IsEventCollapsed(AEvent, False) then inherited AddEventForCalculation(ABuilder, AEvent, AResourceIndex); end; procedure TcxSchedulerGanttViewViewInfo.CalculateExpandButtonViewInfos; function CalculateButtonBounds(const AEventRect: TRect): TRect; var ASize: Integer; begin ASize := LookAndFeelPainter.ExpandButtonSize; Result.Left := AEventRect.Right - ASize; Result.Top := (AEventRect.Top + AEventRect.Bottom - ASize) div 2; Result.Bottom := Result.Top + ASize; Result.Right := AEventRect.Right; OffsetRect(Result, 3, 0); end; var AButtonViewInfo: TcxSchedulerGanttViewExpandButtonViewInfo; AEventCell: TcxSchedulerEventCellViewInfo; I: Integer; begin if not FShowLinks then Exit; FExpandButtons.Clear; if View.ShowExpandButtons then for I := 0 to EventCells.Count - 1 do begin AEventCell := TcxSchedulerEventCellViewInfo(EventCells.Items[I]); if AEventCell.Event.TaskLinks = nil then Continue; if not AEventCell.Hidden and (AEventCell.Event.NonExceptionLinkCount > 0) then begin CreateCellInstance(TcxSchedulerGanttViewExpandButtonViewInfo, CalculateButtonBounds(AEventCell.Bounds), LinkViewParams, AButtonViewInfo); AButtonViewInfo.FExpanded := IsTaskLinksExpanded(AEventCell.Event.TaskLinks); AButtonViewInfo.FEvent := AEventCell.Event; FExpandButtons.Add(AButtonViewInfo); end; end; end; procedure TcxSchedulerGanttViewViewInfo.CalculateHitTest( AHitTest: TcxSchedulerCustomResourceViewHitTest); begin if (View.Controller.DragEventHelper <> nil) or ((View.Controller.DragAndDropObject <> nil) and (View.Controller.DragAndDropObject.SizingHelper <> nil)) or not (ExpandButtons.CalculateHitTest(AHitTest) or Links.CalculateHitTest(AHitTest)) then inherited CalculateHitTest(AHitTest); if GetIsHotTrackEnabled then HotLink := TcxSchedulerGanttViewHitTest(AHitTest).HitLink else HotLink := nil; end; procedure TcxSchedulerGanttViewViewInfo.CheckEventVisibility( ACell: TcxSchedulerEventCellViewInfo; AIndex: Integer); begin if not ACell.Hidden and IsEventCollapsed(ACell.Event, False) then begin ACell.Free; FEventCells.Delete(AIndex); end; end; procedure TcxSchedulerGanttViewViewInfo.Clear; begin inherited Clear; FVisibleEventEnumerationList.Clear; FVisibleEventWithoutResourceCount := 0; end; function TcxSchedulerGanttViewViewInfo.CreateEventViewData( AEvent: TcxSchedulerControlEvent; const ABounds: TRect; const AStart: TDateTime; const AFinish: TDateTime; AResource: TcxSchedulerResourceViewInfo): TcxSchedulerEventViewData; procedure IncreaseVisibleEventCounter; var AResourceIndex: Integer; AResourceFound: Boolean; begin AResourceFound := False; for AResourceIndex := 0 to ResourceCount - 1 do if AEvent.IsSharedWithResource(VisibleEventEnumerationList[AResourceIndex].ResourceID) then begin VisibleEventEnumerationList[AResourceIndex].VisibleEventCount := VisibleEventEnumerationList[AResourceIndex].VisibleEventCount + 1; AResourceFound := True; end; if not AResourceFound then Inc(FVisibleEventWithoutResourceCount); end; begin Result := inherited CreateEventViewData(AEvent, ABounds, AStart, AFinish, AResource); Result.DrawAsProgress := View.EventsStyle = esProgress; if (AEvent.Start < FLastVisibleTime) and (AEvent.Finish > View.VisibleStart) then IncreaseVisibleEventCounter; end; function TcxSchedulerGanttViewViewInfo.CreateLayoutBuilder: TcxSchedulerEventLayoutBuilder; begin Result := TcxSchedulerGanttViewEventLayoutBuilder.Create; end; function TcxSchedulerGanttViewViewInfo.CreateLinkItemViewInfo( AEventViewInfo: TcxSchedulerEventCellViewInfo; AEventItemLink: TcxSchedulerEventItemLink; out ALinkItem: TcxSchedulerGanttViewLinkViewInfo): Boolean; var AFinishViewInfo: TcxSchedulerEventCellViewInfo; begin Result := AEventItemLink.Link <> nil; if Result then begin Result := FindEventViewInfoBySource(AEventItemLink.Link, AEventItemLink.Link.Start, AEventViewInfo.ResourceItem, AFinishViewInfo); if Result then begin CreateCellInstance(TcxSchedulerGanttViewLinkViewInfo, AEventViewInfo.Bounds, AEventViewInfo.Bounds, LinkViewParams, ALinkItem); ALinkItem.Initialize(Self, AEventViewInfo, AFinishViewInfo, AEventItemLink); end; end; end; procedure TcxSchedulerGanttViewViewInfo.CreateLinks; var AEventViewInfo: TcxSchedulerEventCellViewInfo; ALinkItem: TcxSchedulerGanttViewLinkViewInfo; ALinkIndex: Integer; ATaskLinks: TcxSchedulerEventLinks; I: Integer; begin if not FShowLinks then Exit; for I := 0 to EventCells.Count - 1 do begin AEventViewInfo := TcxSchedulerEventCellViewInfo(EventCells.Items[I]); ATaskLinks := AEventViewInfo.Event.TaskLinks; if (ATaskLinks = nil) or not IsTaskLinksExpanded(ATaskLinks) then Continue; for ALinkIndex := 0 to ATaskLinks.Count - 1 do if CreateLinkItemViewInfo(AEventViewInfo, ATaskLinks.ItemLinks[ALinkIndex], ALinkItem) then FLinks.Add(ALinkItem); end; end; procedure TcxSchedulerGanttViewViewInfo.DoCalculate; begin Adapter.GetPageResources(FVisibleEventEnumerationList); inherited DoCalculate; CalculateExpandButtonViewInfos; if View.ShowTotalProgressLine then TotalProgressViewInfo.Calculate; RecreateLinks; end; function TcxSchedulerGanttViewViewInfo.FindEventViewInfoBySource( AEvent: TcxSchedulerEvent; const ADate: TDateTime; AResource: TcxSchedulerStorageResourceItem; var AViewInfo: TcxSchedulerEventCellViewInfo): Boolean; var ATempViewInfo: TcxSchedulerEventCellViewInfo; I: Integer; begin AViewInfo := nil; for I := 0 to EventCells.Count - 1 do begin ATempViewInfo := TcxSchedulerEventCellViewInfo(EventCells.Items[I]); if ATempViewInfo.Event.IsOrigin(AEvent) then if (AResource = nil) or (ATempViewInfo.ResourceItem = AResource) then begin AViewInfo := ATempViewInfo; with TcxSchedulerEventCellViewInfoAccess(ATempViewInfo) do if (ADate = NullDate) or ((DateOf(ContentStart) = ADate) or (IsHeaderEvent and (ADate >= DateOf(ContentStart)) and (ADate < DateOf(ContentFinish)))) then Break; end; end; Result := AViewInfo <> nil; end; function TcxSchedulerGanttViewViewInfo.GetEventBounds(AEvent: TcxSchedulerControlEvent; AResource: TcxSchedulerStorageResourceItem; out AEventRect: TRect): Boolean; var I: Integer; ATempViewInfo: TcxSchedulerEventCellViewInfo; begin Result := False; for I := 0 to EventCells.Count - 1 do begin ATempViewInfo := TcxSchedulerEventCellViewInfo(EventCells.Items[I]); Result := (ATempViewInfo.Event = AEvent) and (ATempViewInfo.ResourceItem = AResource); if Result then begin AEventRect := ATempViewInfo.Bounds; Break; end; end; end; function TcxSchedulerGanttViewViewInfo.GetEventForResourceCount( AResourceIndex: Integer; out AResourceID: Variant): Integer; var I: Integer; AFound: Boolean; begin Result := inherited GetEventForResourceCount(AResourceIndex, AResourceID); AFound := False; I := 0; while (I < ResourceCount) and not AFound do begin if VarEquals(VisibleEventEnumerationList[I].ResourceID, AResourceID) then begin Result := VisibleEventEnumerationList[I].VisibleEventCount; AFound := True; end; Inc(I); end; end; function TcxSchedulerGanttViewViewInfo.GetEventWithoutResourceCount: Integer; begin Result := FVisibleEventWithoutResourceCount; end; function TcxSchedulerGanttViewViewInfo.GetGanttView: TcxSchedulerGanttView; begin Result := TcxSchedulerGanttView(inherited View); end; function TcxSchedulerGanttViewViewInfo.GetIndentBetweenLines: Integer; begin Result := 3 * LinkLineOffset; end; function TcxSchedulerGanttViewViewInfo.GetNeedShowCurrentTime: Boolean; begin Result := not View.ShowTotalProgressLine; end; function TcxSchedulerGanttViewViewInfo.GetIsHotTrackEnabled: Boolean; begin Result := View.Scheduler.OptionsBehavior.HotTrack and (View.Controller.EventDragMode = sedmNone); end; function TcxSchedulerGanttViewViewInfo.GetLinkViewParams: TcxViewParams; begin Result.Bitmap := nil; Result.Color := View.LinkLineColor; Result.Font := Scheduler.Font; Result.TextColor := View.LinkLineColor; end; function TcxSchedulerGanttViewViewInfo.GetVisibleEventEnumeration( AIndex: Integer): TcxSchedulerResourceViewInfo; begin if (FVisibleEventEnumerationList <> nil) and (AIndex < FVisibleEventEnumerationList.Count) then Result := TcxSchedulerResourceViewInfo(FVisibleEventEnumerationList[AIndex]) else Result := nil; end; function TcxSchedulerGanttViewViewInfo.HasVisibleEvents: Boolean; var AResourceIndex, AEventCount: Integer; begin AEventCount := 0; for AResourceIndex := 0 to ResourceCount - 1 do Inc(AEventCount, VisibleEventEnumerationList[AResourceIndex].VisibleEventCount); Result := AEventCount + GetEventWithoutResourceCount <> 0; end; function TcxSchedulerGanttViewViewInfo.IsEventCollapsed(AEvent: TcxSchedulerEvent; AHidden: Boolean): Boolean; var AOwnerEvent: TcxSchedulerEvent; I: Integer; begin Result := AHidden; if Result or (AEvent.TaskLinkOwners = nil) then Exit; for I := 0 to AEvent.TaskLinkOwners.Count - 1 do begin AOwnerEvent := AEvent.TaskLinkOwners.Items[I]; Result := IsEventCollapsed(AOwnerEvent, not IsTaskLinksExpanded(AOwnerEvent.TaskLinks)); if not Result then Break; end; end; function TcxSchedulerGanttViewViewInfo.IsTaskLinksExpanded( ATaskLinks: TcxSchedulerEventLinks): Boolean; begin Result := not View.ShowExpandButtons or ATaskLinks.Expanded; end; function TcxSchedulerGanttViewViewInfo.NavigationButtonOffset( AKind: TcxSchedulerContentNavigationButtonKind; AResourceIndex: Integer): Integer; begin AResourceIndex := Max(AResourceIndex, 0); if (AKind = nbkNext) and (TcxSchedulerTimeGridResourceScroll(ResourceViewShift[AResourceIndex]).ScrollBar <> nil) then Result := -TcxSchedulerTimeGridResourceScroll(ResourceViewShift[AResourceIndex]).ScrollBar.Width else Result := 0; end; procedure TcxSchedulerGanttViewViewInfo.RecreateLinks; begin Links.Clear; CreateLinks; end; procedure TcxSchedulerGanttViewViewInfo.SetHotLink( AHotLink: TcxSchedulerGanttViewLinkViewInfo); function GetRefreshRect(AOldLink, ANewLink: TcxSchedulerGanttViewLinkViewInfo): TRect; begin if ANewLink = nil then Result := AOldLink.ClipRect else Result := ANewLink.ClipRect; end; var AOldLink: TcxSchedulerGanttViewLinkViewInfo; begin if HotLink <> AHotLink then begin AOldLink := HotLink; FHotLink := AHotLink; View.RepaintRect(GetRefreshRect(AOldLink, HotLink)); end; end; end.