Fix akonadimodel.cpp:1: warning: unterminated character constant
[kdepim.git] / incidenceeditor-ng / incidenceattachmenteditor.cpp
blob30ab2423a2f9723ef42a9b8d62eae53dbaa4e55e
1 /*
2 Copyright (C) 2010 Bertjan Broeksema b.broeksema@home.nl
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "incidenceattachmenteditor.h"
22 #include <QtCore/QDebug>
23 #include <QtCore/QMimeData>
24 #include <QtCore/QPointer>
25 #include <QtGui/QClipboard>
28 #include <KDE/KABC/VCardDrag>
29 #include <KDE/KAction>
30 #include <KDE/KActionCollection>
31 #include <KDE/KFileDialog>
32 #include <KDE/KIO/Job>
33 #include <KDE/KIO/NetAccess>
34 #include <KDE/KMime/Message>
35 #include <KDE/KMenu>
36 #include <KDE/KMessageBox>
37 #include <KDE/KProtocolManager>
38 #include <KDE/KRun>
40 #include <libkdepimdbusinterfaces/urihandler.h>
42 #include "attachmenticonview.h"
43 #include "attachmenteditdialog.h"
44 #include "ui_incidenceattachmenteditor.h"
46 using namespace IncidenceEditorsNG;
48 IncidenceAttachmentEditor::IncidenceAttachmentEditor( QWidget *parent )
49 : IncidenceEditor( parent )
50 , mUi( new Ui::IncidenceAttachmentEditor )
51 , mPopupMenu( new KMenu( this ) )
53 mUi->setupUi( this );
54 mUi->mAddButton->setIcon( KIcon( "list-add" ) );
55 mUi->mRemoveButton->setIcon( KIcon( "list-remove" ) );
57 setupActions();
58 setupAttachmentIconView();
60 connect( mUi->mAddButton, SIGNAL(clicked()), SLOT(addAttachment()) );
61 connect( mUi->mRemoveButton, SIGNAL(clicked()), SLOT(removeSelectedAttachments()) );
64 void IncidenceAttachmentEditor::load( KCal::Incidence::ConstPtr incidence )
66 mLoadedIncidence = incidence;
67 mAttachmentView->clear();
69 KCal::Attachment::List attachments = incidence->attachments();
70 KCal::Attachment::List::ConstIterator it;
71 for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it )
72 new AttachmentIconItem( (*it), mAttachmentView );
75 void IncidenceAttachmentEditor::save( KCal::Incidence::Ptr incidence )
77 incidence->clearAttachments();
79 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
80 QListWidgetItem *item = mAttachmentView->item( itemIndex );
81 AttachmentIconItem *attitem = dynamic_cast<AttachmentIconItem*>(item);
82 Q_ASSERT( item );
83 incidence->addAttachment( new KCal::Attachment( *( attitem->attachment() ) ) );
87 bool IncidenceAttachmentEditor::isDirty() const
89 if ( mLoadedIncidence ) {
90 if ( mAttachmentView->count() != mLoadedIncidence->attachments().count() )
91 return true;
93 KCal::Attachment::List origAttachments = mLoadedIncidence->attachments();
94 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
95 QListWidgetItem *item = mAttachmentView->item( itemIndex );
96 Q_ASSERT( dynamic_cast<AttachmentIconItem*>( item ) );
98 KCal::Attachment * const listAttachment
99 = static_cast<AttachmentIconItem*>( item )->attachment();
101 bool found = false;
102 for ( int i = 0; i < origAttachments.size() && !found; ++i ) {
103 KCal::Attachment * const attachment = origAttachments.at( i );
105 // Check for changed label first
106 if ( attachment->label() != listAttachment->label() )
107 continue;
109 if ( attachment->isBinary() && listAttachment->isBinary()
110 && attachment->decodedData() == listAttachment->decodedData() ) {
111 // Not sure about this. Might be too expensive.
112 origAttachments.removeAt( i );
113 found = true;
114 } else if ( attachment->isUri() && listAttachment->isUri()
115 && attachment->uri() == listAttachment->uri() ) {
116 origAttachments.removeAt( i );
117 found = true;
122 // All attachments are removed from the list, meaning, the items in mAttachmentView
123 // are equal to the attachements set on mLoadedIncidence.
124 return !origAttachments.isEmpty();
127 } else {
128 // No incidence loaded, so if the user added attachments we're dirty.
129 return mAttachmentView->count() != 0;
132 return false;
136 /// Private slots
138 void IncidenceAttachmentEditor::addAttachment()
140 AttachmentIconItem *item = new AttachmentIconItem( 0, mAttachmentView );
142 QPointer<AttachmentEditDialog> dlg = new AttachmentEditDialog( item, mAttachmentView );
143 dlg->setCaption( i18nc( "@title", "Add Attachment" ) );
144 if ( dlg->exec() == KDialog::Rejected )
145 delete item;
147 delete dlg;
150 void IncidenceAttachmentEditor::copyToClipboard()
152 QApplication::clipboard()->setMimeData( mAttachmentView->mimeData(), QClipboard::Clipboard );
155 void IncidenceAttachmentEditor::openURL( const KUrl &url )
157 QString uri = url.url();
158 UriHandler::process( uri );
161 void IncidenceAttachmentEditor::pasteFromClipboard()
163 handlePasteOrDrop( QApplication::clipboard()->mimeData() );
166 void IncidenceAttachmentEditor::removeSelectedAttachments()
168 QList<QListWidgetItem *> selected;
169 QStringList labels;
171 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
172 QListWidgetItem *it = mAttachmentView->item( itemIndex );
173 if ( it->isSelected() ) {
174 AttachmentIconItem *attitem = static_cast<AttachmentIconItem *>( it );
175 if ( attitem ) {
176 KCal::Attachment *att = attitem->attachment();
177 labels << att->label();
178 selected << it;
183 if ( selected.isEmpty() )
184 return;
186 QString labelsStr = labels.join( "<br>" );
188 if ( KMessageBox::questionYesNo(
189 this,
190 i18nc( "@info",
191 "Do you really want to remove these attachments?<nl>%1</nl>", labelsStr ),
192 i18nc( "@title:window", "Remove Attachments?" ),
193 KStandardGuiItem::yes(), KStandardGuiItem::no(),
194 "calendarRemoveAttachments" ) != KMessageBox::Yes ) {
195 return;
198 for ( QList<QListWidgetItem *>::iterator it( selected.begin() ), end( selected.end() );
199 it != end ; ++it ) {
200 int row = mAttachmentView->row( *it );
201 QListWidgetItem *next = mAttachmentView->item( ++row );
202 QListWidgetItem *prev = mAttachmentView->item( --row );
203 if ( next ) {
204 next->setSelected( true );
205 } else if ( prev ) {
206 prev->setSelected( true );
208 delete *it;
211 mAttachmentView->update();
212 checkDirtyStatus();
215 void IncidenceAttachmentEditor::saveAttachment( QListWidgetItem *item )
217 Q_ASSERT( item );
218 Q_ASSERT( dynamic_cast<AttachmentIconItem*>( item ) );
220 AttachmentIconItem *attitem = static_cast<AttachmentIconItem*>( item );
221 if ( !attitem->attachment() )
222 return;
224 KCal::Attachment *att = attitem->attachment();
226 // get the saveas file name
227 QString saveAsFile = KFileDialog::getSaveFileName(
228 att->label(),
229 QString(), 0,
230 i18nc( "@title", "Save Attachment" ) );
232 if ( saveAsFile.isEmpty() ||
233 ( QFile( saveAsFile ).exists() &&
234 ( KMessageBox::warningYesNo(
236 i18nc( "@info", "%1 already exists. Do you want to overwrite it?",
237 saveAsFile ) ) == KMessageBox::No ) ) ) {
238 return;
241 KUrl sourceUrl;
242 if ( att->isUri() ) {
243 sourceUrl = att->uri();
244 } else {
245 sourceUrl = mAttachmentView->tempFileForAttachment( att );
247 // save the attachment url
248 if ( !KIO::NetAccess::file_copy( sourceUrl, KUrl( saveAsFile ) ) &&
249 KIO::NetAccess::lastError() ) {
250 KMessageBox::error( this, KIO::NetAccess::lastErrorString() );
254 void IncidenceAttachmentEditor::saveSelectedAttachments()
256 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
257 QListWidgetItem *item = mAttachmentView->item( itemIndex );
258 if ( item->isSelected() )
259 saveAttachment( item );
263 void IncidenceAttachmentEditor::showAttachment( QListWidgetItem *item )
265 Q_ASSERT( item );
266 Q_ASSERT( dynamic_cast<AttachmentIconItem*>( item ) );
267 AttachmentIconItem *attitem = static_cast<AttachmentIconItem*>( item );
268 if ( !attitem->attachment() )
269 return;
271 KCal::Attachment *att = attitem->attachment();
272 if ( att->isUri() ) {
273 emit openURL( att->uri() );
274 } else {
275 KRun::runUrl( mAttachmentView->tempFileForAttachment( att ), att->mimeType(), 0, true );
279 void IncidenceAttachmentEditor::showContextMenu( const QPoint &pos )
281 QListWidgetItem *item = mAttachmentView->itemAt( pos );
282 const bool enable = item != 0;
284 int numSelected = 0;
285 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
286 QListWidgetItem *item = mAttachmentView->item( itemIndex );
287 if ( item->isSelected() ) {
288 numSelected++;
292 mOpenAction->setEnabled( enable );
293 //TODO: support saving multiple attachments into a directory
294 mSaveAsAction->setEnabled( enable && numSelected == 1 );
295 mCopyAction->setEnabled( enable && numSelected == 1 );
296 mCutAction->setEnabled( enable && numSelected == 1 );
297 mDeleteAction->setEnabled( enable );
298 mEditAction->setEnabled( enable );
299 mPopupMenu->exec( mAttachmentView->mapToGlobal( pos ) );
302 void IncidenceAttachmentEditor::showSelectedAttachments()
304 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
305 QListWidgetItem *item = mAttachmentView->item( itemIndex );
306 if ( item->isSelected() )
307 showAttachment( item );
311 void IncidenceAttachmentEditor::cutToClipboard()
313 copyToClipboard();
314 removeSelectedAttachments();
317 void IncidenceAttachmentEditor::editSelectedAttachments()
319 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
320 QListWidgetItem *item = mAttachmentView->item( itemIndex );
321 if ( item->isSelected() ) {
322 Q_ASSERT( dynamic_cast<AttachmentIconItem*>( item ) );
324 AttachmentIconItem *attitem = static_cast<AttachmentIconItem*>( item );
325 if ( !attitem->attachment() )
326 return;
328 AttachmentEditDialog *dialog = new AttachmentEditDialog( attitem, mAttachmentView, false );
329 dialog->setModal( false );
330 connect( dialog, SIGNAL(hidden()), dialog, SLOT(delayedDestruct()) );
331 dialog->show();
336 void IncidenceAttachmentEditor::slotItemRenamed ( QListWidgetItem *item )
338 Q_ASSERT( item );
339 Q_ASSERT( dynamic_cast<AttachmentIconItem *>( item ) );
340 static_cast<AttachmentIconItem *>( item )->setLabel( item->text() );
341 checkDirtyStatus();
344 void IncidenceAttachmentEditor::slotSelectionChanged()
346 bool selected = false;
347 for ( int itemIndex = 0; itemIndex < mAttachmentView->count(); ++itemIndex ) {
348 QListWidgetItem *item = mAttachmentView->item( itemIndex );
349 if ( item->isSelected() ) {
350 selected = true;
351 break;
354 mUi->mRemoveButton->setEnabled( selected );
358 /// Private functions
360 void IncidenceAttachmentEditor::handlePasteOrDrop( const QMimeData *mimeData )
362 KUrl::List urls;
363 bool probablyWeHaveUris = false;
364 bool weCanCopy = true;
365 QStringList labels;
367 if ( KABC::VCardDrag::canDecode( mimeData ) ) {
368 KABC::Addressee::List addressees;
369 KABC::VCardDrag::fromMimeData( mimeData, addressees );
370 for ( KABC::Addressee::List::ConstIterator it = addressees.constBegin();
371 it != addressees.constEnd(); ++it ) {
372 urls.append( QLatin1String( "uid:" ) + ( *it ).uid() );
373 // there is some weirdness about realName(), hence fromUtf8
374 labels.append( QString::fromUtf8( ( *it ).realName().toLatin1() ) );
376 probablyWeHaveUris = true;
377 } else if ( KUrl::List::canDecode( mimeData ) ) {
378 QMap<QString,QString> metadata;
380 urls = KUrl::List::fromMimeData( mimeData, &metadata );
381 probablyWeHaveUris = true;
382 labels = metadata["labels"].split( ':', QString::SkipEmptyParts );
383 for ( QStringList::Iterator it = labels.begin(); it != labels.end(); ++it ) {
384 *it = KUrl::fromPercentEncoding( (*it).toLatin1() );
386 } else if ( mimeData->hasText() ) {
387 QString text = mimeData->text();
388 QStringList lst = text.split( '\n', QString::SkipEmptyParts );
389 for ( QStringList::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) {
390 urls.append( *it );
392 probablyWeHaveUris = true;
394 KMenu menu( this );
395 QAction *linkAction = 0, *cancelAction;
396 if ( probablyWeHaveUris ) {
397 linkAction = menu.addAction( KIcon( "insert-link" ), i18nc( "@action:inmenu", "&Link here" ) );
398 // we need to check if we can reasonably expect to copy the objects
399 for ( KUrl::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it ) {
400 if ( !( weCanCopy = KProtocolManager::supportsReading( *it ) ) ) {
401 break; // either we can copy them all, or no copying at all
404 if ( weCanCopy ) {
405 menu.addAction( KIcon( "edit-copy" ), i18nc( "@action:inmenu", "&Copy here" ) );
407 } else {
408 menu.addAction( KIcon( "edit-copy" ), i18nc( "@action:inmenu", "&Copy here" ) );
411 menu.addSeparator();
412 cancelAction = menu.addAction( KIcon( "process-stop" ) , i18nc( "@action:inmenu", "C&ancel" ) );
414 QByteArray data;
415 QString mimeType;
416 QString label;
418 if(!mimeData->formats().isEmpty() && !probablyWeHaveUris) {
419 data=mimeData->data( mimeData->formats().first() );
420 mimeType = mimeData->formats().first();
421 if( KMimeType::mimeType( mimeData->formats().first() ) )
422 label = KMimeType::mimeType( mimeData->formats().first() )->name();
426 QAction *ret = menu.exec( QCursor::pos() );
427 if ( linkAction == ret ) {
428 QStringList::ConstIterator jt = labels.constBegin();
429 for ( KUrl::List::ConstIterator it = urls.constBegin();
430 it != urls.constEnd(); ++it ) {
431 addUriAttachment( (*it).url(), QString(), ( jt == labels.constEnd() ?
432 QString() : *( jt++ ) ), true );
434 } else if ( cancelAction != ret ) {
435 if ( probablyWeHaveUris ) {
436 for ( KUrl::List::ConstIterator it = urls.constBegin();
437 it != urls.constEnd(); ++it ) {
438 KIO::Job *job = KIO::storedGet( *it );
439 connect( job, SIGNAL(result(KJob *)), SLOT(downloadComplete(KJob *)) );
441 } else { // we take anything
442 addDataAttachment( data, mimeType, label );
447 void IncidenceAttachmentEditor::setupActions()
449 KActionCollection *ac = new KActionCollection( this );
450 ac->addAssociatedWidget( this );
452 mOpenAction = new KAction( i18nc( "@action:inmenu open the attachment in a viewer",
453 "&Open" ), this );
454 connect( mOpenAction, SIGNAL(triggered(bool)), SLOT(showSelectedAttachments()) );
455 ac->addAction( "view", mOpenAction );
456 mPopupMenu->addAction( mOpenAction );
458 mSaveAsAction = new KAction( i18nc( "@action:inmenu save the attachment to a file",
459 "Save As..." ), this );
460 connect( mSaveAsAction, SIGNAL(triggered(bool)), SLOT(saveSelectedAttachments()) );
461 mPopupMenu->addAction( mSaveAsAction );
462 mPopupMenu->addSeparator();
464 mCopyAction = KStandardAction::copy( this, SLOT(copyToClipboard()), ac );
465 mPopupMenu->addAction( mCopyAction );
467 mCutAction = KStandardAction::cut( this, SLOT(cutToClipboard()), ac );
468 mPopupMenu->addAction( mCutAction );
470 KAction *action = KStandardAction::paste( this, SLOT(pasteFromClipboard()), ac );
471 mPopupMenu->addAction( action );
472 mPopupMenu->addSeparator();
474 mDeleteAction = new KAction( i18nc( "@action:inmenu remove the attachment",
475 "&Remove" ), this );
476 connect( mDeleteAction, SIGNAL(triggered(bool)), SLOT(removeSelectedAttachments()) );
477 ac->addAction( "remove", mDeleteAction );
478 mDeleteAction->setShortcut( Qt::Key_Delete );
479 mPopupMenu->addAction( mDeleteAction );
480 mPopupMenu->addSeparator();
482 mEditAction = new KAction( i18nc( "@action:inmenu show a dialog used to edit the attachment",
483 "&Properties..." ), this );
484 connect( mEditAction, SIGNAL(triggered(bool)), SLOT(editSelectedAttachments()) );
485 ac->addAction( "edit", mEditAction );
486 mPopupMenu->addAction( mEditAction );
489 void IncidenceAttachmentEditor::setupAttachmentIconView()
491 mAttachmentView = new AttachmentIconView( this );
492 mAttachmentView->setWhatsThis( i18nc( "@info:whatsthis",
493 "Displays items (files, mail, etc.) that "
494 "have been associated with this event or to-do." ) );
496 connect( mAttachmentView, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
497 SLOT(showAttachment(QListWidgetItem*)) );
498 connect( mAttachmentView, SIGNAL(itemChanged(QListWidgetItem*)),
499 SLOT(slotItemRenamed(QListWidgetItem*)) );
500 connect( mAttachmentView, SIGNAL(itemSelectionChanged()),
501 SLOT(slotSelectionChanged()) );
502 connect( mAttachmentView, SIGNAL(customContextMenuRequested(QPoint)),
503 SLOT(showContextMenu(QPoint)) );
506 QGridLayout *layout = new QGridLayout( mUi->mAttachmentViewPlaceHolder );
507 layout->addWidget( mAttachmentView );
510 // void IncidenceAttachmentEditor::addAttachment( KCal::Attachment *attachment )
511 // {
512 // new AttachmentIconItem( attachment, mAttachmentView );
513 // }
515 void IncidenceAttachmentEditor::addDataAttachment( const QByteArray &data,
516 const QString &mimeType,
517 const QString &label )
519 AttachmentIconItem *item = new AttachmentIconItem( 0, mAttachmentView );
521 QString nlabel = label;
522 if ( mimeType == "message/rfc822" ) {
523 // mail message. try to set the label from the mail Subject:
524 KMime::Message msg;
525 msg.setContent( data );
526 msg.parse();
527 nlabel = msg.subject()->asUnicodeString();
530 item->setData( data );
531 item->setLabel( nlabel );
532 if ( mimeType.isEmpty() ) {
533 item->setMimeType( KMimeType::findByContent( data )->name() );
534 } else {
535 item->setMimeType( mimeType );
538 checkDirtyStatus();
541 void IncidenceAttachmentEditor::addUriAttachment( const QString &uri,
542 const QString &mimeType,
543 const QString &label,
544 bool inLine )
546 if ( !inLine ) {
547 AttachmentIconItem *item = new AttachmentIconItem( 0, mAttachmentView );
548 item->setUri( uri );
549 item->setLabel( label );
550 if ( mimeType.isEmpty() ) {
551 if ( uri.startsWith( QLatin1String( "uid:" ) ) ) {
552 item->setMimeType( "text/directory" );
553 } else if ( uri.startsWith( QLatin1String( "kmail:" ) ) ) {
554 item->setMimeType( "message/rfc822" );
555 } else if ( uri.startsWith( QLatin1String( "urn:x-ical" ) ) ) {
556 item->setMimeType( "text/calendar" );
557 } else if ( uri.startsWith( QLatin1String( "news:" ) ) ) {
558 item->setMimeType( "message/news" );
559 } else {
560 item->setMimeType( KMimeType::findByUrl( uri )->name() );
563 } else {
564 QString tmpFile;
565 if ( KIO::NetAccess::download( uri, tmpFile, this ) ) {
566 QFile f( tmpFile );
567 if ( !f.open( QIODevice::ReadOnly ) ) {
568 return;
570 const QByteArray data = f.readAll();
571 f.close();
572 addDataAttachment( data, mimeType, label );
574 KIO::NetAccess::removeTempFile( tmpFile );