1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 1998 Preston Brown <pbrown@kde.org> |
5 | Copyright (c) 2001,2002 Cornelius Schumacher <schumacher@kde.org> |
6 | Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> |
7 | Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net> |
8 | Copyright (c) 2008 Thomas Thrainer <tom_t@gmx.at> |
9 | |
10 | This library is free software; you can redistribute it and/or |
11 | modify it under the terms of the GNU Library General Public |
12 | License as published by the Free Software Foundation; either |
13 | version 2 of the License, or (at your option) any later version. |
14 | |
15 | This library is distributed in the hope that it will be useful, |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | Library General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU Library General Public License |
21 | along with this library; see the file COPYING.LIB. If not, write to |
22 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
23 | Boston, MA 02110-1301, USA. |
24 | */ |
25 | /** |
26 | @file |
27 | This file is part of the API for handling calendar data and |
28 | defines the DndFactory class. |
29 | |
30 | @brief |
31 | vCalendar/iCalendar Drag-and-Drop object factory. |
32 | |
33 | @author Preston Brown \<pbrown@kde.org\> |
34 | @author Cornelius Schumacher \<schumacher@kde.org\> |
35 | @author Reinhold Kainhofer \<reinhold@kainhofer.com\> |
36 | */ |
37 | |
38 | #include "dndfactory.h" |
39 | #include "vcaldrag.h" |
40 | #include "icaldrag.h" |
41 | #include "calendar.h" |
42 | #include "calendarlocal.h" |
43 | |
44 | #include <kiconloader.h> |
45 | #include <kdebug.h> |
46 | #include <klocalizedstring.h> |
47 | #include <kurl.h> |
48 | |
49 | #include <QApplication> |
50 | #include <QClipboard> |
51 | #include <QDropEvent> |
52 | #include <QPixmap> |
53 | |
54 | using namespace KCal; |
55 | |
56 | /** |
57 | Private class that helps to provide binary compatibility between releases. |
58 | @internal |
59 | */ |
60 | //@cond PRIVATE |
61 | class KCal::DndFactory::Private |
62 | { |
63 | public: |
64 | Private( Calendar *cal ) |
65 | : mCalendar ( cal ) |
66 | {} |
67 | |
68 | Incidence * pasteIncidence( Incidence *inc, |
69 | const QDate &newDate, |
70 | const QTime *newTime = 0 ) |
71 | { |
72 | if ( inc ) { |
73 | inc = inc->clone(); |
74 | inc->recreate(); |
75 | } |
76 | |
77 | if ( inc && newDate.isValid() ) { |
78 | if ( inc->type() == "Event" ) { |
79 | |
80 | Event *anEvent = static_cast<Event*>( inc ); |
81 | // Calculate length of event |
82 | int daysOffset = anEvent->dtStart().date().daysTo( |
83 | anEvent->dtEnd().date() ); |
84 | // new end date if event starts at the same time on the new day |
85 | KDateTime endDate( anEvent->dtEnd() ); |
86 | endDate.setDate( newDate.addDays( daysOffset ) ); |
87 | |
88 | KDateTime startDate( anEvent->dtStart() ); |
89 | startDate.setDate( newDate ); |
90 | if ( newTime ) { |
91 | // additional offset for new time of day |
92 | int addSecsOffset( anEvent->dtStart().time().secsTo( *newTime ) ); |
93 | endDate=endDate.addSecs( addSecsOffset ); |
94 | startDate.setTime( *newTime ); |
95 | } |
96 | anEvent->setDtStart( startDate ); |
97 | anEvent->setDtEnd( endDate ); |
98 | |
99 | } else if ( inc->type() == "Todo" ) { |
100 | Todo *anTodo = static_cast<Todo*>( inc ); |
101 | KDateTime dueDate( anTodo->dtDue() ); |
102 | dueDate.setDate( newDate ); |
103 | if ( newTime ) { |
104 | dueDate.setTime( *newTime ); |
105 | } |
106 | anTodo->setDtDue( dueDate ); |
107 | } else if ( inc->type() == "Journal" ) { |
108 | Journal *anJournal = static_cast<Journal*>( inc ); |
109 | KDateTime startDate( anJournal->dtStart() ); |
110 | startDate.setDate( newDate ); |
111 | if ( newTime ) { |
112 | startDate.setTime( *newTime ); |
113 | } else { |
114 | startDate.setTime( QTime( 0, 0, 0 ) ); |
115 | } |
116 | anJournal->setDtStart( startDate ); |
117 | } else { |
118 | kDebug() << "Trying to paste unknown incidence of type" << inc->type(); |
119 | } |
120 | } |
121 | |
122 | return inc; |
123 | } |
124 | |
125 | Calendar *mCalendar; |
126 | }; |
127 | //@endcond |
128 | |
129 | DndFactory::DndFactory( Calendar *cal ) |
130 | : d( new KCal::DndFactory::Private ( cal ) ) |
131 | { |
132 | } |
133 | |
134 | DndFactory::~DndFactory() |
135 | { |
136 | delete d; |
137 | } |
138 | |
139 | QMimeData *DndFactory::createMimeData() |
140 | { |
141 | QMimeData *mimeData = new QMimeData; |
142 | |
143 | ICalDrag::populateMimeData( mimeData, d->mCalendar ); |
144 | VCalDrag::populateMimeData( mimeData, d->mCalendar ); |
145 | |
146 | return mimeData; |
147 | } |
148 | |
149 | QDrag *DndFactory::createDrag( QWidget *owner ) |
150 | { |
151 | QDrag *drag = new QDrag( owner ); |
152 | drag->setMimeData( createMimeData() ); |
153 | |
154 | return drag; |
155 | } |
156 | |
157 | QMimeData *DndFactory::createMimeData( Incidence *incidence ) |
158 | { |
159 | CalendarLocal cal( d->mCalendar->timeSpec() ); |
160 | Incidence *i = incidence->clone(); |
161 | cal.addIncidence( i ); |
162 | |
163 | QMimeData *mimeData = new QMimeData; |
164 | |
165 | ICalDrag::populateMimeData( mimeData, &cal ); |
166 | VCalDrag::populateMimeData( mimeData, &cal ); |
167 | |
168 | KUrl uri = i->uri(); |
169 | if ( uri.isValid() ) { |
170 | QMap<QString, QString> metadata; |
171 | metadata["labels" ] = KUrl::toPercentEncoding( i->summary() ); |
172 | uri.populateMimeData( mimeData, metadata ); |
173 | } |
174 | |
175 | return mimeData; |
176 | } |
177 | |
178 | QDrag *DndFactory::createDrag( Incidence *incidence, QWidget *owner ) |
179 | { |
180 | QDrag *drag = new QDrag( owner ); |
181 | drag->setMimeData( createMimeData( incidence ) ); |
182 | |
183 | if ( incidence->type() == "Event" ) { |
184 | drag->setPixmap( BarIcon( "view-calendar-day" ) ); |
185 | } else if ( incidence->type() == "Todo" ) { |
186 | drag->setPixmap( BarIcon( "view-calendar-tasks" ) ); |
187 | } |
188 | |
189 | return drag; |
190 | } |
191 | |
192 | Calendar *DndFactory::createDropCalendar( const QMimeData *md ) |
193 | { |
194 | return createDropCalendar( md, d->mCalendar->timeSpec() ); |
195 | } |
196 | |
197 | Calendar *DndFactory::createDropCalendar( const QMimeData *md, const KDateTime::Spec &timeSpec ) |
198 | { |
199 | Calendar *cal = new CalendarLocal( timeSpec ); |
200 | |
201 | if ( ICalDrag::fromMimeData( md, cal ) || |
202 | VCalDrag::fromMimeData( md, cal ) ){ |
203 | return cal; |
204 | } |
205 | delete cal; |
206 | return 0; |
207 | } |
208 | |
209 | Calendar *DndFactory::createDropCalendar( QDropEvent *de ) |
210 | { |
211 | Calendar *cal = createDropCalendar( de->mimeData() ); |
212 | if ( cal ) { |
213 | de->accept(); |
214 | return cal; |
215 | } |
216 | return 0; |
217 | } |
218 | |
219 | Event *DndFactory::createDropEvent( const QMimeData *md ) |
220 | { |
221 | kDebug(); |
222 | Event *ev = 0; |
223 | Calendar *cal = createDropCalendar( md ); |
224 | |
225 | if ( cal ) { |
226 | Event::List events = cal->events(); |
227 | if ( !events.isEmpty() ) { |
228 | ev = new Event( *events.first() ); |
229 | } |
230 | delete cal; |
231 | } |
232 | return ev; |
233 | } |
234 | |
235 | Event *DndFactory::createDropEvent( QDropEvent *de ) |
236 | { |
237 | Event *ev = createDropEvent( de->mimeData() ); |
238 | |
239 | if ( ev ) { |
240 | de->accept(); |
241 | } |
242 | |
243 | return ev; |
244 | } |
245 | |
246 | Todo *DndFactory::createDropTodo( const QMimeData *md ) |
247 | { |
248 | kDebug(); |
249 | Todo *todo = 0; |
250 | Calendar *cal = createDropCalendar( md ); |
251 | |
252 | if ( cal ) { |
253 | Todo::List todos = cal->todos(); |
254 | if ( !todos.isEmpty() ) { |
255 | todo = new Todo( *todos.first() ); |
256 | } |
257 | delete cal; |
258 | } |
259 | |
260 | return todo; |
261 | } |
262 | |
263 | Todo *DndFactory::createDropTodo( QDropEvent *de ) |
264 | { |
265 | Todo *todo = createDropTodo( de->mimeData() ); |
266 | |
267 | if ( todo ) { |
268 | de->accept(); |
269 | } |
270 | |
271 | return todo; |
272 | } |
273 | |
274 | void DndFactory::cutIncidence( Incidence *selectedInc ) |
275 | { |
276 | Incidence::List list; |
277 | list.append( selectedInc ); |
278 | cutIncidences( list ); |
279 | } |
280 | |
281 | bool DndFactory::cutIncidences( const Incidence::List &incidences ) |
282 | { |
283 | if ( copyIncidences( incidences ) ) { |
284 | Incidence::List::ConstIterator it; |
285 | for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) { |
286 | d->mCalendar->deleteIncidence( *it ); |
287 | } |
288 | return true; |
289 | } else { |
290 | return false; |
291 | } |
292 | } |
293 | |
294 | bool DndFactory::copyIncidences( const Incidence::List &incidences ) |
295 | { |
296 | QClipboard *cb = QApplication::clipboard(); |
297 | CalendarLocal cal( d->mCalendar->timeSpec() ); |
298 | |
299 | Incidence::List::ConstIterator it; |
300 | for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) { |
301 | if ( *it ) { |
302 | cal.addIncidence( ( *it )->clone() ); |
303 | } |
304 | } |
305 | |
306 | QMimeData *mimeData = new QMimeData; |
307 | |
308 | ICalDrag::populateMimeData( mimeData, &cal ); |
309 | VCalDrag::populateMimeData( mimeData, &cal ); |
310 | |
311 | if ( cal.incidences().isEmpty() ) { |
312 | return false; |
313 | } else { |
314 | cb->setMimeData( mimeData ); |
315 | return true; |
316 | } |
317 | } |
318 | |
319 | bool DndFactory::copyIncidence( Incidence *selectedInc ) |
320 | { |
321 | Incidence::List list; |
322 | list.append( selectedInc ); |
323 | return copyIncidences( list ); |
324 | } |
325 | |
326 | Incidence::List DndFactory::pasteIncidences( const QDate &newDate, |
327 | const QTime *newTime ) |
328 | { |
329 | QClipboard *cb = QApplication::clipboard(); |
330 | Calendar *cal = createDropCalendar( cb->mimeData() ); |
331 | Incidence::List list; |
332 | |
333 | if ( !cal ) { |
334 | kDebug() << "Can't parse clipboard" ; |
335 | return list; |
336 | } |
337 | |
338 | // All pasted incidences get new uids, must keep track of old uids, |
339 | // so we can update child's parents |
340 | QHash<QString,Incidence*> oldUidToNewInc; |
341 | |
342 | Incidence::List::ConstIterator it; |
343 | const Incidence::List incs = cal->incidences(); |
344 | for ( it = incs.constBegin(); |
345 | it != incs.constEnd(); ++it ) { |
346 | Incidence *inc = d->pasteIncidence( *it, newDate, newTime ); |
347 | if ( inc ) { |
348 | list.append( inc ); |
349 | oldUidToNewInc[( *it )->uid()] = inc; |
350 | } |
351 | } |
352 | |
353 | // update relations |
354 | for ( it = list.constBegin(); it != list.constEnd(); ++it ) { |
355 | Incidence *inc = *it; |
356 | if ( oldUidToNewInc.contains( inc->relatedToUid() ) ) { |
357 | Incidence *parentInc = oldUidToNewInc[inc->relatedToUid()]; |
358 | inc->setRelatedToUid( parentInc->uid() ); |
359 | inc->setRelatedTo( parentInc ); |
360 | } else { |
361 | // not related to anything in the clipboard |
362 | inc->setRelatedToUid( QString() ); |
363 | inc->setRelatedTo( 0 ); |
364 | } |
365 | } |
366 | |
367 | return list; |
368 | } |
369 | |
370 | Incidence *DndFactory::pasteIncidence( const QDate &newDate, const QTime *newTime ) |
371 | { |
372 | QClipboard *cb = QApplication::clipboard(); |
373 | Calendar *cal = createDropCalendar( cb->mimeData() ); |
374 | |
375 | if ( !cal ) { |
376 | kDebug() << "Can't parse clipboard" ; |
377 | return 0; |
378 | } |
379 | |
380 | Incidence::List incList = cal->incidences(); |
381 | Incidence *inc = incList.isEmpty() ? 0 : incList.first(); |
382 | |
383 | Incidence *newInc = d->pasteIncidence( inc, newDate, newTime ); |
384 | newInc->setRelatedTo( 0 ); |
385 | return newInc; |
386 | } |
387 | |