korganizer

koeditorattachments.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (c) 2005 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include "koeditorattachments.h"
00027 
00028 #include <libkcal/incidence.h>
00029 #include <libkdepim/kpimurlrequesterdlg.h>
00030 #include <libkdepim/kfileio.h>
00031 
00032 #include <klocale.h>
00033 #include <kdebug.h>
00034 #include <kmdcodec.h>
00035 #include <kmessagebox.h>
00036 #include <kiconview.h>
00037 #include <krun.h>
00038 #include <kurldrag.h>
00039 #include <ktempfile.h>
00040 #include <ktempdir.h>
00041 #include <kio/netaccess.h>
00042 #include <kmimetype.h>
00043 #include <kiconloader.h>
00044 #include <kfiledialog.h>
00045 #include <kstdaction.h>
00046 #include <kactioncollection.h>
00047 #include <kpopupmenu.h>
00048 
00049 #include <qfile.h>
00050 #include <qlabel.h>
00051 #include <qlayout.h>
00052 #include <qlistview.h>
00053 #include <qpushbutton.h>
00054 #include <qdragobject.h>
00055 #include <qtooltip.h>
00056 #include <qwhatsthis.h>
00057 #include <qapplication.h>
00058 #include <qclipboard.h>
00059 
00060 #include <cassert>
00061 #include <set>
00062 
00063 class AttachmentListItem : public KIconViewItem
00064 {
00065   public:
00066     AttachmentListItem( KCal::Attachment*att, QIconView *parent ) :
00067         KIconViewItem( parent )
00068     {
00069       if ( att ) {
00070         mAttachment = new KCal::Attachment( *att );
00071       } else {
00072         mAttachment = new KCal::Attachment( QString::null );
00073       }
00074       readAttachment();
00075       setDragEnabled( true );
00076     }
00077     ~AttachmentListItem() { delete mAttachment; }
00078     KCal::Attachment *attachment() const { return mAttachment; }
00079 
00080     void setUri( const QString &uri )
00081     {
00082       mAttachment->setUri( uri );
00083       readAttachment();
00084     }
00085     void setData( const char *base64 )
00086     {
00087       mAttachment->setData( base64 );
00088       readAttachment();
00089     }
00090     void setMimeType( const QString &mime )
00091     {
00092       mAttachment->setMimeType( mime );
00093       readAttachment();
00094     }
00095     void setLabel( const QString &label )
00096     {
00097       mAttachment->setLabel( label );
00098       readAttachment();
00099     }
00100 
00101     void readAttachment()
00102     {
00103       if ( mAttachment->isUri() )
00104         setText( mAttachment->uri() );
00105       else {
00106         if ( mAttachment->label().isEmpty() )
00107           setText( i18n("[Binary data]") );
00108         else
00109           setText( mAttachment->label() );
00110       }
00111       KMimeType::Ptr mt = KMimeType::mimeType( mAttachment->mimeType() );
00112       if ( mt ) {
00113           const QString iconName( mt->icon( QString(), false ) );
00114           QPixmap pix = KGlobal::iconLoader( )->loadIcon( iconName, KIcon::Small );
00115           if ( pix.isNull() )
00116             pix = KGlobal::iconLoader( )->loadIcon( "unknown", KIcon::Small );
00117             if ( !pix.isNull() )
00118               setPixmap( pix );
00119       }
00120     }
00121 
00122   private:
00123     KCal::Attachment *mAttachment;
00124 };
00125 
00126 class AttachmentIconView : public KIconView
00127 {
00128     friend class KOEditorAttachments;
00129     public:
00130         AttachmentIconView( KOEditorAttachments* parent=0 )
00131             :KIconView( parent ),
00132              mParent( parent )
00133         {
00134             setAcceptDrops( true );
00135             setSelectionMode( QIconView::Extended );
00136             setMode( KIconView::Select );
00137             setItemTextPos( QIconView::Right );
00138             setArrangement( QIconView::LeftToRight );
00139             setMaxItemWidth( QMAX(maxItemWidth(), 250) );
00140             setMinimumHeight( QMAX(fontMetrics().height(), 16) + 12 );
00141         }
00142         ~AttachmentIconView()
00143         {
00144             for ( std::set<KTempDir*>::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) {
00145                 delete *it;
00146             }
00147         }
00148     protected:
00149         QDragObject * dragObject()
00150         {
00151             KURL::List urls;
00152             for ( QIconViewItem *it = firstItem( ); it; it = it->nextItem( ) ) {
00153                 if ( !it->isSelected() ) continue;
00154                 AttachmentListItem * item = dynamic_cast<AttachmentListItem*>( it );
00155                 if ( !item ) return 0;
00156                 KCal::Attachment * att = item->attachment();
00157                 assert( att );
00158                 KURL url;
00159                 if ( att->isUri() ) {
00160                     url.setPath( att->uri() );
00161                 } else {
00162                     KTempDir * tempDir = new KTempDir(); // will be deleted on editor close
00163                     tempDir->setAutoDelete( true );
00164                     mTempDirs.insert( tempDir );
00165                     QByteArray encoded;
00166                     encoded.duplicate( att->data(), strlen(att->data()) );
00167                     QByteArray decoded;
00168                     KCodecs::base64Decode( encoded, decoded );
00169                     const QString fileName = tempDir->name( ) + "/" + att->label();
00170                     KPIM::kByteArrayToFile( decoded, fileName, false, false, false );
00171                     url.setPath( fileName );
00172                 }
00173                 urls << url;
00174             }
00175             KURLDrag *drag  = new KURLDrag( urls, this );
00176             return drag;
00177         }
00178         void contentsDropEvent( QDropEvent* event )
00179         {
00180           mParent->handlePasteOrDrop( event );
00181         }
00182     private:
00183         std::set<KTempDir*> mTempDirs;
00184         KOEditorAttachments* mParent;
00185 };
00186 
00187 KOEditorAttachments::KOEditorAttachments( int spacing, QWidget *parent,
00188                                           const char *name )
00189   : QWidget( parent, name )
00190 {
00191   QBoxLayout *topLayout = new QHBoxLayout( this );
00192   topLayout->setSpacing( spacing );
00193 
00194   QLabel *label = new QLabel( i18n("Attachments:"), this );
00195   topLayout->addWidget( label );
00196 
00197   mAttachments = new AttachmentIconView( this );
00198   QWhatsThis::add( mAttachments,
00199                    i18n("Displays a list of current items (files, mail, etc.) "
00200                         "that have been associated with this event or to-do. ") );
00201   topLayout->addWidget( mAttachments );
00202   connect( mAttachments, SIGNAL( doubleClicked( QIconViewItem * ) ),
00203            SLOT( showAttachment( QIconViewItem * ) ) );
00204   connect( mAttachments, SIGNAL(selectionChanged()),
00205            SLOT(selectionChanged()) );
00206   connect( mAttachments, SIGNAL(contextMenuRequested(QIconViewItem*,const QPoint&)),
00207            SLOT(contextMenu(QIconViewItem*,const QPoint&)) );
00208 
00209   mAddMenu = new KPopupMenu( this );
00210   mContextMenu = new KPopupMenu( this );
00211 
00212   KActionCollection* ac = new KActionCollection( this, this );
00213 
00214   mOpenAction = new KAction( i18n("View"), 0, this, SLOT(slotShow()), ac );
00215   mOpenAction->plug( mContextMenu );
00216   mContextMenu->insertSeparator();
00217 
00218   mCopyAction = KStdAction::copy(this, SLOT(slotCopy( ) ), ac );
00219   mCopyAction->plug( mContextMenu );
00220   mCutAction = KStdAction::cut(this, SLOT(slotCut( ) ), ac );
00221   mCutAction->plug( mContextMenu );
00222   KAction *action = KStdAction::paste(this, SLOT(slotPaste( ) ), ac );
00223   action->plug( mContextMenu );
00224 
00225   action = new KAction( i18n("&Attach File..."), 0, this, SLOT(slotAddData()), ac );
00226   action->setWhatsThis( i18n("Shows a dialog used to select an attachment "
00227                         "to add to this event or to-do as link as inline data.") );
00228   action->plug( mAddMenu );
00229   action = new KAction( i18n("Attach &Link..."), 0, this, SLOT(slotAdd()), ac );
00230   action->setWhatsThis( i18n("Shows a dialog used to select an attachment "
00231                         "to add to this event or to-do as link.") );
00232   action->plug( mAddMenu );
00233 
00234   QPushButton *addButton = new QPushButton( this );
00235   addButton->setIconSet( SmallIconSet( "add" ) );
00236   addButton->setPopup( mAddMenu );
00237   topLayout->addWidget( addButton );
00238 
00239   mRemoveBtn = new QPushButton( this );
00240   mRemoveBtn->setIconSet( SmallIconSet( "remove" ) );
00241   QToolTip::add( mRemoveBtn, i18n("&Remove") );
00242   QWhatsThis::add( mRemoveBtn,
00243                    i18n("Removes the attachment selected in the list above "
00244                         "from this event or to-do.") );
00245   topLayout->addWidget( mRemoveBtn );
00246   connect( mRemoveBtn, SIGNAL( clicked() ), SLOT( slotRemove() ) );
00247 
00248   selectionChanged();
00249   setAcceptDrops( true );
00250 }
00251 
00252 KOEditorAttachments::~KOEditorAttachments()
00253 {
00254 }
00255 
00256 bool KOEditorAttachments::hasAttachments()
00257 {
00258   return mAttachments->count() != 0;
00259 }
00260 
00261 void KOEditorAttachments::dragEnterEvent( QDragEnterEvent* event )
00262 {
00263   event->accept( KURLDrag::canDecode( event ) | QTextDrag::canDecode( event ) );
00264 }
00265 
00266 void KOEditorAttachments::handlePasteOrDrop( QMimeSource* source )
00267 {
00268   KURL::List urls;
00269   QString text;
00270   if ( KURLDrag::decode( source, urls ) ) {
00271     const bool asUri = KMessageBox::questionYesNo( this,
00272             i18n("Do you want to link to the attachments, or include them in the event?"),
00273             i18n("Attach as link?"), i18n("As Link"), i18n("As File") ) == KMessageBox::Yes;
00274     for ( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) {
00275       addAttachment( (*it).url(), QString::null, asUri );
00276     }
00277   } else if ( QTextDrag::decode( source, text ) ) {
00278     QStringList lst = QStringList::split( '\n', text );
00279     for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
00280       addAttachment( (*it)  );
00281     }
00282   }
00283 }
00284 
00285 void KOEditorAttachments::dropEvent( QDropEvent* event )
00286 {
00287     handlePasteOrDrop( event );
00288 }
00289 
00290 void KOEditorAttachments::showAttachment( QIconViewItem *item )
00291 {
00292   AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
00293   if ( !attitem || !attitem->attachment() ) return;
00294 
00295   KCal::Attachment *att = attitem->attachment();
00296   if ( att->isUri() ) {
00297     emit openURL( att->uri() );
00298   } else {
00299     KTempFile f;
00300     if ( !f.file() )
00301       return;
00302     QByteArray encoded;
00303     encoded.duplicate( att->data(), strlen(att->data()) );
00304     QByteArray decoded;
00305     KCodecs::base64Decode( encoded, decoded );
00306     f.file()->writeBlock( decoded );
00307     f.file()->close();
00308     KRun::runURL( f.name(), att->mimeType(), true, false );
00309   }
00310 }
00311 
00312 void KOEditorAttachments::slotAdd()
00313 {
00314   KURL uri = KPimURLRequesterDlg::getURL( QString::null, i18n(
00315          "URL (e.g. a web page) or file to be attached (only "
00316          "the link will be attached, not the file itself):"), this,
00317                                        i18n("Add Attachment") );
00318   if ( !uri.isEmpty() ) {
00319     addAttachment( uri );
00320   }
00321 }
00322 
00323 void KOEditorAttachments::slotAddData()
00324 {
00325   KURL uri = KFileDialog::getOpenFileName( QString(), QString(), this, i18n("Add Attachment") );
00326   if ( !uri.isEmpty() ) {
00327     addAttachment( uri, QString::null, false );
00328   }
00329 }
00330 
00331 void KOEditorAttachments::slotEdit()
00332 {
00333   QIconViewItem *item = mAttachments->currentItem();
00334   AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
00335   if ( !attitem || !attitem->attachment() ) return;
00336 
00337   KCal::Attachment *att = attitem->attachment();
00338   if ( att->isUri() ) {
00339     KURL uri = KPimURLRequesterDlg::getURL( att->uri(), i18n(
00340          "URL (e.g. a web page) or file to be attached (only "
00341          "the link will be attached, not the file itself):"), this,
00342                                          i18n("Edit Attachment") );
00343 
00344     if ( !uri.isEmpty() )
00345       attitem->setUri( uri.url() );
00346   } else {
00347     KURL uri = KPimURLRequesterDlg::getURL( QString::null, i18n(
00348          "File to be attached:"), this, i18n("Add Attachment") );
00349     if ( !uri.isEmpty() ) {
00350           QString tmpFile;
00351       if ( KIO::NetAccess::download( uri, tmpFile, this ) ) {
00352         QFile f( tmpFile );
00353         if ( !f.open( IO_ReadOnly ) )
00354           return;
00355         QByteArray data = f.readAll();
00356         f.close();
00357         attitem->setData( KCodecs::base64Encode( data ) );
00358         attitem->setMimeType( KIO::NetAccess::mimetype( uri, this ) );
00359         QString label = uri.fileName();
00360         if ( label.isEmpty() )
00361           label = uri.prettyURL();
00362         attitem->setLabel( label );
00363         KIO::NetAccess::removeTempFile( tmpFile );
00364       }
00365     }
00366   }
00367 }
00368 
00369 void KOEditorAttachments::slotRemove()
00370 {
00371     QValueList<QIconViewItem*> selected;
00372     for ( QIconViewItem *it = mAttachments->firstItem( ); it; it = it->nextItem( ) ) {
00373         if ( !it->isSelected() ) continue;
00374         selected << it;
00375     }
00376     if ( selected.isEmpty() || KMessageBox::warningContinueCancel(this,
00377                     selected.count() == 1?i18n("This item will be permanently deleted."):
00378                     i18n("The selected items will be permanently deleted."),
00379                     i18n("KOrganizer Confirmation"),KStdGuiItem::del()) != KMessageBox::Continue )
00380         return;
00381 
00382     for ( QValueList<QIconViewItem*>::iterator it( selected.begin() ), end( selected.end() ); it != end ; ++it ) {
00383         delete *it;
00384     }
00385 }
00386 
00387 void KOEditorAttachments::slotShow()
00388 {
00389   for ( QIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) {
00390     if ( !it->isSelected() )
00391       continue;
00392     showAttachment( it );
00393   }
00394 }
00395 
00396 void KOEditorAttachments::setDefaults()
00397 {
00398   mAttachments->clear();
00399 }
00400 
00401 void KOEditorAttachments::addAttachment( const KURL &uri,
00402                                          const QString &mimeType, bool asUri )
00403 {
00404   AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );
00405   if ( asUri ) {
00406     item->setUri( uri.url() );
00407     if ( !mimeType.isEmpty() ) item->setMimeType( mimeType );
00408   } else {
00409     QString tmpFile;
00410     if ( KIO::NetAccess::download( uri, tmpFile, this ) ) {
00411       QFile f( tmpFile );
00412       if ( !f.open( IO_ReadOnly ) )
00413         return;
00414       QByteArray data = f.readAll();
00415       f.close();
00416       item->setData( KCodecs::base64Encode( data ) );
00417       if ( !mimeType.isEmpty() )
00418         item->setMimeType( mimeType );
00419       else
00420         item->setMimeType( KIO::NetAccess::mimetype( uri, this ) );
00421       QString label = uri.fileName();
00422       if ( label.isEmpty() )
00423         label = uri.prettyURL();
00424       item->setLabel( label );
00425       KIO::NetAccess::removeTempFile( tmpFile );
00426     }
00427   }
00428 }
00429 
00430 
00431 void KOEditorAttachments::addAttachment( KCal::Attachment *attachment )
00432 {
00433   new AttachmentListItem( attachment, mAttachments );
00434 }
00435 
00436 void KOEditorAttachments::readIncidence( KCal::Incidence *i )
00437 {
00438   mAttachments->clear();
00439 
00440   KCal::Attachment::List attachments = i->attachments();
00441   KCal::Attachment::List::ConstIterator it;
00442   for( it = attachments.begin(); it != attachments.end(); ++it ) {
00443     addAttachment( (*it) );
00444   }
00445   if ( mAttachments->count() > 0 ) {
00446     QTimer::singleShot( 0, mAttachments, SLOT(arrangeItemsInGrid()) );
00447   }
00448 }
00449 
00450 void KOEditorAttachments::writeIncidence( KCal::Incidence *i )
00451 {
00452   i->clearAttachments();
00453 
00454   QIconViewItem *item;
00455   AttachmentListItem *attitem;
00456   for( item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00457     attitem = static_cast<AttachmentListItem*>(item);
00458     if ( attitem )
00459       i->addAttachment( new KCal::Attachment( *(attitem->attachment() ) ) );
00460   }
00461 }
00462 
00463 
00464 void KOEditorAttachments::slotCopy()
00465 {
00466     QApplication::clipboard()->setData( mAttachments->dragObject(), QClipboard::Clipboard );
00467 }
00468 
00469 void KOEditorAttachments::slotCut()
00470 {
00471     slotCopy();
00472     slotRemove();
00473 }
00474 
00475 void KOEditorAttachments::slotPaste()
00476 {
00477     handlePasteOrDrop( QApplication::clipboard()->data() );
00478 }
00479 
00480 void KOEditorAttachments::selectionChanged()
00481 {
00482   bool selected = false;
00483   for ( QIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00484     if ( item->isSelected() ) {
00485       selected = true;
00486       break;
00487     }
00488   }
00489   mRemoveBtn->setEnabled( selected );
00490 }
00491 
00492 void KOEditorAttachments::contextMenu(QIconViewItem * item, const QPoint & pos)
00493 {
00494   const bool enable = item != 0;
00495   mOpenAction->setEnabled( enable );
00496   mCopyAction->setEnabled( enable );
00497   mCutAction->setEnabled( enable );
00498   mContextMenu->exec( pos );
00499 }
00500 
00501 #include "koeditorattachments.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys