tl;drThis is a simple Qt code example, written in C++ and QML to:
- retrieve calendar data from calDAV-based servers like ownCloud, Nextcloud or others
- retrieve calendar date from remote iCalendar files
- retrieve calendar data from local iCalendar files
- parse the iCalendar format, including some of the most common recurrence rules for regular appointments
- manage multiple calendars; including saving and loading calendar settings to/from an INI file
- generate a list of events for a specific date
- edit, add and delete calendar events
- display calendar events in a GUI
Download: iCalendar_example_code.zip (987 KB)
Licence and disclaimerThe example project and the corresponding code files are licensed under CC BY-NC-SA 3.0.
It uses the SimpleCrypt library from Andre Somers, © 2001.
This project comes with absolutely no warranty.
Project genesisRecently, I wrote some calendar pages for websites to show upcoming events (see cccfr.de and roboterclub-freiburg.de).
During this I got to know the iCalendar file format for the first time. Unlike the name suggests, the iCalendar format is not Apple-proprietary, but a widely supported standard for exchanging calendar information such as events or regular appointments. For instance Outlook, Thunderbird, Google Calendar, Apple Calendar and Lotus Notes support the iCalendar format.
And since I also had to do with installing Nextcloud on one of my servers at that time, I thought it would be a very nice thing to integrate a calendar into my Star Trek Computer GUI which I have developed some time ago and synchronize it with the Nextcloud calendar server to display my upcoming events.
Among the better known file storage feature, Nextcloud also provides a calDAV service which allows clients to connect via HTTP/HTTPS and download or upload calendar information in iCalendar format.
So the plan was to have an easy to integrate C++ class or API to connect with my Nextcloud server, get my upcoming events and display them in a QML GUI.
I came to the conclusion that understanding all features of Libical and integrating it into a project would certainly take much more time than to develop a more lightweight solution from scratch by myself.
A very helpful resource in doing so was this guidance on sabre.io and - of course - the official iCalendar specification.
Managing calendarsWhen running the project, you'll see a QML application which however is still quite empty since there aren't any calendar sources configured yet.
So click the button if the bottom left corner "Add calendar" and a dialog to specify a calDAV or file based calendar opens:
|Type||ICS for local or remote files in iCalendar format or calDAV for calDAV based servers (i.e. ownCloud or Nextcloud)|
|Name||Name to display for this calendar (will be overwritten by calendar information if calDAV is used)|
|URL||In case of ICS type a local file path like file:///../../filename.ics or file:///c:/directory/filename.ics or a remote file in the scheme https://server.tld/folder/filename.ics.
If calDAV is selected, a calendar URL like https://server.tld/owncloud/remote.php/dav/calendars/username/calendarname/ needs to be entered. Note the trailing / character.
|Color||Click this rectangle to open a color picker dialog and chose a color to assign to this calendar.
Note that this setting might be overwritten by calendar information if the
|Username||Username to access calDAV server (irrelevant to ICS type calendars)|
|Password||Password to access calDAV server (irrelevant to ICS type calendars)|
For a first test select ICS type, enter https://cccfr.de/calendar.ics in the URL field, name the calendar "CCCFr" and click OK.
The application should now display a calendar in the left column:
If it doesn't sync with the calendar file, then make sure to have the DLL files libeay32.dll and ssleay32.dll in the same directory where the CalDAV_Client.exe is located and check the calendar URL by clicking the "edit" button.
Now let's try the same with a calDAV based calendar.
There is an example ownCloud server at nimmerland.de (English: "neverland") we will use for this. Click the "Add calendar" button again and enter the following:
After clicking OK, the Nimmerland calendar will be added to the application.
Managing eventsClick the "Add Event" button of the Nimmerland calendar. An event options dialog will open. Enter some data and click "Save": The application will update the calendar and display the just created event in the middle column:
You might also want to try editing or deleting events (this is supported for calDAV calendars only).
Event recurrencesIf a recurrence rule has been specified as like above you will notice, that the calendar not just displays a single event but a series of repeated appointments.
The recurrence rules must follow the iCalendar RRULE specification. The currently supported set of
So a setting of
FREQ=WEEKLY;INTERVAL=2will generate a event which repeats every 2nd week.
Others examples are:
FREQ=YEARLY;INTERVAL=1= every year on this date (i.e. for birthdays).
FREQ=MONTHLY;BYDAY=FR,2MO,-1SA;INTERVAL=1= every month on every Friday, every 2nd Monday and the last Saturday.
FREQ=WEEKLY;BYDAY=WE;INTERVAL=2;COUNT=10= every 2nd week on every Wednesday - but not more than 10 times.
Please note that the set of supported RRULES also depends on the server implementation.
For instance ownCloud apparently does not support RRULES with negative values like
FREQ=MONTHLY;BYDAY=-1FR;INTERVAL=1(every last Friday of a month) at the time this project has been written (January 2017).
ExdatesExdates are excluded dates - dates when a regularly appointment is canceled. Specifying an exdate must follow the iCalendar EXDATE specification.
To use this feature, enter a comma separated list of dates when the event is canceled.
In our example above we had entered a event which repeats every 2nd week and thus should also appear on February, 14th. But since we entered
20170214T000000Zinto the "Canceled on:" field, it won't be shown:
Build the code
- Qt 5.5 or higher (maybe it will work with older versions too, but I never tested that)
- Qt Creator
- C++ compiler (usually installed together with Qt)
Building on Windows
- Download the example project from here (987 KB) and extract it anywhere on your hard drive.
- Open the CalDAV_Client.pro file with Qt Creator and configure the build configuration.
- Run qmake and build the project.
- Open the build directory where the CalDAV_Client.exe is located and paste the DLLs libeay32.dll and ssleay32.dll here (those are needed for HTTPS connections, you'll find them in the download archive).
About the code
StructureThe central software element is the CalendarManager class which manages a list of CalendarClient instances.
CalendarClient is the abstract base class for the classes CalendarClient_CalDAV (which can connect with calDAV servers) and CalendarClient_ICS (which can handle local or remote iCalendar files).
BehaviorIn main.c an instance of CalendarManager is created and registered as property for the QML context:
The QML contains a Calendar item. Whenever the displayed month or year of this item changes, the calendarManager context property is updated to load events for that month: This allows the CalendarManager to instruct its CalendarClient_CalDAV objects to not just download a vast file of all events from the beginning of time to the end of the universe but to limit it to a specific month: If now the selected date of the QML calendar changes, the QML-invokable function
eventsForDate()of the CalendarManager is called which returns a list of events for that date.
Other things to know about
Synchronization timerFor the purpose of regular synchronization, every CalendarClient owns a QTimer (m_SynchronizationTimer) which triggers the internal state machine to reload the calendar information. If an earlier reload is required due to changed year/month or when the calendar or an event has changed, the CalendarManager calls
By default, the synchronization interval is set to 60 seconds in the constructor of CalendarClient.
Synchronization stateBesides the internal state machine, which handles the transitions and activities depending on the type of CalendarClient (calDAV or ICS), there is also a public
syncStateproperty which represents the synchronization state with the calendar source and which can have the following values:
E_STATE_IDLE- calendar is waiting for next synchronization
E_STATE_BUSY- calendar is currently synchronizing
E_STATE_ERROR- calendar has encountered an error condition
CalendarClient::recover()which resets the internal state machine and initiates a re-synchronization.
On the GUI, the user can do so by clicking on the error icon of the calendar.
Debugging outputIf you encounter a programming error, you can activate debugging output by setting the
DEBUG_****flags in the source files to 1.
Password encryptionAll calendar settings (URL, username, password, etc.) are stored by the CalendarManager in a settings file which is specified as constructor parameter: To avoid having files lying around your hard disk with plain-text passwords, the CalendarManager encryps the passowrds using SimpleCrypt.
The decryption key however is stored as a compiler define (
PWD_CRYPTin CalendarManager.c) and thus provides just a very basic protection and should be replaced for sensible information by something stronger.
Links and filesDownload Qt example project: iCalendar_example_code.zip (987 KB)
How to build a calDAV client
IETF iCalendar specification
iCalendar RRULE specification
iCalendar EXDATE specification