KCalUtils

dndfactory.cpp
Go to the documentation of this file.
1/*
2 This file is part of the kcalutils library.
3
4 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 2001, 2002 Cornelius Schumacher <schumacher@kde.org>
6 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
7 SPDX-FileCopyrightText: 2005 Rafal Rzepecki <divide@users.sourceforge.net>
8 SPDX-FileCopyrightText: 2008 Thomas Thrainer <tom_t@gmx.at>
9
10 SPDX-License-Identifier: LGPL-2.0-or-later
11*/
12/**
13 @file
14 This file is part of the API for handling calendar data and
15 defines the DndFactory class.
16
17 @brief
18 vCalendar/iCalendar Drag-and-Drop object factory.
19
20 @author Preston Brown <pbrown@kde.org>
21 @author Cornelius Schumacher <schumacher@kde.org>
22 @author Reinhold Kainhofer <reinhold@kainhofer.com>
23*/
24#include "dndfactory.h"
25#include "icaldrag.h"
26#include "vcaldrag.h"
27
28#include "kcalutils_debug.h"
29#include <KCalendarCore/MemoryCalendar>
30#include <KIconLoader>
31#include <KUrlMimeData>
32#include <QUrl>
33
34#include <QApplication>
35#include <QClipboard>
36#include <QDate>
37#include <QDrag>
38#include <QDropEvent>
39#include <QIcon>
40#include <QMimeData>
41#include <QTimeZone>
42
43using namespace KCalendarCore;
44using namespace KCalUtils;
45
46static QDateTime copyTimeSpec(QDateTime dt, const QDateTime &source)
47{
48 switch (source.timeSpec()) {
49 case Qt::TimeZone:
50 return dt.toTimeZone(source.timeZone());
51 case Qt::LocalTime:
52 case Qt::UTC:
53 return dt.toTimeSpec(source.timeSpec());
55 return dt.toOffsetFromUtc(source.offsetFromUtc());
56 }
57
58 Q_UNREACHABLE();
59}
60
61/**
62 DndFactoryPrivate class that helps to provide binary compatibility between releases.
63 @internal
64*/
65//@cond PRIVATE
66class KCalUtils::DndFactoryPrivate
67{
68public:
69 DndFactoryPrivate(const Calendar::Ptr &calendar)
70 : mCalendar(calendar)
71 {
72 }
73
74 Incidence::Ptr pasteIncidence(const Incidence::Ptr &incidence, QDateTime newDateTime, DndFactory::PasteFlags pasteOptions)
75 {
76 Incidence::Ptr inc(incidence);
77
78 if (inc) {
79 inc = Incidence::Ptr(inc->clone());
80 inc->recreate();
81 }
82
83 if (inc && newDateTime.isValid()) {
84 if (inc->type() == Incidence::TypeEvent) {
85 Event::Ptr event = inc.staticCast<Event>();
86 if (pasteOptions & DndFactory::FlagPasteAtOriginalTime) {
87 // Set date and preserve time and timezone stuff
88 const QDate date = newDateTime.date();
89 newDateTime = event->dtStart();
90 newDateTime.setDate(date);
91 }
92
93 // in seconds
94 const qint64 durationInSeconds = event->dtStart().secsTo(event->dtEnd());
95 const qint64 durationInDays = event->dtStart().daysTo(event->dtEnd());
96
97 if (incidence->allDay()) {
98 event->setDtStart(QDateTime(newDateTime.date(), {}));
99 event->setDtEnd(newDateTime.addDays(durationInDays));
100 } else {
101 event->setDtStart(copyTimeSpec(newDateTime, event->dtStart()));
102 event->setDtEnd(copyTimeSpec(newDateTime.addSecs(durationInSeconds), event->dtEnd()));
103 }
104 } else if (inc->type() == Incidence::TypeTodo) {
105 Todo::Ptr aTodo = inc.staticCast<Todo>();
106 const bool pasteAtDtStart = (pasteOptions & DndFactory::FlagTodosPasteAtDtStart);
107 if (pasteOptions & DndFactory::FlagPasteAtOriginalTime) {
108 // Set date and preserve time and timezone stuff
109 const QDate date = newDateTime.date();
110 newDateTime = pasteAtDtStart ? aTodo->dtStart() : aTodo->dtDue();
111 newDateTime.setDate(date);
112 }
113 if (pasteAtDtStart) {
114 aTodo->setDtStart(copyTimeSpec(newDateTime, aTodo->dtStart()));
115 } else {
116 aTodo->setDtDue(copyTimeSpec(newDateTime, aTodo->dtDue()));
117 }
118 } else if (inc->type() == Incidence::TypeJournal) {
119 if (pasteOptions & DndFactory::FlagPasteAtOriginalTime) {
120 // Set date and preserve time and timezone stuff
121 const QDate date = newDateTime.date();
122 newDateTime = inc->dtStart();
123 newDateTime.setDate(date);
124 }
125 inc->setDtStart(copyTimeSpec(newDateTime, inc->dtStart()));
126 } else {
127 qCDebug(KCALUTILS_LOG) << "Trying to paste unknown incidence of type" << int(inc->type());
128 }
129 }
130
131 return inc;
132 }
133
134 const Calendar::Ptr mCalendar;
135};
136//@endcond
137
139 : d(new KCalUtils::DndFactoryPrivate(calendar))
140{
141}
142
143DndFactory::~DndFactory() = default;
144
146{
147 auto mimeData = new QMimeData;
148
149 ICalDrag::populateMimeData(mimeData, d->mCalendar);
150
151 return mimeData;
152}
153
155{
156 auto drag = new QDrag(owner);
157 drag->setMimeData(createMimeData());
158
159 return drag;
160}
161
163{
164 Calendar::Ptr cal(new MemoryCalendar(d->mCalendar->timeZone()));
165 Incidence::Ptr i(incidence->clone());
166 // strip recurrence id's, We don't want to drag the exception but the occurrence.
167 i->setRecurrenceId({});
168 cal->addIncidence(i);
169
170 auto mimeData = new QMimeData;
171
172 ICalDrag::populateMimeData(mimeData, cal);
173
174 QUrl uri = i->uri();
175 if (uri.isValid()) {
176 QMap<QString, QString> metadata;
177 metadata[QStringLiteral("labels")] = QLatin1StringView(QUrl::toPercentEncoding(i->summary()));
178 mimeData->setUrls(QList<QUrl>() << uri);
179 KUrlMimeData::setMetaData(metadata, mimeData);
180 }
181
182 return mimeData;
183}
184
186{
187 auto drag = new QDrag(owner);
188 drag->setMimeData(createMimeData(incidence));
189 drag->setPixmap(QIcon::fromTheme(incidence->iconName()).pixmap(KIconLoader::SizeSmallMedium));
190
191 return drag;
192}
193
195{
196 if (mimeData) {
198
199 if (ICalDrag::fromMimeData(mimeData, calendar) || VCalDrag::fromMimeData(mimeData, calendar)) {
200 return calendar;
201 }
202 }
203
204 return Calendar::Ptr();
205}
206
208{
209 Calendar::Ptr calendar(createDropCalendar(dropEvent->mimeData()));
210 if (calendar) {
211 dropEvent->accept();
212 return calendar;
213 }
214 return MemoryCalendar::Ptr();
215}
216
218{
219 // qCDebug(KCALUTILS_LOG);
220 Event::Ptr event;
221 Calendar::Ptr calendar(createDropCalendar(mimeData));
222
223 if (calendar) {
224 Event::List events = calendar->events();
225 if (!events.isEmpty()) {
226 event = Event::Ptr(new Event(*events.first()));
227 }
228 }
229 return event;
230}
231
233{
234 Event::Ptr event = createDropEvent(dropEvent->mimeData());
235
236 if (event) {
237 dropEvent->accept();
238 }
239
240 return event;
241}
242
244{
245 // qCDebug(KCALUTILS_LOG);
246 Todo::Ptr todo;
247 Calendar::Ptr calendar(createDropCalendar(mimeData));
248
249 if (calendar) {
250 Todo::List todos = calendar->todos();
251 if (!todos.isEmpty()) {
252 todo = Todo::Ptr(new Todo(*todos.first()));
253 }
254 }
255
256 return todo;
257}
258
260{
261 Todo::Ptr todo = createDropTodo(dropEvent->mimeData());
262
263 if (todo) {
264 dropEvent->accept();
265 }
266
267 return todo;
268}
269
270void DndFactory::cutIncidence(const Incidence::Ptr &selectedIncidence)
271{
272 Incidence::List list;
273 list.append(selectedIncidence);
274 cutIncidences(list);
275}
276
278{
279 if (copyIncidences(incidences)) {
281 const Incidence::List::ConstIterator end(incidences.constEnd());
282 for (it = incidences.constBegin(); it != end; ++it) {
283 d->mCalendar->deleteIncidence(*it);
284 }
285 return true;
286 } else {
287 return false;
288 }
289}
290
292{
293 QClipboard *clipboard = QApplication::clipboard();
294 Q_ASSERT(clipboard);
295 Calendar::Ptr calendar(new MemoryCalendar(d->mCalendar->timeZone()));
296
298 const Incidence::List::ConstIterator end(incidences.constEnd());
299 for (it = incidences.constBegin(); it != end; ++it) {
300 if (*it) {
301 calendar->addIncidence(Incidence::Ptr((*it)->clone()));
302 }
303 }
304
305 auto mimeData = new QMimeData;
306
307 ICalDrag::populateMimeData(mimeData, calendar);
308
309 if (calendar->incidences().isEmpty()) {
310 return false;
311 } else {
312 clipboard->setMimeData(mimeData);
313 return true;
314 }
315}
316
318{
319 Incidence::List list;
320 list.append(selectedInc);
321 return copyIncidences(list);
322}
323
325{
326 QClipboard *clipboard = QApplication::clipboard();
327 Q_ASSERT(clipboard);
328 Calendar::Ptr calendar(createDropCalendar(clipboard->mimeData()));
329 Incidence::List list;
330
331 if (!calendar) {
332 qCDebug(KCALUTILS_LOG) << "Can't parse clipboard";
333 return list;
334 }
335
336 // All pasted incidences get new uids, must keep track of old uids,
337 // so we can update child's parents
338 QHash<QString, Incidence::Ptr> oldUidToNewInc;
339
341 const Incidence::List incidences = calendar->incidences();
343 for (it = incidences.constBegin(); it != end; ++it) {
344 Incidence::Ptr incidence = d->pasteIncidence(*it, newDateTime, pasteOptions);
345 if (incidence) {
346 list.append(incidence);
347 oldUidToNewInc[(*it)->uid()] = *it;
348 }
349 }
350
351 // update relations
352 end = list.constEnd();
353 for (it = list.constBegin(); it != end; ++it) {
354 Incidence::Ptr incidence = *it;
355 if (oldUidToNewInc.contains(incidence->relatedTo())) {
356 Incidence::Ptr parentInc = oldUidToNewInc[incidence->relatedTo()];
357 incidence->setRelatedTo(parentInc->uid());
358 } else {
359 // not related to anything in the clipboard
360 incidence->setRelatedTo(QString());
361 }
362 }
363
364 return list;
365}
366
368{
369 QClipboard *clipboard = QApplication::clipboard();
370 Calendar::Ptr calendar(createDropCalendar(clipboard->mimeData()));
371
372 if (!calendar) {
373 qCDebug(KCALUTILS_LOG) << "Can't parse clipboard";
374 return Incidence::Ptr();
375 }
376
377 Incidence::List incidenceList = calendar->incidences();
378 Incidence::Ptr incidence = incidenceList.isEmpty() ? Incidence::Ptr() : incidenceList.first();
379
380 return d->pasteIncidence(incidence, newDateTime, pasteOptions);
381}
QDrag * createDrag(QObject *owner)
Create a drag object for the whole calendar.
bool cutIncidences(const KCalendarCore::Incidence::List &incidences)
Cuts a list of incidences to the clipboard.
KCalendarCore::Incidence::Ptr pasteIncidence(const QDateTime &newDateTime=QDateTime(), PasteFlags pasteOptions=PasteFlags())
This function clones the incidence that's in the clipboard and sets the clone's date/time to the spec...
QMimeData * createMimeData()
Create the mime data for the whole calendar.
KCalendarCore::Event::Ptr createDropEvent(const QMimeData *md)
Create Event object from mime data.
KCalendarCore::Todo::Ptr createDropTodo(const QMimeData *md)
Create Todo object from mime data.
bool copyIncidence(const KCalendarCore::Incidence::Ptr &)
Copy the incidence to clipboard/.
void cutIncidence(const KCalendarCore::Incidence::Ptr &)
Cut the incidence to the clipboard.
bool copyIncidences(const KCalendarCore::Incidence::List &incidences)
Copies a list of incidences to the clipboard.
KCalendarCore::Calendar::Ptr createDropCalendar(QDropEvent *de)
Create the calendar that is contained in the drop event's data.
KCalendarCore::Incidence::List pasteIncidences(const QDateTime &newDateTime=QDateTime(), PasteFlags pasteOptions=PasteFlags())
This function clones the incidences that are in the clipboard and sets the clone's date/time to the s...
@ FlagPasteAtOriginalTime
If set, incidences will be pasted at the specified date but will preserve their original time.
Definition dndfactory.h:53
@ FlagTodosPasteAtDtStart
If the cloned incidence is a to-do, the date/time passed to DndFactory::pasteIncidence() will change ...
Definition dndfactory.h:50
DndFactory(const KCalendarCore::Calendar::Ptr &cal)
DndFactoryPrivate class that helps to provide binary compatibility between releases.
QSharedPointer< Calendar > Ptr
QSharedPointer< Event > Ptr
virtual QDateTime dtStart() const
QSharedPointer< Incidence > Ptr
QSharedPointer< MemoryCalendar > Ptr
QSharedPointer< Todo > Ptr
This file is part of the API for handling calendar data and defines the DndFactory class.
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
KCALUTILS_EXPORT bool fromMimeData(const QMimeData *e, const KCalendarCore::Calendar::Ptr &cal)
Decode drag&drop object to iCalendar component cal.
Definition icaldrag.cpp:45
KCALUTILS_EXPORT bool populateMimeData(QMimeData *e, const KCalendarCore::Calendar::Ptr &cal)
Sets the iCalendar representation as data of the drag object.
Definition icaldrag.cpp:25
KCALUTILS_EXPORT bool fromMimeData(const QMimeData *e, const KCalendarCore::Calendar::Ptr &cal)
Decode drag&drop object to vCalendar component vcal.
Definition vcaldrag.cpp:34
KCOREADDONS_EXPORT void setMetaData(const MetaDataMap &metaData, QMimeData *mimeData)
const QMimeData * mimeData(Mode mode) const const
void setMimeData(QMimeData *src, Mode mode)
QDateTime toTimeSpec(Qt::TimeSpec spec) const const
QDateTime addDays(qint64 ndays) const const
QDateTime addSecs(qint64 s) const const
QDate date() const const
bool isValid() const const
int offsetFromUtc() const const
qint64 secsTo(const QDateTime &other) const const
void setDate(QDate date)
Qt::TimeSpec timeSpec() const const
QTimeZone timeZone() const const
QDateTime toOffsetFromUtc(int offsetSeconds) const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
const QMimeData * mimeData() const const
void accept()
QClipboard * clipboard()
bool contains(const Key &key) const const
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
T & first()
bool isEmpty() const const
QSharedPointer< X > staticCast() const const
TimeZone
QTimeZone systemTimeZone()
bool isValid() const const
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:57:29 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.