2022-08-22 21:01:27 +00:00
|
|
|
unit VpGanttView;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
2023-01-16 12:43:17 +00:00
|
|
|
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
|
2022-08-22 21:01:27 +00:00
|
|
|
interface
|
|
|
|
|
2023-10-09 22:46:32 +00:00
|
|
|
uses lazloggerbase,
|
2022-08-28 18:17:04 +00:00
|
|
|
LCLType, LCLIntf, LMessages,
|
2022-09-01 12:12:52 +00:00
|
|
|
Classes, SysUtils, Graphics, Types, Controls, StdCtrls, Menus, Forms,
|
2022-08-28 18:17:04 +00:00
|
|
|
VpConst, VpMisc, VpBase, VpBaseDS, VpData;
|
2022-08-22 21:01:27 +00:00
|
|
|
|
2022-08-30 21:00:26 +00:00
|
|
|
type
|
|
|
|
TVpGanttViewOption = (
|
2022-08-31 21:43:14 +00:00
|
|
|
gvoActiveDate, gvoHorizGrid, gvoVertGrid, gvoWeekends, gvoHolidays
|
2022-08-30 21:00:26 +00:00
|
|
|
);
|
|
|
|
TVpGanttViewOptions = set of TVpGanttViewOption;
|
|
|
|
|
2022-09-01 09:42:04 +00:00
|
|
|
TVpGanttSpecialDayMode = (sdmColumn, sdmHeader);
|
|
|
|
|
2022-08-30 21:00:26 +00:00
|
|
|
const
|
2022-09-19 21:42:45 +00:00
|
|
|
DEFAULT_GANTTVIEW_OPTIONS = [
|
2022-08-31 21:43:14 +00:00
|
|
|
gvoActiveDate, gvoHorizGrid, gvoVertGrid, gvoWeekends, gvoHolidays
|
2022-08-30 21:00:26 +00:00
|
|
|
];
|
|
|
|
|
2022-08-22 21:01:27 +00:00
|
|
|
type
|
2022-08-23 22:38:13 +00:00
|
|
|
TVpGanttView = class;
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
TVpGanttEventRec = record
|
|
|
|
Event: TVpEvent;
|
|
|
|
Caption: String;
|
2022-10-12 16:40:15 +00:00
|
|
|
StartTime, EndTime: TDateTime;
|
2022-08-26 22:35:42 +00:00
|
|
|
HeadRect: TRect;
|
|
|
|
EventRect: TRect;
|
|
|
|
end;
|
2022-10-12 16:40:15 +00:00
|
|
|
PVpGanttEventRec = ^TVpGanttEventRec;
|
|
|
|
|
|
|
|
TVpGanttEventList = class(TFPList)
|
|
|
|
private
|
|
|
|
FStartDate, FEndDate: TDateTime;
|
|
|
|
function GetItem(AIndex: Integer): PVpGanttEventRec;
|
|
|
|
procedure SetItem(AIndex: Integer; AItem: PVpGanttEventRec);
|
|
|
|
protected
|
|
|
|
procedure ClipDates(AEventRec: PVpGanttEventRec);
|
2023-10-08 22:41:21 +00:00
|
|
|
function FindFirstRecurrence(AEvent: TVpEvent; out AStart, AEnd: TDateTime): Boolean;
|
|
|
|
function FindNextRecurrence(AEvent: TVpEvent; var AStart, AEnd: TDateTime): Boolean;
|
2022-10-12 16:40:15 +00:00
|
|
|
public
|
|
|
|
constructor Create(AStartDate, AEndDate: TDateTime);
|
|
|
|
destructor Destroy; override;
|
|
|
|
function AddSingleEvent(AEvent: TVpEvent): PVpGanttEventRec;
|
|
|
|
procedure AddRecurringEvents(AEvent: TVpEvent);
|
|
|
|
procedure Clear;
|
|
|
|
procedure Delete(AIndex: Integer);
|
|
|
|
property Items[AIndex: Integer]: PVpGanttEventRec read GetItem write SetItem; default;
|
|
|
|
end;
|
2022-08-26 22:35:42 +00:00
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
TVpGanttHourRec = record
|
|
|
|
Hour: Integer;
|
|
|
|
Date: TDateTime;
|
|
|
|
Rect: TRect;
|
|
|
|
end;
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
TVpGanttDayRec = record
|
|
|
|
Date: TDateTime;
|
|
|
|
Rect: TRect;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
TVpGanttWeekRec = record
|
|
|
|
WeekNo: Integer;
|
|
|
|
Date: TDateTime;
|
|
|
|
Rect: TRect;
|
|
|
|
end;
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
TVpGanttMonthRec = record
|
|
|
|
Date: TDateTime;
|
|
|
|
Rect: TRect;
|
|
|
|
end;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
TVpGanttHeaderAttributes = class(TPersistent)
|
|
|
|
private
|
|
|
|
FGanttView: TVpGanttView;
|
|
|
|
FColor: TColor;
|
|
|
|
procedure SetColor(AValue: TColor);
|
|
|
|
protected
|
|
|
|
procedure UpdateGanttView;
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TVpGanttView); virtual;
|
|
|
|
published
|
2022-09-05 09:27:54 +00:00
|
|
|
property Color: TColor read FColor write SetColor default DEFAULT_HEADERCOLOR;
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
TVpGanttRowHeaderAttributes = class(TVpGanttHeaderAttributes)
|
|
|
|
private
|
2022-09-05 09:27:54 +00:00
|
|
|
FEventFont: TVpFont;
|
|
|
|
procedure SetEventFont(AValue: TVpFont);
|
2022-08-23 22:38:13 +00:00
|
|
|
protected
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TVpGanttView); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
published
|
2022-09-05 09:27:54 +00:00
|
|
|
property EventFont: TVpFont read FEventFont write SetEventFont;
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
TVpGanttColHeaderKind = (gchMonth, gchWeek, gchDay, gchHour);
|
2022-09-21 22:50:43 +00:00
|
|
|
TVpGanttColHeaderKinds = set of TVpGanttColHeaderKind;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
TVpGanttColHeaderAttributes = class(TVpGanttHeaderAttributes)
|
|
|
|
private
|
2023-10-08 22:41:21 +00:00
|
|
|
FHourFont: TVpFont;
|
2022-09-05 09:27:54 +00:00
|
|
|
FDayFont: TVpFont;
|
|
|
|
FMonthFont: TVpFont;
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekFont: TVpFont;
|
|
|
|
FVisible: TVpGanttColHeaderKinds;
|
2022-09-05 09:27:54 +00:00
|
|
|
procedure SetDayFont(AValue: TVpFont);
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure SetHourfont(AValue: TVpFont);
|
2022-09-05 09:27:54 +00:00
|
|
|
procedure SetMonthFont(AValue: TVpFont);
|
2022-09-21 22:50:43 +00:00
|
|
|
procedure SetVisible(AValue: TVpGanttColHeaderKinds);
|
|
|
|
procedure SetWeekFont(AValue: TVpFont);
|
2022-08-23 22:38:13 +00:00
|
|
|
public
|
|
|
|
constructor Create(AOwner: TVpGanttView); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
published
|
2022-09-05 09:27:54 +00:00
|
|
|
property DayFont: TVpFont read FDayFont write SetDayFont;
|
2023-10-08 22:41:21 +00:00
|
|
|
property HourFont: TVpFont read FHourFont write SetHourFont;
|
2022-09-05 09:27:54 +00:00
|
|
|
property MonthFont: TVpFont read FMonthFont write SetMonthFont;
|
2022-09-21 22:50:43 +00:00
|
|
|
property Visible: TVpGanttColHeaderKinds read FVisible write SetVisible default [gchMonth, gchDay];
|
|
|
|
property WeekFont: TVpFont read FWeekFont write SetWeekFont;
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-22 21:01:27 +00:00
|
|
|
TVpGanttView = class(TVpLinkableControl)
|
|
|
|
private
|
2022-08-29 15:09:38 +00:00
|
|
|
FActiveCol: Integer; // Selected column
|
|
|
|
FActiveRow: Integer; // Selected row
|
2022-08-28 23:41:45 +00:00
|
|
|
FActiveEvent: TVpEvent; // Selected event
|
|
|
|
FActiveDate: TDateTime; // Selected date
|
2022-09-08 21:00:40 +00:00
|
|
|
FFirstDate: TDateTime; // Date of the first event in the resource
|
|
|
|
FLastDate: TDateTime; // Date of the last event in the resource
|
2022-09-14 21:24:33 +00:00
|
|
|
FStartDate: TDateTime; // Date of the first event to be displayed/printed (0 = first event ever)
|
|
|
|
FEndDate: TDateTime; // Date of the last event to be displayed/printed (0 = last event ever)
|
|
|
|
FRealStartDate: TDate; // Date of the first event to be displayed/printed (0 replaced)
|
|
|
|
FRealEndDate: TDate; // Date of the last event to be displayed/printed (0 repalaced)
|
2022-08-23 22:38:13 +00:00
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
FLeftCol: Integer; // Index of the left-most day column
|
|
|
|
FTopRow: Integer; // Index of the top-most event row
|
2022-08-27 10:31:14 +00:00
|
|
|
FVisibleCols: Integer;
|
|
|
|
FVisibleRows: Integer;
|
|
|
|
FRowCount: Integer;
|
|
|
|
FColCount: Integer;
|
|
|
|
FScrollBars: TScrollStyle;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
FInLinkHandler: Boolean;
|
|
|
|
FLoaded: Boolean;
|
|
|
|
FPainting: Boolean;
|
2022-08-28 23:41:45 +00:00
|
|
|
FMouseDown: Boolean;
|
|
|
|
FMouseDownPoint: TPoint;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
|
|
|
FColWidth: Integer;
|
|
|
|
FFixedColWidth: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
FRowHeight: Integer;
|
|
|
|
FMonthColHeaderHeight: Integer;
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekColHeaderHeight: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
FDayColHeaderHeight: Integer;
|
2023-10-08 22:41:21 +00:00
|
|
|
FHourColHeaderHeight: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
FTotalColHeaderHeight: Integer;
|
|
|
|
FTextMargin: Integer;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
|
|
|
FColor: TColor;
|
2022-08-31 21:43:14 +00:00
|
|
|
FHolidayColor: TColor;
|
2022-08-23 22:38:13 +00:00
|
|
|
FLineColor: TColor;
|
2022-08-29 22:00:20 +00:00
|
|
|
FWeekendColor: TColor;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
|
|
|
FColHeaderAttributes: TVpGanttColHeaderAttributes;
|
|
|
|
FRowHeaderAttributes: TVpGanttRowHeaderAttributes;
|
|
|
|
|
2022-09-01 12:12:52 +00:00
|
|
|
FComponentHint: TTranslateString;
|
2023-10-08 22:41:21 +00:00
|
|
|
FDateFormat: array[0..3] of String;
|
2022-08-28 23:41:45 +00:00
|
|
|
FDrawingStyle: TVpDrawingStyle;
|
|
|
|
FDefaultPopup: TPopupMenu;
|
|
|
|
FExternalPopup: TPopupMenu;
|
2022-09-01 12:12:52 +00:00
|
|
|
FHintMode: TVpHintMode;
|
|
|
|
FMouseEvent: TVpEvent;
|
2022-08-30 21:00:26 +00:00
|
|
|
FOptions: TVpGanttViewOptions;
|
2022-09-01 09:42:04 +00:00
|
|
|
FSpecialDayMode: TVpGanttSpecialDayMode;
|
2022-09-01 12:12:52 +00:00
|
|
|
FTimeFormat: TVpTimeFormat;
|
2022-09-19 21:42:45 +00:00
|
|
|
FWeekStartsOn: TVpDayType;
|
2022-08-28 23:41:45 +00:00
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
FStartHour: TVpHours;
|
|
|
|
FEndHour: TVpHours;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
FOnAddEvent: TVpOnAddNewEvent;
|
2022-08-29 15:09:38 +00:00
|
|
|
FOnDeletingEvent: TVpOnDeletingEvent;
|
2022-08-31 21:43:14 +00:00
|
|
|
FOnHoliday: TVpHolidayEvent;
|
2022-08-28 23:41:45 +00:00
|
|
|
FOnModifyEvent: TVpOnModifyEvent;
|
|
|
|
FOwnerEditEvent: TVpEditEvent;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
function GetDateFormat(AIndex: Integer): String;
|
2022-08-28 18:17:04 +00:00
|
|
|
function GetDayRec(AIndex: Integer): TVpGanttDayRec;
|
2022-10-12 16:40:15 +00:00
|
|
|
function GetEventRec(AIndex: Integer): PVpGanttEventRec;
|
2023-10-08 22:41:21 +00:00
|
|
|
function GetHourRec(AIndex: Integer): TVpGanttHourRec;
|
2022-08-28 18:17:04 +00:00
|
|
|
function GetMonthRec(AIndex: Integer): TVpGanttMonthRec;
|
|
|
|
function GetNumDays: Integer;
|
|
|
|
function GetNumEvents: Integer;
|
2023-10-08 22:41:21 +00:00
|
|
|
function GetNumHours: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
function GetNumMonths: Integer;
|
2022-09-21 22:50:43 +00:00
|
|
|
function GetNumWeeks: Integer;
|
|
|
|
function GetWeekRec(AIndex: Integer): TVpGanttWeekRec;
|
2022-08-26 22:35:42 +00:00
|
|
|
function IsStoredColWidth: Boolean;
|
|
|
|
function IsStoredDateFormat(AIndex: Integer): Boolean;
|
2023-10-08 22:41:21 +00:00
|
|
|
function IsStoredEndHour: Boolean;
|
|
|
|
function IsStoredStartHour: Boolean;
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure SetActiveCol(AValue: Integer);
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure SetActiveDate(AValue: TDateTime);
|
2022-08-29 15:09:38 +00:00
|
|
|
procedure SetActiveEvent(AValue: TVpEvent);
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure SetActiveRow(AValue: Integer);
|
2022-08-23 22:38:13 +00:00
|
|
|
procedure SetColor(Value: TColor); reintroduce;
|
|
|
|
procedure SetColWidth(AValue: Integer);
|
2022-08-26 22:35:42 +00:00
|
|
|
procedure SetDateFormat(AIndex: Integer; AValue: String);
|
2022-08-23 22:38:13 +00:00
|
|
|
procedure SetDrawingStyle(AValue: TVpDrawingStyle);
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure SetEndHour(AValue: TVpHours);
|
2022-08-23 22:38:13 +00:00
|
|
|
procedure SetFixedColWidth(AValue: Integer);
|
2022-08-31 21:43:14 +00:00
|
|
|
procedure SetHolidayColor(AValue: TColor);
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure SetLeftCol(AValue: Integer);
|
|
|
|
procedure SetLineColor(AValue: TColor);
|
2022-08-30 21:00:26 +00:00
|
|
|
procedure SetOptions(AValue: TVpGanttViewOptions);
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure SetPopupMenu(AValue: TPopupMenu);
|
2022-09-01 09:42:04 +00:00
|
|
|
procedure SetSpecialDayMode(AValue: TVpGanttSpecialDayMode);
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure SetStartHour(AValue: TVpHours);
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure SetTextMargin(AValue: Integer);
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure SetTopRow(AValue: Integer);
|
2022-08-29 22:00:20 +00:00
|
|
|
procedure SetWeekendColor(AValue: TColor);
|
2022-09-19 21:42:45 +00:00
|
|
|
procedure SetWeekStartsOn(Value: TVpDayType);
|
2022-08-22 21:01:27 +00:00
|
|
|
|
|
|
|
protected
|
2022-08-26 22:35:42 +00:00
|
|
|
// Needed by the painter
|
2022-10-12 16:40:15 +00:00
|
|
|
FEventRecords: TVpGanttEventList;
|
2023-10-08 22:41:21 +00:00
|
|
|
FHourRecords: array of TVpGanttHourRec;
|
2022-08-26 22:35:42 +00:00
|
|
|
FDayRecords: array of TVpGanttDayRec;
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekRecords: array of TVpGanttWeekRec;
|
2022-08-26 22:35:42 +00:00
|
|
|
FMonthRecords: array of TVpGanttMonthRec;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
{ internal methods }
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure CalcColHeaderHeight;
|
2023-10-08 22:41:21 +00:00
|
|
|
function CalcDaysWidth(ANumDays: Integer): Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure CalcRowHeight;
|
2023-10-08 22:41:21 +00:00
|
|
|
function ColToDateIndex(ACol: Integer): Integer;
|
2022-08-29 15:09:38 +00:00
|
|
|
function GetColAtCoord(X: Integer): Integer;
|
2022-08-29 21:25:03 +00:00
|
|
|
function GetDateOfCol(ACol: Integer): TDateTime;
|
2022-08-28 23:41:45 +00:00
|
|
|
function GetDateTimeAtCoord(X: Integer): TDateTime;
|
|
|
|
function GetEventAtCoord(X, Y: Integer): TVpEvent;
|
2022-08-29 21:25:03 +00:00
|
|
|
function GetEventOfRow(ARow: Integer): TVpEvent;
|
2022-09-14 21:24:33 +00:00
|
|
|
procedure GetRealEventDateRange(out AStartDate, AEndDate: TDate);
|
2022-08-29 15:09:38 +00:00
|
|
|
function GetRowAtCoord(Y: Integer): Integer;
|
2022-08-29 18:34:35 +00:00
|
|
|
function GetRowOfEvent(AEvent: TVpEvent): Integer;
|
2022-09-14 21:24:33 +00:00
|
|
|
procedure GetEventDateRange(out AFirstDate, ALastDate: TDate);
|
|
|
|
function IsEventOnDate(AEvent: TVpEvent; ADate: TDate): Boolean;
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure Hookup;
|
|
|
|
procedure Populate;
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure PopulateDayRecords;
|
|
|
|
procedure PopulateEventRecords;
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure PopulateHourRecords;
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure PopulateMonthRecords;
|
2022-09-21 22:50:43 +00:00
|
|
|
procedure PopulateWeekRecords;
|
2022-08-29 15:09:38 +00:00
|
|
|
procedure ScrollDateIntoView(ADate: TDateTime);
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure ScrollHorizontal(ANumCols: Integer);
|
2022-08-29 18:34:35 +00:00
|
|
|
procedure ScrollRowIntoView(ARow: Integer);
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure ScrollVertical(ANumRows: Integer);
|
|
|
|
procedure SetHScrollPos;
|
|
|
|
procedure SetVScrollPos;
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure SpawnEventEditDialog(IsNewEvent: Boolean);
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
{ inherited methods }
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure CreateParams(var AParams: TCreateParams); override;
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure DblClick; override;
|
2022-08-27 10:31:14 +00:00
|
|
|
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
|
|
|
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
|
2022-08-29 18:34:35 +00:00
|
|
|
procedure DoOnResize; override;
|
2022-08-23 22:38:13 +00:00
|
|
|
class function GetControlClassDefaultSize: TSize; override;
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure Loaded; override;
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
|
2022-09-01 12:12:52 +00:00
|
|
|
procedure MouseEnter; override;
|
|
|
|
procedure MouseLeave; override;
|
|
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure Paint; override;
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure WMHScroll(var Msg: TLMHScroll); message LM_HSCROLL;
|
|
|
|
procedure WMVScroll(var Msg: TLMVScroll); message LM_VSCROLL;
|
2022-08-22 21:01:27 +00:00
|
|
|
|
2022-09-02 09:39:21 +00:00
|
|
|
{ LCL scaling }
|
|
|
|
{$IF VP_LCL_SCALING <> 0}
|
|
|
|
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
|
|
|
|
const AXProportion, AYProportion: Double); override;
|
|
|
|
{$IFEND}
|
|
|
|
|
2022-09-01 12:12:52 +00:00
|
|
|
{ Hints }
|
|
|
|
procedure ShowHintWindow(APoint: TPoint; AEvent: TVpEvent);
|
|
|
|
procedure HideHintWindow;
|
|
|
|
procedure SetHint(const AValue: TTranslateString); override;
|
|
|
|
procedure SetHintMode(const AValue: TVpHintMode);
|
|
|
|
|
2022-08-29 18:34:35 +00:00
|
|
|
{ Popup }
|
|
|
|
function GetPopupMenu: TPopupMenu; override;
|
|
|
|
procedure InitializeDefaultPopup;
|
|
|
|
procedure PopupAddEvent(Sender: TObject);
|
|
|
|
procedure PopupDeleteEvent(Sender: TObject);
|
|
|
|
procedure PopupEditEvent(Sender: TObject);
|
2022-08-31 20:56:36 +00:00
|
|
|
procedure UpdatePopupMenuState;
|
2022-08-29 18:34:35 +00:00
|
|
|
|
2022-08-22 21:01:27 +00:00
|
|
|
public
|
2022-08-22 22:30:13 +00:00
|
|
|
constructor Create(AOwner: TComponent); override;
|
2022-08-23 22:38:13 +00:00
|
|
|
destructor Destroy; override;
|
2022-09-01 12:12:52 +00:00
|
|
|
|
|
|
|
function BuildEventString(AEvent: TVpEvent; UseAsHint: Boolean): String;
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure DeleteActiveEvent(Prompt: Boolean);
|
2022-09-05 18:01:40 +00:00
|
|
|
function GetControlType: TVpItemType; override;
|
2023-10-08 22:41:21 +00:00
|
|
|
function HourMode: Boolean;
|
|
|
|
function HoursPerDay: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure Init;
|
2022-08-31 21:43:14 +00:00
|
|
|
function IsHoliday(ADate: TDate; out AHolidayName: String): Boolean;
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure LoadLanguage;
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure LinkHandler(Sender: TComponent;
|
|
|
|
NotificationType: TVpNotificationType; const Value: Variant); override;
|
|
|
|
procedure RenderToCanvas(RenderCanvas: TCanvas; RenderIn: TRect;
|
|
|
|
Angle: TVpRotationAngle; Scale: Extended; RenderDate: TDateTime;
|
|
|
|
StartLine, StopLine: Integer; UseGran: TVpGranularity;
|
|
|
|
DisplayOnly: Boolean); override;
|
2022-09-14 21:24:33 +00:00
|
|
|
procedure SetDateLimits(AStartDate, AEndDate: TDateTime);
|
2022-08-22 22:30:13 +00:00
|
|
|
|
2022-09-02 09:39:21 +00:00
|
|
|
{$IF VP_LCL_SCALING = 2}
|
|
|
|
procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); override;
|
|
|
|
procedure ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); override;
|
|
|
|
{$ELSE}
|
|
|
|
{$IF VP_LCL_SCALING = 1}
|
|
|
|
procedure ScaleFontsPPI(const AProportion: Double); override;
|
|
|
|
{$ENDIF}
|
|
|
|
{$ENDIF}
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
// Methods/properties used by painter. Not meant to be called by user.
|
2022-09-07 22:20:13 +00:00
|
|
|
function CalcVisibleCols(AWidth, AFixedColWidth, AColWidth: Integer): Integer;
|
|
|
|
function CalcVisibleRows(AHeight, AHeaderHeight, ARowHeight: Integer): Integer;
|
2022-09-14 21:24:33 +00:00
|
|
|
property FirstDate: TDateTime read FFirstDate;
|
|
|
|
property LastDate: TDateTime read FLastDate;
|
|
|
|
property RealStartDate: TDateTime read FRealStartDate;
|
|
|
|
property RealEndDate: TDateTime read FRealEndDate;
|
2022-08-28 20:32:17 +00:00
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
property ActiveCol: Integer read FActiveCol write SetActiveCol;
|
2022-08-29 15:09:38 +00:00
|
|
|
property ActiveEvent: TVpEvent read FActiveEvent write SetActiveEvent;
|
2022-08-28 23:41:45 +00:00
|
|
|
property ActiveDate: TDateTime read FActiveDate write SetActiveDate;
|
2022-08-29 21:25:03 +00:00
|
|
|
property ActiveRow: Integer read FActiveRow write SetActiveRow;
|
2022-08-26 22:35:42 +00:00
|
|
|
property StartDate: TDateTime read FStartDate write FStartDate;
|
|
|
|
property EndDate: TDateTime read FEndDate write FEndDate;
|
2022-08-27 10:31:14 +00:00
|
|
|
property ColCount: Integer read FColCount write FColCount;
|
|
|
|
property RowCount: Integer read FRowCount write FRowCount;
|
|
|
|
property VisibleCols: Integer read FVisibleCols write FVisibleCols;
|
|
|
|
property VisibleRows: Integer read FVisibleRows write FVisibleRows;
|
|
|
|
property LeftCol: Integer read FLeftCol write SetLeftCol;
|
|
|
|
property TopRow: Integer read FTopRow write SetTopRow;
|
2022-08-22 21:01:27 +00:00
|
|
|
|
2022-10-07 17:45:28 +00:00
|
|
|
// Unscaled dimensions
|
2022-08-28 18:17:04 +00:00
|
|
|
property RowHeight: Integer read FRowHeight;
|
|
|
|
property DayColHeaderHeight: Integer read FDayColHeaderHeight;
|
2023-10-08 22:41:21 +00:00
|
|
|
property HourColHeaderHeight: Integer read FHourColHeaderHeight;
|
2022-08-28 18:17:04 +00:00
|
|
|
property MonthColHeaderHeight: Integer read FMonthColHeaderHeight;
|
|
|
|
property TotalColHeaderHeight: Integer read FTotalColHeaderHeight;
|
|
|
|
|
|
|
|
property DayRecords[AIndex: Integer]: TVpGanttDayRec read GetDayRec;
|
2022-10-12 16:40:15 +00:00
|
|
|
property EventRecords[AIndex: Integer]: PVpGanttEventRec read GetEventRec;
|
2023-10-08 22:41:21 +00:00
|
|
|
property HourRecords[AIndex: Integer]: TVPGanttHourRec read GetHourRec;
|
2022-08-28 18:17:04 +00:00
|
|
|
property MonthRecords[AIndex: Integer]: TVpGanttMonthRec read GetMonthRec;
|
2022-09-21 22:50:43 +00:00
|
|
|
property WeekRecords[AIndex: Integer]: TVpGanttWeekRec read GetWeekRec;
|
2022-08-28 18:17:04 +00:00
|
|
|
|
|
|
|
property NumDays: Integer read GetNumDays;
|
|
|
|
property NumEvents: Integer read GetNumEvents;
|
2023-10-08 22:41:21 +00:00
|
|
|
property NumHours: Integer read GetNumHours;
|
2022-08-28 18:17:04 +00:00
|
|
|
property NumMonths: Integer read GetNumMonths;
|
2022-09-21 22:50:43 +00:00
|
|
|
property NumWeeks: Integer read GetNumWeeks;
|
2022-08-29 15:09:38 +00:00
|
|
|
|
2022-08-22 21:01:27 +00:00
|
|
|
published
|
2022-08-28 23:41:45 +00:00
|
|
|
// inherited properties
|
2022-08-26 22:35:42 +00:00
|
|
|
property Align;
|
|
|
|
property Anchors;
|
|
|
|
property BorderSpacing;
|
2022-08-28 23:41:45 +00:00
|
|
|
property ReadOnly;
|
|
|
|
// new properties
|
2022-08-23 22:38:13 +00:00
|
|
|
property ColHeaderAttributes: TVpGanttColHeaderAttributes read FColHeaderAttributes write FColHeaderAttributes;
|
|
|
|
property Color: TColor read FColor write SetColor default DEFAULT_COLOR;
|
2022-08-26 22:35:42 +00:00
|
|
|
property ColWidth: Integer read FColWidth write SetColWidth stored IsStoredColWidth;
|
|
|
|
property DayFormat: String index 0 read GetDateFormat write SetDateFormat stored IsStoredDateFormat;
|
2023-10-08 22:41:21 +00:00
|
|
|
property DayFormat_HourMode: String index 3 read GetDateFormat write SetDateFormat stored IsStoredDateFormat;
|
2022-08-23 22:38:13 +00:00
|
|
|
property DrawingStyle: TVpDrawingStyle read FDrawingStyle write SetDrawingStyle default ds3d;
|
2023-10-08 22:41:21 +00:00
|
|
|
property EndHour: TVpHours read FEndHour write SetEndHour stored IsStoredEndHour;
|
2022-08-23 22:38:13 +00:00
|
|
|
property FixedColWidth: Integer read FFixedColWidth write SetFixedColWidth default 120;
|
2022-09-01 12:12:52 +00:00
|
|
|
property HintMode: TVpHintMode read FHintMode write SetHintMode default hmPlannerHint;
|
2022-08-31 21:43:14 +00:00
|
|
|
property HolidayColor: TColor read FHolidayColor write SetHolidayColor default HOLIDAY_COLOR;
|
2022-08-23 22:38:13 +00:00
|
|
|
property LineColor: TColor read FLineColor write SetLineColor default DEFAULT_LINECOLOR;
|
2022-08-26 22:35:42 +00:00
|
|
|
property MonthFormat: String index 1 read GetDateFormat write SetDateFormat stored IsStoredDateFormat;
|
|
|
|
property MonthFormat_short: String index 2 read GetDateFormat write SetDateFormat stored IsStoredDateFormat;
|
2022-09-19 21:42:45 +00:00
|
|
|
property Options: TVpGanttViewOptions read FOptions write SetOptions default DEFAULT_GANTTVIEW_OPTIONS;
|
2022-08-28 23:41:45 +00:00
|
|
|
property PopupMenu: TPopupMenu read GetPopupMenu write SetPopupMenu;
|
2022-08-23 22:38:13 +00:00
|
|
|
property RowHeaderAttributes: TVpGanttRowHeaderAttributes read FRowHeaderAttributes write FRowHeaderAttributes;
|
2022-09-01 09:42:04 +00:00
|
|
|
property SpecialDayMode: TVpGanttSpecialDayMode read FSpecialDayMode write SetSpecialDayMode default sdmColumn;
|
2023-10-08 22:41:21 +00:00
|
|
|
property StartHour: TVpHours read FStartHour write SetStartHour stored IsStoredStartHour;
|
2022-08-28 18:17:04 +00:00
|
|
|
property TextMargin: Integer read FTextMargin write SetTextMargin default 2;
|
2022-09-01 12:12:52 +00:00
|
|
|
property TimeFormat: TVpTimeFormat read FTimeFormat write FTimeFormat default tf12Hour;
|
2022-08-29 22:00:20 +00:00
|
|
|
property WeekendColor: TColor read FWeekendColor write SetWeekendColor default WEEKEND_COLOR;
|
2022-09-19 21:42:45 +00:00
|
|
|
property WeekStartsOn: TVpDayType read FWeekStartsOn write SetWeekStartsOn default dtSunday;
|
2022-08-28 23:41:45 +00:00
|
|
|
// inherited events
|
|
|
|
property OnClick;
|
|
|
|
// new events
|
|
|
|
property OnAddEvent: TVpOnAddNewEvent read FOnAddEvent write FOnAddEvent;
|
2022-08-29 15:09:38 +00:00
|
|
|
property OnDeletingEvent: TVpOnDeletingEvent read FOnDeletingEvent write FOnDeletingEvent;
|
2022-09-19 21:32:43 +00:00
|
|
|
property OnHoliday: TVpHolidayEvent read FOnHoliday write FOnHoliday; deprecated 'Use TControlLink.OnHoliday instead';
|
2022-08-28 23:41:45 +00:00
|
|
|
property OnModifyEvent: TVpOnModifyEvent read FOnModifyEvent write FOnModifyEvent;
|
|
|
|
property OwnerEditEvent: TVpEditEvent read FOwnerEditEvent write FOwnerEditEvent;
|
2022-08-22 21:01:27 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
uses
|
2022-08-28 23:41:45 +00:00
|
|
|
DateUtils, Math, Dialogs,
|
|
|
|
VpSR, VpGanttViewPainter, VpEvntEditDlg;
|
2022-08-22 22:30:13 +00:00
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
const
|
|
|
|
DEFAULT_DAYFORMAT = 'd';
|
|
|
|
DEFAULT_MONTHFORMAT = 'mmmm yyyy';
|
|
|
|
DEFAULT_MONTHFORMAT_SHORT = 'mmm yyyy';
|
2023-10-08 22:41:21 +00:00
|
|
|
DEFAULT_DAYFORMAT_HOURMODE = 'dddddd'; // long date format
|
2022-08-26 22:35:42 +00:00
|
|
|
DEFAULT_COLWIDTH = 20;
|
2023-10-08 22:41:21 +00:00
|
|
|
DEFAULT_START_HOUR = h_07;
|
|
|
|
DEFAULT_END_HOUR = h_20;
|
2022-08-26 22:35:42 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
{ Compare function for sorting event records: Compares the start times of two events.
|
|
|
|
If the times are equal (within 1 seconds) then end times are compared.
|
|
|
|
The function is used by TVpGanttEventList.Sort. }
|
|
|
|
function CompareEventRecs(Item1, Item2: Pointer): Integer;
|
|
|
|
var
|
|
|
|
eventRec1, eventRec2: PVpGanttEventRec;
|
|
|
|
begin
|
|
|
|
eventRec1 := PVpGanttEventRec(Item1);
|
|
|
|
eventRec2 := PVpGanttEventRec(Item2);
|
|
|
|
if SameValue(eventRec1^.StartTime, eventRec2^.StartTime, TIME_EPS) then
|
|
|
|
Result := CompareValue(eventRec1^.EndTime, eventRec2^.EndTime)
|
|
|
|
else
|
|
|
|
Result := CompareValue(eventRec1^.StartTime, eventRec2^.StartTime);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{******************************************************************************}
|
|
|
|
{ TVpGanttEventList }
|
|
|
|
{******************************************************************************}
|
|
|
|
constructor TVpGanttEventList.Create(AStartDate, AEndDate: TDateTime);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FStartDate := AStartDate;
|
|
|
|
FEndDate := AEndDate;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TVpGanttEventList.Destroy;
|
|
|
|
begin
|
|
|
|
Clear;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttEventList.AddRecurringEvents(AEvent: TVpEvent);
|
|
|
|
var
|
|
|
|
eventRec: PVpGanttEventRec;
|
|
|
|
dt1, dt2: TDateTime;
|
|
|
|
begin
|
|
|
|
if AEvent.AllDayEvent then
|
|
|
|
begin
|
|
|
|
dt1 := DatePart(AEvent.StartTime);
|
|
|
|
dt2 := DatePart(AEvent.EndTime) + 1;
|
|
|
|
if frac(AEvent.EndTime) = 0 then dt2 := dt2 + 1;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
dt1 := AEvent.StartTime;
|
|
|
|
dt2 := AEvent.EndTime;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
if FindFirstRecurrence(AEvent, dt1, dt2) then
|
|
|
|
repeat
|
|
|
|
eventRec := AddSingleEvent(AEvent);
|
|
|
|
eventRec^.StartTime := dt1;
|
|
|
|
eventRec^.EndTime := dt2;
|
|
|
|
until not FindNextRecurrence(AEvent, dt1, dt2);
|
2022-10-12 16:40:15 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TVpGanttEventList.AddSingleEvent(AEvent: TVpEvent): PVpGanttEventRec;
|
2023-10-09 15:47:05 +00:00
|
|
|
const
|
|
|
|
EPS = 1e-9;
|
2022-10-12 16:40:15 +00:00
|
|
|
var
|
|
|
|
eventRec: PVpGanttEventRec;
|
|
|
|
dt1, dt2: TDateTime;
|
|
|
|
begin
|
|
|
|
// Handle the start/end times of all-day events correctly.
|
|
|
|
if AEvent.AllDayEvent then
|
|
|
|
begin
|
|
|
|
dt1 := DatePart(AEvent.StartTime);
|
2023-10-09 15:47:05 +00:00
|
|
|
dt2 := DatePart(AEvent.EndTime) + 1 - EPS;
|
2022-10-12 16:40:15 +00:00
|
|
|
end else
|
|
|
|
begin
|
|
|
|
dt1 := AEvent.StartTime;
|
|
|
|
dt2 := AEvent.EndTime;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Populate the event record
|
|
|
|
New(eventRec);
|
|
|
|
eventRec^ := Default(TVpGanttEventRec);
|
|
|
|
eventRec^.Event := AEvent;
|
|
|
|
eventRec^.Caption := AEvent.Description;
|
|
|
|
eventRec^.StartTime := dt1;
|
|
|
|
eventRec^.EndTime := dt2;
|
|
|
|
eventRec^.HeadRect := Rect(-1, -1, -1, -1);
|
|
|
|
eventRec^.EventRect := Rect(-1, -1, -1, -1);
|
|
|
|
|
|
|
|
ClipDates(eventRec);
|
|
|
|
|
|
|
|
Result := eventRec;
|
|
|
|
|
|
|
|
Add(Result);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttEventList.Clear;
|
|
|
|
var
|
|
|
|
eventRec: PVpGanttEventRec;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
for i := 0 to Count-1 do
|
|
|
|
begin
|
|
|
|
eventRec := GetItem(i);
|
|
|
|
Dispose(eventRec);
|
|
|
|
end;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttEventList.ClipDates(AEventRec: PVpGanttEventRec);
|
|
|
|
begin
|
|
|
|
// The time range of events reaching out of the displayed date range
|
|
|
|
// must be clipped at the edges.
|
|
|
|
if AEventRec^.StartTime < FStartDate then
|
|
|
|
AEventRec^.StartTime := FStartDate;
|
|
|
|
if AEventRec^.EndTime > FEndDate + 1 then
|
|
|
|
AEventRec^.EndTime := FEndDate + 1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttEventList.Delete(AIndex: Integer);
|
|
|
|
var
|
|
|
|
eventRec: PVpGanttEventRec;
|
|
|
|
begin
|
|
|
|
eventRec := GetItem(AIndex);
|
|
|
|
Dispose(eventRec);
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
// Find date/times of first recurrence of the specified event in the gantt view's
|
|
|
|
// date range.
|
|
|
|
function TVpGanttEventList.FindFirstRecurrence(AEvent: TVpEvent;
|
|
|
|
out AStart, AEnd: TDateTime): Boolean;
|
|
|
|
var
|
|
|
|
delta: Double;
|
|
|
|
d: TDateTime;
|
|
|
|
eventYear, eventMonth, eventDay: Word;
|
|
|
|
startYear, startMonth, startDay: Word;
|
|
|
|
begin
|
|
|
|
Result := false;
|
|
|
|
if AEvent.StartTime >= FEndDate then
|
|
|
|
exit;
|
|
|
|
if (AEvent.RepeatRangeEnd > 0) and (AEvent.RepeatRangeEnd <= FStartDate) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
if AEvent.StartTime >= FStartDate then
|
|
|
|
begin
|
|
|
|
AStart := AEvent.StartTime;
|
|
|
|
AEnd := AEvent.EndTime;
|
|
|
|
Result := true;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
DecodeDate(AEvent.StartTime, eventYear, eventMonth, eventDay);
|
|
|
|
DecodeDate(FStartDate, startYear, startMonth, startDay);
|
|
|
|
|
|
|
|
case AEvent.RepeatCode of
|
|
|
|
rtDaily:
|
|
|
|
AStart := DatePart(FStartDate) + TimePart(AEvent.StartTime);
|
|
|
|
rtWeekly:
|
|
|
|
begin
|
|
|
|
delta := AEvent.StartTime - StartOfTheWeek(AEvent.StartTime);
|
|
|
|
AStart := StartOfTheWeek(FStartDate) + delta;
|
|
|
|
end;
|
|
|
|
rtMonthlyByDay:
|
|
|
|
begin
|
|
|
|
delta := AEvent.StartTime - StartOfTheMonth(AEvent.StartTime);
|
|
|
|
AStart := StartOfTheMonth(FStartDate) + delta;
|
|
|
|
end;
|
|
|
|
rtMonthlyByDate:
|
|
|
|
AStart := EncodeDate(startYear, startMonth, eventDay);
|
|
|
|
rtYearlyByDay:
|
|
|
|
begin
|
|
|
|
delta := AEvent.StartTime - StartofTheYear(AEvent.StartTime);
|
|
|
|
AStart := StartOfTheYear(FStartDate) + delta;
|
|
|
|
end;
|
|
|
|
rtYearlyByDate:
|
|
|
|
if not TryEncodeDate(startYear, eventMonth, eventDay, AStart) then
|
|
|
|
AStart := EncodeDate(startYear, 2, 28);
|
|
|
|
rtCustom:
|
|
|
|
begin
|
|
|
|
AStart := trunc((FStartDate - DatePart(AEvent.StartTime)) / AEvent.CustomInterval) * AEvent.CustomInterval + TimePart(AEvent.StartTime);
|
|
|
|
if AStart < FStartdate then
|
|
|
|
AStart := AStart + AEvent.CustomInterval;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
AEnd := AStart + (AEvent.EndTime - AEvent.StartTime);
|
|
|
|
if (AEvent.RepeatRangeEnd > 0) and (AEnd > AEvent.RepeatRangeEnd) then
|
|
|
|
exit;
|
|
|
|
if AStart > FEndDate + 1 then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
Result := true;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Find date/times of next recurrence of the specified event.
|
|
|
|
function TVpGanttEventList.FindNextRecurrence(AEvent: TVpEvent;
|
|
|
|
var AStart, AEnd: TDateTime): Boolean;
|
|
|
|
var
|
|
|
|
delta: Double;
|
|
|
|
begin
|
|
|
|
Result := false;
|
|
|
|
if (AStart >= FEndDate) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
case AEvent.RepeatCode of
|
|
|
|
rtDaily:
|
|
|
|
begin
|
|
|
|
AStart := AStart + 1;
|
|
|
|
AEnd := AEnd + 1;
|
|
|
|
end;
|
|
|
|
rtWeekly:
|
|
|
|
begin
|
|
|
|
AStart := AStart + 7;
|
|
|
|
AEnd := AEnd + 7;
|
|
|
|
end;
|
|
|
|
rtMonthlyByDay:
|
|
|
|
begin
|
|
|
|
delta := DatePart(AStart) - StartOfTheMonth(AStart);
|
|
|
|
AStart := AStart + delta;
|
|
|
|
AEnd := AEnd + delta;
|
|
|
|
end;
|
|
|
|
rtMonthlyByDate:
|
|
|
|
begin
|
|
|
|
AStart := IncMonth(AStart, 1);
|
|
|
|
AEnd := IncMonth(AEnd, 1);
|
|
|
|
end;
|
|
|
|
rtYearlyByDay:
|
|
|
|
begin
|
|
|
|
delta := DatePart(AStart) - StartOfTheYear(AStart);
|
|
|
|
AStart := AStart + delta;
|
|
|
|
AEnd := AEnd + delta;
|
|
|
|
end;
|
|
|
|
rtYearlyByDate:
|
|
|
|
begin
|
|
|
|
AStart := IncYear(AStart, 1);
|
|
|
|
AEnd := IncYear(AEnd, 1);
|
|
|
|
end;
|
|
|
|
rtCustom:
|
|
|
|
begin
|
|
|
|
AStart := AStart + AEvent.CustomInterval;
|
|
|
|
AEnd := AEnd + AEvent.CustomInterval;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (AEvent.RepeatRangeEnd > 0) and (AEnd > AEvent.RepeatRangeEnd) then
|
|
|
|
exit;
|
|
|
|
if AStart > FEndDate + 1 then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
Result := true;
|
|
|
|
end;
|
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
function TVpGanttEventList.GetItem(AIndex: Integer): PVpGanttEventRec;
|
|
|
|
begin
|
|
|
|
Result := PVpGanttEventRec(inherited Items[AIndex]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttEventList.SetItem(AIndex: Integer; AItem: PVpGanttEventRec);
|
|
|
|
begin
|
|
|
|
inherited Items[AIndex] := AItem;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
{******************************************************************************}
|
|
|
|
{ TVpGanttHeaderAttributes }
|
|
|
|
{******************************************************************************}
|
|
|
|
constructor TVpGanttHeaderAttributes.Create(AOwner: TVpGanttView);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
FGanttView := AOwner;
|
2022-09-05 09:27:54 +00:00
|
|
|
FColor := DEFAULT_HEADERCOLOR;
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttHeaderAttributes.SetColor(AValue: TColor);
|
|
|
|
begin
|
|
|
|
if FColor <> AValue then
|
|
|
|
begin
|
|
|
|
FColor := AValue;
|
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttHeaderAttributes.UpdateGanttView;
|
|
|
|
begin
|
|
|
|
if Assigned(FGanttView) then
|
|
|
|
FGanttView.Invalidate;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{******************************************************************************}
|
|
|
|
{ TVpGanttRowHeaderAttributes }
|
|
|
|
{******************************************************************************}
|
|
|
|
constructor TVpGanttRowHeaderAttributes.Create(AOwner: TVpGanttView);
|
|
|
|
begin
|
|
|
|
inherited Create(AOwner);
|
2022-09-05 09:27:54 +00:00
|
|
|
FEventFont := TVpFont.Create(AOwner);
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TVpGanttRowHeaderAttributes.Destroy;
|
|
|
|
begin
|
|
|
|
FEventFont.Free;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2022-09-05 09:27:54 +00:00
|
|
|
procedure TVpGanttRowHeaderAttributes.SetEventFont(AValue: TVpFont);
|
2022-08-23 22:38:13 +00:00
|
|
|
begin
|
|
|
|
if FEventFont <> AValue then
|
|
|
|
begin
|
|
|
|
FEventFont := AValue;
|
2022-09-05 09:27:54 +00:00
|
|
|
FEventFont.Owner := FGanttView;
|
2022-08-23 22:38:13 +00:00
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{******************************************************************************}
|
|
|
|
{ TVpGanttColHeaderAttributes }
|
|
|
|
{******************************************************************************}
|
|
|
|
constructor TVpGanttColHeaderAttributes.Create(AOwner: TVpGanttView);
|
|
|
|
begin
|
|
|
|
inherited Create(AOwner);
|
2023-10-08 22:41:21 +00:00
|
|
|
FHourFont := TVpFont.Create(AOwner);
|
2022-09-05 09:27:54 +00:00
|
|
|
FDayFont := TVpFont.Create(AOwner);
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekFont := TVpFont.Create(AOwner);
|
2022-09-05 09:27:54 +00:00
|
|
|
FMonthFont := TVpFont.Create(AOwner);
|
2022-09-21 22:50:43 +00:00
|
|
|
FVisible := [gchMonth, gchDay];
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TVpGanttColHeaderAttributes.Destroy;
|
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
FHourFont.Free;
|
2022-08-23 22:38:13 +00:00
|
|
|
FDayFont.Free;
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekFont.Free;
|
2022-08-23 22:38:13 +00:00
|
|
|
FMonthFont.Free;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2022-09-05 09:27:54 +00:00
|
|
|
procedure TVpGanttColHeaderAttributes.SetDayFont(AValue: TVpFont);
|
2022-08-23 22:38:13 +00:00
|
|
|
begin
|
|
|
|
if FDayFont <> AValue then
|
|
|
|
begin
|
|
|
|
FDayFont := AValue;
|
2022-09-05 09:27:54 +00:00
|
|
|
FDayFont.Owner := FGanttView;
|
2022-08-23 22:38:13 +00:00
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure TVpGanttColHeaderAttributes.SetHourFont(AValue: TVpFont);
|
|
|
|
begin
|
|
|
|
if FHourFont <> AValue then
|
|
|
|
begin
|
|
|
|
FHourFont := AValue;
|
|
|
|
FHourFont.Owner := FGanttView;
|
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-05 09:27:54 +00:00
|
|
|
procedure TVpGanttColHeaderAttributes.SetMonthFont(AValue: TVpFont);
|
2022-08-23 22:38:13 +00:00
|
|
|
begin
|
|
|
|
if FMonthFont <> AValue then
|
|
|
|
begin
|
|
|
|
FMonthFont := AValue;
|
2022-09-05 09:27:54 +00:00
|
|
|
FMonthFont.Owner := FGanttView;
|
2022-08-23 22:38:13 +00:00
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
procedure TVpGanttColHeaderAttributes.SetVisible(AValue: TVpGanttColHeaderKinds);
|
2023-10-09 15:47:05 +00:00
|
|
|
var
|
|
|
|
HourModeChanged: Boolean;
|
|
|
|
d: TDateTime;
|
2022-09-21 22:50:43 +00:00
|
|
|
begin
|
|
|
|
if FVisible <> AValue then
|
|
|
|
begin
|
2023-10-09 15:47:05 +00:00
|
|
|
HourModeChanged := (gchHour in FVisible) <> (gchHour in AValue);
|
2022-09-21 22:50:43 +00:00
|
|
|
FVisible := AValue;
|
2023-10-09 15:47:05 +00:00
|
|
|
if HourModeChanged then
|
|
|
|
begin
|
|
|
|
d := FGanttView.ActiveDate;
|
|
|
|
FGanttView.Init;
|
|
|
|
FGanttView.FActiveDate := 0; // Enforce execution of SetActiveDate
|
|
|
|
FGanttView.SetActiveDate(d);
|
|
|
|
end;
|
2022-09-21 22:50:43 +00:00
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttColHeaderAttributes.SetWeekFont(AValue: TVpFont);
|
|
|
|
begin
|
|
|
|
if FWeekFont <> AValue then
|
|
|
|
begin
|
|
|
|
FWeekFont := AValue;
|
|
|
|
FWeekFont.Owner := FGanttView;
|
|
|
|
UpdateGanttView;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
|
|
|
|
{******************************************************************************}
|
2023-10-09 15:47:05 +00:00
|
|
|
{ TVpGanttView }
|
2022-08-23 22:38:13 +00:00
|
|
|
{******************************************************************************}
|
2022-08-22 22:30:13 +00:00
|
|
|
constructor TVpGanttView.Create(AOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited;
|
2022-08-29 18:34:35 +00:00
|
|
|
ControlStyle := [csCaptureMouse, csOpaque, csClickEvents, csDoubleClicks];
|
2022-08-22 22:30:13 +00:00
|
|
|
|
|
|
|
FInLinkHandler := false;
|
|
|
|
FLoaded := false;
|
|
|
|
FPainting := false;
|
2022-08-29 15:09:38 +00:00
|
|
|
FMouseDown := false;
|
2022-08-22 22:30:13 +00:00
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
SetActiveDate(Now);
|
2022-08-23 22:38:13 +00:00
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
FColWidth := DEFAULT_COLWIDTH;
|
2022-08-23 22:38:13 +00:00
|
|
|
FFixedColWidth := 120;
|
2022-08-28 18:17:04 +00:00
|
|
|
FTextMargin := 2;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
|
|
|
FColor := DEFAULT_COLOR;
|
|
|
|
FLineColor := DEFAULT_LINECOLOR;
|
2022-08-29 22:00:20 +00:00
|
|
|
FWeekendColor := WEEKEND_COLOR;
|
2022-08-31 21:43:14 +00:00
|
|
|
FHolidayColor := HOLIDAY_COLOR;
|
2022-08-23 22:38:13 +00:00
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
FStartHour := DEFAULT_START_HOUR;
|
|
|
|
FEndHour := DEFAULT_END_HOUR;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
FRowHeaderAttributes := TVpGanttRowHeaderAttributes.Create(self);
|
|
|
|
FColHeaderAttributes := TVpGanttColHeaderAttributes.Create(self);
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
FDateFormat[0] := DEFAULT_DAYFORMAT;
|
|
|
|
FDateFormat[1] := DEFAULT_MONTHFORMAT;
|
|
|
|
FDateFormat[2] := DEFAULT_MONTHFORMAT_SHORT;
|
2023-10-08 22:41:21 +00:00
|
|
|
FDateFormat[3] := DEFAULT_DAYFORMAT_HOURMODE;
|
2022-08-29 18:34:35 +00:00
|
|
|
FDrawingStyle := ds3d;
|
2022-09-19 21:42:45 +00:00
|
|
|
FOptions := DEFAULT_GANTTVIEW_OPTIONS;
|
2022-08-27 10:31:14 +00:00
|
|
|
FScrollBars := ssBoth;
|
2022-09-19 21:42:45 +00:00
|
|
|
FWeekStartsOn := dtSunday;
|
2022-08-30 21:00:26 +00:00
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
// Popup menu
|
2022-08-28 23:41:45 +00:00
|
|
|
FDefaultPopup := TPopupMenu.Create(Self);
|
|
|
|
FDefaultPopup.Name := 'default';
|
|
|
|
InitializeDefaultPopup;
|
|
|
|
Self.PopupMenu := FDefaultPopup;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
// Initial size of the control
|
2022-08-23 22:38:13 +00:00
|
|
|
with GetControlClassDefaultSize do
|
|
|
|
SetInitialBounds(0, 0, CX, CY);
|
2022-09-05 18:01:40 +00:00
|
|
|
|
|
|
|
Hookup;
|
2022-08-23 22:38:13 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TVpGanttView.Destroy;
|
|
|
|
begin
|
2022-10-12 16:40:15 +00:00
|
|
|
FEventRecords.Free;
|
2022-08-23 22:38:13 +00:00
|
|
|
FRowHeaderAttributes.Free;
|
|
|
|
FColHeaderAttributes.Free;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2022-09-01 12:12:52 +00:00
|
|
|
function TVpGanttView.BuildEventString(AEvent: TVpEvent;
|
|
|
|
UseAsHint: Boolean): String;
|
|
|
|
const
|
|
|
|
DATE_FORMAT = 'ddddd';
|
|
|
|
var
|
|
|
|
timeFmt: String;
|
|
|
|
startDateStr, endDateStr: string;
|
|
|
|
s: String;
|
|
|
|
begin
|
|
|
|
Result := '';
|
|
|
|
|
|
|
|
if (AEvent = nil) or (Datastore = nil) or (Datastore.Resource = nil) then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
if UseAsHint then begin
|
|
|
|
{ Usage as hint }
|
|
|
|
startDateStr := FormatDateTime(DATE_FORMAT, AEvent.StartTime);
|
2022-09-14 21:24:33 +00:00
|
|
|
if SameDate(AEvent.StartTime, AEvent.EndTime) then
|
2022-09-01 12:12:52 +00:00
|
|
|
endDateStr := ''
|
|
|
|
else
|
|
|
|
endDateStr := FormatDateTime(DATE_FORMAT, AEvent.EndTime);
|
|
|
|
|
|
|
|
if AEvent.AllDayEvent then
|
|
|
|
begin
|
|
|
|
if endDateStr <> '' then endDateStr := ' - ' + endDateStr;
|
|
|
|
Result := startDateStr + endDatestr + LineEnding + RSAllDay;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
timefmt := GetTimeFormatStr(TimeFormat);
|
|
|
|
startDateStr := startDateStr + ' ' + FormatDateTime(timeFmt, AEvent.StartTime) + ' - ';
|
|
|
|
if endDateStr <> '' then endDateStr := endDateStr + ' ';
|
|
|
|
endDateStr := endDateStr + FormatDateTime(timeFmt, AEvent.EndTime);
|
|
|
|
Result := startDateStr + endDateStr;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Event description
|
|
|
|
Result := Result + LineEnding2 +
|
|
|
|
RSEvent + ':' + LineEnding + AEvent.Description;
|
|
|
|
|
|
|
|
// Event notes
|
|
|
|
if (AEvent.Notes <> '') then begin
|
|
|
|
s := WrapText(AEvent.Notes, MAX_HINT_WIDTH);
|
|
|
|
s := StripLastLineEnding(s);
|
|
|
|
Result := Result + LineEnding2 +
|
|
|
|
RSNotes + ':' + LineEnding + s;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Event location
|
|
|
|
if (AEvent.Location <> '') then
|
|
|
|
Result := Result + LineEnding2 +
|
|
|
|
RSLocation + ':' + LineEnding + AEvent.Location;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
{ Usage as cell text }
|
|
|
|
Result := '';
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.CalcColHeaderHeight;
|
2022-08-30 21:39:47 +00:00
|
|
|
var
|
|
|
|
s: String;
|
2022-09-07 22:20:13 +00:00
|
|
|
h: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-09-07 22:20:13 +00:00
|
|
|
h := GetCanvasTextHeight(Canvas, FColHeaderAttributes.MonthFont);
|
2023-10-08 22:41:21 +00:00
|
|
|
FMonthColHeaderHeight := h;
|
2022-09-21 22:50:43 +00:00
|
|
|
|
|
|
|
h := GetCanvasTextHeight(Canvas, FColHeaderAttributes.WeekFont);
|
2023-10-08 22:41:21 +00:00
|
|
|
FWeekColHeaderHeight := h;
|
2022-08-30 21:39:47 +00:00
|
|
|
|
|
|
|
// A typical date string to measure the text height (line breaks in DayFormat allowed)
|
|
|
|
s := FormatDateTime(DayFormat, EncodeDate(2000, 12, 28));
|
2022-09-07 22:20:13 +00:00
|
|
|
h := GetCanvasTextHeight(Canvas, FColHeaderAttributes.DayFont, s);
|
2023-10-08 22:41:21 +00:00
|
|
|
FDayColHeaderHeight := h;
|
|
|
|
|
|
|
|
h := GetCanvasTextHeight(Canvas, FColHeaderAttributes.HourFont);
|
|
|
|
FHourColHeaderHeight := h;
|
2022-09-21 22:50:43 +00:00
|
|
|
|
|
|
|
FTotalColHeaderHeight := 0;
|
|
|
|
if (gchMonth in FColHeaderAttributes.Visible) then
|
|
|
|
inc(FTotalColHeaderHeight, FMonthColHeaderHeight + FTextMargin);
|
|
|
|
if (gchWeek in FColHeaderAttributes.Visible) then
|
|
|
|
inc(FTotalColHeaderHeight, FWeekColHeaderHeight + FTextMargin);
|
|
|
|
if (gchDay in FColHeaderAttributes.Visible) then
|
|
|
|
inc(FTotalColHeaderHeight, FDayColHeaderHeight + FTextMargin);
|
2023-10-08 22:41:21 +00:00
|
|
|
if (gchHour in FColHeaderAttributes.Visible) then
|
|
|
|
inc(FTotalColHeaderHeight, FHourColHeaderHeight + FTextMargin);
|
2022-09-21 22:50:43 +00:00
|
|
|
if FTotalColHeaderHeight > 0 then
|
|
|
|
inc(FTotalColHeaderHeight, FTextMargin);
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TvpGanttView.CalcDaysWidth(ANumDays: Integer): Integer;
|
|
|
|
begin
|
|
|
|
Result := ANumDays * FColWidth;
|
|
|
|
if HourMode then
|
|
|
|
Result := Result * HoursPerDay;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.CalcRowHeight;
|
2022-09-07 22:20:13 +00:00
|
|
|
var
|
|
|
|
h: Integer;
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-09-07 22:20:13 +00:00
|
|
|
h := GetCanvasTextHeight(Canvas, FRowHeaderAttributes.EventFont);
|
|
|
|
FRowHeight := h + 2 * FTextMargin;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
2022-09-07 22:20:13 +00:00
|
|
|
function TVpGanttView.CalcVisibleCols(AWidth, AFixedColWidth, AColWidth: Integer): Integer;
|
2022-08-28 20:32:17 +00:00
|
|
|
var
|
2022-09-02 09:39:21 +00:00
|
|
|
d: Integer = 0; // Result of div
|
|
|
|
m: Integer = 0; // Result of mod
|
2022-08-28 20:32:17 +00:00
|
|
|
begin
|
2022-09-07 22:20:13 +00:00
|
|
|
if AColWidth <> 0 then
|
|
|
|
begin
|
|
|
|
DivMod(AWidth - AFixedColWidth, AColWidth, d, m);
|
|
|
|
if (m = 0) and (d > 1) then dec(d);
|
|
|
|
Result := d;
|
|
|
|
end else
|
|
|
|
Result := 0;
|
2022-08-28 20:32:17 +00:00
|
|
|
end;
|
|
|
|
|
2022-09-07 22:20:13 +00:00
|
|
|
function TVpGanttView.CalcVisibleRows(AHeight, AHeaderHeight, ARowHeight: Integer): Integer;
|
2022-08-28 20:32:17 +00:00
|
|
|
var
|
2022-09-02 09:39:21 +00:00
|
|
|
d: Integer = 0; // Result of div
|
|
|
|
m: Integer = 0; // Result of mod
|
2022-08-28 20:32:17 +00:00
|
|
|
begin
|
2022-09-07 22:20:13 +00:00
|
|
|
if ARowHeight <> 0 then
|
|
|
|
begin
|
|
|
|
DivMod(AHeight - AHeaderHeight, ARowHeight, d, m);
|
|
|
|
if (m = 0) and (d > 1) then dec(d);
|
|
|
|
Result := d;
|
|
|
|
end else
|
|
|
|
Result := 0;
|
2022-08-28 20:32:17 +00:00
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TVpGanttView.ColToDateIndex(ACol: Integer): Integer;
|
|
|
|
begin
|
|
|
|
if HourMode then
|
|
|
|
Result := ACol div HoursPerDay
|
|
|
|
else
|
|
|
|
Result := ACol;
|
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.CreateParams(var AParams: TCreateParams);
|
|
|
|
begin
|
|
|
|
inherited CreateParams(AParams);
|
|
|
|
with AParams do
|
|
|
|
begin
|
|
|
|
Style := Style or WS_TABSTOP;
|
|
|
|
if FScrollBars in [ssVertical, ssBoth, ssAutoVertical, ssAutoBoth] then
|
|
|
|
Style := Style or WS_VSCROLL;
|
|
|
|
if FScrollBars in [ssHorizontal, ssBoth, ssAutoHorizontal, ssAutoBoth] then
|
|
|
|
Style := Style or WS_HSCROLL;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.DblClick;
|
|
|
|
var
|
|
|
|
dt, startTime, endTime: TDateTime;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
FMouseDown := false;
|
|
|
|
|
|
|
|
// If the mouse was pressed down in the client area, then select the cell.
|
|
|
|
if not Focused then
|
|
|
|
SetFocus;
|
|
|
|
|
|
|
|
if ReadOnly then
|
|
|
|
begin
|
|
|
|
FMouseDownPoint := Point(0, 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
// Is there an event at the clicked cell?
|
2022-08-28 23:41:45 +00:00
|
|
|
FActiveEvent := GetEventAtCoord(FMouseDownPoint.X, FMouseDownPoint.Y);
|
|
|
|
if (FActiveEvent <> nil) then
|
2022-09-14 21:24:33 +00:00
|
|
|
// yes: edit the event
|
2022-08-28 23:41:45 +00:00
|
|
|
SpawnEventEditDialog(False)
|
|
|
|
else
|
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
// no: add a new event
|
2022-08-28 23:41:45 +00:00
|
|
|
dt := GetDateTimeAtCoord(FMouseDownPoint.X);
|
|
|
|
if dt <> NO_DATE then
|
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
startTime := DatePart(dt);
|
2022-10-14 10:44:22 +00:00
|
|
|
endTime := startTime + 1.0 - OneSecond;
|
2022-10-14 21:26:21 +00:00
|
|
|
FActiveEvent := Datastore.Resource.Schedule.AddEvent(
|
2022-08-28 23:41:45 +00:00
|
|
|
Datastore.GetNextID(EventsTableName),
|
|
|
|
startTime,
|
|
|
|
endTime
|
|
|
|
);
|
2022-10-14 10:44:22 +00:00
|
|
|
FActiveEvent.AllDayEvent := true;
|
2022-10-14 21:26:21 +00:00
|
|
|
SetActiveEvent(FActiveEvent);
|
2022-08-28 23:41:45 +00:00
|
|
|
SpawnEventEditDialog(True);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
FMouseDownPoint := Point(0, 0);
|
|
|
|
end;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure TVpGanttView.DeleteActiveEvent(Prompt: Boolean);
|
2022-08-28 23:41:45 +00:00
|
|
|
var
|
|
|
|
DoIt: Boolean;
|
|
|
|
begin
|
|
|
|
if ReadOnly then
|
|
|
|
Exit;
|
|
|
|
if (FActiveEvent <> nil) and (not FActiveEvent.CanEdit) then
|
|
|
|
exit;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
DoIt := not Prompt;
|
2022-08-28 23:41:45 +00:00
|
|
|
|
|
|
|
if FActiveEvent <> nil then begin
|
|
|
|
if Assigned(FOnDeletingEvent) then
|
|
|
|
begin
|
|
|
|
DoIt := true;
|
|
|
|
FOnDeletingEvent(self, FActiveEvent, DoIt);
|
|
|
|
end else
|
2022-08-29 21:25:03 +00:00
|
|
|
if Prompt then
|
2022-08-28 23:41:45 +00:00
|
|
|
DoIt := (MessageDlg(RSConfirmDeleteEvent + LineEnding2 + RSPermanent,
|
|
|
|
mtConfirmation, [mbYes, mbNo], 0) = mrYes);
|
|
|
|
|
|
|
|
if DoIt then begin
|
|
|
|
FActiveEvent.Deleted := true;
|
|
|
|
DataStore.PostEvents;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-02 17:38:26 +00:00
|
|
|
{$IF VP_LCL_SCALING <> 0}
|
2022-09-02 09:39:21 +00:00
|
|
|
procedure TVpGanttView.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
|
|
|
|
const AXProportion, AYProportion: Double);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
|
|
|
|
begin
|
|
|
|
ColWidth := round(ColWidth * AXProportion);
|
|
|
|
FixedColWidth := round(FixedColWidth * AXProportion);
|
2022-09-02 17:38:26 +00:00
|
|
|
TextMargin := round(TextMargin * AXProportion);
|
2022-09-02 09:39:21 +00:00
|
|
|
end;
|
|
|
|
end;
|
2022-09-02 17:38:26 +00:00
|
|
|
{$IFEND}
|
2022-09-02 09:39:21 +00:00
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
function TVpGanttView.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
|
|
|
|
begin
|
|
|
|
Result := inherited DoMouseWheelDown(Shift, MousePos);
|
|
|
|
if not Result then begin
|
|
|
|
if Shift = [] then
|
|
|
|
ScrollVertical(1)
|
|
|
|
else if Shift = [ssCtrl] then
|
|
|
|
ScrollHorizontal(1)
|
|
|
|
else
|
|
|
|
exit;
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TVpGanttView.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
|
|
|
|
begin
|
|
|
|
Result := inherited DoMouseWheelUp(Shift, MousePos);
|
|
|
|
if not Result then begin
|
|
|
|
if Shift = [] then
|
|
|
|
ScrollVertical(-1)
|
|
|
|
else if Shift = [ssCtrl] then
|
|
|
|
ScrollHorizontal(-1)
|
|
|
|
else
|
|
|
|
exit;
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 18:34:35 +00:00
|
|
|
procedure TVpGanttView.DoOnResize;
|
|
|
|
var
|
|
|
|
emptyRows, emptyCols: Integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
2022-10-12 17:07:53 +00:00
|
|
|
if (FRowHeight > 0) and Assigned(FEventRecords) and (FEventRecords.Count > 0) then
|
2022-08-29 18:34:35 +00:00
|
|
|
begin
|
2022-09-07 22:20:13 +00:00
|
|
|
VisibleRows := CalcVisibleRows(ClientHeight, FTotalColHeaderHeight, FRowHeight);
|
2022-10-12 16:40:15 +00:00
|
|
|
emptyRows := VisibleRows - (FEventRecords.Count - FTopRow);
|
2022-08-29 18:34:35 +00:00
|
|
|
if emptyRows > 0 then
|
|
|
|
ScrollVertical(-emptyRows);
|
|
|
|
|
2022-09-07 22:20:13 +00:00
|
|
|
VisibleCols := CalcVisibleCols(ClientWidth, FFixedColWidth, FColWidth);
|
2023-10-08 22:41:21 +00:00
|
|
|
emptyCols := VisibleCols - (ColCount - FLeftCol);
|
2022-08-29 18:34:35 +00:00
|
|
|
if emptyCols > 0 then
|
|
|
|
ScrollHorizontal(-emptyCols);
|
|
|
|
end;
|
|
|
|
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
function TVpGanttView.GetColAtCoord(X: Integer): Integer;
|
|
|
|
begin
|
|
|
|
Result := (X - FixedColWidth) div FColWidth + FLeftCol;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
{ Defines the initial size of the control. }
|
2022-08-23 22:38:13 +00:00
|
|
|
class function TVpGanttView.GetControlClassDefaultSize: TSize;
|
|
|
|
begin
|
|
|
|
Result.CX := 300;
|
|
|
|
Result.CY := 200;
|
2022-08-22 22:30:13 +00:00
|
|
|
end;
|
|
|
|
|
2022-09-05 18:01:40 +00:00
|
|
|
function TVpGanttView.GetControlType: TVpItemType;
|
|
|
|
begin
|
|
|
|
Result := itGanttView;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TVpGanttView.GetDateFormat(AIndex: Integer): String;
|
|
|
|
begin
|
|
|
|
Result := FDateFormat[AIndex];
|
|
|
|
end;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
function TVpGanttView.GetDateOfCol(ACol: Integer): TDateTime;
|
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
Result := FRealStartDate + ColToDateIndex(ACol);
|
2022-08-29 21:25:03 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
function TVpGanttView.GetDateTimeAtCoord(X: Integer): TDateTime;
|
2022-08-29 15:09:38 +00:00
|
|
|
var
|
2023-10-08 22:41:21 +00:00
|
|
|
c: Integer;
|
2023-10-09 22:46:32 +00:00
|
|
|
dayIdx: Integer;
|
|
|
|
dayPos: Integer;
|
|
|
|
dayWidth: Integer;
|
|
|
|
timePart: TTime;
|
2022-08-28 23:41:45 +00:00
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
c := GetColAtCoord(X);
|
|
|
|
if (c >= 0) and (c < ColCount) then
|
2023-10-09 22:46:32 +00:00
|
|
|
begin
|
|
|
|
Result := GetDateOfCol(c);
|
|
|
|
X := X - FFixedColWidth;
|
|
|
|
dayWidth := CalcDaysWidth(1);
|
|
|
|
if HourMode then
|
|
|
|
begin
|
|
|
|
dayIdx := ColToDateIndex(c);
|
|
|
|
dayPos := FDayRecords[dayIdx].Rect.Left - FFixedColWidth;
|
|
|
|
dec(dayPos, FLeftCol * FColWidth);
|
|
|
|
timePart := (((X - dayPos) / dayWidth) * HoursPerDay + ord(FStartHour)) / 24;
|
|
|
|
end else
|
|
|
|
timePart := frac(X / dayWidth);
|
|
|
|
Result := Result + timePart;
|
|
|
|
end
|
2022-08-28 23:41:45 +00:00
|
|
|
else
|
|
|
|
Result := NO_DATE;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
function TVpGanttView.GetDayRec(AIndex: Integer): TVpGanttDayRec;
|
|
|
|
begin
|
|
|
|
Result := FDayRecords[AIndex];
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
function TVpGanttView.GetEventAtCoord(X, Y: Integer): TVpEvent;
|
|
|
|
var
|
|
|
|
idx: Integer;
|
2022-10-12 16:40:15 +00:00
|
|
|
eventRec: PVpGanttEventRec;
|
2022-08-28 23:41:45 +00:00
|
|
|
dt: TDateTime;
|
|
|
|
begin
|
|
|
|
Result := nil;
|
2023-10-08 22:41:21 +00:00
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
dt := GetDateTimeAtCoord(X);
|
2023-10-08 22:41:21 +00:00
|
|
|
if (dt = NO_DATE) or (FRowHeight = 0) then
|
2022-08-28 23:41:45 +00:00
|
|
|
exit;
|
2023-10-08 22:41:21 +00:00
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
idx := GetRowAtCoord(Y);
|
2022-08-28 23:41:45 +00:00
|
|
|
if (idx >= 0) and (idx < NumEvents) then
|
|
|
|
begin
|
|
|
|
eventRec := FEventRecords[idx];
|
2022-10-12 16:40:15 +00:00
|
|
|
Result := eventRec^.Event;
|
2022-08-28 23:41:45 +00:00
|
|
|
if Result.AllDayEvent then
|
|
|
|
begin
|
2022-10-12 16:40:15 +00:00
|
|
|
if (dt < DatePart(eventRec^.StartTime)) or (dt > DatePart(eventRec^.EndTime) + 1) then
|
2022-08-28 23:41:45 +00:00
|
|
|
Result := nil;
|
|
|
|
end else
|
2022-10-12 16:40:15 +00:00
|
|
|
if (dt < eventRec^.StartTime) or (dt > eventRec^.EndTime) then
|
2022-08-28 23:41:45 +00:00
|
|
|
Result := nil;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
{ Determines the dates when the earliest event in the entire Schedule starts,
|
|
|
|
and when the latest event ends. }
|
2022-09-14 21:24:33 +00:00
|
|
|
procedure TVpGanttView.GetEventDateRange(out AFirstDate, ALastDate: TDate);
|
2022-08-28 18:17:04 +00:00
|
|
|
var
|
2023-10-08 22:41:21 +00:00
|
|
|
i, j: Integer;
|
|
|
|
event: TVpEvent = nil;
|
2022-08-28 18:17:04 +00:00
|
|
|
d: TDateTime;
|
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
AFirstDate := NO_DATE;
|
|
|
|
ALastDate := NO_DATE;
|
2022-08-28 18:17:04 +00:00
|
|
|
if (Datastore = nil) or (Datastore.Resource = nil) or (Datastore.Resource.Schedule.EventCount = 0) then
|
2023-10-08 22:41:21 +00:00
|
|
|
exit;
|
|
|
|
|
|
|
|
// Find first non-recurring event; recurring events will be accepted only
|
|
|
|
// between FStartDate and FEndDate.
|
|
|
|
i := 0;
|
|
|
|
repeat
|
|
|
|
event := Datastore.Resource.Schedule.GetEvent(i);
|
|
|
|
inc(i);
|
|
|
|
until (event.RepeatCode = rtNone) or (i = DataStore.Resource.Schedule.EventCount);
|
|
|
|
|
|
|
|
if event <> nil then
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-09-19 21:32:43 +00:00
|
|
|
AFirstDate := DatePart(event.StartTime);
|
|
|
|
ALastDate := -99999;
|
2023-10-08 22:41:21 +00:00
|
|
|
for j := i-1 to Datastore.Resource.Schedule.EventCount-1 do
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
event := Datastore.Resource.Schedule.GetEvent(j);
|
|
|
|
if event.RepeatCode = rtNone then
|
|
|
|
begin
|
|
|
|
d := DatePart(event.EndTime);
|
|
|
|
if d > ALastDate then ALastDate := d;
|
|
|
|
end;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
end;
|
2023-10-08 22:41:21 +00:00
|
|
|
|
|
|
|
// To do: handle recurring events
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
function TVpGanttView.GetEventOfRow(ARow: Integer): TVpEvent;
|
|
|
|
begin
|
2022-10-12 16:40:15 +00:00
|
|
|
Result := EventRecords[ARow]^.Event;
|
2022-08-29 21:25:03 +00:00
|
|
|
end;
|
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
function TVpGanttView.GetEventRec(AIndex: Integer): PVpGanttEventRec;
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
|
|
|
Result := FEventRecords[AIndex];
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TVpGanttView.GetHourRec(AIndex: Integer): TVpGanttHourRec;
|
|
|
|
begin
|
|
|
|
Result := FHourRecords[AIndex];
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
function TVpGanttView.GetMonthRec(AIndex: Integer): TVpGanttMonthRec;
|
|
|
|
begin
|
|
|
|
Result := FMonthRecords[AIndex];
|
|
|
|
end;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
{ Determines the number of days between the first and last Gantt event. This is
|
2022-08-28 18:17:04 +00:00
|
|
|
the number of day columns in the view. }
|
|
|
|
function TVpGanttView.GetNumDays: Integer;
|
|
|
|
begin
|
2022-10-05 16:30:14 +00:00
|
|
|
Result := 0;
|
2022-10-15 13:03:27 +00:00
|
|
|
if ValidDate(FRealStartDate) and ValidDate(FRealEndDate) then
|
2022-10-05 16:30:14 +00:00
|
|
|
begin
|
|
|
|
Result := trunc(FRealEndDate) - trunc(FRealStartDate) + 1;
|
|
|
|
if Result < 0 then
|
|
|
|
Result := 0;
|
|
|
|
end;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ Determines the number of events (= rows) to be displayed in the GanttView. }
|
|
|
|
function TVpGanttView.GetNumEvents: Integer;
|
|
|
|
begin
|
2022-10-12 16:40:15 +00:00
|
|
|
if FEventRecords <> nil then
|
|
|
|
Result := FEventRecords.Count
|
|
|
|
else
|
|
|
|
Result := 0;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
{ Determines the number of hours between the first and last Gantt event.
|
|
|
|
This is the number of hour columns in the view. }
|
|
|
|
function TVpGanttView.GetNumHours: Integer;
|
|
|
|
begin
|
|
|
|
Result := GetNumDays * HoursPerDay;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
{ Determines the number of months (complete or partial) between the first and
|
|
|
|
last Gantt event. }
|
|
|
|
function TVpGanttView.GetNumMonths: Integer;
|
|
|
|
var
|
2022-09-07 17:35:46 +00:00
|
|
|
y1, m1, d1: Word;
|
|
|
|
y2, m2, d2: Word;
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-10-15 13:03:27 +00:00
|
|
|
if ValidDate(FRealStartDate) and ValidDate(FRealEndDate) then
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
DecodeDate(FRealStartDate, y1, m1, d1);
|
|
|
|
DecodeDate(FRealEndDate, y2, m2, d2);
|
2022-09-07 17:35:46 +00:00
|
|
|
if (y1 = y2) then
|
|
|
|
Result := m2 - m1 + 1
|
|
|
|
else
|
|
|
|
Result := 13 - m1 + m2 + (y2 - y1 - 1)*12;
|
2022-08-28 18:17:04 +00:00
|
|
|
end else
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
{ Determines the number of weeks (complete or partial) between the first and
|
|
|
|
last Gantt events.
|
|
|
|
NOTE: Week calculations are based on ISO week numbers only! }
|
|
|
|
function TVpGanttView.GetNumWeeks: Integer;
|
|
|
|
var
|
|
|
|
d1, d2: TVpDayType;
|
|
|
|
dt1, dt2: TDate;
|
|
|
|
wn1, wn2: Integer;
|
|
|
|
begin
|
2022-10-15 13:03:27 +00:00
|
|
|
if ValidDate(FRealStartDate) and ValidDate(FRealEndDate) then
|
2022-09-21 22:50:43 +00:00
|
|
|
begin
|
|
|
|
d1 := GetVpDayType(FRealStartDate);
|
|
|
|
d2 := GetVpDayType(FRealEndDate);
|
|
|
|
if (FRealEndDate - FRealStartDate < 7) then
|
|
|
|
begin
|
|
|
|
wn1 := WeekOfTheYear(FRealStartDate);
|
|
|
|
wn2 := WeekOfTheYear(FRealEndDate);
|
|
|
|
if wn1 = wn2 then
|
|
|
|
Result := 1
|
|
|
|
else
|
|
|
|
Result := 2;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if d1 = dtMonday then
|
|
|
|
begin
|
|
|
|
// No partial week at start
|
|
|
|
Result := 0;
|
|
|
|
dt1 := FRealStartDate;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
// Partial week at start
|
|
|
|
Result := 1;
|
|
|
|
dt1 := StartOfTheWeek(FRealStartDate) + 7; // Start of next week
|
|
|
|
end;
|
|
|
|
|
|
|
|
if d2 = dtSunday then
|
|
|
|
// No partial week at end
|
|
|
|
dt2 := FRealEndDate
|
|
|
|
else begin
|
|
|
|
// Partial week at the end
|
|
|
|
Inc(Result);
|
|
|
|
dt2 := StartOfTheWeek(FRealEndDate) - 1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := Result + (trunc(dt2) - trunc(dt1) + 1) div 7;
|
|
|
|
end else
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
{ Calculates the first and last date to be displayed in the GanttView. This
|
|
|
|
is necessary because the properties StartDate and EndDate may be 0 which
|
|
|
|
means the very first/last event. }
|
|
|
|
procedure TVpGanttView.GetRealEventDateRange(out AStartDate, AEndDate: TDate);
|
|
|
|
begin
|
2022-10-15 13:03:27 +00:00
|
|
|
if ValidDate(FStartDate) then
|
|
|
|
AStartDate := DatePart(FStartDate)
|
|
|
|
else
|
|
|
|
AStartDate := FFirstDate;
|
|
|
|
|
|
|
|
if ValidDate(FEndDate) then
|
|
|
|
AEndDate := DatePart(FEndDate)
|
|
|
|
else
|
|
|
|
AEndDate := FLastDate;
|
2022-09-14 21:24:33 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
function TVpGanttView.GetRowAtCoord(Y: Integer): Integer;
|
|
|
|
begin
|
|
|
|
Result := (Y - FTotalColHeaderHeight) div FRowHeight + FTopRow;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 18:34:35 +00:00
|
|
|
function TVpGanttView.GetRowOfEvent(AEvent: TVpEvent): Integer;
|
|
|
|
var
|
|
|
|
i: Integer;
|
2022-10-14 21:17:20 +00:00
|
|
|
eventRec: PVpGanttEventRec;
|
|
|
|
found: Boolean;
|
2022-08-29 18:34:35 +00:00
|
|
|
begin
|
2022-10-12 16:40:15 +00:00
|
|
|
for i := 0 to FEventRecords.Count-1 do
|
2022-10-14 21:17:20 +00:00
|
|
|
begin
|
|
|
|
eventRec := FEventRecords[i];
|
|
|
|
if (eventRec^.Event = AEvent) then
|
2022-08-29 18:34:35 +00:00
|
|
|
begin
|
2022-10-14 21:17:20 +00:00
|
|
|
if AEvent.RepeatCode = rtNone then
|
|
|
|
found := true
|
|
|
|
else
|
|
|
|
found := (DatePart(eventRec^.StartTime) <= FActiveDate) and (DatePart(eventRec^.EndTime) >= FActiveDate);
|
|
|
|
if found then
|
|
|
|
begin
|
|
|
|
Result := i;
|
|
|
|
exit;
|
|
|
|
end;
|
2022-08-29 18:34:35 +00:00
|
|
|
end;
|
2022-10-14 21:17:20 +00:00
|
|
|
end;
|
2022-08-29 18:34:35 +00:00
|
|
|
Result := -1;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
function TVpGanttView.GetWeekRec(AIndex: Integer): TVpGanttWeekRec;
|
|
|
|
begin
|
|
|
|
Result := FWeekRecords[AIndex];
|
|
|
|
end;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
{ If the component is being dropped on a form at designtime, then
|
|
|
|
automatically hook up to the first datastore component found. }
|
|
|
|
procedure TVpGanttView.HookUp;
|
|
|
|
var
|
|
|
|
I: Integer;
|
|
|
|
begin
|
|
|
|
if csDesigning in ComponentState then
|
|
|
|
for I := 0 to pred(Owner.ComponentCount) do begin
|
|
|
|
if (Owner.Components[I] is TVpCustomDataStore) then begin
|
|
|
|
DataStore := TVpCustomDataStore(Owner.Components[I]);
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TVpGanttView.HourMode: Boolean;
|
|
|
|
begin
|
|
|
|
Result := (gchHour in FColHeaderAttributes.Visible);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TVpGanttView.HoursPerDay: Integer;
|
|
|
|
begin
|
|
|
|
Result := ord(FEndHour) - ord(FStartHour) + 1;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.Init;
|
|
|
|
begin
|
|
|
|
CalcRowHeight;
|
|
|
|
CalcColHeaderHeight;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
GetEventDateRange(FFirstDate, FLastDate);
|
|
|
|
GetRealEventDateRange(FRealStartDate, FRealEndDate);
|
2023-10-08 22:41:21 +00:00
|
|
|
if HourMode then
|
|
|
|
FColCount := GetNumHours
|
|
|
|
else
|
|
|
|
FColCount := GetNumDays;
|
2022-08-28 18:17:04 +00:00
|
|
|
FRowCount := GetNumEvents;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
PopulateHourRecords;
|
2022-08-28 18:17:04 +00:00
|
|
|
PopulateDayRecords;
|
2022-09-21 22:50:43 +00:00
|
|
|
PopulateWeekRecords;
|
2022-08-28 18:17:04 +00:00
|
|
|
PopulateMonthRecords;
|
|
|
|
PopulateEventRecords;
|
|
|
|
end;
|
2023-10-08 22:41:21 +00:00
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
{ Checks whether the specified date belongs to the specified event.
|
|
|
|
The function returns true if the event begins before or at the date and ends
|
|
|
|
at or after it. }
|
|
|
|
function TVpGanttView.IsEventOnDate(AEvent: TVpEvent; ADate: TDate): Boolean;
|
2022-08-29 21:25:03 +00:00
|
|
|
var
|
2022-09-14 21:24:33 +00:00
|
|
|
dEv1, dEv2: TDate;
|
2022-08-29 21:25:03 +00:00
|
|
|
begin
|
|
|
|
if AEvent <> nil then
|
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
dEv1 := DatePart(AEvent.StartTime);
|
|
|
|
dEv2 := DatePart(AEvent.EndTime);
|
|
|
|
Result := (dEv1 <= ADate) and (ADate <= dEv2);
|
2022-08-29 21:25:03 +00:00
|
|
|
end else
|
|
|
|
Result := false;
|
|
|
|
end;
|
|
|
|
|
2022-08-31 21:43:14 +00:00
|
|
|
function TVpGanttView.IsHoliday(ADate: TDate; out AHolidayName: String): Boolean;
|
|
|
|
begin
|
|
|
|
AHolidayName := '';
|
|
|
|
if Assigned(FOnHoliday) then
|
2022-09-19 21:32:43 +00:00
|
|
|
begin
|
2022-08-31 21:43:14 +00:00
|
|
|
FOnHoliday(Self, ADate, AHolidayName);
|
2022-09-19 21:32:43 +00:00
|
|
|
Result := AHolidayName <> '';
|
|
|
|
end else
|
2022-10-13 14:43:54 +00:00
|
|
|
if Assigned(FControlLink) then
|
2022-09-19 21:32:43 +00:00
|
|
|
Result := FControlLink.IsHoliday(ADate, AHolidayName);
|
2022-08-31 21:43:14 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.KeyDown(var Key: Word; Shift: TShiftState);
|
2022-08-29 21:25:03 +00:00
|
|
|
|
|
|
|
procedure ScrollCols(ADelta: Integer);
|
|
|
|
begin
|
|
|
|
SetActiveCol(FActiveCol + ADelta);
|
|
|
|
if FActiveCol <= FLeftCol then
|
|
|
|
ScrollHorizontal(FActiveCol - FLeftCol)
|
|
|
|
else
|
|
|
|
if FActiveCol >= FLeftCol + FVisibleCols then
|
|
|
|
ScrollHorizontal(FActiveCol - (FLeftCol + FVisibleCols) + 1);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure ScrollRows(ADelta: Integer);
|
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
FActiveRow := FActiveRow + ADelta;
|
|
|
|
if FActiveRow < 0 then FActiveRow := 0;
|
|
|
|
if FActiveRow >= RowCount then FActiveRow := RowCount-1;
|
|
|
|
// SetActiveRow(FActiveRow + ADelta);
|
2022-08-29 21:25:03 +00:00
|
|
|
if FActiveRow <= FTopRow then
|
|
|
|
ScrollVertical(FActiveRow - FTopRow)
|
|
|
|
else
|
|
|
|
if FActiveRow >= FTopRow + FVisibleRows then
|
|
|
|
ScrollVertical(FActiveRow - (FTopRow + FVisibleRows) + 1);
|
2023-10-08 22:41:21 +00:00
|
|
|
SetActiveRow(FActiveRow);
|
2022-08-29 21:25:03 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
var
|
2022-08-29 21:25:03 +00:00
|
|
|
P: TPoint;
|
2023-10-08 22:41:21 +00:00
|
|
|
OneDay: Integer;
|
|
|
|
c: Integer;
|
2022-08-27 10:31:14 +00:00
|
|
|
begin
|
2022-08-29 15:09:38 +00:00
|
|
|
inherited;
|
2023-10-08 22:41:21 +00:00
|
|
|
|
|
|
|
if HourMode then
|
|
|
|
OneDay := HoursPerDay
|
|
|
|
else
|
|
|
|
OneDay := 1;
|
2022-08-27 10:31:14 +00:00
|
|
|
case Key of
|
2022-08-29 21:25:03 +00:00
|
|
|
VK_LEFT:
|
2023-10-08 22:41:21 +00:00
|
|
|
ScrollCols(-OneDay);
|
2022-08-29 21:25:03 +00:00
|
|
|
VK_RIGHT:
|
2023-10-08 22:41:21 +00:00
|
|
|
ScrollCols(OneDay);
|
2022-08-29 21:25:03 +00:00
|
|
|
VK_DOWN:
|
|
|
|
ScrollRows(1);
|
|
|
|
VK_UP:
|
|
|
|
ScrollRows(-1);
|
|
|
|
VK_HOME:
|
|
|
|
if Shift = [ssCtrl] then
|
|
|
|
begin
|
|
|
|
ActiveCol := 0;
|
|
|
|
FLeftCol := 0;
|
|
|
|
end else
|
|
|
|
if Shift = [ssCtrl, ssShift] then
|
|
|
|
begin
|
|
|
|
ActiveCol := 0;
|
|
|
|
ActiveRow := 0;
|
|
|
|
FLeftCol := 0;
|
|
|
|
FTopRow := 0;
|
|
|
|
end else
|
|
|
|
if Shift = [] then
|
|
|
|
ScrollCols(-FVisibleCols);
|
|
|
|
VK_END:
|
|
|
|
if Shift = [ssCtrl] then
|
|
|
|
begin
|
|
|
|
ActiveCol := ColCount-1;
|
|
|
|
ScrollHorizontal(FLeftCol + FVisibleCols);
|
|
|
|
end else
|
|
|
|
if Shift = [ssCtrl, ssShift] then
|
|
|
|
begin
|
|
|
|
ActiveCol := ColCount-1;
|
|
|
|
ActiveRow := RowCount-1;
|
|
|
|
FLeftCol := ColCount - FVisibleCols;
|
2023-10-08 22:41:21 +00:00
|
|
|
if FLeftCol < 0 then FLeftCol := 0;
|
2022-08-29 21:25:03 +00:00
|
|
|
FTopRow := RowCount - FVisibleRows;
|
2023-10-08 22:41:21 +00:00
|
|
|
if FTopRow < 0 then FTopRow := 0;
|
2022-08-29 21:25:03 +00:00
|
|
|
end else
|
|
|
|
ScrollCols(FVisibleCols);
|
|
|
|
VK_NEXT:
|
|
|
|
if Shift = [ssCtrl] then // ctrl + page down
|
2023-10-08 22:41:21 +00:00
|
|
|
ScrollRows(FRowCount)
|
|
|
|
else
|
2022-08-29 21:25:03 +00:00
|
|
|
ScrollRows(FVisibleRows); // page down
|
|
|
|
VK_PRIOR:
|
|
|
|
if Shift = [ssCtrl] then // ctrl + page up
|
2023-10-08 22:41:21 +00:00
|
|
|
ScrollRows(-FRowCount)
|
|
|
|
else
|
2022-08-29 21:25:03 +00:00
|
|
|
ScrollRows(-FVisibleRows); // page up
|
|
|
|
VK_F10, VK_APPS:
|
|
|
|
if (ssShift in Shift) then
|
|
|
|
begin
|
|
|
|
P := GetClientOrigin;
|
2023-10-08 22:41:21 +00:00
|
|
|
if HourMode then
|
|
|
|
begin
|
|
|
|
P.X := P.X + FHourRecords[FActiveCol].Rect.Right;
|
|
|
|
P.Y := P.Y + FHourRecords[FActiveCol].Rect.Top;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
P.X := P.X + FDayRecords[FActiveCol].Rect.Right;
|
|
|
|
P.Y := P.Y + FDayRecords[FActiveCol].Rect.Top;
|
|
|
|
end;
|
2022-08-29 21:25:03 +00:00
|
|
|
PopupMenu.Popup(P.X + 10, P.Y + 10);
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
2022-08-29 21:25:03 +00:00
|
|
|
VK_RETURN:
|
|
|
|
PopupEditEvent(Self);
|
|
|
|
VK_INSERT:
|
|
|
|
PopupAddEvent(Self);
|
|
|
|
VK_DELETE:
|
|
|
|
DeleteActiveEvent(true);
|
|
|
|
else
|
|
|
|
exit;
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
2022-08-29 21:25:03 +00:00
|
|
|
Invalidate;
|
|
|
|
Key := 0;
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
function TVpGanttView.IsStoredColWidth: Boolean;
|
|
|
|
begin
|
|
|
|
Result := FColWidth <> DEFAULT_COLWIDTH;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TVpGanttView.IsStoredDateFormat(AIndex: Integer): Boolean;
|
|
|
|
begin
|
|
|
|
case AIndex of
|
|
|
|
0: Result := FDateFormat[0] <> DEFAULT_DAYFORMAT;
|
|
|
|
1: Result := FDateFormat[1] <> DEFAULT_MONTHFORMAT;
|
|
|
|
2: Result := FDateFormat[2] <> DEFAULT_MONTHFORMAT_SHORT;
|
2023-10-08 22:41:21 +00:00
|
|
|
3: Result := FDateFormat[3] <> DEFAULT_DAYFORMAT_HOURMODE;
|
2022-08-26 22:35:42 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
function TVpGanttView.IsStoredEndHour: Boolean;
|
|
|
|
begin
|
|
|
|
Result := FEndHour <> DEFAULT_END_HOUR;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TVpGanttView.IsStoredStartHour: Boolean;
|
|
|
|
begin
|
|
|
|
Result := FStartHour <> DEFAULT_START_HOUR;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.LoadLanguage;
|
|
|
|
var
|
|
|
|
item: TMenuItem;
|
|
|
|
begin
|
|
|
|
for item in FDefaultPopup.Items do
|
|
|
|
if item is TVpMenuItem then
|
|
|
|
TVpMenuItem(item).Translate;
|
|
|
|
end;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure TVpGanttView.LinkHandler(Sender: TComponent;
|
|
|
|
NotificationType: TVpNotificationType; const Value: Variant);
|
|
|
|
begin
|
|
|
|
FInLinkHandler := true;
|
|
|
|
try
|
|
|
|
case NotificationType of
|
2022-08-29 15:09:38 +00:00
|
|
|
neDateChange : SetActiveDate(Value);
|
2022-08-22 22:30:13 +00:00
|
|
|
neDataStoreChange : Invalidate;
|
|
|
|
neInvalidate : Invalidate;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
FInLinkHandler := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.Loaded;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
FLoaded := true;
|
|
|
|
Populate;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
|
|
X, Y: Integer);
|
|
|
|
var
|
|
|
|
dt: TDateTime;
|
|
|
|
begin
|
|
|
|
inherited MouseDown(Button, Shift, X, Y);
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
if not Focused then
|
|
|
|
SetFocus;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
FMouseDownPoint := Point(X, Y);
|
|
|
|
FActiveCol := GetColAtCoord(X);
|
|
|
|
FActiveRow := GetRowAtCoord(Y);
|
|
|
|
SetActiveEvent(GetEventAtCoord(X, Y));
|
|
|
|
dt := GetDateTimeAtCoord(X);
|
|
|
|
if dt <> NO_DATE then
|
|
|
|
SetActiveDate(dt);
|
2023-10-09 22:46:32 +00:00
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
Invalidate;
|
2022-08-28 23:41:45 +00:00
|
|
|
end;
|
|
|
|
|
2022-09-01 12:12:52 +00:00
|
|
|
procedure TVpGanttView.MouseEnter;
|
|
|
|
begin
|
|
|
|
FMouseEvent := nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.MouseLeave;
|
|
|
|
begin
|
|
|
|
HideHintWindow;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
|
|
var
|
|
|
|
event: TVpEvent;
|
|
|
|
begin
|
|
|
|
inherited MouseMove(Shift, X, Y);
|
|
|
|
if ShowHint then
|
|
|
|
begin
|
|
|
|
event := GetEventAtCoord(X, Y);
|
|
|
|
if event = nil then
|
|
|
|
HideHintWindow
|
|
|
|
else
|
|
|
|
if FMouseEvent <> event then begin
|
|
|
|
ShowHintWindow(Point(X, Y), event);
|
|
|
|
FMouseEvent := event;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure TVpGanttView.Paint;
|
|
|
|
begin
|
|
|
|
RenderToCanvas(
|
|
|
|
Canvas, // Paint Canvas
|
|
|
|
Rect(0, 0, Width, Height), // Paint Rectangle
|
|
|
|
ra0, // Rotation angle: none
|
|
|
|
1, // Scale
|
2022-09-14 21:24:33 +00:00
|
|
|
FActiveDate, // Date
|
2022-08-22 22:30:13 +00:00
|
|
|
-1, // Start At
|
|
|
|
-1, // End At
|
|
|
|
gr30Min, // Granularity
|
|
|
|
False // Display Only
|
|
|
|
);
|
2022-08-27 10:31:14 +00:00
|
|
|
SetVScrollPos;
|
|
|
|
SetHScrollPos;
|
2022-08-22 22:30:13 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.Populate;
|
|
|
|
begin
|
|
|
|
if DataStore <> nil then
|
2022-08-28 23:41:45 +00:00
|
|
|
DataStore.Date := FActiveDate;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
// Populates the array of TVpGanttRec records containing the date of each day
|
|
|
|
// cell and the *unscrolled* cell rectangle coordinates.
|
|
|
|
procedure TVpGanttView.PopulateDayRecords;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
x1, y1, x2, y2: Integer;
|
|
|
|
begin
|
|
|
|
SetLength(FDayRecords, GetNumDays);
|
|
|
|
x1 := FixedColWidth;
|
2022-09-21 22:50:43 +00:00
|
|
|
y1 := 0;
|
|
|
|
if (gchMonth in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FMonthColHeaderHeight + FTextMargin);
|
|
|
|
if (gchWeek in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FWeekColHeaderHeight + FTextMargin);
|
2023-10-08 22:41:21 +00:00
|
|
|
if (gchHour in FColHeaderAttributes.Visible) then
|
|
|
|
y2 := y1 + FDayColHeaderHeight + FTextMargin
|
|
|
|
else
|
|
|
|
y2 := FTotalColHeaderHeight;
|
2022-08-28 18:17:04 +00:00
|
|
|
for i := 0 to High(FDayRecords) do
|
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
x2 := x1 + CalcDaysWidth(1);
|
2022-08-28 18:17:04 +00:00
|
|
|
FDayRecords[i].Rect := Rect(x1, y1, x2, y2);
|
2022-09-14 21:24:33 +00:00
|
|
|
FDayRecords[i].Date := FRealStartDate + i;
|
2022-08-28 18:17:04 +00:00
|
|
|
x1 := x2;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.PopulateEventRecords;
|
|
|
|
var
|
|
|
|
event: TVpEvent;
|
2022-10-12 16:40:15 +00:00
|
|
|
eventRec: PVpGanttEventRec;
|
2022-08-28 18:17:04 +00:00
|
|
|
i: Integer;
|
|
|
|
xh1, xh2, y1, xe1, xe2, y2: Integer;
|
|
|
|
t1, t2: TDateTime;
|
2023-10-09 15:47:05 +00:00
|
|
|
startHr, endHr: TDateTime;
|
|
|
|
dayWidth, totalWidth: Integer;
|
|
|
|
dayFactor, hourFactor: Double;
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
|
|
|
if (Datastore = nil) or (DataStore.Resource = nil) then
|
|
|
|
exit;
|
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// The EventRecords list is supposed to collect all events displayed by the
|
|
|
|
// GanttView.
|
|
|
|
FEventRecords.Free;
|
|
|
|
FEventRecords := TVpGanttEventList.Create(FRealStartDate, FRealEndDate);
|
|
|
|
|
|
|
|
// Consider only events which are, fully or partly, inside the
|
|
|
|
// displayed date range between FRealStartDate and FRealEndDate
|
|
|
|
for i := 0 to Datastore.Resource.Schedule.EventCount-1 do
|
|
|
|
begin
|
|
|
|
event := Datastore.Resource.Schedule.GetEvent(i);
|
|
|
|
if event.RepeatCode <> rtNone then
|
|
|
|
FEventRecords.AddRecurringEvents(event)
|
|
|
|
else
|
2022-09-14 21:24:33 +00:00
|
|
|
begin
|
|
|
|
if DatePart(event.EndTime) < FRealStartDate then
|
|
|
|
continue;
|
|
|
|
if DatePart(event.StartTime) > FRealEndDate then
|
|
|
|
continue;
|
2022-10-12 16:40:15 +00:00
|
|
|
FEventRecords.AddSingleEvent(event);
|
2022-09-14 21:24:33 +00:00
|
|
|
end;
|
2022-10-12 16:40:15 +00:00
|
|
|
end;
|
2022-09-14 21:24:33 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// Sort events by date/time - this is a general requirement for Gantt
|
|
|
|
FEventRecords.Sort(@CompareEventRecs);
|
2022-08-29 15:09:38 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// Iterate over all considered events, fill the event record and store it
|
|
|
|
// in the array
|
|
|
|
xh1 := 0;
|
|
|
|
xh2 := FixedColWidth;
|
|
|
|
y1 := FTotalColHeaderHeight;
|
2023-10-08 22:41:21 +00:00
|
|
|
totalWidth := CalcDaysWidth(GetNumDays);
|
2023-10-09 15:47:05 +00:00
|
|
|
dayWidth := CalcDaysWidth(1);
|
|
|
|
startHr := ord(FStartHour) / 24;
|
|
|
|
endHr := (ord(FEndHour) + 1) / 24; // extend to the end of the endhour box
|
|
|
|
dayFactor := totalWidth / GetNumDays;
|
|
|
|
hourFactor := dayWidth / HoursPerDay * 24;
|
2022-10-12 16:40:15 +00:00
|
|
|
for i := 0 to FEventRecords.Count-1 do
|
|
|
|
begin
|
|
|
|
eventRec := FEventRecords[i];
|
|
|
|
t1 := eventRec^.StartTime;
|
|
|
|
t2 := eventRec^.EndTime;
|
2022-09-14 21:24:33 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// Store event rectangle coordinates in the EventRec
|
|
|
|
y2 := y1 + FRowHeight;
|
2023-10-09 15:47:05 +00:00
|
|
|
if HourMode then
|
|
|
|
begin
|
|
|
|
// Visible beginning of day at which the event starts
|
|
|
|
xe1 := round((DatePart(t1) - FRealStartDate) * dayFactor) + FixedColWidth;
|
|
|
|
// Add time part if event starts after FStartHour
|
|
|
|
if TimePart(t1) >= startHr then
|
|
|
|
xe1 := xe1 + round((TimePart(t1) - startHr) * hourFactor);
|
|
|
|
// Visible beginning of day at which the event ends
|
|
|
|
xe2 := round((DatePart(t2) - FRealStartDate) * dayFactor) + FixedColWidth;
|
|
|
|
// Add time part of event end, clipped at end of EndHour.
|
|
|
|
if TimePart(t2) <= endHr then
|
|
|
|
xe2 := xe2 + round((TimePart(t2) - startHr) * hourFactor)
|
|
|
|
else
|
|
|
|
xe2 := xe2 + round((endHr - startHr) * hourFactor);
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
xe1 := round((t1 - FRealStartDate) / numDays * totalWidth) + FixedColWidth;
|
|
|
|
xe2 := round((t2 - FRealStartDate) / numDays * totalWidth) + FixedColWidth;
|
|
|
|
end;
|
2022-10-12 16:40:15 +00:00
|
|
|
if xe1 = xe2 then xe2 := xe1 + 1;
|
2022-09-14 21:24:33 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
eventRec^.HeadRect := Rect(xh1, y1, xh2, y2);
|
|
|
|
eventRec^.EventRect := Rect(xe1, y1, xe2, y2);
|
2022-09-14 21:24:33 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// Find the active row. This is the row with the active event.
|
|
|
|
if eventRec^.Event = FActiveEvent then
|
2022-10-14 21:17:20 +00:00
|
|
|
begin
|
|
|
|
if FActiveEvent.RepeatCode = rtNone then
|
|
|
|
FActiveRow := i
|
|
|
|
else
|
|
|
|
if (DatePart(eventRec^.StartTime) <= FActiveDate) and (DatePart(eventRec^.EndTime) >= FActiveDate) then
|
|
|
|
FActiveRow := i;
|
|
|
|
end;
|
2022-08-29 18:34:35 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
// Prepare for next row
|
|
|
|
y1 := y2;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
// Populates the array of TVpGanttRec records containing the hours of each day
|
|
|
|
// cell and the *unscrolled* cell rectangle coordinates.
|
|
|
|
procedure TVpGanttView.PopulateHourRecords;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
x1, y1, x2, y2: Integer;
|
|
|
|
divRes: Integer = 0;
|
|
|
|
modRes: Integer = 0;
|
|
|
|
begin
|
|
|
|
SetLength(FHourRecords, GetNumHours);
|
|
|
|
x1 := FixedColWidth;
|
|
|
|
y1 := 0;
|
|
|
|
if (gchMonth in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FMonthColHeaderHeight + FTextMargin);
|
|
|
|
if (gchWeek in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FWeekColHeaderHeight + FTextMargin);
|
|
|
|
if (gchDay in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FDayColHeaderHeight + FTextMargin);
|
|
|
|
y2 := FTotalColHeaderHeight;
|
|
|
|
for i := 0 to High(FHourRecords) do
|
|
|
|
begin
|
|
|
|
x2 := x1 + ColWidth;
|
|
|
|
FHourRecords[i].Rect := Rect(x1, y1, x2, y2);
|
|
|
|
DivMod(i, HoursPerDay, divRes, modRes);
|
|
|
|
FHourRecords[i].Date := FRealStartDate + divRes;
|
|
|
|
FHourRecords[i].Hour := ord(FStartHour) + modRes;
|
|
|
|
x1 := x2;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.PopulateMonthRecords;
|
|
|
|
var
|
|
|
|
i, n: Integer;
|
|
|
|
x1, y1, x2, y2: Integer;
|
|
|
|
dm: TDateTime;
|
|
|
|
ndays: Integer;
|
|
|
|
begin
|
|
|
|
n := GetNumMonths;
|
|
|
|
SetLength(FMonthRecords, n);
|
|
|
|
if (Datastore = nil) or (Datastore.Resource = nil) then
|
|
|
|
exit;
|
2022-09-23 20:07:48 +00:00
|
|
|
if not (gchMonth in FColHeaderAttributes.Visible) then
|
|
|
|
exit;
|
2022-08-28 18:17:04 +00:00
|
|
|
|
|
|
|
x1 := FixedColWidth;
|
|
|
|
y1 := 0;
|
2022-09-23 20:07:48 +00:00
|
|
|
if [gchWeek, gchDay] * FColHeaderAttributes.Visible = [gchWeek, gchDay] then
|
|
|
|
y2 := FMonthColHeaderHeight + FTextMargin
|
|
|
|
else
|
|
|
|
y2 := FTotalColHeaderHeight;
|
2022-09-07 17:35:46 +00:00
|
|
|
|
|
|
|
if n > 1 then
|
2022-08-28 18:17:04 +00:00
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
// Date interval crosses one or more month boundaries
|
|
|
|
dm := FRealStartDate;
|
2022-09-07 17:35:46 +00:00
|
|
|
for i := 0 to n - 1 do
|
|
|
|
begin
|
|
|
|
if i = 0 then begin
|
|
|
|
nDays := DaysInMonth(dm) - DayOf(dm) + 1;
|
|
|
|
dm := StartOfTheMonth(dm);
|
|
|
|
end else
|
|
|
|
if i = n-1 then
|
2022-09-14 21:24:33 +00:00
|
|
|
nDays := DayOf(FRealEndDate)
|
2022-09-07 17:35:46 +00:00
|
|
|
else
|
|
|
|
nDays := DaysInMonth(dm);
|
2022-09-14 21:24:33 +00:00
|
|
|
if dm + nDays > FRealEndDate then
|
|
|
|
nDays := trunc(FRealEndDate) - trunc(dm) + 1;
|
2023-10-08 22:41:21 +00:00
|
|
|
x2 := x1 + CalcDaysWidth(nDays);
|
2022-09-07 17:35:46 +00:00
|
|
|
FMonthRecords[i].Rect := Rect(x1, y1, x2, y2);
|
|
|
|
FMonthRecords[i].Date := dm;
|
|
|
|
dm := IncMonth(dm, 1);
|
|
|
|
x1 := x2;
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
// Date interval is within the same month
|
|
|
|
nDays := DayOf(FRealEndDate) - DayOf(FRealStartDate) + 1;
|
2023-10-08 22:41:21 +00:00
|
|
|
x2 := x1 + CalcDaysWidth(nDays);
|
2022-09-07 17:35:46 +00:00
|
|
|
FMonthRecords[0].Rect := Rect(x1, y1, x2, y2);
|
2022-09-14 21:24:33 +00:00
|
|
|
FMonthRecords[0].Date := FRealStartDate;
|
2022-08-28 18:17:04 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
procedure TVpGanttView.PopulateWeekRecords;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
x1, y1, x2, y2: Integer;
|
|
|
|
d: TVpDayType;
|
|
|
|
dt1, dt2: TDateTime;
|
|
|
|
begin
|
2022-09-23 20:07:48 +00:00
|
|
|
if not (gchWeek in FColHeaderAttributes.Visible) then
|
|
|
|
begin
|
|
|
|
SetLength(FWeekRecords, 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
SetLength(FWeekRecords, GetNumWeeks);
|
2023-01-16 12:23:11 +00:00
|
|
|
if Length(FWeekRecords) = 0 then
|
|
|
|
exit;
|
|
|
|
|
2022-09-21 22:50:43 +00:00
|
|
|
x1 := FixedColWidth;
|
|
|
|
y1 := 0;
|
|
|
|
if (gchMonth in FColHeaderAttributes.Visible) then
|
|
|
|
inc(y1, FMonthColHeaderHeight + FTextMargin);
|
|
|
|
if (gchDay in FColHeaderAttributes.Visible) then
|
|
|
|
y2 := y1 + FWeekColHeaderHeight + FTextMargin
|
|
|
|
else
|
|
|
|
y2 := FTotalColHeaderHeight;
|
|
|
|
|
|
|
|
d := GetVpDayType(FRealStartDate);
|
|
|
|
dt1 := FRealStartDate;
|
|
|
|
if d = dtMonday then
|
|
|
|
dt2 := dt1 + 6
|
|
|
|
else
|
|
|
|
dt2 := StartOfTheWeek(FRealStartDate + 7) - 1;
|
|
|
|
if dt2 > FRealEndDate then
|
|
|
|
dt2 := FRealEndDate;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
x2 := x1 + CalcDaysWidth(trunc(dt2) - trunc(dt1) + 1);
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekRecords[0].Rect := Rect(x1, y1, x2, y2);
|
|
|
|
FWeekRecords[0].Date := dt1;
|
|
|
|
FWeekRecords[0].WeekNo := WeekOfTheYear(dt1);
|
|
|
|
|
|
|
|
for i := 1 to High(FWeekRecords) do
|
|
|
|
begin
|
|
|
|
dt1 := dt2 + 1;
|
|
|
|
dt2 := Min(dt1 + 6, FRealEndDate);
|
|
|
|
x1 := x2;
|
2023-10-08 22:41:21 +00:00
|
|
|
x2 := x1 + CalcDaysWidth(trunc(dt2) - trunc(dt1) + 1);
|
2022-09-21 22:50:43 +00:00
|
|
|
FWeekRecords[i].Rect := Rect(x1, y1, x2, y2);
|
|
|
|
FWeekRecords[i].Date := dt1;
|
|
|
|
FWeekRecords[i].WeekNo := WeekOfTheYear(dt1);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
procedure TVpGanttView.RenderToCanvas(RenderCanvas: TCanvas; RenderIn: TRect;
|
|
|
|
Angle: TVpRotationAngle; Scale: Extended; RenderDate: TDateTime;
|
|
|
|
StartLine, StopLine: Integer; UseGran: TVpGranularity; DisplayOnly: Boolean);
|
|
|
|
var
|
|
|
|
painter: TVpGanttViewPainter;
|
|
|
|
begin
|
|
|
|
FPainting := true;
|
|
|
|
painter := TVpGanttViewPainter.Create(Self, RenderCanvas);
|
|
|
|
try
|
|
|
|
painter.RenderToCanvas(RenderIn, Angle, Scale, RenderDate, StartLine,
|
|
|
|
StopLine, UseGran, DisplayOnly);
|
|
|
|
finally
|
|
|
|
painter.Free;
|
|
|
|
FPainting := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
procedure TVpGanttView.SetDateLimits(AStartDate, AEndDate: TDateTime);
|
|
|
|
var
|
|
|
|
oldDate: TDateTime;
|
|
|
|
begin
|
|
|
|
oldDate := FActiveDate;
|
|
|
|
|
|
|
|
FStartDate := AStartDate;
|
|
|
|
FEndDate := AEndDate;
|
|
|
|
Init;
|
|
|
|
|
|
|
|
FActiveDate := 0;
|
|
|
|
SetActiveDate(oldDate);
|
|
|
|
end;
|
|
|
|
|
2022-09-02 09:39:21 +00:00
|
|
|
{$IF VP_LCL_SCALING = 2}
|
|
|
|
procedure TVpGanttView.FixDesignFontsPPI(const ADesignTimePPI: Integer);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
DoFixDesignFontPPI(ColHeaderAttributes.MonthFont, ADesignTimePPI);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoFixDesignFontPPI(ColHeaderAttributes.WeekFont, ADesignTimePPI);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoFixDesignFontPPI(ColHeaderAttributes.DayFont, ADesignTimePPI);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoFixDesignFontPPI(ColHeaderAttributes.HourFont, ADesignTimePPI);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoFixDesignFontPPI(RowHeaderAttributes.EventFont, ADesignTimePPI);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.ScaleFontsPPI(const AToPPI: Integer;
|
|
|
|
const AProportion: Double);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
DoScaleFontPPI(ColHeaderAttributes.MonthFont, AToPPI, AProportion);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.WeekFont, AToPPI, AProportion);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.DayFont, AToPPI, AProportion);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.HourFont, AToPPI, AProportion);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoScaleFontPPI(RowHeaderAttributes.EventFont, AToPPI, AProportion);
|
|
|
|
end;
|
|
|
|
{$ELSEIF VP_LCL_SCALING = 1}
|
|
|
|
procedure TVpGantView.ScaleFontsPPI(const AProportion: Double);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
DoScaleFontPPI(ColHeaderAttributes.MonthFont, AProportion);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.WeekFont, AProportion);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.DayFont, AProportion);
|
2023-10-08 22:41:21 +00:00
|
|
|
DoScaleFontPPI(ColHeaderAttributes.HourFont, AProportion);
|
2022-09-02 09:39:21 +00:00
|
|
|
DoScaleFontPPI(RowHeadAttributes.EventFont, AProportion);
|
|
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
procedure TVpGanttView.ScrollDateIntoView(ADate: TDateTime);
|
2023-10-08 22:41:21 +00:00
|
|
|
var
|
|
|
|
nCols: Integer;
|
2022-08-29 15:09:38 +00:00
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
if (FRealStartDate = 0) or (FRealStartDate = NO_DATE) then
|
2022-08-29 15:09:38 +00:00
|
|
|
exit;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
if HourMode then
|
|
|
|
nCols := GetNumHours
|
|
|
|
else
|
|
|
|
nCols := GetNumDays;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
if ADate < FRealStartDate then
|
2022-08-29 15:09:38 +00:00
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
FRealStartDate := DatePart(ADate);
|
2023-10-08 22:41:21 +00:00
|
|
|
FColCount := nCols;
|
2022-08-29 18:34:35 +00:00
|
|
|
SetLeftCol(-MaxInt);
|
2022-08-29 15:09:38 +00:00
|
|
|
end else
|
2022-09-14 21:24:33 +00:00
|
|
|
if ADate > FRealEndDate then
|
2022-08-29 15:09:38 +00:00
|
|
|
begin
|
2022-09-14 21:24:33 +00:00
|
|
|
FRealEndDate := DatePart(ADate);
|
2023-10-08 22:41:21 +00:00
|
|
|
FColCount := nCols;
|
2022-08-31 20:47:45 +00:00
|
|
|
SetLeftCol(FColCount - 1 - FVisibleCols);
|
2022-08-29 15:09:38 +00:00
|
|
|
end else
|
2022-09-14 21:24:33 +00:00
|
|
|
if ADate < FRealStartDate + FLeftCol then
|
2023-10-08 22:41:21 +00:00
|
|
|
begin
|
|
|
|
if HourMode then
|
|
|
|
SetLeftCol((trunc(ADate) - trunc(FRealStartDate))*HoursPerDay)
|
|
|
|
else
|
|
|
|
SetLeftCol(trunc(ADate) - trunc(FRealStartDate));
|
|
|
|
end else
|
2022-09-14 21:24:33 +00:00
|
|
|
if ADate > FRealStartDate + FVisibleCols then
|
2023-10-08 22:41:21 +00:00
|
|
|
begin
|
|
|
|
if HourMode then
|
|
|
|
SetLeftCol(trunc(ADate*HoursPerDay) - FVisibleCols)
|
|
|
|
else
|
|
|
|
SetLeftCol(trunc(ADate) - FVisibleCols);
|
|
|
|
end else
|
2022-08-29 15:09:38 +00:00
|
|
|
exit;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.ScrollHorizontal(ANumCols: Integer);
|
|
|
|
begin
|
2022-08-28 18:17:04 +00:00
|
|
|
SetLeftCol(FLeftCol + ANumCols);
|
2022-08-27 10:31:14 +00:00
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 18:34:35 +00:00
|
|
|
procedure TVpGanttView.ScrollRowIntoView(ARow: Integer);
|
|
|
|
begin
|
|
|
|
if (ARow < TopRow) or (ARow >= TopRow + FVisibleRows) then
|
|
|
|
SetTopRow(ARow - FVisibleRows div 2)
|
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.ScrollVertical(ANumRows: Integer);
|
|
|
|
begin
|
2022-08-28 18:17:04 +00:00
|
|
|
SetTopRow(FTopRow + ANumRows);
|
2022-08-27 10:31:14 +00:00
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure TVpGanttView.SetActiveCol(AValue: Integer);
|
|
|
|
var
|
2022-09-02 09:39:21 +00:00
|
|
|
R: TRect = (Left:0; Top:0; Right:0; Bottom:0);
|
|
|
|
eventRect, dayRect: TRect;
|
2022-08-29 21:25:03 +00:00
|
|
|
dt: TDateTime;
|
|
|
|
event: TVpEvent;
|
2023-10-08 22:41:21 +00:00
|
|
|
c: Integer;
|
2022-08-29 21:25:03 +00:00
|
|
|
begin
|
|
|
|
if AValue <= 0 then
|
|
|
|
FActiveCol := 0
|
|
|
|
else if AValue >= ColCount then
|
|
|
|
FActiveCol := ColCount - 1
|
|
|
|
else
|
|
|
|
FActiveCol := AValue;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
c := ColToDateIndex(FActiveCol);
|
|
|
|
dt := DayRecords[c].Date;
|
|
|
|
dayRect := DayRecords[c].Rect;
|
2022-08-29 21:25:03 +00:00
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
event := EventRecords[FActiveRow]^.Event;
|
|
|
|
eventRect := EventRecords[FActiveRow]^.EventRect;
|
2022-08-29 21:25:03 +00:00
|
|
|
dayRect.Top := eventRect.Top;
|
|
|
|
dayRect.Bottom := eventRect.Bottom;
|
|
|
|
|
|
|
|
if IntersectRect(R, dayRect, eventRect) then
|
|
|
|
SetActiveEvent(event)
|
|
|
|
else
|
|
|
|
SetActiveEvent(nil);
|
|
|
|
|
|
|
|
SetActiveDate(dt);
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.SetActiveDate(AValue: TDateTime);
|
2023-10-08 22:41:21 +00:00
|
|
|
var
|
|
|
|
days: Integer;
|
2022-08-23 22:38:13 +00:00
|
|
|
begin
|
2023-10-08 22:41:21 +00:00
|
|
|
if FColHeaderAttributes = nil then // Needed for HourMode
|
|
|
|
exit;
|
|
|
|
|
2022-09-14 21:24:33 +00:00
|
|
|
if FActiveDate <> DatePart(AValue) then begin
|
|
|
|
FActiveDate := DatePart(AValue);
|
2022-08-22 22:30:13 +00:00
|
|
|
|
|
|
|
if FLoaded then
|
|
|
|
Populate;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
ScrollDateIntoView(FActiveDate);
|
2023-10-08 22:41:21 +00:00
|
|
|
days := trunc(FActiveDate) - trunc(FRealStartDate);
|
|
|
|
if HourMode then
|
|
|
|
FActiveCol := days * HoursPerDay
|
|
|
|
else
|
|
|
|
FActiveCol := days;
|
2022-10-14 21:17:20 +00:00
|
|
|
|
2022-08-22 22:30:13 +00:00
|
|
|
Invalidate;
|
|
|
|
|
|
|
|
if (not FInLinkHandler) and (ControlLink <> nil) then
|
2022-08-28 23:41:45 +00:00
|
|
|
ControlLink.Notify(self, neDateChange, FActiveDate);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
procedure TVpGanttView.SetActiveEvent(AValue: TVpEvent);
|
|
|
|
begin
|
|
|
|
if FActiveEvent <> AValue then
|
|
|
|
begin
|
|
|
|
FActiveEvent := AValue;
|
|
|
|
if FActiveEvent <> nil then
|
2022-08-29 18:34:35 +00:00
|
|
|
begin
|
|
|
|
FActiveRow := GetRowOfEvent(FActiveEvent);
|
|
|
|
ScrollRowIntoView(FActiveRow);
|
|
|
|
end;
|
2022-08-29 15:09:38 +00:00
|
|
|
end;
|
2022-08-31 20:56:36 +00:00
|
|
|
UpdatePopupMenuState;
|
2022-08-29 15:09:38 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-29 21:25:03 +00:00
|
|
|
procedure TVpGanttView.SetActiveRow(AValue: Integer);
|
|
|
|
var
|
2022-09-02 09:39:21 +00:00
|
|
|
R: TRect = (Left:0; Top:0; Right:0; Bottom:0);
|
|
|
|
eventRect, dayRect: TRect;
|
2022-08-29 21:25:03 +00:00
|
|
|
event: TVpEvent;
|
|
|
|
dt: TDateTime;
|
2023-10-08 22:41:21 +00:00
|
|
|
c: Integer;
|
2022-08-29 21:25:03 +00:00
|
|
|
begin
|
|
|
|
if AValue < 0 then
|
|
|
|
FActiveRow := 0
|
|
|
|
else if AValue >= RowCount then
|
|
|
|
FActiveRow := RowCount - 1
|
|
|
|
else
|
|
|
|
FActiveRow := AValue;
|
|
|
|
|
2022-10-12 16:40:15 +00:00
|
|
|
event := EventRecords[FActiveRow]^.Event;
|
|
|
|
eventRect := EventRecords[FActiveRow]^.EventRect;
|
2023-10-08 22:41:21 +00:00
|
|
|
c := ColToDateIndex(FActiveCol);
|
|
|
|
dt := DayRecords[c].Date;
|
|
|
|
dayRect := DayRecords[c].Rect;
|
2022-08-29 21:25:03 +00:00
|
|
|
dayRect.Top := eventRect.Top;
|
|
|
|
dayRect.Bottom := eventRect.Bottom;
|
|
|
|
|
|
|
|
if IntersectRect(R, dayRect, eventRect) then
|
|
|
|
SetActiveEvent(event)
|
|
|
|
else
|
|
|
|
SetActiveEvent(nil);
|
|
|
|
|
|
|
|
SetActiveDate(dt);
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.SetColor(Value: TColor);
|
|
|
|
begin
|
|
|
|
if FColor <> Value then begin
|
|
|
|
FColor := Value;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetColWidth(AValue: Integer);
|
|
|
|
begin
|
|
|
|
if FColWidth <> AValue then
|
|
|
|
begin
|
|
|
|
FColWidth := AValue;
|
|
|
|
Invalidate;
|
2022-08-22 22:30:13 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-26 22:35:42 +00:00
|
|
|
procedure TVpGanttView.SetDateFormat(AIndex: Integer; AValue: String);
|
|
|
|
begin
|
|
|
|
if FDateFormat[AIndex] <> AValue then
|
|
|
|
begin
|
|
|
|
FDateFormat[AIndex] := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
procedure TVpGanttView.SetDrawingStyle(AValue: TVpDrawingStyle);
|
|
|
|
begin
|
2022-08-26 22:35:42 +00:00
|
|
|
if FDrawingStyle <> AValue then
|
|
|
|
begin
|
2022-08-23 22:38:13 +00:00
|
|
|
FDrawingStyle := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure TVpGanttView.SetEndHour(AValue: TVpHours);
|
|
|
|
begin
|
|
|
|
if FEndHour <> AValue then
|
|
|
|
begin
|
|
|
|
FEndHour := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-23 22:38:13 +00:00
|
|
|
procedure TVpGanttView.SetFixedColWidth(AValue: Integer);
|
|
|
|
begin
|
|
|
|
if FFixedColWidth <> AValue then
|
|
|
|
begin
|
|
|
|
FFixedColWidth := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-31 21:43:14 +00:00
|
|
|
procedure TVpGanttView.SetHolidayColor(AValue: TColor);
|
|
|
|
begin
|
|
|
|
if FHolidayColor <> AValue then
|
|
|
|
begin
|
|
|
|
FHolidayColor := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-30 21:00:26 +00:00
|
|
|
procedure TVpGanttView.SetHScrollPos;
|
|
|
|
var
|
|
|
|
scrollInfo: TScrollInfo;
|
|
|
|
begin
|
|
|
|
if not HandleAllocated then
|
|
|
|
Exit;
|
|
|
|
with scrollInfo do
|
|
|
|
begin
|
|
|
|
cbSize := SizeOf(scrollInfo);
|
|
|
|
fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
|
|
|
|
nMin := 0;
|
|
|
|
nMax := FColCount;
|
|
|
|
if FVisibleCols >= FColCount then
|
|
|
|
nPage := nMax
|
|
|
|
else
|
|
|
|
nPage := FVisibleCols;
|
|
|
|
if FLeftCol = pred(ColCount) - VisibleCols then
|
|
|
|
nPos := ColCount
|
|
|
|
else
|
|
|
|
nPos := FLeftCol;
|
|
|
|
nTrackPos := nPos;
|
|
|
|
end;
|
|
|
|
SetScrollInfo(Handle, SB_HORZ, scrollInfo, True);
|
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.SetLeftCol(AValue: Integer);
|
2022-08-23 22:38:13 +00:00
|
|
|
begin
|
2022-08-27 10:31:14 +00:00
|
|
|
if AValue <> FLeftCol then begin
|
2022-08-28 20:32:17 +00:00
|
|
|
if AValue + FVisibleCols >= FColCount then begin
|
|
|
|
FLeftCol := FColCount - FVisibleCols;
|
2022-08-27 10:31:14 +00:00
|
|
|
if FLeftCol < 0 then
|
|
|
|
FLeftCol := 0;
|
|
|
|
// Prevent the control from hanging at the right
|
|
|
|
if (AValue < FLeftCol) and (AValue > 0) then
|
|
|
|
FLeftCol := AValue;
|
|
|
|
end
|
|
|
|
else if AValue < 0 then
|
|
|
|
FLeftCol := 0
|
|
|
|
else
|
|
|
|
FLeftCol := AValue;
|
|
|
|
Invalidate;
|
|
|
|
SetHScrollPos;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetLineColor(AValue: TColor);
|
|
|
|
begin
|
|
|
|
if FLineColor <> AValue then begin
|
|
|
|
FLineColor := AValue;
|
2022-08-23 22:38:13 +00:00
|
|
|
Repaint;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-30 21:00:26 +00:00
|
|
|
procedure TVpGanttView.SetOptions(AValue: TVpGanttViewOptions);
|
2022-08-29 21:25:03 +00:00
|
|
|
begin
|
2022-08-30 21:00:26 +00:00
|
|
|
if FOptions <> AValue then
|
2022-08-29 21:25:03 +00:00
|
|
|
begin
|
2022-08-30 21:00:26 +00:00
|
|
|
FOptions := AValue;
|
2022-08-29 21:25:03 +00:00
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-01 09:42:04 +00:00
|
|
|
procedure TVpGanttView.SetSpecialDayMode(AValue: TVpGanttSpecialDayMode);
|
|
|
|
begin
|
|
|
|
if FSpecialDayMode <> AValue then
|
|
|
|
begin
|
|
|
|
FSpecialDayMode := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2023-10-08 22:41:21 +00:00
|
|
|
procedure TVpGanttView.SetStartHour(AValue: TVpHours);
|
|
|
|
begin
|
|
|
|
if FStartHour <> AValue then
|
|
|
|
begin
|
|
|
|
FStartHour := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 18:17:04 +00:00
|
|
|
procedure TVpGanttView.SetTextMargin(AValue: Integer);
|
|
|
|
begin
|
|
|
|
if FTextMargin <> AValue then
|
|
|
|
begin
|
|
|
|
FTextMargin := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetTopRow(AValue: Integer);
|
|
|
|
begin
|
|
|
|
if AValue <> FTopRow then begin
|
2022-08-28 20:32:17 +00:00
|
|
|
if AValue + FVisibleRows >= RowCount then begin
|
|
|
|
FTopRow := FRowCount - FVisibleRows;
|
2022-08-27 10:31:14 +00:00
|
|
|
if FTopRow < 0 then
|
|
|
|
FTopRow := 0;
|
|
|
|
// Prevent the control from hanging at the bottom
|
|
|
|
if (AValue < FTopRow) and (AValue > 0) then
|
|
|
|
FTopRow := AValue;
|
|
|
|
end
|
|
|
|
else if AValue < 0 then
|
|
|
|
FTopRow := 0
|
|
|
|
else
|
|
|
|
FTopRow:= AValue;
|
|
|
|
Invalidate;
|
|
|
|
SetVScrollPos;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetVScrollPos;
|
|
|
|
var
|
2022-08-28 18:17:04 +00:00
|
|
|
scrollInfo: TScrollInfo;
|
2022-08-27 10:31:14 +00:00
|
|
|
begin
|
|
|
|
if not HandleAllocated then
|
|
|
|
Exit;
|
2022-08-28 18:17:04 +00:00
|
|
|
with scrollInfo do
|
2022-08-27 10:31:14 +00:00
|
|
|
begin
|
2022-08-28 18:17:04 +00:00
|
|
|
cbSize := SizeOf(scrollInfo);
|
2022-08-27 10:31:14 +00:00
|
|
|
fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
|
|
|
|
nMin := 0;
|
|
|
|
nMax := FRowCount;
|
|
|
|
if FVisibleRows >= FRowCount then
|
|
|
|
nPage := nMax
|
|
|
|
else
|
|
|
|
nPage := FVisibleRows;
|
|
|
|
if FTopRow = pred(RowCount) - VisibleRows then
|
2022-08-28 18:17:04 +00:00
|
|
|
nPos := FRowCount
|
2022-08-27 10:31:14 +00:00
|
|
|
else
|
|
|
|
nPos := FTopRow;
|
|
|
|
nTrackPos := nPos;
|
|
|
|
end;
|
2022-08-28 18:17:04 +00:00
|
|
|
SetScrollInfo(Handle, SB_VERT, scrollInfo, True);
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-29 22:00:20 +00:00
|
|
|
procedure TVpGanttView.SetWeekendColor(AValue: TColor);
|
|
|
|
begin
|
|
|
|
if FWeekendColor <> AValue then
|
|
|
|
begin
|
|
|
|
FWeekendColor := AValue;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-09-19 21:42:45 +00:00
|
|
|
procedure TVpGanttView.SetWeekStartsOn(Value: TVpDayType);
|
|
|
|
begin
|
|
|
|
if FWeekStartsOn <> Value then begin
|
|
|
|
FWeekStartsOn := Value;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
procedure TVpGanttView.SpawnEventEditDialog(IsNewEvent: Boolean);
|
|
|
|
var
|
|
|
|
AllowIt: Boolean;
|
|
|
|
EventDlg : TVpEventEditDialog;
|
|
|
|
begin
|
|
|
|
if (DataStore = nil) or (DataStore.Resource = nil) or ReadOnly then
|
|
|
|
Exit;
|
|
|
|
|
|
|
|
AllowIt := false;
|
|
|
|
if Assigned(FOwnerEditEvent) then
|
|
|
|
FOwnerEditEvent(self, FActiveEvent, IsNewEvent, DataStore.Resource, AllowIt)
|
|
|
|
else begin
|
|
|
|
EventDlg := TVpEventEditDialog.Create(nil);
|
|
|
|
try
|
|
|
|
EventDlg.DataStore := DataStore;
|
|
|
|
AllowIt := EventDlg.Execute(FActiveEvent);
|
|
|
|
finally
|
|
|
|
EventDlg.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if AllowIt then begin
|
|
|
|
FActiveEvent.Changed := true;
|
|
|
|
DataStore.PostEvents;
|
2022-08-29 18:34:35 +00:00
|
|
|
|
|
|
|
// The new or edited event may be at a different position in the grid.
|
|
|
|
// --> Re-read the events to sort them, find the new active row/col and
|
|
|
|
// scroll them into view
|
|
|
|
PopulateEventRecords;
|
|
|
|
SetActiveDate(FActiveEvent.StartTime);
|
|
|
|
ScrollRowIntoView(FActiveRow);
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
if IsNewEvent and Assigned(FOnAddEvent) then
|
|
|
|
FOnAddEvent(self, FActiveEvent);
|
|
|
|
if not IsNewEvent and Assigned(FOnModifyEvent) then
|
|
|
|
FOnModifyEvent(self, FActiveEvent);
|
|
|
|
end else begin
|
|
|
|
if IsNewEvent then begin
|
|
|
|
FActiveEvent.Deleted := true;
|
|
|
|
DataStore.PostEvents;
|
2022-08-29 15:09:38 +00:00
|
|
|
SetActiveEvent(nil);
|
2022-08-28 23:41:45 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
Invalidate;
|
|
|
|
end;
|
|
|
|
|
2022-08-27 10:31:14 +00:00
|
|
|
procedure TVpGanttView.WMHScroll(var Msg: TLMHScroll);
|
|
|
|
begin
|
|
|
|
case Msg.ScrollCode of
|
|
|
|
SB_LINELEFT : ScrollHorizontal(-1);
|
|
|
|
SB_LINERIGHT : ScrollHorizontal(1);
|
|
|
|
SB_PAGELEFT : ScrollHorizontal(-FVisibleCols);
|
|
|
|
SB_PAGERIGHT : ScrollHorizontal(FVisibleCols);
|
2022-08-28 20:32:17 +00:00
|
|
|
SB_THUMBPOSITION, SB_THUMBTRACK : SetLeftCol(Msg.Pos);
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.WMVScroll(var Msg: TLMVScroll);
|
|
|
|
begin
|
|
|
|
case Msg.ScrollCode of
|
|
|
|
SB_LINEUP : ScrollVertical(-1);
|
|
|
|
SB_LINEDOWN : ScrollVertical(1);
|
|
|
|
SB_PAGEUP : ScrollVertical(-FVisibleRows);
|
|
|
|
SB_PAGEDOWN : ScrollVertical(FVisibleRows);
|
2022-08-28 20:32:17 +00:00
|
|
|
SB_THUMBPOSITION, SB_THUMBTRACK : SetTopRow(Msg.Pos);
|
2022-08-27 10:31:14 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
|
2022-09-01 12:12:52 +00:00
|
|
|
{ Hint support }
|
|
|
|
|
|
|
|
procedure TVpGanttView.ShowHintWindow(APoint: TPoint; AEvent: TVpEvent);
|
|
|
|
var
|
|
|
|
txt: String;
|
|
|
|
begin
|
|
|
|
HideHintWindow;
|
|
|
|
case FHintMode of
|
|
|
|
hmPlannerHint:
|
|
|
|
begin
|
|
|
|
if (AEvent = nil) or (Datastore = nil) or (Datastore.Resource = nil) then
|
|
|
|
exit;
|
|
|
|
txt := BuildEventString(AEvent, true);
|
|
|
|
end;
|
|
|
|
hmComponentHint:
|
|
|
|
txt := FComponentHint;
|
|
|
|
end;
|
|
|
|
if (txt <> '') and not (csDesigning in ComponentState) then
|
|
|
|
begin
|
|
|
|
Hint := txt;
|
|
|
|
Application.Hint := Hint;
|
|
|
|
Application.ActivateHint(ClientToScreen(APoint), true);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.HideHintWindow;
|
|
|
|
begin
|
|
|
|
Application.CancelHint;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetHint(const AValue: TTranslateString);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
if FHintMode = hmComponentHint then
|
|
|
|
FComponentHint := AValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetHintMode(const AValue: TVpHintMode);
|
|
|
|
begin
|
|
|
|
if AValue = FHintMode then
|
|
|
|
exit;
|
|
|
|
FHintMode := AValue;
|
|
|
|
if FHintMode = hmPlannerHint then
|
|
|
|
FComponentHint := Hint;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2022-08-28 23:41:45 +00:00
|
|
|
{ Popup menu }
|
|
|
|
|
|
|
|
function TVpGanttView.GetPopupMenu: TPopupMenu;
|
|
|
|
begin
|
|
|
|
if FExternalPopup = nil then
|
|
|
|
Result := FDefaultPopup
|
|
|
|
else
|
|
|
|
Result := FExternalPopup;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.SetPopupMenu(AValue: TPopupMenu);
|
|
|
|
begin
|
|
|
|
if (AValue = nil) or (AValue = FDefaultPopup) then
|
|
|
|
FExternalPopup := nil
|
|
|
|
else
|
|
|
|
FExternalPopup := AValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.InitializeDefaultPopup;
|
|
|
|
var
|
|
|
|
NewItem: TVpMenuItem;
|
|
|
|
canEdit: Boolean;
|
|
|
|
begin
|
|
|
|
canEdit := (FActiveEvent <> nil) and FActiveEvent.CanEdit;
|
|
|
|
FDefaultPopup.Items.Clear;
|
|
|
|
|
|
|
|
if RSPopupAddEvent <> '' then begin // Add
|
|
|
|
NewItem := TVpMenuItem.Create(Self);
|
|
|
|
NewItem.Kind := mikAddEvent;
|
|
|
|
NewItem.OnClick := @PopupAddEvent;
|
|
|
|
NewItem.Tag := 0;
|
|
|
|
FDefaultPopup.Items.Add(NewItem);
|
|
|
|
end;
|
|
|
|
|
|
|
|
if RSPopupEditEvent <> '' then begin // Edit
|
|
|
|
NewItem := TVpMenuItem.Create(Self);
|
|
|
|
NewItem.Kind := mikEditEvent;
|
|
|
|
NewItem.Enabled := canEdit;
|
|
|
|
NewItem.OnClick := @PopupEditEvent;
|
|
|
|
NewItem.Tag := 1;
|
|
|
|
FDefaultPopup.Items.Add(NewItem);
|
|
|
|
end;
|
|
|
|
|
|
|
|
if RSPopupDeleteEvent <> '' then begin // Delete
|
|
|
|
NewItem := TVpMenuItem.Create(Self);
|
|
|
|
NewItem.Kind := mikDeleteEvent;
|
|
|
|
NewItem.Enabled := canEdit;
|
|
|
|
NewItem.OnClick := @PopupDeleteEvent;
|
|
|
|
NewItem.Tag := 1;
|
|
|
|
FDefaultPopup.Items.Add(NewItem);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.PopupAddEvent(Sender: TObject);
|
|
|
|
var
|
2022-08-29 15:09:38 +00:00
|
|
|
startTime: TDateTime;
|
|
|
|
endTime: TDateTime;
|
2022-08-28 23:41:45 +00:00
|
|
|
begin
|
|
|
|
if ReadOnly or (not CheckCreateResource) or
|
|
|
|
(not Assigned(DataStore)) or (not Assigned(DataStore.Resource))
|
|
|
|
then
|
|
|
|
Exit;
|
2022-08-29 15:09:38 +00:00
|
|
|
|
|
|
|
// Create the new event as an all-day event for the clicked day.
|
2022-09-14 21:24:33 +00:00
|
|
|
startTime := DatePart(FActiveDate);
|
2022-08-29 15:09:38 +00:00
|
|
|
endTime := startTime + 1 - OneMilliSecond;
|
2022-08-28 23:41:45 +00:00
|
|
|
FActiveEvent := DataStore.Resource.Schedule.AddEvent(
|
|
|
|
DataStore.GetNextID(EventsTableName),
|
|
|
|
StartTime,
|
|
|
|
EndTime
|
|
|
|
);
|
2022-08-29 15:09:38 +00:00
|
|
|
FActiveEvent.AllDayEvent := true;
|
2022-08-28 23:41:45 +00:00
|
|
|
|
2022-08-29 15:09:38 +00:00
|
|
|
// Edit this new event
|
|
|
|
SpawnEventEditDialog(True);
|
2022-08-28 23:41:45 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.PopupDeleteEvent(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if ReadOnly then
|
|
|
|
Exit;
|
|
|
|
Invalidate;
|
|
|
|
if FActiveEvent <> nil then
|
|
|
|
DeleteActiveEvent(True);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TVpGanttView.PopupEditEvent(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if ReadOnly then
|
|
|
|
Exit;
|
|
|
|
Invalidate;
|
|
|
|
if FActiveEvent <> nil then
|
|
|
|
// edit this event
|
|
|
|
SpawnEventEditDialog(false);
|
|
|
|
end;
|
2022-08-29 15:09:38 +00:00
|
|
|
|
2022-08-31 20:56:36 +00:00
|
|
|
procedure TVpGanttView.UpdatePopupMenuState;
|
2022-08-29 15:09:38 +00:00
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
if Assigned(FActiveEvent) then
|
|
|
|
begin
|
|
|
|
for i := 0 to FDefaultPopup.Items.Count - 1 do
|
|
|
|
FDefaultPopup.Items[i].Enabled := True;
|
|
|
|
end else
|
|
|
|
for i := 0 to FDefaultPopup.Items.Count - 1 do
|
|
|
|
if (FDefaultPopup.Items[i].Tag = 1) or (ReadOnly) then
|
|
|
|
FDefaultPopup.Items[i].Enabled := False;
|
2022-08-28 23:41:45 +00:00
|
|
|
end;
|
|
|
|
|
2022-08-22 21:01:27 +00:00
|
|
|
end.
|
|
|
|
|