tvplanit: Add preview form for ical import of task items.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8377 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2022-08-10 21:21:17 +00:00
parent a2ba5cddc7
commit 47dcca9bed
14 changed files with 248 additions and 56 deletions

View File

@ -775,6 +775,11 @@ msgstr "Komponente muss mit einem TVpControlLink verbunden sein"
msgid "Day increment unit not specified."
msgstr "Intervall nicht angegeben."
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr "Keine Termine gefunden in \"%s\"."
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr "Fehler: Datastore-Dateiname nicht angegeben."
@ -811,6 +816,11 @@ msgstr "Es sind keine Druckformate definiert"
msgid "Normal"
msgstr "Normal"
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr "Keine Aufgaben gefunden in \"%s\"."
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "Dieses Feature ist zur Zeit nicht implementiert."

View File

@ -697,7 +697,7 @@ msgstr "Monthly by day"
#: vpsr.rsmonthlyon
#, object-pascal-format
msgid "Monthly on day %s"
msgstr ""
msgstr "Monthly on day %s"
#: vpsr.rsmonths
msgid "Months"
@ -767,6 +767,11 @@ msgstr "Component must be linked to a TVpControlLink"
msgid "Day increment unit not specified."
msgstr "Day increment unit not specified."
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr "No event items found in \"%s\"."
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr "Error: No datastore filename specified."
@ -803,6 +808,11 @@ msgstr "No print formats have been defined"
msgid "Normal"
msgstr "Normal"
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr "No task items found in \"%s\"."
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "This feature is not implemented at this time."

View File

@ -766,6 +766,11 @@ msgstr ""
msgid "Day increment unit not specified."
msgstr ""
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr ""
@ -802,6 +807,11 @@ msgstr ""
msgid "Normal"
msgstr ""
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr ""

View File

@ -781,6 +781,11 @@ msgstr "Le composant doit être lié à un TVpControlLink"
msgid "Day increment unit not specified."
msgstr ""
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr ""
@ -817,6 +822,11 @@ msgstr "Formats d'impression non-défini"
msgid "Normal"
msgstr ""
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "Cette fonctionnalité n'est pas mise en œuvre à ce moment"

View File

@ -775,6 +775,11 @@ msgstr "Component moet moet met een TVpControlLink verbonden zijn"
msgid "Day increment unit not specified."
msgstr ""
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr ""
@ -811,6 +816,11 @@ msgstr "Er zijn geen afdrukformaten gedefinieerd."
msgid "Normal"
msgstr ""
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "Dit onderdeel is nog niet geimplementeerd."

View File

@ -775,6 +775,11 @@ msgstr "Komponent musi być przypisany do TVpControlLink."
msgid "Day increment unit not specified."
msgstr "Nie określono interwału dnia."
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr "Błąd: Brak nazwy datastore."
@ -811,6 +816,11 @@ msgstr "Nie zdefiniowano formatu wydruku"
msgid "Normal"
msgstr "Normalny"
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "Brak implementacji funkcji."

View File

@ -757,6 +757,11 @@ msgstr ""
msgid "Day increment unit not specified."
msgstr ""
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr ""
@ -793,6 +798,11 @@ msgstr ""
msgid "Normal"
msgstr ""
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr ""

View File

@ -775,6 +775,11 @@ msgstr ""
msgid "Day increment unit not specified."
msgstr ""
#: vpsr.rsnoeventitemsfoundinical
#, object-pascal-format
msgid "No event items found in \"%s\"."
msgstr ""
#: vpsr.rsnofilenamespecified
msgid "Error: No datastore filename specified."
msgstr ""
@ -811,6 +816,11 @@ msgstr "Не определён формат печати"
msgid "Normal"
msgstr ""
#: vpsr.rsnotaskitemsfoundinical
#, object-pascal-format
msgid "No task items found in \"%s\"."
msgstr ""
#: vpsr.rsnotdoneyet
msgid "This feature is not implemented at this time."
msgstr "Эта возможность не рализована."

View File

@ -183,6 +183,8 @@ resourcestring
RSOverlayed = 'Overlayed';
RSICalFilter = 'iCalendar files (*.ical;*.ics)|*.ical;*.ics';
RSRepeat = 'Repeat:';
RSNoEventItemsFoundInICAL = 'No event items found in "%s".';
RSNoTaskItemsFoundInICAL = 'No task items found in "%s".';
{Task Specific}
RSConfirmDeleteTask = 'Delete this task from your list?';

View File

@ -313,7 +313,8 @@ type
procedure DeleteResource(Res: TVpResource);
function FindResource(const AResourceName: String): TVpResource;
function FindBestCategory(const ACategoryNames: TStrings): String;
function FindBestEventCategory(const ACategoryNames: TStrings): String;
function FindBestTaskCategory(const ACategoryNames: TStrings): String;
property Connected : boolean read FConnected write SetConnected;
property Loading : Boolean read FLoading write FLoading;
@ -548,12 +549,12 @@ end;
{ ACategoryNames is a list of category names, e.g. like in ical files.
Searches among the datastore's categories whether there is one which matches
an element of the category names and returns its name. }
function TVpCustomDatastore.FindBestCategory(const ACategoryNames: TStrings): String;
function TVpCustomDatastore.FindBestEventCategory(const ACategoryNames: TStrings): String;
var
i, j: Integer;
begin
Result := '';
if ACategoryNames.Count > 0 then begin
if Assigned(ACategoryNames) and (ACategoryNames.Count > 0) then begin
for i := 0 to ACategoryNames.Count-1 do begin
j := CategoryColorMap.IndexOfCategory(ACategoryNames[i]);
if j <> -1 then begin
@ -564,6 +565,31 @@ begin
end;
end;
function TVpCustomDatastore.FindBestTaskCategory(const ACategoryNames: TStrings): String;
const
CAT_NAMES: array[TVpCategoryType] of string = ('BUSINESS', 'CLIENTS', 'FAMILY', 'OTHER', 'PERSONAL');
var
i: Integer;
catName: String;
ct: TVpCategoryType;
begin
Result := '';
if (ACategoryNames <> nil) then
begin
for i := 0 to ACategoryNames.Count-1 do
begin
catName := Uppercase(ACategoryNames[i]);
for ct in TVpCategoryType do
if catName = CAT_NAMES[ct] then
begin
Result := CategoryLabel(ct);
exit;
end;
end;
end;
Result := CategoryLabel(ctOther);
end;
procedure TVpCustomDataStore.DeregisterAllWatchers;
var
i: Integer;

View File

@ -384,7 +384,8 @@ type
procedure DeleteTask(Task: TVpTask);
function First: TVpTask;
function FirstByDay(Date: TDateTime): TVpTask;
function ImportICalFile(const AFileName: String; ADefaultCategory: Integer = -1): TVpTaskArr;
function ImportICalFile(const AFileName: String; APreview: Boolean = false;
ADefaultCategory: Integer = -1): TVpTaskArr;
function IndexOf(ATask: TVpTask): Integer;
function Last: TVpTask;
function LastByDay(Date: TDateTime): TVpTask;
@ -436,6 +437,7 @@ type
constructor Create(Owner: TVpTasks);
destructor Destroy; override;
procedure LoadFromICalendar(AEntry: TVpICalToDo);
class function GetTaskPriority(APriority:Integer): TVpTaskPriority;
property Loading: Boolean read FLoading write FLoading;
property Changed: Boolean read FChanged write SetChanged;
property Deleted: Boolean read FDeleted write FDeleted;
@ -1404,7 +1406,7 @@ begin
datastore := TVpCustomDatastore(Owner.Owner.Owner.Owner);
k := -1;
for i := 0 to AEntry.CategoryCount-1 do begin
cat := AEntry.category[i];
cat := AEntry.Category[i];
j := datastore.CategoryColorMap.IndexOfCategory(cat);
if j <> -1 then begin
k := j;
@ -1762,6 +1764,11 @@ begin
ical := TVpICalendar.Create;
try
ical.LoadFromFile(AFileName);
if ical.EventCount = 0 then
begin
MessageDlg(Format(RSNoEventItemsFoundInICAL, [AFileName]), mtInformation, [mbOK], 0);
exit;
end;
if APreview then
begin
previewForm := TVpICalPreviewForm.Create(nil);
@ -1772,7 +1779,10 @@ begin
if ADefaultCategory <> -1 then
previewForm.DefaultCategory := datastore.CategoryColorMap.GetCategoryName(ADefaultCategory);
if not previewForm.Execute then
begin
SetLength(Result, 0);
exit;
end;
end;
for i := 0 to ical.Count-1 do begin
@ -1793,8 +1803,10 @@ begin
if eventCounter mod BLOCK_SIZE = 0 then
SetLength(Result, eventCounter + BLOCK_SIZE);
end;
finally
SetLength(Result, eventCounter);
if Length(Result) = 0 then
MessageDlg(Format(RSNoEventItemsFoundInICAL, [AFileName]), mtInformation, [mbOK], 0);
finally
if APreview then
previewForm.Free;
ical.Free;
@ -2808,6 +2820,18 @@ begin
inherited;
end;
{ Converts the priority numbers from ical files (1=highest ... 9=lowest) to the
TvPlanit type TVpTaskPriority. }
class function TVpTask.GetTaskPriority(APriority:Integer): TVpTaskPriority;
begin
if APriority <= 3 then
Result := tpHigh
else if APriority >= 7 then
Result := tpLow
else
Result := tpNormal;
end;
function TVpTask.IsOverdue: Boolean;
begin
result := (Trunc(DueDate) < now + 1);
@ -2816,9 +2840,8 @@ end;
procedure TVpTask.LoadFromICalendar(AEntry: TVpICalToDo);
var
cat: String;
i: Integer;
ct: TVpCategoryType;
catFound: Boolean;
datastore: TVpCustomDatastore;
begin
if AEntry = nil then
exit;
@ -2834,31 +2857,20 @@ begin
FComplete := SameText(AEntry.Status, 'COMPLETED');
{ Priority }
case AEntry.Priority of
1, 2, 3: FPriority := ord(tpHigh);
4, 5, 6: FPriority := ord(tpNormal);
7, 8, 9: FPriority := ord(tpLow);
else FPriority := ord(tpNormal);
end;
FPriority := ord(GetTaskPriority(AEntry.Priority));
{ Category }
{ tvplanit has only 1 category, ical may have several. We pick the first one
defined by TVpCategorytype. If none is found we select ctOther. }
FCategory := ord(ctOther);
catFound := false;
if AEntry.CategoryCount > 0 then begin
for i := 0 to AEntry.CategoryCount-1 do begin
cat := AEntry.category[i];
for ct in TVpCategoryType do begin
if cat = CategoryLabel(ct) then begin
FCategory := ord(ct);
catFound := true;
break;
end;
end;
if catFound then break;
FCategory :=ord(ctOther);
datastore := TVpCustomDatastore(Owner.Owner.Owner.Owner);
cat := datastore.FindBestTaskCategory(AEntry.Categories);
for ct in TVpCategoryType do
if cat = CategoryLabel(ct) then
begin
FCategory := ord(ct);
break;
end;
end;
end;
procedure TVpTask.SetCategory(const Value: Integer);
@ -2985,8 +2997,8 @@ begin
result := FTaskList.Count;
end;
function TVpTasks.ImportICalFile(const AFileName: String;
ADefaultCategory: Integer = -1): TVpTaskArr;
function TVpTasks.ImportICalFile(const AFileName: String;
APreview: Boolean = false; ADefaultCategory: Integer = -1): TVpTaskArr;
const
BLOCK_SIZE = 10;
var
@ -2996,6 +3008,7 @@ var
task: TVpTask;
taskCounter: Integer;
datastore: TVpCustomDatastore;
previewForm: TVpICalPreviewForm = nil;
begin
Result := nil;
SetLength(Result, BLOCK_SIZE);
@ -3006,9 +3019,31 @@ begin
ical := TVpICalendar.Create;
try
ical.LoadFromFile(AFileName);
if ical.ToDoCount = 0 then
begin
MessageDlg(Format(RSNoTaskItemsFoundInICAL, [AFileName]), mtInformation, [mbOK], 0);
exit;
end;
if APreview then
begin
previewForm := TVpICalPreviewForm.Create(nil);
previewForm.Position := poMainFormCenter;
previewForm.Kind := icToDo;
previewForm.Calendar := ical;
previewForm.Datastore := datastore;
if ADefaultCategory <> -1 then
previewForm.DefaultCategory := CategoryLabel(TVpCategoryType(ADefaultCategory));
if not previewForm.Execute then
begin
SetLength(Result, 0);
exit;
end;
end;
for i := 0 to ical.Count-1 do begin
if not (ical[i] is TVpICalToDo) then
if ical[i].Skip or (not (ical[i] is TVpICalToDo)) then
Continue;
id := dataStore.GetNextID(TasksTableName);
task := AddTask(id);
task.Changed := true;
@ -3021,7 +3056,11 @@ begin
SetLength(Result, taskCounter + BLOCK_SIZE);
end;
SetLength(Result, taskCounter);
if Length(Result) = 0 then
MessageDlg(Format(RSNoTaskItemsFoundInICAL, [AFileName]), mtInformation, [mbOK], 0);
finally
if APreview then
previewForm.Free;
ical.Free;
end;
end;

View File

@ -10,6 +10,8 @@ uses
type
TVpICalendar = class;
TVpIcalItemKind = (icEvent, icToDo);
TVpICalItem = class(TVpFileItem)
public
function GetAttribute(AName: String): string;
@ -115,6 +117,7 @@ type
constructor Create(AOwner: TVpICalendar); override;
destructor Destroy; override;
procedure Analyze; override;
function Categories: TStrings;
property Summary: String read FSummary;
property Comment: String read FComment;
property StartTime[UTC: Boolean]: TDateTime read GetStartTime;
@ -132,6 +135,8 @@ type
FVersion: String;
function GetCount: Integer;
function GetEntry(AIndex: Integer): TVpICalEntry;
function GetEventCount: Integer;
function GetToDoCount: Integer;
protected
// Reading
procedure LoadFromStrings(const AStrings: TStrings);
@ -146,6 +151,8 @@ type
procedure LoadFromFile(const AFileName: String);
procedure LoadFromStream(const AStream: TStream);
property Count: Integer read GetCount;
property EventCount: Integer read GetEventCount;
property TodoCount: Integer read GetToDoCount;
property Entry[AIndex: Integer]: TVpICalEntry read GetEntry; default;
end;
@ -443,7 +450,7 @@ constructor TVpICalToDo.Create(AOwner: TVpICalendar);
begin
inherited;
FCategories := TStringList.Create;
FCategories.Delimiter := VALUE_DELIMITER;
FCategories.Delimiter := ','; // ToDo categories are separated by comma in ical file.
FCategories.StrictDelimiter := true;
end;
@ -501,6 +508,11 @@ begin
end;
end;
function TVpICalToDo.Categories: TStrings;
begin
Result := FCategories;
end;
function TVpICalToDo.GetCategory(AIndex: Integer): String;
begin
if (AIndex >= 0) and (AIndex < FCategories.Count) then
@ -576,6 +588,26 @@ begin
Result := FEntries[AIndex];
end;
function TVpICalendar.GetEventCount: Integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to High(FEntries) do
if (FEntries[i] is TVpICalEvent) then
inc(Result);
end;
function TVpICalendar.GetToDoCount: Integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to High(FEntries) do
if (FEntries[i] is TVpICalToDo) then
inc(Result);
end;
procedure TVpICalendar.LoadFromFile(const AFilename: String);
var
L: TStrings;

View File

@ -10,9 +10,7 @@ uses LazLogger,
VpData, VpIcal, VpBaseDS;
type
TVpIcalItemKind = (icEvent, icToDo);
{ TVpICalPreviewForm }
TVpICalPreviewForm = class(TForm)
@ -157,7 +155,7 @@ begin
// Categories
if Assigned(FDatastore) then
begin
cat := FDatastore.FindBestCategory(AEvent.Categories);
cat := FDatastore.FindBestEventCategory(AEvent.Categories);
if cat = '' then cat := FDefaultCategory;
Result := Result + cat;
end;
@ -219,8 +217,27 @@ begin
end;
function TVpICalPreviewForm.GetToDoText(AToDo: TVpICalToDo): String;
var
s: String;
cat: String;
begin
Result := '';
Result := RSDescriptionLbl + ' ' + AToDo.Summary + LineEnding +
RSDueDateLabel + ' ' + FormatDateTime(FTimeFormat, AToDo.DueTime[false]) + LineEnding +
RSCreatedOn + ' ' + FormatDateTime(FTimeFormat, AToDo.StartTime[false]);
case TVpTask.GetTaskPriority(AToDo.Priority) of
tpLow: s := RSLow;
tpNormal: s := RSNormal;
tpHigh: s := RSHigh;
end;
Result := Result + LineEnding + RSPriorityLabel + ' ' + s + LineEnding + RSCategoryLabel + ' ';
if Assigned(FDatastore) then
begin
cat := FDatastore.FindBestTaskCategory(AToDo.Categories);
if cat = '' then cat := FDefaultCategory;
Result := Result + cat;
end;
end;
procedure TVpICalPreviewForm.GridDrawCell(Sender: TObject; aCol, aRow: Integer;
@ -293,7 +310,7 @@ begin
icToDo:
for i := 0 to FCalendar.Count-1 do
if (FCalendar.Entry[i] is TVpICalToDo) then
FItems.Add(FCalendar.Entry[i]);
FItems.Add(FCalendar.Entry[i]);
end;
Grid.RowCount := Grid.FixedRows + FItems.Count;
CalcRowHeights;

View File

@ -216,7 +216,8 @@ type
procedure LinkHandler(Sender: TComponent; NotificationType: TVpNotificationType;
const Value: Variant); override;
function GetControlType: TVpItemType; override;
function ImportICalFile(const AFileName: String; ADefaultCategory: Integer = -1): TVpTaskArr;
function ImportICalFile(const AFileName: String; APreview: Boolean = false;
ADefaultCategory: Integer = -1): TVpTaskArr;
procedure PaintToCanvas(ACanvas: TCanvas; ARect: TRect; Angle: TVpRotationAngle);
procedure RenderToCanvas(RenderCanvas: TCanvas; RenderIn: TRect;
Angle: TVpRotationAngle; Scale: Extended; RenderDate: TDateTime;
@ -620,27 +621,22 @@ begin
Result := itTasks;
end;
function TVpTaskList.ImportICalFile(const AFileName: String;
ADefaultCategory: Integer = -1): TVpTaskArr;
function TVpTaskList.ImportICalFile(const AFileName: String;
APreview: Boolean = false; ADefaultCategory: Integer = -1): TVpTaskArr;
begin
if ReadOnly or (not CheckCreateResource) or
if ReadOnly or (not CheckCreateResource) or
(not Assigned(DataStore)) or (not Assigned(DataStore.Resource))
then
Exit;
Screen.Cursor := crHourglass;
try
Result := Datastore.Resource.Tasks.ImportICalFile(AFileName, ADefaultCategory);
if Length(Result) > 0 then
begin
FActiveTask := Result[High(Result)];
Datastore.PostTasks;
Datastore.NotifyDependents;
Invalidate;
end;
finally
Screen.Cursor := crDefault;
end;
Result := Datastore.Resource.Tasks.ImportICalFile(AFileName, APreview, ADefaultCategory);
if Length(Result) > 0 then
begin
FActiveTask := Result[High(Result)];
Datastore.PostTasks;
Datastore.NotifyDependents;
Invalidate;
end;
end;
procedure TVpTaskList.Paint;