Revision control
Copy as Markdown
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example time/calendarbackendplugin
\title Calendar Backend Plugin Example
\examplecategory {Data Processing & I/O}
\ingroup examples-time
\ingroup corelib_examples
\brief QCalendar example illustrating user-supplied custom calendars.
\image calendarwindow_transition.png {Calendar widget showing October
1582 with the Julian to Gregorian transition gap}
\section1 Introduction
There are numerous different calendar systems in use around the globe.
Qt has built-in support for some of them (see \l{QCalendar::}{System}),
but can't provide general support due to their high number.
Additional calendar systems can be provided by implementing a
custom QCalendarBackend, which is a private API.
This example demonstrates how to write a custom calendar backend
and how to use the low-level plugin API to extend an application
to user-selectable calendars.
Many countries transitioned from the Julian to the Gregorian calendar
at some point in their history, and this custom calendar backend will
implement the respective calendar as an example. The custom backend
is compiled into a plugin and loaded at runtime by the main application.
The exact transition date, different for various regions, is provided
as a string to the plugin and can be determined by the user.
\section1 Calendar backend
The calendar backend class must inherit from \c QCalendarBackend and implement its
pure virtual functions in a \c thread-safe way. It may also override
some other virtual functions as needed.
\section2 Example implementation
This example inherits from the already existing \c QRomanCalendar,
which in turn inherits from the \c QCalendarBackend and implements some of
its virtual functions.
It's constructive to do this because the transition calendar shares,
with both Julian and Gregorian calendars, parts provided by the Roman calendar.
Here is the class declaration of \c JulianGregorianCalendar:
\snippet time/calendarbackendplugin/plugin/calendarbackend.h 0
The \c QDate passed to the constructor - \a endJulian - is the date of the last day
of the Julian calendar. The calendar will automatically calculate the shift for a given
year, e.g in 1582, 10 days were omitted, but in 1700, 12 days had to be omitted.
The calendar backend is registered under \a name and a calendar instance can be
created using that name. The class only overrides functions where the two calendars
it combines differ from the Roman base. It has instances of the Julian and Gregorian
calendars to which these functions can delegate.
\section2 Julian Day conversions
\c dateToJulianDay(int year, int month, int day, qint64 *jd) computes the Julian
day number corresponding to the specified \a year, \a month and \a day.
Returns \c true and sets \a jd if there is such a date in this calendar; otherwise,
returns \c false.
\snippet time/calendarbackendplugin/plugin/calendarbackend.cpp 0
\c julianDayToDate(qint64 jd) computes year, month and day in this calendar for the given
Julian day number, \a jd. If the given day falls outside this calendar's scope,
the return value for \c isValid() is \c false. In this example, if the given date
falls in the gap jumped over by the transition from Julian to Gregorian calendar,
it is out of scope.
\snippet time/calendarbackendplugin/plugin/calendarbackend.cpp 1
\section2 Locale support
A calendar may, in general, have its own naming of months of the year
and days of the week. These must be suitably localized to be intelligible
to all users. By default the backend baseclass takes care of week day
names for us, which is entirely sufficient for these Julian/Gregorian
transition calendars.
Although a backend can directly override the month naming methods, the
baseclass version of these can be customized by implementing
\c localeMonthData() and \c localeMonthIndexData() to provide tables
of localized month names. Since the Julian and Gregorian
calendars use the same month naming, they inherit that customization
from a common base, \c QRomanCalendar. This also means the custom
calendar can use the same names, again by inheriting from that base.
This takes care of localization.
\sa QCalendarWidget, QCalendar, QDate, QLocale
\section1 Plugin
Qt applications can be extended through plugins. This requires the application to
detect and load plugins using \l{QPluginLoader}.
\section2 Writing a plugin
To write a plugin, the first thing that has to be done is to
create a pure virtual class that defines the interface between
plugin and application.
In this example the following interface was used:
\snippet time/calendarbackendplugin/common/calendarBackendInterface.h 0
and register it in the Qt meta-object system:
\snippet time/calendarbackendplugin/common/calendarBackendInterface.h 1
\l{<QtPlugin>::}{Q_DECLARE_INTERFACE()} macro is used to associate the
\c ClassName (here: \c RequestedCalendarInterface) with the defined
\c Identifier (here: \c RequestedCalendarInterface_iid). The \c Identifier
must be unique. This interface can be implemented by plugins that load
other calendars, interpreting \c loadCalendar()'s string parameter in
various ways. It isn't limited to this particular plugin that will be implemented
using it, so it has a generic name, not one specific to this particular backend.
Then a plugin class that inherits from \l{QObject} and from the interface is created.
\snippet time/calendarbackendplugin/plugin/calendarplugin.h 0
\l{<QtPlugin>::}{Q_PLUGIN_METADATA()} and \l{QObject::}{Q_INTERFACES()}
are being used to declare meta data that was also declared
in the interface class and to tell Qt which interface the class implements.
\sa QtPlugin
This plugin instantiates and registers a custom calendar backend which
can in turn be used to instantiate \l{QCalendar} by the application at
any point.
Qt Plugins are stored in a single shared library (a DLL) and \l{QPluginLoader}
is used for detecting and dynamically loading the plugin file (for more
see \l{How to Create Qt Plugins}).
\section2 Loading the plugin
\l{QPluginLoader} checks if the plugin's version of Qt
is the same as that of the application and
provides direct access to a Qt plugin.
Here is the use of \l QPluginLoader in the example:
\snippet time/calendarbackendplugin/application/main.cpp 0
First, an instance of a QPluginLoader object needs to be initialized. Next,
it has to be specified which plugin to load by passing a DLL file name to
\l{QPluginLoader::}{setFileName()}. Then, by using \l{QPluginLoader::}{load()},
the plugin file is dynamically loaded. At the end, a call to \l{QObject::}{qobject_cast()}
tests whether a plugin implements a given interface. \l{QObject::}{qobject_cast()}
uses \l{QPluginLoader::}{instance()} to access the root component in the plugin.
If the plugin has been loaded correctly, its functions should be available.
\sa QPluginLoader
\section2 Instantiating the backend
In this example there is only one function in the plugin. \c loadCalendar()
is responsible for registering the custom calendar backend in
\c QCalendarRegistry with given date of the transition and names.
\snippet time/calendarbackendplugin/plugin/calendarplugin.cpp 0
String argument for \c loadCalendar() is supplied by the user via command
line arguments. Then, the date of transition from the Julian calendar to
the Gregorian is extracted by splitting the given string.
After validation, a custom backend object is created.
The backend must be registered before it can be used in \l{QCalendar},
using the \c registerCustomBackend() method.
Once a backend is registered, a QCalendar can be instantiated with
the respective \l{QCalendar::}{SystemId} or \c name.
Here is the use of \c loadCalendar in the \c main:
\snippet time/calendarbackendplugin/application/main.cpp 1
\section2 Extending QCalendarWidget
By creating a \l{QCalendar} instance with a specific calendar as a backend,
it is possible to provide \l{QCalendarWidget} with that backend and
visualize it.
\snippet time/calendarbackendplugin/application/main.cpp 2
*/