tvplanit: Export events to ical files (still buggy).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8401 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2022-08-20 17:06:03 +00:00
parent cbf979cfcc
commit 8cdd98b2ff
16 changed files with 485 additions and 73 deletions

View File

@ -1023,11 +1023,6 @@ msgctxt "vpsr.rspopupaddevent"
msgid "Add event..." msgid "Add event..."
msgstr "Ereignis hinzufügen..." msgstr "Ereignis hinzufügen..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Von iCalendar-Datei(en) importieren..."
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1048,6 +1043,16 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "Ereignis bearbeiten..." msgstr "Ereignis bearbeiten..."
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
#, fuzzy
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Von iCalendar-Datei(en) importieren..."
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "Ereignisse überlagern" msgstr "Ereignisse überlagern"
@ -1206,6 +1211,10 @@ msgstr "Format in Datei speichern?"
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "Format speichern als \"%s\"?" msgstr "Format speichern als \"%s\"?"
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "Als vCard exportieren" msgstr "Als vCard exportieren"
@ -1798,3 +1807,4 @@ msgstr "Unbekannte Achsen-Spezifikation: %s"
#: vpsr.sxmldecnotatbeg #: vpsr.sxmldecnotatbeg
msgid "The XML declaration must appear before the first element" msgid "The XML declaration must appear before the first element"
msgstr "Die XML-Deklaration muss vor dem ersten Element erscheinen" msgstr "Die XML-Deklaration muss vor dem ersten Element erscheinen"

View File

@ -1014,11 +1014,6 @@ msgstr "Pixels"
msgid "Add event..." msgid "Add event..."
msgstr "Add event..." msgstr "Add event..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Import from iCalendar file(s)..."
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1036,6 +1031,16 @@ msgstr "&Delete event..."
msgid "Edit event..." msgid "Edit event..."
msgstr "Edit event..." msgstr "Edit event..."
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
#, fuzzy
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Import from iCalendar file(s)..."
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "Overlay events" msgstr "Overlay events"
@ -1192,6 +1197,10 @@ msgstr "Save format to file?"
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "Save format to \"%s\"?" msgstr "Save format to \"%s\"?"
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "Export to vCard" msgstr "Export to vCard"
@ -1779,3 +1788,4 @@ msgstr "Unknown axis specifier: %s"
#: vpsr.sxmldecnotatbeg #: vpsr.sxmldecnotatbeg
msgid "The XML declaration must appear before the first element" msgid "The XML declaration must appear before the first element"
msgstr "The XML declaration must appear before the first element" msgstr "The XML declaration must appear before the first element"

View File

@ -1014,11 +1014,6 @@ msgctxt "vpsr.rspopupaddevent"
msgid "Add event..." msgid "Add event..."
msgstr "" msgstr ""
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1039,6 +1034,15 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "" msgstr ""
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "" msgstr ""
@ -1197,6 +1201,10 @@ msgstr ""
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "" msgstr ""
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -1029,11 +1029,6 @@ msgctxt "vpsr.rspopupaddevent"
msgid "Add event..." msgid "Add event..."
msgstr "Ajouter un événement..." msgstr "Ajouter un événement..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1054,6 +1049,15 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "Modifier un événement" msgstr "Modifier un événement"
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "" msgstr ""
@ -1212,6 +1216,10 @@ msgstr ""
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "" msgstr ""
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -1023,11 +1023,6 @@ msgctxt "vpsr.rspopupaddevent"
msgid "Add event..." msgid "Add event..."
msgstr "Gebeurtenis toevoegen..." msgstr "Gebeurtenis toevoegen..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1048,6 +1043,15 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "Gebeurtenis bewerken..." msgstr "Gebeurtenis bewerken..."
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "" msgstr ""
@ -1206,6 +1210,10 @@ msgstr ""
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "" msgstr ""
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -1022,11 +1022,6 @@ msgstr "Pixele"
msgid "Add event..." msgid "Add event..."
msgstr "Dodaj wydarzenie..." msgstr "Dodaj wydarzenie..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Import z pliku iCalendar"
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1047,6 +1042,16 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "Edytuj wydarzenie..." msgstr "Edytuj wydarzenie..."
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
#, fuzzy
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr "Import z pliku iCalendar"
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "Nakładające się wydarzenia" msgstr "Nakładające się wydarzenia"
@ -1205,6 +1210,10 @@ msgstr "Zapisać dane do pliku?"
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "Zapisać format do \"%s\"?" msgstr "Zapisać format do \"%s\"?"
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -1004,11 +1004,6 @@ msgstr ""
msgid "Add event..." msgid "Add event..."
msgstr "" msgstr ""
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1026,6 +1021,15 @@ msgstr ""
msgid "Edit event..." msgid "Edit event..."
msgstr "" msgstr ""
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "" msgstr ""
@ -1182,6 +1186,10 @@ msgstr ""
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "" msgstr ""
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -1023,11 +1023,6 @@ msgctxt "vpsr.rspopupaddevent"
msgid "Add event..." msgid "Add event..."
msgstr "Добавить событие..." msgstr "Добавить событие..."
#: vpsr.rspopupaddeventfromical
msgctxt "vpsr.rspopupaddeventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupaddtaskfromical #: vpsr.rspopupaddtaskfromical
msgctxt "vpsr.rspopupaddtaskfromical" msgctxt "vpsr.rspopupaddtaskfromical"
msgid "Import from iCalendar file(s)..." msgid "Import from iCalendar file(s)..."
@ -1048,6 +1043,15 @@ msgctxt "vpsr.rspopupeditevent"
msgid "Edit event..." msgid "Edit event..."
msgstr "Изменить событие..." msgstr "Изменить событие..."
#: vpsr.rspopupexporteventtoical
msgid "Export to iCalendar file..."
msgstr ""
#: vpsr.rspopupimporteventfromical
msgctxt "vpsr.rspopupimporteventfromical"
msgid "Import from iCalendar file(s)..."
msgstr ""
#: vpsr.rspopupresourcegroups #: vpsr.rspopupresourcegroups
msgid "Overlay events" msgid "Overlay events"
msgstr "" msgstr ""
@ -1206,6 +1210,10 @@ msgstr ""
msgid "Save format to \"%s\"?" msgid "Save format to \"%s\"?"
msgstr "" msgstr ""
#: vpsr.rssaveicaltitle
msgid "Export to iCal file"
msgstr ""
#: vpsr.rssavevcardtitle #: vpsr.rssavevcardtitle
msgid "Export to vCard" msgid "Export to vCard"
msgstr "" msgstr ""

View File

@ -181,6 +181,7 @@ resourcestring
RSStartEndTimesEqual = 'Start and end times cannot be equal.'; RSStartEndTimesEqual = 'Start and end times cannot be equal.';
RSCannotEditOverlayedEvent= 'Cannot edit this overlayed event.'; RSCannotEditOverlayedEvent= 'Cannot edit this overlayed event.';
RSLoadICalTitle = 'Import from iCal file(s)'; RSLoadICalTitle = 'Import from iCal file(s)';
RSSaveICalTitle = 'Export to iCal file';
RSNoOverlayedEvents = 'none'; RSNoOverlayedEvents = 'none';
RSOverlayedEvent = 'overlayed'; RSOverlayedEvent = 'overlayed';
RSOverlayed = 'Overlayed'; RSOverlayed = 'Overlayed';
@ -207,7 +208,8 @@ resourcestring
{ Popup specific } { Popup specific }
RSPopupAddEvent = 'Add event...'; RSPopupAddEvent = 'Add event...';
RSPopupAddEventFromICal = 'Import from iCalendar file(s)...'; RSPopupImportEventFromICal= 'Import from iCalendar file(s)...';
RSPopupExportEventToICal = 'Export to iCalendar file...';
RSPopupEditEvent = 'Edit event...'; RSPopupEditEvent = 'Edit event...';
RSPopupDeleteEvent = '&Delete event...'; RSPopupDeleteEvent = '&Delete event...';
RSPopupChangeDate = 'Change date'; RSPopupChangeDate = 'Change date';

View File

@ -81,7 +81,8 @@ type
mikAddEvent, mikEditEvent, mikDeleteEvent, mikAddEvent, mikEditEvent, mikDeleteEvent,
mikAddTask, mikEditTask, mikDeleteTask, mikAddTask, mikEditTask, mikDeleteTask,
mikAddContact, mikEditContact, mikDeleteContact, mikAddContact, mikEditContact, mikDeleteContact,
mikImportEventFromICal, mikImportTaskFromICal, mikImportEventFromICal, mikExportEventToICal,
mikImportTaskFromICal,
mikImportContactFromVCards, mikExportContactToVCard, mikImportContactFromVCards, mikExportContactToVCard,
mikResourceGroups, mikNoOverlaidEvents, mikResourceGroups, mikNoOverlaidEvents,
mikChangeDate, mikCustomDate, mikToday, mikYesterday, mikTomorrow, mikChangeDate, mikCustomDate, mikToday, mikYesterday, mikTomorrow,
@ -94,7 +95,8 @@ type
RSPopupAddEvent, RSPopupEditEvent, RSPopupDeleteEvent, RSPopupAddEvent, RSPopupEditEvent, RSPopupDeleteEvent,
RSTaskPopupAdd, RSTaskPopupEdit, RSTaskPopupDelete, RSTaskPopupAdd, RSTaskPopupEdit, RSTaskPopupDelete,
RSContactPopupAdd, RSContactPopupEdit, RSContactPopupDelete, RSContactPopupAdd, RSContactPopupEdit, RSContactPopupDelete,
RSPopupAddEventFromICal, RSPopupAddTaskFromICal, RSPopupImportEventFromICal, RSPopupExportEventToICal,
RSPopupAddTaskFromICal,
RSContactPopupImportVCards, RSContactPopupExportVCard, RSContactPopupImportVCards, RSContactPopupExportVCard,
RSPopupResourceGroups, RSNoOverlayedEvents, RSPopupResourceGroups, RSNoOverlayedEvents,
RSPopupChangeDate, RSCustomDate, RSToday, RSYesterday, RSTomorrow, RSPopupChangeDate, RSCustomDate, RSToday, RSYesterday, RSTomorrow,

View File

@ -1676,7 +1676,6 @@ end;
procedure TVpContactGrid.PopupImportVCards(Sender: TObject); procedure TVpContactGrid.PopupImportVCards(Sender: TObject);
var var
dlg: TOpenDialog; dlg: TOpenDialog;
vcards: TVpVCards;
i: Integer; i: Integer;
fn: String; fn: String;
id: Integer; id: Integer;

View File

@ -241,6 +241,7 @@ type
procedure DeleteEvent(Event: TVpEvent); procedure DeleteEvent(Event: TVpEvent);
function EventCountByDay(Value: TDateTime): Integer; function EventCountByDay(Value: TDateTime): Integer;
procedure EventsByDate(Date: TDateTime; EventList: TList); procedure EventsByDate(Date: TDateTime; EventList: TList);
procedure ExportICalFile(const AFileName: String; const AEvents: TVpEventArr);
function GetEvent(Index: Integer): TVpEvent; function GetEvent(Index: Integer): TVpEvent;
function ImportICalFile(const AFileName: String; APreview: Boolean = false; function ImportICalFile(const AFileName: String; APreview: Boolean = false;
ADefaultCategory: Integer = -1): TVpEventArr; ADefaultCategory: Integer = -1): TVpEventArr;
@ -312,6 +313,7 @@ type
destructor Destroy; override; destructor Destroy; override;
function CanEdit: Boolean; function CanEdit: Boolean;
function CopyToSchedule(ASchedule: TVpSchedule): TVpEvent; function CopyToSchedule(ASchedule: TVpSchedule): TVpEvent;
function CreateICalEvent(ACalendar: TVpICalendar): TVpICalEvent;
class procedure GetAlarmParams(ATrigger: TDateTime; out AdvTime: Integer; class procedure GetAlarmParams(ATrigger: TDateTime; out AdvTime: Integer;
out AdvTimeUnits: TVpAlarmAdvType); out AdvTimeUnits: TVpAlarmAdvType);
function GetResource: TVpResource; function GetResource: TVpResource;
@ -1343,6 +1345,94 @@ begin
Result.UserField8 := FUserField9; Result.UserField8 := FUserField9;
end; end;
{ Warning: Recurrence and Alarm poorly tested... }
function TVpEvent.CreateICalEvent(ACalendar: TVpICalendar): TVpICalEvent;
var
datastore: TVpCustomDatastore;
begin
Result := TVpICalEvent.Create(ACalendar);
Result.Summary := FDescription;
Result.Description := FNotes;
Result.Location := FLocation;
Result.StartTime[false] := FStartTime;
Result.EndTime[false] := FEndTime + OneSecond;
datastore := TVpCustomDatastore(GetResource.Owner.Owner);
Result.Categories.Text := datastore.CategoryColorMap.GetCategoryName(FCategory);
Result.RecurrenceFrequency := '';
Result.RecurrenceInterval := 0;
Result.RecurrenceCount := 0;
Result.RecurrenceByXXX := '';
case FRepeatCode of
rtDaily:
begin
Result.RecurrenceFrequency := 'DAILY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime));
end;
rtWeekly:
begin
Result.RecurrenceFrequency := 'WEEKLY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime)) div 7;
end;
rtMonthlyByDay:
begin
Result.RecurrenceFrequency := 'MONTHLY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime)) div 30;
end;
rtMonthlyByDate:
begin
Result.RecurrenceFrequency := 'MONTHLY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime)) div 30;
end;
rtYearlyByDay:
begin
Result.RecurrenceFrequency := 'YEARLY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime)) div 365;
end;
rtYearlyByDate:
begin
Result.RecurrenceFrequency := 'YEARLY';
Result.RecurrenceInterval := 1;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime)) div 365;
end;
rtCustom:
begin
Result.RecurrenceFrequency := 'DAILY';
Result.RecurrenceInterval := FCustInterval;
if FRepeatRangeEnd <> FOREVER_DATE then
Result.RecurrenceCount := DaysBetween(DayOf(FRepeatRangeEnd), DayOf(FStartTime));
end;
else
;
end;
if AlarmSet then
begin
Result.UseAlarm(true);
Result.Alarm.Audio := (FDingPath <> '') and FileExists(FDingPath);
Result.Alarm.AudioSrc := FDingPath;
case FAlarmAdvType of
atMinutes: Result.Alarm.Trigger := FAlarmAdv * OneMinute;
atHours: Result.Alarm.Trigger := FAlarmAdv * OneHour;
atDays: Result.Alarm.Trigger := FAlarmAdv;
end;
Result.Alarm.RepeatCount := 1;
end;
end;
{ Returns the resource to which the event belongs. } { Returns the resource to which the event belongs. }
function TVpEvent.GetResource: TVpResource; function TVpEvent.GetResource: TVpResource;
begin begin
@ -1748,6 +1838,23 @@ begin
result := TVpEvent(FEventList[Index]); result := TVpEvent(FEventList[Index]);
end; end;
procedure TVpSchedule.ExportICalFile(const AFileName: String;
const AEvents: TVpEventArr);
var
cal: TVpICalendar;
lEvent: TVpEvent;
begin
cal := TVpICalendar.Create;
try
for lEvent in AEvents do
if lEvent <> nil then
cal.Add(lEvent.CreateICalEvent(cal));
cal.SaveToFile(AFileName);
finally
cal.Free;
end;
end;
function TVpSchedule.ImportICalFile(const AFileName: String; function TVpSchedule.ImportICalFile(const AFileName: String;
APreview: Boolean = false; ADefaultCategory: Integer = -1): TVpEventArr; APreview: Boolean = false; ADefaultCategory: Integer = -1): TVpEventArr;
const const

View File

@ -371,7 +371,8 @@ type
{ Popup } { Popup }
function GetPopupMenu: TPopupMenu; override; function GetPopupMenu: TPopupMenu; override;
procedure PopupAddEvent(Sender: TObject); procedure PopupAddEvent(Sender: TObject);
procedure PopupAddFromICalFile(Sender: TObject); procedure PopupImportICalFile(Sender: TObject);
procedure PopupExportICalFile(Sender: TObject);
procedure PopupDeleteEvent(Sender: TObject); procedure PopupDeleteEvent(Sender: TObject);
procedure PopupEditEvent(Sender: TObject); procedure PopupEditEvent(Sender: TObject);
procedure PopupToday(Sender: TObject); procedure PopupToday(Sender: TObject);
@ -451,7 +452,8 @@ type
function BuildEventString(AEvent: TVpEvent; UseAsHint: Boolean): String; function BuildEventString(AEvent: TVpEvent; UseAsHint: Boolean): String;
procedure DeleteActiveEvent(Verify: Boolean); procedure DeleteActiveEvent(Verify: Boolean);
procedure DragDrop(Source: TObject; X, Y: Integer); override; procedure DragDrop(Source: TObject; X, Y: Integer); override;
function ImportICalFile(const AFileName: String; APreview: Boolean = false; procedure ExportICalFile(const AFileName: String; const AEvents: TVpEventArr);
function ImportICalFile(const AFileName: String; APreview: Boolean = false;
ADefaultCategory: Integer = -1): TVpEventArr; ADefaultCategory: Integer = -1): TVpEventArr;
procedure Invalidate; override; procedure Invalidate; override;
function IsHoliday(ADate: TDate; out AHolidayName: String): Boolean; function IsHoliday(ADate: TDate; out AHolidayName: String): Boolean;
@ -1197,14 +1199,22 @@ begin
NewItem.Kind := mikSeparator; NewItem.Kind := mikSeparator;
FDefaultPopup.Items.Add(NewItem); FDefaultPopup.Items.Add(NewItem);
if RSPopupAddEventFromICal <> '' then begin if RSPopupImportEventFromICal <> '' then begin
NewItem := TVpMenuItem.Create(Self); // Import from iCal NewItem := TVpMenuItem.Create(Self); // Import from iCal
NewItem.Kind := mikImportEventFromICal; NewItem.Kind := mikImportEventFromICal;
NewItem.OnClick := PopupAddFromICalFile; NewItem.OnClick := PopupImportICalFile;
NewItem.Tag := 0; NewItem.Tag := 0;
FDefaultPopup.Items.Add(NewItem); FDefaultPopup.Items.Add(NewItem);
end; end;
if RSPopupExportEventToICal <> '' then begin // Export ical
NewItem := TVpMenuItem.Create(Self);
NewItem.Kind := mikExportEventToICal;
NewItem.OnClick := PopupExportICalFile;
NewItem.Tag := 1;
FDefaultPopup.Items.Add(NewItem);
end;
NewItem := TVpMenuItem.Create(Self); // ---- NewItem := TVpMenuItem.Create(Self); // ----
NewItem.Kind := mikSeparator; NewItem.Kind := mikSeparator;
FDefaultPopup.Items.Add(NewItem); FDefaultPopup.Items.Add(NewItem);
@ -1363,7 +1373,29 @@ begin
dvSpawnEventEditDialog(True); dvSpawnEventEditDialog(True);
end; end;
procedure TVpDayView.PopupAddFromICalFile(Sender: TObject); procedure TVpDayView.PopupExportICalFile(Sender: TObject);
var
dlg: TSaveDialog;
begin
if (not Assigned(Datastore)) or (not Assigned(Datastore.Resource)) or
(FActiveEvent = nil)
then
exit;
dlg := TSaveDialog.Create(nil);
try
dlg.Title := RSSaveICalTitle;
dlg.Filter := RSICalFilter;
dlg.FileName := '';
dlg.Options := dlg.Options - [ofAllowMultiSelect] + [ofOverwritePrompt];
if dlg.Execute then
ExportICalFile(dlg.FileName, [FActiveEvent]);
finally
dlg.Free;
end;
end;
procedure TVpDayView.PopupImportICalFile(Sender: TObject);
var var
dlg: TOpenDialog; dlg: TOpenDialog;
fn: String; fn: String;
@ -2353,7 +2385,6 @@ begin
Invalidate; Invalidate;
dvInPlaceEditor.SetFocus; dvInPlaceEditor.SetFocus;
end; end;
{=====}
procedure TVpDayView.EndEdit(Sender: TObject); procedure TVpDayView.EndEdit(Sender: TObject);
begin begin
@ -2377,6 +2408,15 @@ begin
end; end;
end; end;
procedure TVpDayview.ExportICalFile(const AFileName: String;
const AEvents: TVpEventArr);
begin
if (not Assigned(Datastore)) or (not Assigned(Datastore.Resource)) then
exit;
Datastore.Resource.Schedule.ExportICalFile(AFileName, AEvents);
end;
{ Reads the events listed in the specified ical file and adds them to the { Reads the events listed in the specified ical file and adds them to the
day view control. All events imported are collected in the Result array. day view control. All events imported are collected in the Result array.
ADefaultCategory is the category to which the event is assigned if no fitting ADefaultCategory is the category to which the event is assigned if no fitting

View File

@ -19,6 +19,8 @@ type
private private
FCalendar: TVpICalendar; FCalendar: TVpICalendar;
FChecked: Boolean; FChecked: Boolean;
protected
procedure SaveToStrings(const AList: TStrings); virtual;
public public
constructor Create(ACalendar: TVpICalendar); virtual; constructor Create(ACalendar: TVpICalendar); virtual;
function FindItem(AKey: String): TVpICalItem; function FindItem(AKey: String): TVpICalItem;
@ -39,13 +41,15 @@ type
FTrigger: Double; // "AlarmAdvance" FTrigger: Double; // "AlarmAdvance"
FAudio: Boolean; FAudio: Boolean;
FAudioSrc: String; FAudioSrc: String;
protected
procedure SaveToStrings(const AList: TStrings); override;
public public
procedure Analyze; override; procedure Analyze; override;
property Duration: Double read FDuration; property Duration: Double read FDuration write FDuration;
property RepeatCount: Integer read FRepeat; property RepeatCount: Integer read FRepeat write FRepeat;
property Trigger: Double read FTrigger; property Trigger: Double read FTrigger write FTrigger;
property Audio: Boolean read FAudio; property Audio: Boolean read FAudio write FAudio;
property AudioSrc: String read FAudioSrc; property AudioSrc: String read FAudioSrc write FAudioSrc;
end; end;
{ TVpICalEvent } { TVpICalEvent }
@ -72,26 +76,29 @@ type
function GetCategoryCount: Integer; function GetCategoryCount: Integer;
function GetEndTime(UTC: Boolean): TDateTime; function GetEndTime(UTC: Boolean): TDateTime;
function GetStartTime(UTC: Boolean): TDateTime; function GetStartTime(UTC: Boolean): TDateTime;
procedure SetEndTime(UTC: Boolean; const AValue: TDateTime);
procedure SetStartTime(UTC: Boolean; const AValue: TDateTime);
public public
constructor Create(ACalendar: TVpICalendar); override; constructor Create(ACalendar: TVpICalendar); override;
destructor Destroy; override; destructor Destroy; override;
procedure Analyze; override; procedure Analyze; override;
function Categories: TStrings; function Categories: TStrings;
function IsAllDayEvent: Boolean; function IsAllDayEvent: Boolean;
procedure UseAlarm; procedure SaveToStrings(const AList: TStrings); override;
property Summary: String read FSummary; // is "Description" of tvp procedure UseAlarm(AEnable: Boolean);
property Description: String read FDescription; // is "Notes" of tvp property Summary: String read FSummary write FSummary; // is "Description" of tvp
property Location: String read FLocation; property Description: String read FDescription write FDescription; // is "Notes" of tvp
property StartTime[UTC: Boolean]: TDateTime read GetStartTime; property Location: String read FLocation write FLocation;
property EndTime[UTC: Boolean]: TDateTime read GetEndTime; property StartTime[UTC: Boolean]: TDateTime read GetStartTime write SetStartTime;
property EndTime[UTC: Boolean]: TDateTime read GetEndTime write SetEndTime;
property Category[AIndex: Integer]: String read GetCategory; property Category[AIndex: Integer]: String read GetCategory;
property CategoryCount: Integer read GetCategoryCount; property CategoryCount: Integer read GetCategoryCount;
property Alarm: TVpICalAlarm read FAlarm; property Alarm: TVpICalAlarm read FAlarm;
property RecurrenceFrequency: String read FRecurrenceFreq; property RecurrenceFrequency: String read FRecurrenceFreq write FRecurrenceFreq;
property RecurrenceInterval: Integer read FRecurrenceInterval; property RecurrenceInterval: Integer read FRecurrenceInterval write FRecurrenceInterval;
property RecurrenceEndDate: TDateTime read FRecurrenceEndDate; property RecurrenceEndDate: TDateTime read FRecurrenceEndDate write FRecurrenceEndDate;
property RecurrenceCount: Integer read FRecurrenceCount; property RecurrenceCount: Integer read FRecurrenceCount write FRecurrenceCount;
property RecurrenceByXXX: String read FRecurrenceByXXX; property RecurrenceByXXX: String read FRecurrenceByXXX write FRecurrenceByXXX;
property PickedCategory: Integer read FPickedCategory write FPickedCategory; property PickedCategory: Integer read FPickedCategory write FPickedCategory;
end; end;
@ -120,6 +127,7 @@ type
destructor Destroy; override; destructor Destroy; override;
procedure Analyze; override; procedure Analyze; override;
function Categories: TStrings; function Categories: TStrings;
procedure SaveToStrings(const AList: TStrings); override;
property Summary: String read FSummary; property Summary: String read FSummary;
property Comment: String read FComment; property Comment: String read FComment;
property StartTime[UTC: Boolean]: TDateTime read GetStartTime; property StartTime[UTC: Boolean]: TDateTime read GetStartTime;
@ -143,6 +151,8 @@ type
protected protected
// Reading // Reading
procedure LoadFromStrings(const AStrings: TStrings); procedure LoadFromStrings(const AStrings: TStrings);
// Writing
procedure SaveToStrings(const AList: TStrings);
// Time conversion // Time conversion
function ConvertTime(ADateTime: TDateTime; ATimeZoneID: String; ToUTC: Boolean): TDateTime; function ConvertTime(ADateTime: TDateTime; ATimeZoneID: String; ToUTC: Boolean): TDateTime;
function LocalTimeToUTC(ADateTime: TDateTime; ATimeZoneID: String): TDateTime; function LocalTimeToUTC(ADateTime: TDateTime; ATimeZoneID: String): TDateTime;
@ -150,13 +160,17 @@ type
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
procedure Add(AEntry: TVpICalEntry);
procedure Clear; procedure Clear;
procedure LoadFromFile(const AFileName: String); procedure LoadFromFile(const AFileName: String);
procedure LoadFromStream(const AStream: TStream); procedure LoadFromStream(const AStream: TStream);
procedure SaveToFile(const AFileName: String);
procedure SaveToStream(const AStream: TStream);
property Count: Integer read GetCount; property Count: Integer read GetCount;
property EventCount: Integer read GetEventCount; property EventCount: Integer read GetEventCount;
property TodoCount: Integer read GetToDoCount; property TodoCount: Integer read GetToDoCount;
property Entry[AIndex: Integer]: TVpICalEntry read GetEntry; default; property Entry[AIndex: Integer]: TVpICalEntry read GetEntry; default;
property Version: String read FVersion write FVersion;
end; end;
@ -165,6 +179,9 @@ implementation
uses uses
VpConst, VpBase; VpConst, VpBase;
const
TIME_FORMAT = 'yyyymmdd"T"hhnnss';
TIME_FORMAT_UTC = TIME_FORMAT + '"Z"';
// Examples: 19970702T160000, or T123000, or 20120101 // Examples: 19970702T160000, or T123000, or 20120101
function iCalDateTime(AText: String; out IsUTC: Boolean): TDateTime; function iCalDateTime(AText: String; out IsUTC: Boolean): TDateTime;
@ -208,6 +225,34 @@ begin
IsUTC := AText[Length(AText)] = 'Z'; IsUTC := AText[Length(AText)] = 'Z';
end; end;
function IsInteger(d, Epsilon: Double): Boolean;
begin
Result := abs(d - round(d)) < Epsilon;
end;
// Converts a duration (in day units) to a string according to ical specs
function Duration2iCalStr(AValue: double): String;
var
isNeg: Boolean = false;
begin
if AValue < 0 then
begin
isNeg := true;
AValue := -AValue;
end;
if IsInteger(AValue, 1.0 / SecondsInDay) then
Result :=Format('P%dS', [round(AValue * SecondsInDay)])
else if IsInteger(AValue, 1.0/MinutesInDay) then
Result := Format('P%dM', [round(AValue * MinutesInDay)])
else if IsInteger(AValue, 1.0/HoursInday) then
Result := Format('P%dH', [round(AValue * HoursInDay)])
else if IsInteger(AValue, 1.0) then
Result := Format('P%dD', [round(AValue)])
else
Result := Format('P%D%s', [trunc(AValue), FormatDateTime('h"H"n"M"s"S"', AValue)]);
if isNeg then Result := '-' + Result;
end;
// Example: PT0H20M0S, or -PT15M, or -P2D // Example: PT0H20M0S, or -PT15M, or -P2D
function iCalDuration(AText: String): Double; function iCalDuration(AText: String): Double;
var var
@ -292,6 +337,9 @@ begin
Result := TVpICalItem(inherited FindItem(AKey, '')); Result := TVpICalItem(inherited FindItem(AKey, ''));
end; end;
procedure TVpICalEntry.SaveToStrings(const AList: TStrings);
begin
end;
{==============================================================================} {==============================================================================}
{ TVpICalAlarm } { TVpICalAlarm }
@ -320,6 +368,22 @@ begin
end; end;
end; end;
procedure TVpICalAlarm.SaveToStrings(const AList: TStrings);
begin
AList.Add('BEGIN:VALARM');
if Audio then
AList.Add('ACTION:AUDIO');
if AudioSrc <> '' then
AList.Add('ATTACH;FMTTYPE=AUDIO:' + AudioSrc);
if RepeatCount > 0 then
AList.Add('REPEAT:' + IntToStr(RepeatCount));
if Trigger <> 0 then
AList.Add('TRIGGER;VALUE=DURATION:' + Duration2iCalStr(Trigger));
if Duration <> 0 then
AList.Add('DURATION;VALUE=DURATION:' + Duration2iCalStr(Duration));
AList.Add('END:VALARM');
end;
{==============================================================================} {==============================================================================}
{ TVpICalEvent } { TVpICalEvent }
@ -471,10 +535,80 @@ begin
Result := false; Result := false;
end; end;
procedure TVpICalEvent.UseAlarm; procedure TVpICalEvent.SaveToStrings(const AList: TStrings);
var
lKey: String = '';
lValue: String = '';
begin begin
FAlarm.Free; AList.Add('BEGIN:VEVENT');
FAlarm := TVpICalAlarm.Create(FCalendar);
if FSummary <> '' then
AList.Add('SUMMARY:' + FSummary);
if FDescription <> '' then
AList.Add('DESCRIPTION:' + FDescription);
if FLocation <> '' then
AList.Add('LOCATION:' + FLocation);
if FCategories.Count > 0 then
AList.Add('CATEGORIES:' + FCategories.CommaText);
// todo: check time zones!
if FStartTimeTZ <> '' then
lKey := 'DTSTART;TZID=' + FStartTimeTZ + ':'
else
lKey := 'DTSTART:';
AList.Add(lKey + FormatDateTime(TIME_FORMAT_UTC, StartTime[true]));
if FEndTimeTZ <> '' then
lKey := 'DTEND;TZID=' + FEndTimeTZ + ':'
else
lKey := 'DTEND:';
AList.Add(lKey + FormatDateTime(TIME_FORMAT_UTC, EndTime[true]));
if RecurrenceFrequency <> '' then
begin
lKey := 'RRULE:';
lValue := 'FREQ=' + RecurrenceFrequency;
if RecurrenceInterval > 0 then
lValue := lValue + ';INTERVAL=' + IntToStr(RecurrenceInterval);
if RecurrenceEndDate <> 0 then
lValue := lValue + ';UNTIL=' + FormatDateTime(TIME_FORMAT, RecurrenceEndDate);
if RecurrenceCount > 0 then
lValue := lValue + ';COUNT=' + IntToStr(RecurrenceCount);
if RecurrenceByXXX <> '' then
lValue := lValue + ';' + RecurrenceByXXX;
AList.Add(lKey + lValue);
end;
if Alarm <> nil then
Alarm.SaveToStrings(AList);
end;
procedure TVpICalEvent.SetEndTime(UTC: Boolean; const AValue: TDateTime);
begin
if AValue = NO_DATE then
FEndTime := NO_DATE
else
if UTC then
FEndTime := AValue
else
FEndTime := FCalendar.LocalTimeToUTC(AValue, FEndTimeTZ);
end;
procedure TVpICalEvent.SetStartTime(UTC: Boolean; const AValue: TDateTime);
begin
if AValue = NO_DATE then
FStartTime := NO_DATE
else
if UTC then
FStartTime := AValue
else
FStartTime := FCalendar.LocalTimeToUTC(AValue, FStartTimeTZ);
end;
procedure TVpICalEvent.UseAlarm(AEnable: Boolean);
begin
FreeAndNil(FAlarm);
if AEnable then
FAlarm := TVpICalAlarm.Create(FCalendar);
end; end;
@ -589,6 +723,10 @@ begin
Result := FCalendar.LocalTimeToUTC(FStartTime, FStartTimeTZ); Result := FCalendar.LocalTimeToUTC(FStartTime, FStartTimeTZ);
end; end;
procedure TVpICalToDo.SaveToStrings(const AList: TStrings);
begin
// to do...
end;
{==============================================================================} {==============================================================================}
@ -598,15 +736,25 @@ end;
constructor TVpICalendar.Create; constructor TVpICalendar.Create;
begin begin
inherited; inherited;
FVersion := '2.0';
SetLength(FEntries, 0); SetLength(FEntries, 0);
end; end;
destructor TVpICalendar.Destroy; destructor TVpICalendar.Destroy;
begin begin
SetLength(FEntries, 0); Clear;
inherited; inherited;
end; end;
procedure TVpICalendar.Add(AEntry: TVpICalEntry);
var
n: Integer;
begin
n := Length(FEntries);
SetLength(FEntries, n+1);
FEntries[n] := AEntry;
end;
procedure TVpICalendar.Clear; procedure TVpICalendar.Clear;
var var
j: Integer; j: Integer;
@ -724,7 +872,7 @@ begin
'VALARM': 'VALARM':
if currEntry is TVpICalEvent then begin if currEntry is TVpICalEvent then begin
oldEntry := currEntry; oldEntry := currEntry;
TVpICalEvent(currEntry).UseAlarm; TVpICalEvent(currEntry).UseAlarm(true);
currEntry := TVpICalEvent(currEntry).Alarm; currEntry := TVpICalEvent(currEntry).Alarm;
end; end;
else else
@ -756,6 +904,51 @@ begin
SetLength(FEntries, n); SetLength(FEntries, n);
end; end;
procedure TVpICalendar.SaveToFile(const AFileName: String);
var
L: TStrings;
begin
L := TStringList.Create;
try
SaveToStrings(L);
L.SaveToFile(AFileName);
finally
L.Free;
end;
end;
procedure TVpICalendar.SaveToStream(const AStream: TStream);
var
L: TStrings;
begin
L := TStringList.Create;
try
SaveToStrings(L);
L.SaveToStream(AStream);
finally
L.Free;
end;
end;
procedure TVpICalendar.SaveToStrings(const AList: TStrings);
var
i: Integer;
begin
AList.Clear;
AList.Add('BEGIN:VCALENDAR');
if FVersion <> '' then
AList.Add('VERSION:' + FVersion);
AList.Add('BEGIN:VTIMEZONE');
// to do: complete here TIMEZONE section with DAYLIGHT and STANDARD sections
AList.Add('END:VTIMEZONE');
for i := 0 to Count-1 do
Entry[i].SaveToStrings(AList);
AList.Add('END:VCALENDAR');
end;
function TVpICalendar.ConvertTime(ADateTime: TDateTime; function TVpICalendar.ConvertTime(ADateTime: TDateTime;
ATimeZoneID: String; ToUTC: Boolean): TDateTime; ATimeZoneID: String; ToUTC: Boolean): TDateTime;
var var

View File

@ -595,7 +595,7 @@ procedure TVpVCards.SaveToStrings(AList: TStrings);
var var
i: Integer; i: Integer;
begin begin
AList.clear; AList.Clear;
for i := 0 to Count-1 do for i := 0 to Count-1 do
FCards[i].SaveToStrings(AList); FCards[i].SaveToStrings(AList);
end; end;

View File

@ -1227,7 +1227,7 @@ begin
NewItem.Kind := mikSeparator; NewItem.Kind := mikSeparator;
FDefaultPopup.Items.Add(NewItem); FDefaultPopup.Items.Add(NewItem);
if RSPopupAddEventFromICal <> '' then begin if RSPopupImportEventFromICal <> '' then begin
NewItem := TVpMenuItem.Create(Self); NewItem := TVpMenuItem.Create(Self);
NewItem.Kind := mikImportEventFromICal; // Import from iCal NewItem.Kind := mikImportEventFromICal; // Import from iCal
NewItem.OnClick := PopupAddFromICalFile; NewItem.OnClick := PopupAddFromICalFile;