1058 lines
33 KiB
ObjectPascal
1058 lines
33 KiB
ObjectPascal
{********************************************************************}
|
|
{ }
|
|
{ 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 cxSchedulerAggregateStorage;
|
|
|
|
{$I cxVer.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, cxClasses, Controls, cxSchedulerStorage, cxSchedulerHolidays,
|
|
cxSchedulerUtils, cxCustomData;
|
|
|
|
type
|
|
|
|
TcxSchedulerStorageLinks = class;
|
|
TcxSchedulerAggregateStorage = class;
|
|
|
|
TcxSchedulerStorageLinkClass = class of TcxSchedulerStorageLink;
|
|
|
|
{ TcxSchedulerStorageLink }
|
|
|
|
TcxSchedulerStorageLink = class(TCollectionItem)
|
|
private
|
|
FIsDestroying: Boolean;
|
|
FStorage: TcxCustomSchedulerStorage;
|
|
function GetAggregateStorage: TcxSchedulerAggregateStorage;
|
|
function GetDefault: Boolean;
|
|
function GetLinks: TcxSchedulerStorageLinks;
|
|
procedure SetDefault(AValue: Boolean);
|
|
procedure SetStorage(AValue: TcxCustomSchedulerStorage);
|
|
protected
|
|
property IsDestroying: Boolean read FIsDestroying;
|
|
public
|
|
destructor Destroy; override;
|
|
|
|
procedure Assign(Source: TPersistent); override;
|
|
|
|
property AggregateStorage: TcxSchedulerAggregateStorage read GetAggregateStorage;
|
|
property Links: TcxSchedulerStorageLinks read GetLinks;
|
|
published
|
|
property Default: Boolean read GetDefault write SetDefault default False;
|
|
property Storage: TcxCustomSchedulerStorage read FStorage write SetStorage;
|
|
end;
|
|
|
|
{ TcxSchedulerStorageLinks }
|
|
|
|
TcxSchedulerStorageLinks = class(TCollection)
|
|
private
|
|
FAggregateStorage: TcxSchedulerAggregateStorage;
|
|
FDefaultLink: TcxSchedulerStorageLink;
|
|
function GetDefaultStorage: TcxCustomSchedulerStorage;
|
|
function GetItem(AIndex: Integer): TcxSchedulerStorageLink;
|
|
procedure SetDefaultLink(AValue: TcxSchedulerStorageLink);
|
|
procedure SetItem(AIndex: Integer; AValue: TcxSchedulerStorageLink);
|
|
protected
|
|
function GetItemLinkClass: TcxSchedulerStorageLinkClass; virtual;
|
|
procedure Update(Item: TCollectionItem); override;
|
|
public
|
|
constructor CreateEx(AOwner: TcxSchedulerAggregateStorage); virtual;
|
|
|
|
function AddStorageLink(AStorage: TcxCustomSchedulerStorage): TcxSchedulerStorageLink;
|
|
function GetStorageLinkIndex(AStorage: TcxCustomSchedulerStorage): Integer;
|
|
procedure PopulateHolidays(AList: TList);
|
|
|
|
property AggregateStorage: TcxSchedulerAggregateStorage read FAggregateStorage;
|
|
property DefaultLink: TcxSchedulerStorageLink read FDefaultLink write SetDefaultLink;
|
|
property DefaultStorage: TcxCustomSchedulerStorage read GetDefaultStorage;
|
|
property Items[AIndex: Integer]: TcxSchedulerStorageLink read GetItem write SetItem; default;
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorageEvent }
|
|
|
|
TcxSchedulerAggregateStorageEvent = class(TcxSchedulerEvent)
|
|
private
|
|
FSourceEvent: TcxSchedulerEvent;
|
|
function GetStorage: TcxSchedulerAggregateStorage;
|
|
protected
|
|
MappedID: Integer;
|
|
MappedParentID: Integer;
|
|
procedure CheckParentID;
|
|
function GetParentID: Variant; override;
|
|
function GetValueByIndex(AIndex: Integer): Variant; override;
|
|
procedure SetSourceEvent(AValue: TcxSchedulerEvent);
|
|
procedure SetValue(AField: TcxCustomSchedulerStorageField; const AValue: Variant); override;
|
|
public
|
|
procedure DeleteExceptions; override;
|
|
procedure RemoveRecurrence; override;
|
|
|
|
property SourceEvent: TcxSchedulerEvent read FSourceEvent;
|
|
property Storage: TcxSchedulerAggregateStorage read GetStorage;
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorageDataSource }
|
|
|
|
TcxSchedulerAggregateStorageDataSource = class(TcxCustomDataSource)
|
|
private
|
|
FStorage: TcxSchedulerAggregateStorage;
|
|
protected
|
|
function GetRecordCount: Integer; override;
|
|
function GetRecordId(ARecordHandle: TcxDataRecordHandle): Variant; override;
|
|
function GetRecordHandle(ARecordIndex: Integer): TcxDataRecordHandle; override;
|
|
function GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant; override;
|
|
function IsRecordIdSupported: Boolean; override;
|
|
procedure SetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; const AValue: Variant); override;
|
|
public
|
|
constructor Create(AStorage: TcxSchedulerAggregateStorage); virtual;
|
|
|
|
property Storage: TcxSchedulerAggregateStorage read FStorage;
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorage }
|
|
|
|
TcxSchedulerEventInsertingEvent = procedure(Sender: TcxSchedulerAggregateStorage;
|
|
AEvent: TcxSchedulerEvent; var AStorage: TcxCustomSchedulerStorage) of object;
|
|
|
|
TcxSchedulerAggregateStorage = class(TcxCustomSchedulerStorage, IcxSchedulerStorageListener)
|
|
private
|
|
FCounter: Integer;
|
|
FLinks: TcxSchedulerStorageLinks;
|
|
FProvider: TcxSchedulerAggregateStorageDataSource;
|
|
FSynchronizing: Boolean;
|
|
FOnEventInserting: TcxSchedulerEventInsertingEvent;
|
|
function CanRefresh: Boolean;
|
|
function GetCustomFields: TcxSchedulerStorageFields;
|
|
procedure SetCustomFields(const AValue: TcxSchedulerStorageFields);
|
|
protected
|
|
function CreateDataProvider: TcxSchedulerAggregateStorageDataSource; virtual;
|
|
procedure DestroyDataProvider;
|
|
procedure DoDeleteEvent(const ARecordIndex: Integer); override;
|
|
procedure DoEventInserting(AEvent: TcxSchedulerEvent; out AStorage: TcxCustomSchedulerStorage); virtual;
|
|
function DoFilterEvent(AEvent: TcxSchedulerEvent): Boolean; override;
|
|
procedure DoRefresh; override;
|
|
function GetEventClass: TcxSchedulerEventClass; override;
|
|
function GetMappedID(ASource: TcxSchedulerEvent): Integer;
|
|
function GetRecordID(const ARecordIndex: Integer): Variant; override;
|
|
function InternalAddEvent(AEvent: TcxSchedulerEvent): TcxSchedulerAggregateStorageEvent; virtual;
|
|
function IsDesigning: Boolean;
|
|
function IsDestroining: Boolean;
|
|
procedure PopulateEvents(var AList: TList); virtual;
|
|
procedure PostEditingData(AEvent: TcxSchedulerEvent); override;
|
|
procedure PostEvent(AEvent: TcxSchedulerEvent); override;
|
|
procedure SendNotification(AIsRemoved: Boolean = False); override;
|
|
procedure SynchronizeEventsWithRecords; override;
|
|
|
|
procedure BeginUpdateDataController; override;
|
|
procedure EndUpdateDataController; override;
|
|
// IcxSchedulerStorageListener & IcxSchedulerStorageListener2
|
|
procedure StorageChanged(Sender: TObject);
|
|
procedure StorageRemoved(Sender: TObject);
|
|
|
|
property Provider: TcxSchedulerAggregateStorageDataSource read FProvider;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure Clear; override;
|
|
function createEvent: TcxSchedulerEvent; override;
|
|
procedure GenerateHolidayEvents(const AResourceID: Variant); override;
|
|
function GetHolidayNamesByDate(ADate: TDate; var ANames: string;
|
|
AOnlyVisible: Boolean = True): Boolean; override;
|
|
function IsActive: Boolean; override;
|
|
procedure PopulateHolidayDates(AList: TcxSchedulerDateList; AStart, AFinish: TDate;
|
|
AOnlyVisible: Boolean = True; AClearList: Boolean = True); override;
|
|
published
|
|
property CustomFields: TcxSchedulerStorageFields read GetCustomFields write SetCustomFields;
|
|
property Reminders;
|
|
property Resources;
|
|
property Links: TcxSchedulerStorageLinks read FLinks write FLinks;
|
|
property OnEventInserting: TcxSchedulerEventInsertingEvent read
|
|
FOnEventInserting write FOnEventInserting;
|
|
property OnEventIntersect;
|
|
property OnFilterEvent;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils, Variants, Types, cxVariants;
|
|
|
|
type
|
|
|
|
TcxCustomSchedulerStorageAccess = class(TcxCustomSchedulerStorage);
|
|
|
|
TcxSyncObject = class(TList)
|
|
public
|
|
IsSource: Boolean;
|
|
Link: TObject;
|
|
Source: TObject;
|
|
IsUnique: Boolean;
|
|
end;
|
|
|
|
TcxSynchronizer = class(TcxObjectList)
|
|
private
|
|
function GetItem(AIndex: Integer): TcxSyncObject;
|
|
protected
|
|
function Add(ALink, ASource: TObject; AIsSource: Boolean): TcxSyncObject;
|
|
public
|
|
procedure AddItems(AEvents: TList);
|
|
procedure AddSources(AEvents: TList);
|
|
procedure Synchronize;
|
|
|
|
property Items[AIndex: Integer]: TcxSyncObject read GetItem;
|
|
end;
|
|
|
|
function cxCompareSyncObjects(AItem1, AItem2: TcxSyncObject): Integer;
|
|
begin
|
|
Result := Integer(AItem1.Source) - Integer(AItem2.Source);
|
|
if Result = 0 then
|
|
Result := Integer(AItem2.IsSource) - Integer(AItem1.IsSource);
|
|
end;
|
|
|
|
function cxCompareEventsByIndex(AEvent1, AEvent2: TcxSchedulerEvent): Integer;
|
|
begin
|
|
Result := Integer(AEvent1) - Integer(AEvent2);
|
|
if Result <> 0 then
|
|
begin
|
|
if AEvent1 = nil then
|
|
Result := 1
|
|
else
|
|
if AEvent2 = nil then
|
|
Result := -1
|
|
else
|
|
Result := AEvent1.RecordIndex - AEvent2.RecordIndex;
|
|
end;
|
|
end;
|
|
|
|
{ TcxSynchronizer }
|
|
|
|
procedure TcxSynchronizer.AddItems(AEvents: TList);
|
|
var
|
|
I: Integer;
|
|
AEvent: TcxSchedulerAggregateStorageEvent;
|
|
begin
|
|
for I := 0 to AEvents.Count - 1 do
|
|
begin
|
|
AEvent := TcxSchedulerAggregateStorageEvent(AEvents.List^[I]);
|
|
Add(AEvent, AEvent.SourceEvent, False);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSynchronizer.AddSources(AEvents: TList);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to AEvents.Count - 1 do
|
|
Add(AEvents.List^[I], AEvents.List^[I], True);
|
|
end;
|
|
|
|
function TcxSynchronizer.Add(ALink, ASource: TObject; AIsSource: Boolean): TcxSyncObject;
|
|
begin
|
|
Result := TcxSyncObject.Create;
|
|
Result.Link := ALink;
|
|
Result.Source := ASource;
|
|
Result.IsSource := AIsSource;
|
|
inherited Add(Result);
|
|
end;
|
|
|
|
{$B-}
|
|
|
|
procedure TcxSynchronizer.Synchronize;
|
|
var
|
|
I: Integer;
|
|
AItem: TcxSyncObject;
|
|
begin
|
|
Sort(@cxCompareSyncObjects);
|
|
for I := 0 to Count - 1 do
|
|
begin
|
|
AItem := Items[I];
|
|
if AItem.IsSource then
|
|
AItem.IsUnique := (I = (Count - 1)) or (AItem.Source <> Items[I + 1].Source)
|
|
else
|
|
AItem.IsUnique := (I = 0) or (AItem.Source <> Items[I - 1].Source);
|
|
end;
|
|
end;
|
|
|
|
function TcxSynchronizer.GetItem(AIndex: Integer): TcxSyncObject;
|
|
begin
|
|
Result := TcxSyncObject(List^[AIndex]);
|
|
end;
|
|
|
|
function cxCompareEvents(AItem1, AItem2: Integer): Integer;
|
|
begin
|
|
Result := AItem1 - AItem2;
|
|
end;
|
|
|
|
procedure cxMakeUniqueList(AList: TList);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
AList.Sort(@cxCompareEvents);
|
|
for I := AList.Count - 1 downto 1 do
|
|
if AList[I] = AList[I - 1] then
|
|
AList.Delete(I);
|
|
end;
|
|
|
|
{ TcxSchedulerStorageLink }
|
|
|
|
destructor TcxSchedulerStorageLink.Destroy;
|
|
begin
|
|
FIsDestroying := True;
|
|
if Default then
|
|
Links.FDefaultLink := nil;
|
|
Storage := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLink.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TcxSchedulerStorageLink then
|
|
with Source as TcxSchedulerStorageLink do
|
|
begin
|
|
Self.Storage := Storage;
|
|
Self.Default := Default;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function TcxSchedulerStorageLink.GetAggregateStorage: TcxSchedulerAggregateStorage;
|
|
begin
|
|
Result := Links.AggregateStorage;
|
|
end;
|
|
|
|
function TcxSchedulerStorageLink.GetDefault: Boolean;
|
|
begin
|
|
Result := Links.DefaultLink = Self;
|
|
end;
|
|
|
|
function TcxSchedulerStorageLink.GetLinks: TcxSchedulerStorageLinks;
|
|
begin
|
|
Result := inherited Collection as TcxSchedulerStorageLinks;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLink.SetDefault(AValue: Boolean);
|
|
begin
|
|
if AValue then
|
|
Links.DefaultLink := Self;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLink.SetStorage(AValue: TcxCustomSchedulerStorage);
|
|
begin
|
|
if (Links.GetStorageLinkIndex(AValue) = -1) and
|
|
(AValue <> AggregateStorage) then
|
|
begin
|
|
if FStorage <> nil then
|
|
FStorage.RemoveListener(AggregateStorage);
|
|
FStorage := AValue;
|
|
if FStorage <> nil then
|
|
FStorage.AddListener(AggregateStorage);
|
|
if not IsDestroying then
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
{ TcxSchedulerStorageLinks }
|
|
|
|
constructor TcxSchedulerStorageLinks.CreateEx(AOwner: TcxSchedulerAggregateStorage);
|
|
begin
|
|
Create(GetItemLinkClass);
|
|
FAggregateStorage := AOwner;
|
|
end;
|
|
|
|
function TcxSchedulerStorageLinks.AddStorageLink(
|
|
AStorage: TcxCustomSchedulerStorage): TcxSchedulerStorageLink;
|
|
begin
|
|
Result := nil;
|
|
if GetStorageLinkIndex(AStorage) < 0 then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Result := Add as TcxSchedulerStorageLink;
|
|
Result.Storage := AStorage;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerStorageLinks.GetStorageLinkIndex(
|
|
AStorage: TcxCustomSchedulerStorage): Integer;
|
|
begin
|
|
if AStorage <> nil then
|
|
for Result := 0 to Count - 1 do
|
|
if Items[Result].Storage = AStorage then Exit;
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLinks.PopulateHolidays(AList: TList);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
with Items[I] do
|
|
begin
|
|
if (Storage <> nil) then
|
|
if Storage is TcxSchedulerAggregateStorage then
|
|
TcxSchedulerAggregateStorage(Storage).Links.PopulateHolidays(AList)
|
|
else
|
|
if Storage.Holidays <> nil then
|
|
AList.Add(Storage.Holidays);
|
|
end;
|
|
cxMakeUniqueList(AList);
|
|
end;
|
|
|
|
function TcxSchedulerStorageLinks.GetItemLinkClass: TcxSchedulerStorageLinkClass;
|
|
begin
|
|
Result := TcxSchedulerStorageLink;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLinks.Update(Item: TCollectionItem);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Update(Item);
|
|
if Count > 0 then
|
|
begin
|
|
if (FDefaultLink = nil) or (FDefaultLink.Storage = nil) then
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
if not Items[I].IsDestroying and (Items[I].Storage <> nil) then
|
|
begin
|
|
FDefaultLink := Items[I];
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
AggregateStorage.StorageChanged(nil);
|
|
end;
|
|
|
|
function TcxSchedulerStorageLinks.GetDefaultStorage: TcxCustomSchedulerStorage;
|
|
begin
|
|
if FDefaultLink <> nil then
|
|
Result := FDefaultLink.Storage
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TcxSchedulerStorageLinks.GetItem(AIndex:
|
|
Integer): TcxSchedulerStorageLink;
|
|
begin
|
|
Result := TcxSchedulerStorageLink(inherited Items[AIndex]);
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLinks.SetDefaultLink(
|
|
AValue: TcxSchedulerStorageLink);
|
|
begin
|
|
if ((AValue = nil) or ((AValue.Storage <> nil) and
|
|
not AValue.IsDestroying)) and (FDefaultLink <> AValue) then
|
|
begin
|
|
FDefaultLink := AValue;
|
|
Changed;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerStorageLinks.SetItem(AIndex: Integer;
|
|
AValue: TcxSchedulerStorageLink);
|
|
begin
|
|
Items[AIndex].Assign(AValue);
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorageEvent }
|
|
|
|
procedure TcxSchedulerAggregateStorageEvent.DeleteExceptions;
|
|
begin
|
|
SourceEvent.DeleteExceptions;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorageEvent.RemoveRecurrence;
|
|
begin
|
|
SourceEvent.RemoveRecurrence;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorageEvent.CheckParentID;
|
|
begin
|
|
if (Storage = nil) or Storage.IsDestroying then
|
|
Exit;
|
|
if (MappedParentID = -1) and (SourceEvent.Pattern <> nil) then
|
|
MappedParentID := Storage.GetMappedID(SourceEvent.Pattern)
|
|
else
|
|
if (MappedParentID = -2) and (SourceEvent <> nil) and
|
|
(SourceEvent.ParentID >= 0) then
|
|
MappedParentID := Storage.GetMappedID(SourceEvent.Storage.GetEventByID(SourceEvent.ParentID));
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageEvent.GetParentID: Variant;
|
|
begin
|
|
Result := MappedParentID;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageEvent.GetValueByIndex(AIndex: Integer): Variant;
|
|
begin
|
|
if (SourceEvent = nil) or IsEditing then
|
|
begin
|
|
if (AIndex = TcxSchedulerAggregateStorage(Storage).FParentIDField.Index) and
|
|
(EventType in [etCustom, etException]) and
|
|
((FPattern <> nil) or (ParentID >= 0)) then
|
|
begin
|
|
if FPattern <> nil then
|
|
Result := TcxSchedulerAggregateStorageEvent(FPattern).SourceEvent.ID
|
|
else
|
|
Result := TcxSchedulerAggregateStorageEvent(Storage.GetEventByID(ParentID)).SourceEvent.ID;
|
|
end
|
|
else
|
|
Result := FEditValues[AIndex]
|
|
end
|
|
else
|
|
{ if SourceEvent <> nil then
|
|
Result := SourceEvent.Values[AIndex]
|
|
else }
|
|
Result := inherited GetValueByIndex(AIndex);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorageEvent.SetSourceEvent(AValue: TcxSchedulerEvent);
|
|
begin
|
|
FSourceEvent := AValue;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorageEvent.SetValue(AField: TcxCustomSchedulerStorageField;
|
|
const AValue: Variant);
|
|
begin
|
|
if IsEditing and
|
|
(AField = TcxSchedulerAggregateStorage(Storage).FParentIDField) then
|
|
MappedParentID := AValue
|
|
else
|
|
inherited SetValue(AField, AValue);
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageEvent.GetStorage: TcxSchedulerAggregateStorage;
|
|
begin
|
|
Result := TcxSchedulerAggregateStorage(inherited Storage);
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorageDataSource }
|
|
|
|
constructor TcxSchedulerAggregateStorageDataSource.Create(AStorage: TcxSchedulerAggregateStorage);
|
|
begin
|
|
FStorage := AStorage;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageDataSource.GetRecordCount: Integer;
|
|
begin
|
|
Result := Storage.EventCount;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageDataSource.GetRecordId(
|
|
ARecordHandle: TcxDataRecordHandle): Variant;
|
|
begin
|
|
Result := TcxSchedulerAggregateStorageEvent(ARecordHandle).MappedID;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageDataSource.GetRecordHandle(
|
|
ARecordIndex: Integer): TcxDataRecordHandle;
|
|
begin
|
|
Result := Storage.FEventsList[ARecordIndex];
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageDataSource.GetValue(
|
|
ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant;
|
|
begin
|
|
with TcxSchedulerAggregateStorageEvent(ARecordHandle).SourceEvent do
|
|
Result := Values[Integer(AItemHandle)];
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorageDataSource.IsRecordIdSupported: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorageDataSource.SetValue(
|
|
ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; const AValue: Variant);
|
|
begin
|
|
with TcxSchedulerAggregateStorageEvent(ARecordHandle).SourceEvent do
|
|
Values[Integer(AItemHandle)] := AValue;
|
|
end;
|
|
|
|
{ TcxSchedulerAggregateStorage }
|
|
|
|
constructor TcxSchedulerAggregateStorage.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FLinks := TcxSchedulerStorageLinks.CreateEx(Self);
|
|
FProvider := CreateDataProvider;
|
|
DataController.CustomDataSource := FProvider;
|
|
end;
|
|
|
|
destructor TcxSchedulerAggregateStorage.Destroy;
|
|
begin
|
|
FLinks.Free;
|
|
DestroyDataProvider;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TcxSchedulerAggregateStorage then
|
|
Links.Assign(TcxSchedulerAggregateStorage(Source).Links);
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.Clear;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
for I := 0 to Links.Count - 1 do
|
|
if Links[I].Storage <> nil then
|
|
Links[I].Storage.Clear;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.createEvent: TcxSchedulerEvent;
|
|
begin
|
|
Result := inherited createEvent;
|
|
Inc(FCounter);
|
|
with TcxSchedulerAggregateStorageEvent(Result) do
|
|
begin
|
|
MappedID := FCounter;
|
|
MappedParentID := -2;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.GenerateHolidayEvents(const AResourceID: Variant);
|
|
var
|
|
AHolidaysList: TList;
|
|
I: Integer;
|
|
begin
|
|
AHolidaysList := TList.Create;
|
|
try
|
|
Links.PopulateHolidays(AHolidaysList);
|
|
for I := 0 to AHolidaysList.Count - 1 do
|
|
GenerateHolidayEventsBySchedulerHolidays(AResourceID, TcxSchedulerHolidays(AHolidaysList[I]));
|
|
finally
|
|
AHolidaysList.Free;
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.GetHolidayNamesByDate(ADate: TDate; var ANames: string;
|
|
AOnlyVisible: Boolean = True): Boolean;
|
|
var
|
|
I: Integer;
|
|
AHolidaysList: TList;
|
|
begin
|
|
AHolidaysList := TList.Create;
|
|
try
|
|
Links.PopulateHolidays(AHolidaysList);
|
|
for I := 0 to AHolidaysList.Count - 1 do
|
|
TcxSchedulerHolidays(AHolidaysList.Items[I]).GetHolidayNamesByDate(ADate, ANames, AOnlyVisible);
|
|
finally
|
|
FreeAndNil(AHolidaysList);
|
|
Result := ANames <> '';
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.IsActive: Boolean;
|
|
begin
|
|
Result := (Links <> nil) and (Links.Count > 0) and (Links.DefaultStorage <> nil) and
|
|
(Links.DefaultStorage.IsActive or Assigned(FOnEventInserting));
|
|
if Result then
|
|
Result := CanRefresh;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.PopulateHolidayDates(AList: TcxSchedulerDateList;
|
|
AStart, AFinish: TDate; AOnlyVisible: Boolean = True; AClearList: Boolean = True);
|
|
|
|
procedure AddHolidays(AHolidays: TcxSchedulerHolidays);
|
|
begin
|
|
AHolidays.PopulateHolidayDates(AList, AStart, AFinish, AOnlyVisible, False);
|
|
end;
|
|
|
|
var
|
|
AHolidaysList: TList;
|
|
I: Integer;
|
|
begin
|
|
AHolidaysList := TList.Create;
|
|
try
|
|
Links.PopulateHolidays(AHolidaysList);
|
|
if AClearList then
|
|
AList.Clear;
|
|
for I := 0 to AHolidaysList.Count - 1 do
|
|
AddHolidays(AHolidaysList[I]);
|
|
finally
|
|
FreeAndNil(AHolidaysList);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.DoDeleteEvent(const ARecordIndex: Integer);
|
|
begin
|
|
if (ARecordIndex >= 0) and (ARecordIndex < EventCount) then
|
|
begin
|
|
if Events[ARecordIndex] = LastEditedEvent then
|
|
FLastEditedEvent := nil;
|
|
TcxSchedulerAggregateStorageEvent(Events[ARecordIndex]).SourceEvent.Delete;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.DoEventInserting(AEvent: TcxSchedulerEvent;
|
|
out AStorage: TcxCustomSchedulerStorage);
|
|
begin
|
|
if (AEvent.EventType in [etCustom, etException]) and
|
|
((AEvent.Pattern <> nil) or (AEvent.ParentID >= 0)) then
|
|
begin
|
|
if AEvent.Pattern <> nil then
|
|
AStorage := TcxSchedulerAggregateStorageEvent(AEvent.Pattern).SourceEvent.Storage
|
|
else
|
|
AStorage := TcxSchedulerAggregateStorageEvent(GetEventByID(AEvent.ParentID)).SourceEvent.Storage;
|
|
Exit;
|
|
end;
|
|
AStorage := Links.DefaultStorage;
|
|
if Assigned(FOnEventInserting) then
|
|
FOnEventInserting(Self, AEvent, AStorage);
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.DoFilterEvent(AEvent: TcxSchedulerEvent): Boolean;
|
|
var
|
|
AStorage: TcxCustomSchedulerStorageAccess;
|
|
begin
|
|
if AEvent is TcxSchedulerAggregateStorageEvent then
|
|
begin
|
|
AStorage := TcxCustomSchedulerStorageAccess(TcxSchedulerAggregateStorageEvent(AEvent).SourceEvent.Storage);
|
|
Result := AStorage.DoFilterEvent(TcxSchedulerAggregateStorageEvent(AEvent).SourceEvent);
|
|
end
|
|
else
|
|
Result := True;
|
|
if Assigned(OnFilterEvent) then
|
|
OnFilterEvent(Self, AEvent, Result);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.DoRefresh;
|
|
begin
|
|
if CanRefresh then
|
|
inherited DoRefresh;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.CreateDataProvider: TcxSchedulerAggregateStorageDataSource;
|
|
begin
|
|
Result := TcxSchedulerAggregateStorageDataSource.Create(Self);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.DestroyDataProvider;
|
|
begin
|
|
DataController.CustomDataSource := nil;
|
|
FreeAndNil(FProvider);
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.GetEventClass:
|
|
TcxSchedulerEventClass;
|
|
begin
|
|
Result := TcxSchedulerAggregateStorageEvent;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.GetMappedID(ASource: TcxSchedulerEvent): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := -2;
|
|
if ASource = nil then Exit;
|
|
for I := EventCount - 1 downto 0 do
|
|
if FEventsList[I] <> nil then
|
|
with TcxSchedulerAggregateStorageEvent(FEventsList[I]) do
|
|
if SourceEvent = ASource then
|
|
begin
|
|
Result := MappedID;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.GetRecordID(
|
|
const ARecordIndex: Integer): Variant;
|
|
begin
|
|
Result := TcxSchedulerAggregateStorageEvent(Events[ARecordIndex]).MappedID;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.InternalAddEvent(
|
|
AEvent: TcxSchedulerEvent): TcxSchedulerAggregateStorageEvent;
|
|
begin
|
|
Inc(FCounter);
|
|
Result := GetEventClass.Create(Self, -1) as TcxSchedulerAggregateStorageEvent;
|
|
Result.FSourceEvent := AEvent;
|
|
Result.MappedID := FCounter;
|
|
Result.SetRecordIndex(FEventsList.Add(Result));
|
|
Result.MappedParentID := GetMappedID(AEvent.Pattern);
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.IsDesigning: Boolean;
|
|
begin
|
|
Result := csDesigning in ComponentState;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.IsDestroining: Boolean;
|
|
begin
|
|
Result := csDestroying in ComponentState;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.PopulateEvents(var AList: TList);
|
|
|
|
procedure PopulateStorageEvents(AStorage: TcxCustomSchedulerStorage);
|
|
var
|
|
AIndex: Integer;
|
|
begin
|
|
if AStorage = nil then Exit;
|
|
for AIndex := 0 to AStorage.EventCount - 1 do
|
|
AList.Add(AStorage.Events[AIndex]);
|
|
end;
|
|
|
|
var
|
|
ALinkIndex: Integer;
|
|
begin
|
|
AList.Clear;
|
|
if Links = nil then
|
|
Exit;
|
|
for ALinkIndex := 0 to Links.Count - 1 do
|
|
PopulateStorageEvents(Links[ALinkIndex].Storage);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.PostEditingData(
|
|
AEvent: TcxSchedulerEvent);
|
|
var
|
|
ASource: TcxSchedulerEvent;
|
|
begin
|
|
ASource := TcxSchedulerAggregateStorageEvent(AEvent).SourceEvent;
|
|
if (ASource <> nil) and (AEvent.RecordIndex >= 0) and
|
|
not (ASource is TcxSchedulerAggregateStorageEvent) then
|
|
begin
|
|
if DoEventModified(AEvent) then Exit;
|
|
ASource.BeginEditing;
|
|
try
|
|
ASource.Assign(AEvent);
|
|
finally
|
|
ASource.EndEditing;
|
|
end;
|
|
end
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.PostEvent(AEvent: TcxSchedulerEvent);
|
|
var
|
|
ASource: TcxSchedulerEvent;
|
|
AStorage: TcxCustomSchedulerStorage;
|
|
APattern: TcxSchedulerEvent;
|
|
begin
|
|
if TcxSchedulerAggregateStorageEvent(AEvent).IsNewEvent then
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
FNewEvents.Remove(AEvent);
|
|
if not IsDestroying then
|
|
begin
|
|
APattern := nil;
|
|
if AEvent.Pattern <> nil then
|
|
APattern := AEvent.Pattern
|
|
else
|
|
if AEvent.ParentID >= 0 then
|
|
APattern := GetEventByID(AEvent.ParentID);
|
|
DoEventInserting(AEvent, AStorage);
|
|
if AStorage <> nil then
|
|
begin
|
|
ASource := AStorage.createEvent;
|
|
ASource.Assign(AEvent);
|
|
if APattern <> nil then
|
|
begin
|
|
ASource.ParentID := TcxSchedulerAggregateStorageEvent(APattern).SourceEvent.ID;
|
|
TcxSchedulerAggregateStorageEvent(ASource).FPattern := TcxSchedulerAggregateStorageEvent(APattern).SourceEvent;
|
|
TcxSchedulerAggregateStorageEvent(AEvent).MappedParentID := APattern.ID;
|
|
end;
|
|
TcxSchedulerAggregateStorageEvent(AEvent).SetSourceEvent(ASource);
|
|
AEvent.EndEditing;
|
|
with TcxSchedulerAggregateStorageEvent(AEvent) do
|
|
SetRecordIndex(FEventsList.Add(AEvent));
|
|
ASource.Post;
|
|
end
|
|
else
|
|
AEvent.Free;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.SendNotification(AIsRemoved: Boolean = False);
|
|
begin
|
|
if (AIsRemoved or IsDestroining or CanRefresh) and not FSynchronizing then
|
|
inherited SendNotification(AIsRemoved);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.SynchronizeEventsWithRecords;
|
|
var
|
|
AEvents: TList;
|
|
I, ADeletedCount: Integer;
|
|
ASynchronizer: TcxSynchronizer;
|
|
begin
|
|
if FSynchronizing then Exit;
|
|
ADeletedCount := 0;
|
|
AEvents := TList.Create;
|
|
try
|
|
PopulateEvents(AEvents);
|
|
ASynchronizer := TcxSynchronizer.Create;
|
|
try
|
|
ASynchronizer.Capacity := AEvents.Count + EventCount;
|
|
ASynchronizer.AddItems(FEventsList.List);
|
|
ASynchronizer.AddSources(AEvents);
|
|
ASynchronizer.Synchronize;
|
|
for I := 0 to ASynchronizer.Count - 1 do
|
|
with ASynchronizer.Items[I] do
|
|
begin
|
|
if IsUnique then
|
|
begin
|
|
if IsSource then
|
|
InternalAddEvent(TcxSchedulerEvent(Source))
|
|
else
|
|
begin
|
|
if FEventsList.List.List^[TcxSchedulerEvent(Link).RecordIndex] = FLastEditedEvent then
|
|
FLastEditedEvent := nil;
|
|
FEventsList.List.List^[TcxSchedulerEvent(Link).RecordIndex] := nil;
|
|
TcxSchedulerAggregateStorageEvent(Link).FIsDeletion := True;
|
|
Link.Free;
|
|
Inc(ADeletedCount);
|
|
end
|
|
end
|
|
end;
|
|
finally
|
|
ASynchronizer.Free;
|
|
end;
|
|
finally
|
|
AEvents.Free;
|
|
end;
|
|
if ADeletedCount > 0 then
|
|
begin
|
|
FEventsList.Sort(@cxCompareEventsByIndex);
|
|
FEventsList.List.Count := FEventsList.List.Count - ADeletedCount;
|
|
end;
|
|
for I := 0 to FEventsList.Count - 1 do
|
|
with TcxSchedulerAggregateStorageEvent(FEventsList[I]) do
|
|
begin
|
|
SetRecordIndex(I);
|
|
FPattern := nil;
|
|
CheckParentID;
|
|
end;
|
|
Provider.LoadRecordHandles;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.BeginUpdateDataController;
|
|
var
|
|
ALinkIndex: Integer;
|
|
begin
|
|
if Links = nil then Exit;
|
|
for ALinkIndex := 0 to Links.Count - 1 do
|
|
if (Links[ALinkIndex].Storage <> nil) then
|
|
Links[ALinkIndex].Storage.BeginUpdate;
|
|
inherited BeginUpdateDataController;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.EndUpdateDataController;
|
|
var
|
|
ALinkIndex: Integer;
|
|
begin
|
|
if Links = nil then Exit;
|
|
for ALinkIndex := 0 to Links.Count - 1 do
|
|
if (Links[ALinkIndex].Storage <> nil) then
|
|
Links[ALinkIndex].Storage.EndUpdate;
|
|
inherited EndUpdateDataController;
|
|
end;
|
|
|
|
// IcxSchedulerStorageListener
|
|
procedure TcxSchedulerAggregateStorage.StorageChanged(Sender: TObject);
|
|
begin
|
|
SynchronizeEventsWithRecords;
|
|
FSynchronizing := True;
|
|
try
|
|
Provider.DataChanged;
|
|
finally
|
|
FSynchronizing := False;
|
|
SendNotification();
|
|
end;
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.StorageRemoved(Sender: TObject);
|
|
var
|
|
AIndex: Integer;
|
|
begin
|
|
TcxCustomSchedulerStorage(Sender).RemoveListener(Self);
|
|
AIndex := Links.GetStorageLinkIndex(TcxCustomSchedulerStorage(Sender));
|
|
if AIndex >= 0 then
|
|
begin
|
|
Links[AIndex].FStorage := nil;
|
|
if Links.DefaultLink = Links[AIndex] then
|
|
Links.DefaultLink := nil;
|
|
end;
|
|
if not IsDestroying then
|
|
FullRefresh;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.CanRefresh: Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := True;
|
|
if Links <> nil then
|
|
for I := 0 to Links.Count - 1 do
|
|
if Links[I].Storage <> nil then
|
|
begin
|
|
Result := Result and (FieldCount = Links[I].Storage.FieldCount);
|
|
if not Result then
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TcxSchedulerAggregateStorage.GetCustomFields: TcxSchedulerStorageFields;
|
|
begin
|
|
Result := TcxSchedulerStorageFields(inherited CustomFields);
|
|
end;
|
|
|
|
procedure TcxSchedulerAggregateStorage.SetCustomFields(const AValue: TcxSchedulerStorageFields);
|
|
begin
|
|
CustomFields.Assign(AValue);
|
|
end;
|
|
|
|
end.
|
|
|
|
|