QCalendar example illustrating the user-supplied plugin mechanism
This example demonstrates how to write a calendar backend plugin using a low-level API for extending Qt applications. Fixes: QTBUG-115200 Change-Id: If0b7f2552ba8c2203acdcbff238fb0ffa7cfca55 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
4079ecfb9b
commit
f071d4ee8a
@ -6,6 +6,7 @@ add_subdirectory(mimetypes)
|
|||||||
add_subdirectory(serialization)
|
add_subdirectory(serialization)
|
||||||
add_subdirectory(tools)
|
add_subdirectory(tools)
|
||||||
add_subdirectory(platform)
|
add_subdirectory(platform)
|
||||||
|
add_subdirectory(time)
|
||||||
if(QT_FEATURE_thread)
|
if(QT_FEATURE_thread)
|
||||||
add_subdirectory(threads)
|
add_subdirectory(threads)
|
||||||
endif()
|
endif()
|
||||||
|
@ -6,6 +6,7 @@ SUBDIRS = \
|
|||||||
mimetypes \
|
mimetypes \
|
||||||
serialization \
|
serialization \
|
||||||
tools \
|
tools \
|
||||||
platform
|
platform \
|
||||||
|
time
|
||||||
|
|
||||||
qtConfig(thread): SUBDIRS += threads
|
qtConfig(thread): SUBDIRS += threads
|
||||||
|
4
examples/corelib/time/CMakeLists.txt
Normal file
4
examples/corelib/time/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
add_subdirectory(calendarbackendplugin)
|
32
examples/corelib/time/calendarbackendplugin/CMakeLists.txt
Normal file
32
examples/corelib/time/calendarbackendplugin/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(JulianGregorianCalendar VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
add_subdirectory(plugin)
|
||||||
|
add_subdirectory(application)
|
||||||
|
|
||||||
|
install(TARGETS calendarPlugin JulianGregorianCalendar
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_generate_deploy_app_script(
|
||||||
|
TARGET calendarPlugin
|
||||||
|
OUTPUT_SCRIPT deploy_script
|
||||||
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
|
)
|
||||||
|
install(SCRIPT ${deploy_script})
|
||||||
|
|
||||||
|
qt_generate_deploy_app_script(
|
||||||
|
TARGET JulianGregorianCalendar
|
||||||
|
OUTPUT_SCRIPT deploy_script
|
||||||
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
|
)
|
||||||
|
install(SCRIPT ${deploy_script})
|
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(JulianGregorianCalendar VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Core)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Core)
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
include_directories(../common/)
|
||||||
|
|
||||||
|
qt_add_executable(JulianGregorianCalendar
|
||||||
|
../common/calendarBackendInterface.h
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(JulianGregorianCalendar
|
||||||
|
PRIVATE
|
||||||
|
Qt::Widgets
|
||||||
|
Qt::Core
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
TARGET = application
|
||||||
|
INCLUDEPATH += . \
|
||||||
|
../common/
|
||||||
|
QT += core core-private widgets
|
||||||
|
|
||||||
|
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/datetime/calendarbackendplugin/application
|
||||||
|
INSTALLS += target
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
HEADERS += ../common/calendarBackendInterface.h
|
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#include "calendarBackendInterface.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCalendar>
|
||||||
|
#include <QCalendarWidget>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QPluginLoader>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QCoreApplication::setApplicationName("JulianGregorianCalendar");
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("Calendar Backend Plugin Example");
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addPositionalArgument("date; names",
|
||||||
|
"Date of transition between "
|
||||||
|
"Julian and Gregorian calendars "
|
||||||
|
"as string in the format 'yyyy-MM-dd;'. Optionally, user can "
|
||||||
|
"provide names for the calendar separated with ';'");
|
||||||
|
parser.process(a);
|
||||||
|
const QStringList args = parser.positionalArguments();
|
||||||
|
if (args.isEmpty())
|
||||||
|
parser.showHelp(1);
|
||||||
|
if (args.at(0).isEmpty())
|
||||||
|
parser.showHelp(1);
|
||||||
|
//![0]
|
||||||
|
QPluginLoader loader;
|
||||||
|
loader.setFileName("../plugin/calendarPlugin");
|
||||||
|
loader.load();
|
||||||
|
if (!loader.isLoaded())
|
||||||
|
return 1;
|
||||||
|
auto *myplugin = qobject_cast<RequestedCalendarInterface*>(loader.instance());
|
||||||
|
//![0]
|
||||||
|
//![1]
|
||||||
|
const auto cid = myplugin->loadCalendar(args.at(0));
|
||||||
|
if (!cid.isValid()) {
|
||||||
|
qWarning() << "Invalid ID";
|
||||||
|
parser.showHelp(1);
|
||||||
|
}
|
||||||
|
const QCalendar calendar(cid);
|
||||||
|
//![1]
|
||||||
|
//![2]
|
||||||
|
QCalendarWidget widget;
|
||||||
|
widget.setCalendar(calendar);
|
||||||
|
widget.show();
|
||||||
|
QCalendar::YearMonthDay when = { 1582, 10, 4 };
|
||||||
|
QCalendar julian = QCalendar(QCalendar::System::Julian);
|
||||||
|
auto got = QDate::fromString(args.at(0).left(10), u"yyyy-MM-dd", julian);
|
||||||
|
if (got.isValid())
|
||||||
|
when = julian.partsFromDate(got);
|
||||||
|
widget.setCurrentPage(when.year, when.month);
|
||||||
|
//![2]
|
||||||
|
return a.exec();
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
|
SUBDIRS += plugin \
|
||||||
|
application
|
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#ifndef CALENDARINTERFACE_H
|
||||||
|
#define CALENDARINTERFACE_H
|
||||||
|
|
||||||
|
#include <QCalendar>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
//![0]
|
||||||
|
class RequestedCalendarInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RequestedCalendarInterface() = default;
|
||||||
|
virtual QCalendar::SystemId loadCalendar(QAnyStringView requested) = 0;
|
||||||
|
virtual ~RequestedCalendarInterface() = default;
|
||||||
|
};
|
||||||
|
//![0]
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
//![1]
|
||||||
|
#define RequestedCalendarInterface_iid \
|
||||||
|
"org.qt-project.Qt.Examples.CalendarBackend.RequestedCalendarInterface/1.0"
|
||||||
|
Q_DECLARE_INTERFACE(RequestedCalendarInterface, RequestedCalendarInterface_iid)
|
||||||
|
//![1]
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // CALENDARINTERFACE_H
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,186 @@
|
|||||||
|
// 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
|
||||||
|
\brief QCalendar example illustrating user-supplied custom calendars.
|
||||||
|
|
||||||
|
\image calendarwindow_transition.png
|
||||||
|
|
||||||
|
\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
|
||||||
|
*/
|
@ -0,0 +1,27 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(calendarPlugin VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Core)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Core)
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
include_directories(../common/)
|
||||||
|
|
||||||
|
qt_add_library(calendarPlugin SHARED
|
||||||
|
../common/calendarBackendInterface.h
|
||||||
|
calendarplugin.h
|
||||||
|
calendarplugin.cpp
|
||||||
|
calendarbackend.cpp
|
||||||
|
calendarbackend.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(calendarPlugin
|
||||||
|
PRIVATE
|
||||||
|
Qt::Widgets
|
||||||
|
Qt::Core
|
||||||
|
Qt::CorePrivate
|
||||||
|
)
|
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#include "calendarbackend.h"
|
||||||
|
|
||||||
|
#include <QCalendar>
|
||||||
|
|
||||||
|
JulianGregorianCalendar::JulianGregorianCalendar(QDate endJulian, QAnyStringView name = {})
|
||||||
|
: m_julianUntil(julian.partsFromDate(endJulian)),
|
||||||
|
m_gregorianSince(gregorian.partsFromDate(endJulian.addDays(1))),
|
||||||
|
m_name(name.isEmpty()
|
||||||
|
? endJulian.toString(u"Julian until yyyy-MM-dd", julian)
|
||||||
|
: name.toString())
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(m_julianUntil.year < m_gregorianSince.year
|
||||||
|
|| (m_julianUntil.year == m_gregorianSince.year
|
||||||
|
&& (m_julianUntil.month < m_gregorianSince.month
|
||||||
|
|| (m_julianUntil.month == m_gregorianSince.month
|
||||||
|
&& m_julianUntil.day < m_gregorianSince.day))),
|
||||||
|
"JulianGregorianCalendar::JulianGregorianCalendar()",
|
||||||
|
"Perversely early date for Julian-to-Gregorian transition");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString JulianGregorianCalendar::name() const
|
||||||
|
{
|
||||||
|
return QStringLiteral("JulianGregorian");
|
||||||
|
}
|
||||||
|
|
||||||
|
int JulianGregorianCalendar::daysInMonth(int month, int year) const
|
||||||
|
{
|
||||||
|
if (year == QCalendar::Unspecified)
|
||||||
|
return QRomanCalendar::daysInMonth(month, year);
|
||||||
|
if (year < m_julianUntil.year
|
||||||
|
|| (year == m_julianUntil.year && month < m_julianUntil.month)) {
|
||||||
|
return julian.daysInMonth(month, year);
|
||||||
|
}
|
||||||
|
if ((year > m_gregorianSince.year)
|
||||||
|
|| (year == m_gregorianSince.year && month > m_gregorianSince.month)) {
|
||||||
|
return gregorian.daysInMonth(month, year);
|
||||||
|
}
|
||||||
|
if (m_julianUntil.year == m_gregorianSince.year) {
|
||||||
|
Q_ASSERT(year == m_julianUntil.year);
|
||||||
|
if (m_julianUntil.month == m_gregorianSince.month) {
|
||||||
|
Q_ASSERT(month == m_julianUntil.month);
|
||||||
|
return QRomanCalendar::daysInMonth(month, year)
|
||||||
|
+ m_julianUntil.day - m_gregorianSince.day + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (year == m_julianUntil.year && month == m_julianUntil.month)
|
||||||
|
return m_julianUntil.day;
|
||||||
|
if (year == m_gregorianSince.year && month == m_gregorianSince.month)
|
||||||
|
return gregorian.daysInMonth(month, year) + 1 - m_gregorianSince.day;
|
||||||
|
Q_ASSERT(year > 3900);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JulianGregorianCalendar::isLeapYear(int year) const
|
||||||
|
{
|
||||||
|
if (year < m_julianUntil.year
|
||||||
|
|| (year == m_julianUntil.year
|
||||||
|
&& (m_julianUntil.month > 2
|
||||||
|
|| (m_julianUntil.month == 2 && m_julianUntil.day == 29)))) {
|
||||||
|
return julian.isLeapYear(year);
|
||||||
|
}
|
||||||
|
return gregorian.isLeapYear(year);
|
||||||
|
}
|
||||||
|
//![0]
|
||||||
|
bool JulianGregorianCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
|
||||||
|
{
|
||||||
|
if (year == m_julianUntil.year && month == m_julianUntil.month) {
|
||||||
|
if (m_julianUntil.day < day && day < m_gregorianSince.day) {
|
||||||
|
// Requested date is in the gap skipped over by the transition.
|
||||||
|
*jd = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QDate givenDate = gregorian.dateFromParts(year, month, day);
|
||||||
|
QDate julianUntil = julian.dateFromParts(m_julianUntil);
|
||||||
|
if (givenDate > julianUntil) {
|
||||||
|
*jd = givenDate.toJulianDay();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*jd = julian.dateFromParts(year, month, day).toJulianDay();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//![0]
|
||||||
|
//![1]
|
||||||
|
QCalendar::YearMonthDay JulianGregorianCalendar::julianDayToDate(qint64 jd) const
|
||||||
|
{
|
||||||
|
const qint64 jdForChange = julian.dateFromParts(m_julianUntil).toJulianDay();
|
||||||
|
if (jdForChange < jd) {
|
||||||
|
QCalendar gregorian(QCalendar::System::Gregorian);
|
||||||
|
QDate date = QDate::fromJulianDay(jd);
|
||||||
|
return gregorian.partsFromDate(date);
|
||||||
|
} else if (jd <= jdForChange) {
|
||||||
|
QCalendar julian(QCalendar::System::Julian);
|
||||||
|
QDate date = QDate::fromJulianDay(jd);
|
||||||
|
return julian.partsFromDate(date);
|
||||||
|
}
|
||||||
|
return QCalendar::YearMonthDay(QCalendar::Unspecified, QCalendar::Unspecified,
|
||||||
|
QCalendar::Unspecified);
|
||||||
|
}
|
||||||
|
//![1]
|
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#ifndef CALENDARBACKEND_H
|
||||||
|
#define CALENDARBACKEND_H
|
||||||
|
|
||||||
|
#include "private/qromancalendar_p.h"
|
||||||
|
#include "qdatetime.h"
|
||||||
|
|
||||||
|
#include <QtCore/private/qcalendarbackend_p.h>
|
||||||
|
//![0]
|
||||||
|
class JulianGregorianCalendar : public QRomanCalendar
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JulianGregorianCalendar(QDate endJulian, QAnyStringView name);
|
||||||
|
QString name() const override;
|
||||||
|
int daysInMonth(int month, int year = QCalendar::Unspecified) const override;
|
||||||
|
bool isLeapYear(int year) const override;
|
||||||
|
bool dateToJulianDay(int year, int month, int day, qint64 *jd) const override;
|
||||||
|
QCalendar::YearMonthDay julianDayToDate(qint64 jd) const override;
|
||||||
|
private:
|
||||||
|
static inline const QCalendar julian = QCalendar(QCalendar::System::Julian);
|
||||||
|
static inline const QCalendar gregorian = QCalendar(QCalendar::System::Gregorian);
|
||||||
|
QCalendar::YearMonthDay m_julianUntil;
|
||||||
|
QCalendar::YearMonthDay m_gregorianSince;
|
||||||
|
QString m_name;
|
||||||
|
};
|
||||||
|
//![0]
|
||||||
|
#endif // CALENDARBACKEND_H
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#include "calendarplugin.h"
|
||||||
|
|
||||||
|
JulianGregorianPlugin::JulianGregorianPlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//![0]
|
||||||
|
QCalendar::SystemId JulianGregorianPlugin::loadCalendar(QAnyStringView request)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!request.isEmpty());
|
||||||
|
QStringList names = request.toString().split(u';');
|
||||||
|
if (names.size() < 1)
|
||||||
|
return {};
|
||||||
|
QString dateString = names.takeFirst();
|
||||||
|
auto date = QDate::fromString(dateString, u"yyyy-MM-dd",
|
||||||
|
QCalendar(QCalendar::System::Julian));
|
||||||
|
if (!date.isValid())
|
||||||
|
return {};
|
||||||
|
QString primary = names.isEmpty() ?
|
||||||
|
QString::fromStdU16String(u"Julian until ") + dateString : names[0];
|
||||||
|
auto backend = new JulianGregorianCalendar(date, primary);
|
||||||
|
names.emplaceFront(backend->name());
|
||||||
|
auto cid = backend->registerCustomBackend(names);
|
||||||
|
return cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
JulianGregorianPlugin::~JulianGregorianPlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//![0]
|
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#ifndef CALENDARPLUGIN_H
|
||||||
|
#define CALENDARPLUGIN_H
|
||||||
|
|
||||||
|
#include "calendarbackend.h"
|
||||||
|
#include "calendarBackendInterface.h"
|
||||||
|
|
||||||
|
#include <QtPlugin>
|
||||||
|
//![0]
|
||||||
|
class JulianGregorianPlugin : public QObject, public RequestedCalendarInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(RequestedCalendarInterface)
|
||||||
|
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples."
|
||||||
|
"CalendarBackend."
|
||||||
|
"RequestedCalendarInterface/1.0")
|
||||||
|
public:
|
||||||
|
JulianGregorianPlugin();
|
||||||
|
QCalendar::SystemId loadCalendar(QAnyStringView request) override;
|
||||||
|
~JulianGregorianPlugin();
|
||||||
|
};
|
||||||
|
//![0]
|
||||||
|
#endif // CALENDARPLUGIN_H
|
@ -0,0 +1,11 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = calendarPlugin
|
||||||
|
INCLUDEPATH += . \
|
||||||
|
../common/
|
||||||
|
QT += core core-private widgets
|
||||||
|
|
||||||
|
HEADERS += calendarbackend.h calendarplugin.h
|
||||||
|
SOURCES += calendarbackend.cpp calendarplugin.cpp
|
||||||
|
|
||||||
|
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/datetime/calendarbackendplugin/plugin
|
||||||
|
INSTALLS += target
|
4
examples/corelib/time/time.pro
Normal file
4
examples/corelib/time/time.pro
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
CONFIG += no_docs_target
|
||||||
|
|
||||||
|
SUBDIRS = calendarbackendplugin
|
Loading…
x
Reference in New Issue
Block a user