/* This file is part of KOrganizer. Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2005 Reinhold Kainhofer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "koeditorattachments.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class AttachmentListItem : public KIconViewItem { public: AttachmentListItem( KCal::Attachment*att, QIconView *parent ) : KIconViewItem( parent ) { if ( att ) { mAttachment = new KCal::Attachment( *att ); } else { mAttachment = new KCal::Attachment( QString::null ); } readAttachment(); setDragEnabled( true ); } ~AttachmentListItem() { delete mAttachment; } KCal::Attachment *attachment() const { return mAttachment; } void setUri( const QString &uri ) { mAttachment->setUri( uri ); readAttachment(); } void setData( const char *base64 ) { mAttachment->setData( base64 ); readAttachment(); } void setMimeType( const QString &mime ) { mAttachment->setMimeType( mime ); readAttachment(); } void setLabel( const QString &label ) { mAttachment->setLabel( label ); readAttachment(); } void readAttachment() { if ( mAttachment->isUri() ) setText( mAttachment->uri() ); else { if ( mAttachment->label().isEmpty() ) setText( i18n("[Binary data]") ); else setText( mAttachment->label() ); } KMimeType::Ptr mt = KMimeType::mimeType( mAttachment->mimeType() ); if ( mt ) { const QString iconName( mt->icon( QString(), false ) ); QPixmap pix = KGlobal::iconLoader( )->loadIcon( iconName, KIcon::Small ); if ( pix.isNull() ) pix = KGlobal::iconLoader( )->loadIcon( "unknown", KIcon::Small ); if ( !pix.isNull() ) setPixmap( pix ); } } private: KCal::Attachment *mAttachment; }; class AttachmentIconView : public KIconView { friend class KOEditorAttachments; public: AttachmentIconView( KOEditorAttachments* parent=0 ) :KIconView( parent ), mParent( parent ) { setAcceptDrops( true ); setSelectionMode( QIconView::Extended ); setMode( KIconView::Select ); setItemTextPos( QIconView::Right ); setArrangement( QIconView::LeftToRight ); setMaxItemWidth( QMAX(maxItemWidth(), 250) ); setMinimumHeight( QMAX(fontMetrics().height(), 16) + 12 ); } ~AttachmentIconView() { for ( std::set::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) { delete *it; } } protected: QDragObject * dragObject() { KURL::List urls; for ( QIconViewItem *it = firstItem( ); it; it = it->nextItem( ) ) { if ( !it->isSelected() ) continue; AttachmentListItem * item = dynamic_cast( it ); if ( !item ) return 0; KCal::Attachment * att = item->attachment(); assert( att ); KURL url; if ( att->isUri() ) { url.setPath( att->uri() ); } else { KTempDir * tempDir = new KTempDir(); // will be deleted on editor close tempDir->setAutoDelete( true ); mTempDirs.insert( tempDir ); QByteArray encoded; encoded.duplicate( att->data(), strlen(att->data()) ); QByteArray decoded; KCodecs::base64Decode( encoded, decoded ); const QString fileName = tempDir->name( ) + "/" + att->label(); KPIM::kByteArrayToFile( decoded, fileName, false, false, false ); url.setPath( fileName ); } urls << url; } KURLDrag *drag = new KURLDrag( urls, this ); return drag; } void contentsDropEvent( QDropEvent* event ) { mParent->handlePasteOrDrop( event ); } private: std::set mTempDirs; KOEditorAttachments* mParent; }; KOEditorAttachments::KOEditorAttachments( int spacing, QWidget *parent, const char *name ) : QWidget( parent, name ) { QBoxLayout *topLayout = new QHBoxLayout( this ); topLayout->setSpacing( spacing ); QLabel *label = new QLabel( i18n("Attachments:"), this ); topLayout->addWidget( label ); mAttachments = new AttachmentIconView( this ); QWhatsThis::add( mAttachments, i18n("Displays a list of current items (files, mail, etc.) " "that have been associated with this event or to-do. ") ); topLayout->addWidget( mAttachments ); connect( mAttachments, SIGNAL( doubleClicked( QIconViewItem * ) ), SLOT( showAttachment( QIconViewItem * ) ) ); connect( mAttachments, SIGNAL(selectionChanged()), SLOT(selectionChanged()) ); connect( mAttachments, SIGNAL(contextMenuRequested(QIconViewItem*,const QPoint&)), SLOT(contextMenu(QIconViewItem*,const QPoint&)) ); mAddMenu = new KPopupMenu( this ); mContextMenu = new KPopupMenu( this ); KActionCollection* ac = new KActionCollection( this, this ); mOpenAction = new KAction( i18n("View"), 0, this, SLOT(slotShow()), ac ); mOpenAction->plug( mContextMenu ); mContextMenu->insertSeparator(); mCopyAction = KStdAction::copy(this, SLOT(slotCopy( ) ), ac ); mCopyAction->plug( mContextMenu ); mCutAction = KStdAction::cut(this, SLOT(slotCut( ) ), ac ); mCutAction->plug( mContextMenu ); KAction *action = KStdAction::paste(this, SLOT(slotPaste( ) ), ac ); action->plug( mContextMenu ); action = new KAction( i18n("&Attach File..."), 0, this, SLOT(slotAddData()), ac ); action->setWhatsThis( i18n("Shows a dialog used to select an attachment " "to add to this event or to-do as link as inline data.") ); action->plug( mAddMenu ); action = new KAction( i18n("Attach &Link..."), 0, this, SLOT(slotAdd()), ac ); action->setWhatsThis( i18n("Shows a dialog used to select an attachment " "to add to this event or to-do as link.") ); action->plug( mAddMenu ); QPushButton *addButton = new QPushButton( this ); addButton->setIconSet( SmallIconSet( "add" ) ); addButton->setPopup( mAddMenu ); topLayout->addWidget( addButton ); mRemoveBtn = new QPushButton( this ); mRemoveBtn->setIconSet( SmallIconSet( "remove" ) ); QToolTip::add( mRemoveBtn, i18n("&Remove") ); QWhatsThis::add( mRemoveBtn, i18n("Removes the attachment selected in the list above " "from this event or to-do.") ); topLayout->addWidget( mRemoveBtn ); connect( mRemoveBtn, SIGNAL( clicked() ), SLOT( slotRemove() ) ); selectionChanged(); setAcceptDrops( true ); } KOEditorAttachments::~KOEditorAttachments() { } bool KOEditorAttachments::hasAttachments() { return mAttachments->count() != 0; } void KOEditorAttachments::dragEnterEvent( QDragEnterEvent* event ) { event->accept( KURLDrag::canDecode( event ) | QTextDrag::canDecode( event ) ); } void KOEditorAttachments::handlePasteOrDrop( QMimeSource* source ) { KURL::List urls; QString text; if ( KURLDrag::decode( source, urls ) ) { const bool asUri = KMessageBox::questionYesNo( this, i18n("Do you want to link to the attachments, or include them in the event?"), i18n("Attach as link?"), i18n("As Link"), i18n("As File") ) == KMessageBox::Yes; for ( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) { addAttachment( (*it).url(), QString::null, asUri ); } } else if ( QTextDrag::decode( source, text ) ) { QStringList lst = QStringList::split( '\n', text ); for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { addAttachment( (*it) ); } } } void KOEditorAttachments::dropEvent( QDropEvent* event ) { handlePasteOrDrop( event ); } void KOEditorAttachments::showAttachment( QIconViewItem *item ) { AttachmentListItem *attitem = static_cast(item); if ( !attitem || !attitem->attachment() ) return; KCal::Attachment *att = attitem->attachment(); if ( att->isUri() ) { emit openURL( att->uri() ); } else { KTempFile f; if ( !f.file() ) return; QByteArray encoded; encoded.duplicate( att->data(), strlen(att->data()) ); QByteArray decoded; KCodecs::base64Decode( encoded, decoded ); f.file()->writeBlock( decoded ); f.file()->close(); KRun::runURL( f.name(), att->mimeType(), true, false ); } } void KOEditorAttachments::slotAdd() { KURL uri = KPimURLRequesterDlg::getURL( QString::null, i18n( "URL (e.g. a web page) or file to be attached (only " "the link will be attached, not the file itself):"), this, i18n("Add Attachment") ); if ( !uri.isEmpty() ) { addAttachment( uri ); } } void KOEditorAttachments::slotAddData() { KURL uri = KFileDialog::getOpenFileName( QString(), QString(), this, i18n("Add Attachment") ); if ( !uri.isEmpty() ) { addAttachment( uri, QString::null, false ); } } void KOEditorAttachments::slotEdit() { QIconViewItem *item = mAttachments->currentItem(); AttachmentListItem *attitem = static_cast(item); if ( !attitem || !attitem->attachment() ) return; KCal::Attachment *att = attitem->attachment(); if ( att->isUri() ) { KURL uri = KPimURLRequesterDlg::getURL( att->uri(), i18n( "URL (e.g. a web page) or file to be attached (only " "the link will be attached, not the file itself):"), this, i18n("Edit Attachment") ); if ( !uri.isEmpty() ) attitem->setUri( uri.url() ); } else { KURL uri = KPimURLRequesterDlg::getURL( QString::null, i18n( "File to be attached:"), this, i18n("Add Attachment") ); if ( !uri.isEmpty() ) { QString tmpFile; if ( KIO::NetAccess::download( uri, tmpFile, this ) ) { QFile f( tmpFile ); if ( !f.open( IO_ReadOnly ) ) return; QByteArray data = f.readAll(); f.close(); attitem->setData( KCodecs::base64Encode( data ) ); attitem->setMimeType( KIO::NetAccess::mimetype( uri, this ) ); QString label = uri.fileName(); if ( label.isEmpty() ) label = uri.prettyURL(); attitem->setLabel( label ); KIO::NetAccess::removeTempFile( tmpFile ); } } } } void KOEditorAttachments::slotRemove() { QValueList selected; for ( QIconViewItem *it = mAttachments->firstItem( ); it; it = it->nextItem( ) ) { if ( !it->isSelected() ) continue; selected << it; } if ( selected.isEmpty() || KMessageBox::warningContinueCancel(this, selected.count() == 1?i18n("This item will be permanently deleted."): i18n("The selected items will be permanently deleted."), i18n("KOrganizer Confirmation"),KStdGuiItem::del()) != KMessageBox::Continue ) return; for ( QValueList::iterator it( selected.begin() ), end( selected.end() ); it != end ; ++it ) { delete *it; } } void KOEditorAttachments::slotShow() { for ( QIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) { if ( !it->isSelected() ) continue; showAttachment( it ); } } void KOEditorAttachments::setDefaults() { mAttachments->clear(); } void KOEditorAttachments::addAttachment( const KURL &uri, const QString &mimeType, bool asUri ) { AttachmentListItem *item = new AttachmentListItem( 0, mAttachments ); if ( asUri ) { item->setUri( uri.url() ); if ( !mimeType.isEmpty() ) item->setMimeType( mimeType ); } else { QString tmpFile; if ( KIO::NetAccess::download( uri, tmpFile, this ) ) { QFile f( tmpFile ); if ( !f.open( IO_ReadOnly ) ) return; QByteArray data = f.readAll(); f.close(); item->setData( KCodecs::base64Encode( data ) ); if ( !mimeType.isEmpty() ) item->setMimeType( mimeType ); else item->setMimeType( KIO::NetAccess::mimetype( uri, this ) ); QString label = uri.fileName(); if ( label.isEmpty() ) label = uri.prettyURL(); item->setLabel( label ); KIO::NetAccess::removeTempFile( tmpFile ); } } } void KOEditorAttachments::addAttachment( KCal::Attachment *attachment ) { new AttachmentListItem( attachment, mAttachments ); } void KOEditorAttachments::readIncidence( KCal::Incidence *i ) { mAttachments->clear(); KCal::Attachment::List attachments = i->attachments(); KCal::Attachment::List::ConstIterator it; for( it = attachments.begin(); it != attachments.end(); ++it ) { addAttachment( (*it) ); } if ( mAttachments->count() > 0 ) { QTimer::singleShot( 0, mAttachments, SLOT(arrangeItemsInGrid()) ); } } void KOEditorAttachments::writeIncidence( KCal::Incidence *i ) { i->clearAttachments(); QIconViewItem *item; AttachmentListItem *attitem; for( item = mAttachments->firstItem(); item; item = item->nextItem() ) { attitem = static_cast(item); if ( attitem ) i->addAttachment( new KCal::Attachment( *(attitem->attachment() ) ) ); } } void KOEditorAttachments::slotCopy() { QApplication::clipboard()->setData( mAttachments->dragObject(), QClipboard::Clipboard ); } void KOEditorAttachments::slotCut() { slotCopy(); slotRemove(); } void KOEditorAttachments::slotPaste() { handlePasteOrDrop( QApplication::clipboard()->data() ); } void KOEditorAttachments::selectionChanged() { bool selected = false; for ( QIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) { if ( item->isSelected() ) { selected = true; break; } } mRemoveBtn->setEnabled( selected ); } void KOEditorAttachments::contextMenu(QIconViewItem * item, const QPoint & pos) { const bool enable = item != 0; mOpenAction->setEnabled( enable ); mCopyAction->setEnabled( enable ); mCutAction->setEnabled( enable ); mContextMenu->exec( pos ); } #include "koeditorattachments.moc"