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 <[email protected]>
5  SPDX-FileCopyrightText: 2001, 2002 Cornelius Schumacher <[email protected]>
6  SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <[email protected]>
7  SPDX-FileCopyrightText: 2005 Rafal Rzepecki <[email protected]>
8  SPDX-FileCopyrightText: 2008 Thomas Thrainer <[email protected]>
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 <[email protected]>
21  @author Cornelius Schumacher <[email protected]>
22  @author Reinhold Kainhofer <[email protected]>
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 
43 using namespace KCalendarCore;
44 using namespace KCalUtils;
45 
46 static 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());
54  case Qt::OffsetFromUTC:
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
66 class KCalUtils::DndFactoryPrivate
67 {
68 public:
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 
138 DndFactory::DndFactory(const Calendar::Ptr &calendar)
139  : d(new KCalUtils::DndFactoryPrivate(calendar))
140 {
141 }
142 
143 DndFactory::~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")] = QLatin1String(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 
270 void 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)) {
280  Incidence::List::ConstIterator it;
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 
297  Incidence::List::ConstIterator it;
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 
340  Incidence::List::ConstIterator it;
341  const Incidence::List incidences = calendar->incidences();
342  Incidence::List::ConstIterator end(incidences.constEnd());
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 }
void append(const T &value)
QDateTime addSecs(qint64 s) const const
bool isEmpty() const const
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...
Definition: dndfactory.cpp:367
KCALUTILS_EXPORT bool fromMimeData(const QMimeData *e, const KCalendarCore::Calendar::Ptr &cal)
Decode drag&drop object to vCalendar component vcal.
Definition: vcaldrag.cpp:34
QDateTime toOffsetFromUtc(int offsetSeconds) const const
QTimeZone timeZone() const const
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
TimeZone
KCOREADDONS_EXPORT void setMetaData(const MetaDataMap &metaData, QMimeData *mimeData)
KCALUTILS_EXPORT bool fromMimeData(const QMimeData *e, const KCalendarCore::Calendar::Ptr &cal)
Decode drag&drop object to iCalendar component cal.
Definition: icaldrag.cpp:45
QDateTime addDays(qint64 ndays) const const
QIcon fromTheme(const QString &name)
void cutIncidence(const KCalendarCore::Incidence::Ptr &)
Cut the incidence to the clipboard.
Definition: dndfactory.cpp:270
KCalendarCore::Event::Ptr createDropEvent(const QMimeData *md)
Create Event object from mime data.
Definition: dndfactory.cpp:217
QVector::const_iterator constEnd() const const
QList::const_iterator constBegin() const const
const QMimeData * mimeData() const const
T & first()
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
QDrag * createDrag(QObject *owner)
Create a drag object for the whole calendar.
Definition: dndfactory.cpp:154
void setDate(const QDate &date)
bool isValid() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
QClipboard * clipboard()
QSharedPointer< Incidence > Ptr
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...
Definition: dndfactory.cpp:324
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
bool copyIncidences(const KCalendarCore::Incidence::List &incidences)
Copies a list of incidences to the clipboard.
Definition: dndfactory.cpp:291
const QMimeData * mimeData(QClipboard::Mode mode) const const
QList::const_iterator constEnd() const const
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
int offsetFromUtc() const const
Qt::TimeSpec timeSpec() const const
QTimeZone systemTimeZone()
QDate date() const const
QMimeData * createMimeData()
Create the mime data for the whole calendar.
Definition: dndfactory.cpp:145
bool copyIncidence(const KCalendarCore::Incidence::Ptr &)
Copy the incidence to clipboard/.
Definition: dndfactory.cpp:317
bool isValid() const const
void setMimeData(QMimeData *src, QClipboard::Mode mode)
bool cutIncidences(const KCalendarCore::Incidence::List &incidences)
Cuts a list of incidences to the clipboard.
Definition: dndfactory.cpp:277
QSharedPointer< X > staticCast() const const
QVector::const_iterator constBegin() const const
bool contains(const Key &key) const const
KCalendarCore::Calendar::Ptr createDropCalendar(QDropEvent *de)
Create the calendar that is contained in the drop event's data.
Definition: dndfactory.cpp:207
QDateTime toTimeSpec(Qt::TimeSpec spec) const const
KCalendarCore::Todo::Ptr createDropTodo(const QMimeData *md)
Create Todo object from mime data.
Definition: dndfactory.cpp:243
void accept()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Nov 28 2023 04:05:00 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.