[frameworks/kcalendarcore] /: Add methods for encoding/decoding iCal objects in QMimeData
Volker Krause <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit ff8f452e7779228bbce5e8be2158c02b12b706e6 by Volker Krause. Committed on 21/07/2026 at 16:04. Pushed by vkrause into branch 'master'. Add methods for encoding/decoding iCal objects in QMimeData This is moving code from kcalutils and calendarsupport that is used in several PIM repositories for implementing copy/paste and drag&drop of calendar data. M +1 -0 autotests/CMakeLists.txt A +55 -0 autotests/mimedatatest.cpp [License: LGPL(v2.0+)] M +2 -0 src/CMakeLists.txt A +130 -0 src/mimedata.cpp [License: LGPL(v2.0+)] A +82 -0 src/mimedata.h [License: LGPL(v2.0+)] https://invent.kde.org/frameworks/kcalendarcore/-/commit/ff8f452e7779228bbce5e8be2158c02b12b706e6 diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 421983bbc..5fe3276e4 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -63,6 +63,7 @@ macro_unit_tests( testvcalexport testcalendarobserver calendarlistmodeltest + mimedatatest ) set_target_properties( diff --git a/autotests/mimedatatest.cpp b/autotests/mimedatatest.cpp new file mode 100644 index 000000000..2813be930 --- /dev/null +++ b/autotests/mimedatatest.cpp @@ -0,0 +1,55 @@ +/* + SPDX-FileCopyrightText: 2026 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include <KCalendarCore/Incidence> +#include <KCalendarCore/MimeData> + +#include <QDebug> +#include <QMimeData> +#include <QTest> + +using namespace Qt::Literals; +using namespace KCalendarCore; + +class MimeDataTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + static void testMimeData() + { + const Event::Ptr ev(new Event()); + ev->setSummary(u"Summary"_s); + ev->setDtStart(QDateTime(QDate(2010, 8, 8), {})); + ev->setDtEnd(QDateTime(QDate(2010, 8, 9), {})); + ev->setAllDay(true); + const auto originalUid = ev->uid(); + QVERIFY(!originalUid.isEmpty()); + + Incidence::List incidencesToCopy; + incidencesToCopy.append(ev); + + QMimeData mimeData; + QVERIFY(!MimeData::canDecode(&mimeData)); + + MimeData::populate(&mimeData, incidencesToCopy); + QVERIFY(MimeData::canDecode(&mimeData)); + + const auto &cal = MimeData::decodeCalendar(&mimeData); + QVERIFY(cal); + QCOMPARE(cal->incidences().size(), 1); + QCOMPARE(cal->events().size(), 1); + QVERIFY(!cal->events()[0]->uid().isEmpty()); + QCOMPARE(cal->events()[0]->uid(), originalUid); + + QVERIFY(MimeData::decodeEvent(&mimeData)); + QVERIFY(!MimeData::decodeTodo(&mimeData)); + + QCOMPARE(MimeData::decodeIncidences(&mimeData).size(), 1); + } +}; + +QTEST_APPLESS_MAIN(MimeDataTest) + +#include "mimedatatest.moc" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 44afb626d..b50dafae9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,6 +73,7 @@ target_sources( journal.h memorycalendar.cpp memorycalendar.h + mimedata.cpp occurrenceiterator.cpp occurrenceiterator.h period.cpp @@ -172,6 +173,7 @@ set(kcalendarcore_headers IncidenceBase Journal MemoryCalendar + MimeData OccurrenceIterator Period Person diff --git a/src/mimedata.cpp b/src/mimedata.cpp new file mode 100644 index 000000000..4ffae9729 --- /dev/null +++ b/src/mimedata.cpp @@ -0,0 +1,130 @@ +/* + SPDX-FileCopyrightText: 2026 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "mimedata.h" + +#include "icalformat.h" +#include "memorycalendar.h" +#include "vcalformat.h" + +#include <QMimeData> + +using namespace Qt::Literals; +using namespace KCalendarCore; + +QString MimeData::mimeType() +{ + return u"text/calendar"_s; +} + +[[nodiscard]] static QString vcalMimeType() +{ + return u"text/x-vCalendar"_s; +} + +QStringList MimeData::supportedMimeTypes() +{ + static const QStringList l{mimeType(), vcalMimeType()}; + return l; +} + +bool MimeData::canDecode(const QMimeData *mimeData) +{ + if (!mimeData) { + return false; + } + const auto supportedTypes = supportedMimeTypes(); + return std::ranges::any_of(supportedTypes, [mimeData](const QString &mt) { + return mimeData->hasFormat(mt); + }); +} + +void MimeData::populate(QMimeData *mimeData, const KCalendarCore::Calendar::Ptr &calendar) +{ + Q_ASSERT(mimeData); + ICalFormat format; + const auto encoded = format.toString(calendar); + if (!encoded.isEmpty()) { + mimeData->setData(mimeType(), encoded.toUtf8()); + } +} + +void MimeData::populate(QMimeData *mimeData, const KCalendarCore::Incidence::List &incidences) +{ + if (incidences.isEmpty()) { + return; + } + + const Calendar::Ptr calendar(new MemoryCalendar(QTimeZone::systemTimeZone())); + std::ranges::for_each(incidences, [&calendar](const auto &i) { + calendar->addIncidence(Incidence::Ptr(i->clone())); + }); + populate(mimeData, calendar); +} + +Calendar::Ptr MimeData::decodeCalendar(const QMimeData *mimeData) +{ + if (!mimeData) { + return {}; + } + + Calendar::Ptr calendar(new MemoryCalendar(QTimeZone::systemTimeZone())); + if (mimeData->hasFormat(mimeType())) { + const QByteArray payload = mimeData->data(mimeType()); + ICalFormat format; + if (!format.fromRawString(calendar, payload)) { + return {}; + } + return calendar; + } + + if (mimeData->hasFormat(vcalMimeType())) { + const QByteArray payload = mimeData->data(mimeType()); + VCalFormat format; + if (!format.fromRawString(calendar, payload)) { + return {}; + } + return calendar; + } + + return {}; +} + +Incidence::List MimeData::decodeIncidences(const QMimeData *mimeData) +{ + const auto cal = decodeCalendar(mimeData); + if (!cal) { + return {}; + } + + KCalendarCore::Incidence::List result; + const KCalendarCore::Incidence::List decodedIncidences = cal->incidences(); + result.reserve(decodedIncidences.size()); + for (const KCalendarCore::Incidence::Ptr &i : decodedIncidences) { + result.push_back(KCalendarCore::Incidence::Ptr(i->clone())); + } + + return result; +} + +Event::Ptr MimeData::decodeEvent(const QMimeData *mimeData) +{ + const auto cal = decodeCalendar(mimeData); + if (!cal) { + return nullptr; + } + const auto events = cal->events(); + return events.size() == 1 ? Event::Ptr(events.front()->clone()) : nullptr; +} + +Todo::Ptr MimeData::decodeTodo(const QMimeData *mimeData) +{ + const auto cal = decodeCalendar(mimeData); + if (!cal) { + return nullptr; + } + const auto todos = cal->todos(); + return todos.size() == 1 ? Todo::Ptr(todos.front()->clone()) : nullptr; +} diff --git a/src/mimedata.h b/src/mimedata.h new file mode 100644 index 000000000..3f4d05ffb --- /dev/null +++ b/src/mimedata.h @@ -0,0 +1,82 @@ +/* + SPDX-FileCopyrightText: 2026 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KCALENDARCORE_MIMEDATA_H +#define KCALENDARCORE_MIMEDATA_H + +#include "kcalendarcore_export.h" + +#include "calendar.h" + +#include <QStringList> + +class QMimeData; + +namespace KCalendarCore +{ +/*! + * \namespace MimeData + * + * Methods to interface between iCal data and QMimeData. + * + * \since 6.29 + */ +namespace MimeData +{ + +/*! + * Mimetype for generated QMimeData objects with iCal content. + */ +[[nodiscard]] KCALENDARCORE_EXPORT QString mimeType(); + +/*! + * Mimetypes for iCal content that can be decoded. + * For use in QAbstractItemModel::mimeTypes() for example. + */ +[[nodiscard]] KCALENDARCORE_EXPORT QStringList supportedMimeTypes(); + +/*! + * Returns whether \p mimeData contains iCal content we can decode. + */ +[[nodiscard]] KCALENDARCORE_EXPORT bool canDecode(const QMimeData *mimeData); + +/*! + * Populate \p mimeData with the content of \p calendar. + */ +KCALENDARCORE_EXPORT void populate(QMimeData *mimeData, const KCalendarCore::Calendar::Ptr &cal); + +/*! + * Populate \p mimeData with \p incidences. + */ +KCALENDARCORE_EXPORT void populate(QMimeData *mimeData, const KCalendarCore::Incidence::List &incidences); + +/*! + * Create a calendar from \p mimeData. + * If \p mimeData cannot be decoded, \c nullptr is returned. + */ +[[nodiscard]] KCALENDARCORE_EXPORT Calendar::Ptr decodeCalendar(const QMimeData *mimeData); + +/*! + * Create a list of incidences from \p mimeData. + * If \p mimeData cannot be decoded an empty list is returned. + */ +[[nodiscard]] KCALENDARCORE_EXPORT Incidence::List decodeIncidences(const QMimeData *mimeData); + +/*! + * Create an event from \p mimeData. + * If \p mimeData contains iCal data other than exactly one event, \c nullptr is returned. + */ +[[nodiscard]] KCALENDARCORE_EXPORT Event::Ptr decodeEvent(const QMimeData *mimeData); + +/*! + * Create a todo from \p mimeData. + * If \p mimeData contains iCal data other than exactly one todo, \c nullptr is returned. + */ +[[nodiscard]] KCALENDARCORE_EXPORT Todo::Ptr decodeTodo(const QMimeData *mimeData); + +} +} + +#endif