[pim/korganizer] src: Move KCalUtils::DndFactory::pasteIncidences here
Allen Winter <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 9a46a6e02685c6313e275eacf5547baab32c4873 by Allen Winter, on behalf of Volker Krause.
Committed on 01/08/2026 at 12:06.
Pushed by winterz into branch 'master'.
Move KCalUtils::DndFactory::pasteIncidences here
This is the only user of it.
M +1 -0 src/CMakeLists.txt
M +7 -0 src/autotests/CMakeLists.txt
A +156 -0 src/autotests/pastehelpertest.cpp [License: LGPL(v2.0+)]
M +5 -5 src/calendarview.cpp
A +147 -0 src/pastehelper.cpp [License: LGPL(v2.0+)]
A +44 -0 src/pastehelper.h [License: LGPL(v2.0+)]
https://invent.kde.org/pim/korganizer/-/commit/9a46a6e02685c6313e275eacf5547baab32c4873
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5adcf3b64..58e1abfb8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -379,6 +379,7 @@ set(korganizerprivate_LIB_SRCS
korganizer_options.h
collectionsortfilterproxymodel.h
collectionsortfilterproxymodel.cpp
+ pastehelper.cpp
)
if(HAVE_ACTIVITY_SUPPORT)
diff --git a/src/autotests/CMakeLists.txt b/src/autotests/CMakeLists.txt
index eb1b6b9ea..998a732cd 100644
--- a/src/autotests/CMakeLists.txt
+++ b/src/autotests/CMakeLists.txt
@@ -69,3 +69,10 @@ ecm_add_test(testtoggletodo.cpp
korganizer_core
korganizerprivate
)
+
+ecm_add_test(pastehelpertest.cpp
+ LINK_LIBRARIES
+ Qt::Test
+ KF6::CalendarCore
+ korganizerprivate
+)
diff --git a/src/autotests/pastehelpertest.cpp b/src/autotests/pastehelpertest.cpp
new file mode 100644
index 000000000..51abbc98a
--- /dev/null
+++ b/src/autotests/pastehelpertest.cpp
@@ -0,0 +1,156 @@
+/*
+ SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
+ SPDX-FileContributor: Sérgio Martins <[email protected]>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include "../pastehelper.cpp"
+
+#include <KCalendarCore/MemoryCalendar>
+#if KCALENDARCORE_VERSION >= QT_VERSION_CHECK(6, 29, 0)
+#include <KCalendarCore/MimeData>
+#endif
+
+#include <QClipboard>
+#include <QGuiApplication>
+#include <QMimeData>
+#include <QTest>
+#include <QTimeZone>
+
+using namespace KCalendarCore;
+
+namespace
+{
+class PasteHelperTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testPasteAllDayEvent()
+ {
+#if KCALENDARCORE_VERSION >= QT_VERSION_CHECK(6, 29, 0)
+ const Event::Ptr allDayEvent(new Event());
+ allDayEvent->setSummary(QStringLiteral("Summary 1"));
+ allDayEvent->setDtStart(QDateTime(QDate(2010, 8, 8), {}));
+ allDayEvent->setDtEnd(QDateTime(QDate(2010, 8, 9), {}));
+ allDayEvent->setAllDay(true);
+ const QString originalUid = allDayEvent->uid();
+ const bool originalIsAllDay = allDayEvent->allDay();
+
+ Incidence::List incidencesToPaste;
+ incidencesToPaste.append(allDayEvent);
+
+ auto mimeData = new QMimeData;
+ KCalendarCore::MimeData::populate(mimeData, incidencesToPaste);
+ qGuiApp->clipboard()->setMimeData(mimeData);
+
+ Incidence::List pastedIncidences = PasteHelper::pasteIncidences();
+ QVERIFY(pastedIncidences.size() == 1);
+
+ const Incidence::Ptr &incidence = pastedIncidences.first();
+
+ QVERIFY(incidence->type() == Incidence::TypeEvent);
+
+ // check if a new uid was generated.
+ QVERIFY(incidence->uid() != originalUid);
+
+ // we passed an invalid KDateTime to pasteIncidences() so dates don't change.
+ QVERIFY(incidence->allDay() == originalIsAllDay);
+
+ const Event::Ptr pastedEvent = incidence.staticCast<Event>();
+
+ QCOMPARE(pastedEvent->dtStart(), allDayEvent->dtStart());
+ QCOMPARE(pastedEvent->dtEnd(), allDayEvent->dtEnd());
+ QCOMPARE(pastedEvent->summary(), allDayEvent->summary());
+#endif
+ }
+
+ void testPasteAllDayEvent2()
+ {
+#if KCALENDARCORE_VERSION >= QT_VERSION_CHECK(6, 29, 0)
+ const Event::Ptr allDayEvent(new Event());
+ allDayEvent->setSummary(QStringLiteral("Summary 2"));
+ allDayEvent->setDtStart(QDateTime(QDate(2010, 8, 8), {}));
+ allDayEvent->setDtEnd(QDateTime(QDate(2010, 8, 9), {}));
+ allDayEvent->setAllDay(true);
+ const QString originalUid = allDayEvent->uid();
+
+ Incidence::List incidencesToPaste;
+ incidencesToPaste.append(allDayEvent);
+
+ auto mimeData = new QMimeData;
+ KCalendarCore::MimeData::populate(mimeData, incidencesToPaste);
+ qGuiApp->clipboard()->setMimeData(mimeData);
+
+ const QDateTime newDateTime(QDate(2011, 1, 1).startOfDay());
+ const uint originalLength = allDayEvent->dtStart().secsTo(allDayEvent->dtEnd());
+
+ // paste at the new time
+ Incidence::List pastedIncidences = PasteHelper::pasteIncidences(newDateTime);
+
+ // we only copied one incidence
+ QVERIFY(pastedIncidences.size() == 1);
+
+ const Incidence::Ptr &incidence = pastedIncidences.first();
+
+ QVERIFY(incidence->type() == Incidence::TypeEvent);
+
+ // check if a new uid was generated.
+ QVERIFY(incidence->uid() != originalUid);
+
+ // the new dateTime didn't have time component
+ QVERIFY(incidence->allDay());
+
+ const Event::Ptr pastedEvent = incidence.staticCast<Event>();
+ const uint newLength = pastedEvent->dtStart().secsTo(pastedEvent->dtEnd());
+ /*
+ qDebug() << "originalLength was " << originalLength << "; and newLength is "
+ << newLength << "; old dtStart was " << allDayEvent->dtStart()
+ << " and old dtEnd was " << allDayEvent->dtEnd() << endl
+ << "; new dtStart is " << pastedEvent->dtStart()
+ << " and new dtEnd is " << pastedEvent->dtEnd();
+ */
+ QCOMPARE(newLength, originalLength);
+ QCOMPARE(newDateTime, pastedEvent->dtStart());
+ QCOMPARE(allDayEvent->summary(), pastedEvent->summary());
+#endif
+ }
+
+ void testPasteTodo()
+ {
+#if KCALENDARCORE_VERSION >= QT_VERSION_CHECK(6, 29, 0)
+ const Todo::Ptr todo(new Todo());
+ todo->setSummary(QStringLiteral("Summary 1"));
+ todo->setDtDue(QDateTime(QDate(2010, 8, 9), {}));
+
+ Incidence::List incidencesToPaste;
+ incidencesToPaste.append(todo);
+
+ auto mimeData = new QMimeData;
+ KCalendarCore::MimeData::populate(mimeData, incidencesToPaste);
+ qGuiApp->clipboard()->setMimeData(mimeData);
+
+ const QDateTime newDateTime(QDate(2011, 1, 1), QTime(10, 10));
+
+ Incidence::List pastedIncidences = PasteHelper::pasteIncidences(newDateTime);
+ QVERIFY(pastedIncidences.size() == 1);
+
+ const Incidence::Ptr &incidence = pastedIncidences.first();
+
+ QVERIFY(incidence->type() == Incidence::TypeTodo);
+
+ // check if a new uid was generated.
+ QVERIFY(incidence->uid() != todo->uid());
+
+ const Todo::Ptr pastedTodo = incidence.staticCast<Todo>();
+
+ QCOMPARE(newDateTime, pastedTodo->dtDue());
+ QCOMPARE(todo->summary(), pastedTodo->summary());
+#endif
+ }
+};
+}
+
+QTEST_MAIN(PasteHelperTest) // clipboard() needs GUI
+
+#include "pastehelpertest.moc"
diff --git a/src/calendarview.cpp b/src/calendarview.cpp
index 35731870c..3dd574236 100644
--- a/src/calendarview.cpp
+++ b/src/calendarview.cpp
@@ -26,6 +26,7 @@
#include "kodialogmanager.h"
#include "koglobals.h"
#include "koviewmanager.h"
+#include "pastehelper.h"
#include "pimmessagebox.h"
#include "prefs/koprefs.h"
#include "views/agendaview/koagendaview.h"
@@ -63,7 +64,6 @@
#include <KCalendarCore/FileStorage>
#include <KCalendarCore/ICalFormat>
-#include <KCalUtils/DndFactory>
#include <KCalUtils/Stringify>
#include <TextAddonsWidgets/WhatsNewNgDialog>
@@ -884,7 +884,7 @@ void CalendarView::edit_paste()
QDateTime endDT;
QDateTime finalDateTime;
bool useEndTime = false;
- KCalUtils::DndFactory::PasteFlags pasteFlags = {};
+ PasteHelper::PasteFlags pasteFlags = {};
const KOrg::BaseView *curView = mViewManager->currentView();
KOAgendaView *agendaView = mViewManager->agendaView();
@@ -906,13 +906,13 @@ void CalendarView::edit_paste()
}
} else if (curView == monthView && monthView->selectionStart().isValid()) {
finalDateTime = QDateTime(monthView->selectionStart().date().startOfDay());
- pasteFlags = KCalUtils::DndFactory::FlagPasteAtOriginalTime;
+ pasteFlags = PasteHelper::FlagPasteAtOriginalTime;
} else if (!mDateNavigator->selectedDates().isEmpty() && curView->supportsDateNavigation()) {
// default to the selected date from the navigator
const KCalendarCore::DateList dates = mDateNavigator->selectedDates();
if (!dates.isEmpty()) {
finalDateTime = QDateTime(dates.first().startOfDay());
- pasteFlags = KCalUtils::DndFactory::FlagPasteAtOriginalTime;
+ pasteFlags = PasteHelper::FlagPasteAtOriginalTime;
}
}
@@ -921,7 +921,7 @@ void CalendarView::edit_paste()
return;
}
- KCalendarCore::Incidence::List pastedIncidences = KCalUtils::DndFactory::pasteIncidences(finalDateTime, pasteFlags);
+ KCalendarCore::Incidence::List pastedIncidences = PasteHelper::pasteIncidences(finalDateTime, pasteFlags);
KCalendarCore::Incidence::List::Iterator it;
for (it = pastedIncidences.begin(); it != pastedIncidences.end(); ++it) {
diff --git a/src/pastehelper.cpp b/src/pastehelper.cpp
new file mode 100644
index 000000000..37c8101f7
--- /dev/null
+++ b/src/pastehelper.cpp
@@ -0,0 +1,147 @@
+/*
+ SPDX-FileCopyrightText: 1998 Preston Brown <[email protected]>
+ SPDX-FileCopyrightText: 2001, 2002 Cornelius Schumacher <[email protected]>
+ SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <[email protected]>
+ SPDX-FileCopyrightText: 2005 Rafal Rzepecki <[email protected]>
+ SPDX-FileCopyrightText: 2008 Thomas Thrainer <[email protected]>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include "pastehelper.h"
+
+#include <KCalendarCore/MemoryCalendar>
+#if KCALENDARCORE_VERSION >= QT_VERSION_CHECK(6, 29, 0)
+#include <KCalendarCore/MimeData>
+#else
+#include <KCalUtils/DndFactory>
+#endif
+
+#include <QClipboard>
+#include <QDate>
+#include <QGuiApplication>
+#include <QMimeData>
+#include <QTimeZone>
+
+using namespace KCalendarCore;
+
+static QDateTime copyTimeSpec(const QDateTime &dt, const QDateTime &source)
+{
+ switch (source.timeSpec()) {
+ case Qt::TimeZone:
+ case Qt::LocalTime:
+ case Qt::UTC:
+ return dt.toTimeZone(source.timeZone());
+ case Qt::OffsetFromUTC:
+ return dt.toOffsetFromUtc(source.offsetFromUtc());
+ }
+
+ Q_UNREACHABLE();
+}
+
+static Incidence::Ptr pasteIncidence(const Incidence::Ptr &incidence, QDateTime newDateTime, PasteHelper::PasteFlags pasteOptions)
+{
+ Incidence::Ptr inc(incidence);
+
+ if (inc) {
+ inc = Incidence::Ptr(inc->clone());
+ inc->recreate();
+ }
+
+ if (inc && newDateTime.isValid()) {
+ if (inc->type() == Incidence::TypeEvent) {
+ Event::Ptr const event = inc.staticCast<Event>();
+ if (pasteOptions & PasteHelper::FlagPasteAtOriginalTime) {
+ // Set date and preserve time and timezone stuff
+ const QDate date = newDateTime.date();
+ newDateTime = event->dtStart();
+ newDateTime.setDate(date);
+ }
+
+ // in seconds
+ const qint64 durationInSeconds = event->dtStart().secsTo(event->dtEnd());
+ const qint64 durationInDays = event->dtStart().daysTo(event->dtEnd());
+
+ if (incidence->allDay()) {
+ event->setDtStart(QDateTime(newDateTime.date(), {}));
+ event->setDtEnd(newDateTime.addDays(durationInDays));
+ } else {
+ event->setDtStart(copyTimeSpec(newDateTime, event->dtStart()));
+ event->setDtEnd(copyTimeSpec(newDateTime.addSecs(durationInSeconds), event->dtEnd()));
+ }
+ } else if (inc->type() == Incidence::TypeTodo) {
+ Todo::Ptr const aTodo = inc.staticCast<Todo>();
+ const bool pasteAtDtStart = (pasteOptions & PasteHelper::FlagTodosPasteAtDtStart);
+ if (pasteOptions & PasteHelper::FlagPasteAtOriginalTime) {
+ // Set date and preserve time and timezone stuff
+ const QDate date = newDateTime.date();
+ newDateTime = pasteAtDtStart ? aTodo->dtStart() : aTodo->dtDue();
+ newDateTime.setDate(date);
+ }
+ if (pasteAtDtStart) {
+ aTodo->setDtStart(copyTimeSpec(newDateTime, aTodo->dtStart()));
+ } else {
+ aTodo->setDtDue(copyTimeSpec(newDateTime, aTodo->dtDue()));
+ }
+ } else if (inc->type() == Incidence::TypeJournal) {
+ if (pasteOptions & PasteHelper::FlagPasteAtOriginalTime) {
+ // Set date and preserve time and timezone stuff
+ const QDate date = newDateTime.date();
+ newDateTime = inc->dtStart();
+ newDateTime.setDate(date);
+ }
+ inc->setDtStart(copyTimeSpec(newDateTime, inc->dtStart()));
+ } else {
+ qWarning() << "Trying to paste unknown incidence of type" << inc->type();
+ }
+ }
+
+ return inc;
+}
+
+Incidence::List PasteHelper::pasteIncidences(const QDateTime &newDateTime, PasteFlags pasteOptions)
+{
+ QClipboard const *clipboard = QGuiApplication::clipboard();
+ Q_ASSERT(clipboard);
+#if KCALENDARCORE_VERSION < QT_VERSION_CHECK(6, 29, 0)
+ Calendar::Ptr const calendar(KCalUtils::DndFactory::createDropCalendar(clipboard->mimeData()));
+#else
+ Calendar::Ptr const calendar(KCalendarCore::MimeData::decodeCalendar(clipboard->mimeData()));
+#endif
+ Incidence::List list;
+
+ if (!calendar) {
+ qWarning() << "Can't parse clipboard";
+ return list;
+ }
+
+ // All pasted incidences get new uids, must keep track of old uids,
+ // so we can update child's parents
+ QHash<QString, Incidence::Ptr> oldUidToNewInc;
+
+ Incidence::List::ConstIterator it;
+ const Incidence::List incidences = calendar->incidences();
+ Incidence::List::ConstIterator end(incidences.constEnd());
+ for (it = incidences.constBegin(); it != end; ++it) {
+ Incidence::Ptr const incidence = pasteIncidence(*it, newDateTime, pasteOptions);
+ if (incidence) {
+ list.append(incidence);
+ oldUidToNewInc[(*it)->uid()] = *it;
+ }
+ }
+
+ // update relations
+ end = list.constEnd();
+ for (it = list.constBegin(); it != end; ++it) {
+ const Incidence::Ptr &incidence = *it;
+ if (oldUidToNewInc.contains(incidence->relatedTo())) {
+ Incidence::Ptr const parentInc = oldUidToNewInc[incidence->relatedTo()];
+ incidence->setRelatedTo(parentInc->uid());
+ } else {
+ // not related to anything in the clipboard
+ incidence->setRelatedTo(QString());
+ }
+ }
+
+ return list;
+}
diff --git a/src/pastehelper.h b/src/pastehelper.h
new file mode 100644
index 000000000..181e281b1
--- /dev/null
+++ b/src/pastehelper.h
@@ -0,0 +1,44 @@
+/*
+ SPDX-FileCopyrightText: 1998 Preston Brown <[email protected]>
+ SPDX-FileCopyrightText: 2001, 2002, 2003 Cornelius Schumacher <[email protected]>
+ SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <[email protected]>
+ SPDX-FileCopyrightText: 2008 Thomas Thrainer <[email protected]>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#pragma once
+
+#include <KCalendarCore/Calendar>
+#include <KCalendarCore/Incidence>
+
+#include <QDateTime>
+
+namespace PasteHelper
+{
+enum PasteFlag {
+ FlagTodosPasteAtDtStart = 1, /*!< If the cloned incidence is a to-do, the date/time passed
+ to DndFactory::pasteIncidence() will change dtStart if this
+ flag is on, changes dtDue otherwise. */
+ FlagPasteAtOriginalTime = 2 /*!< If set, incidences will be pasted at the specified date
+ but will preserve their original time */
+};
+
+Q_DECLARE_FLAGS(PasteFlags, PasteFlag)
+
+/*!
+ This function clones the incidences that are in the clipboard and sets the clone's
+ date/time to the specified \a newDateTime.
+
+ \a newDateTime The new date/time that the incidence will have. If it's an event
+ or journal, DTSTART will be set. If it's a to-do, DTDUE is set.
+ If you wish another behaviour, like changing DTSTART on to-dos, specify
+ \a pasteOptions. If newDateTime is invalid the original incidence's dateTime
+ will be used, regardless of \a pasteOptions.
+
+ \a pasteOptions Control how \a newDateTime changes the incidence's dates. \sa PasteFlag.
+
+ Returns the cloned incidence.
+*/
+KCalendarCore::Incidence::List pasteIncidences(const QDateTime &newDateTime = QDateTime(), PasteFlags pasteOptions = PasteFlags());
+}