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 #include <libkdepim/kdepimprotocols.h>
00032 #include <libkdepim/maillistdrag.h>
00033 #include <libkdepim/kvcarddrag.h>
00034 #include <libkdepim/kdepimprotocols.h>
00035 
00036 #include <klocale.h>
00037 #include <kdebug.h>
00038 #include <kmdcodec.h>
00039 #include <kmessagebox.h>
00040 #include <krun.h>
00041 #include <kurldrag.h>
00042 #include <ktempfile.h>
00043 #include <ktempdir.h>
00044 #include <kio/netaccess.h>
00045 #include <kmimetype.h>
00046 #include <kiconloader.h>
00047 #include <kfiledialog.h>
00048 #include <kstdaction.h>
00049 #include <kactioncollection.h>
00050 #include <kpopupmenu.h>
00051 #include <kprotocolinfo.h>
00052 #include <klineedit.h>
00053 #include <kseparator.h>
00054 #include <kurlrequester.h>
00055 #include <libkmime/kmime_message.h>
00056 
00057 #include <qcheckbox.h>
00058 #include <qfile.h>
00059 #include <qlabel.h>
00060 #include <qlayout.h>
00061 #include <qlistview.h>
00062 #include <qpushbutton.h>
00063 #include <qdragobject.h>
00064 #include <qtooltip.h>
00065 #include <qwhatsthis.h>
00066 #include <qapplication.h>
00067 #include <qclipboard.h>
00068 
00069 #include <cassert>
00070 #include <cstdlib>
00071 
00072 class AttachmentListItem : public KIconViewItem
00073 {
00074   public:
00075     AttachmentListItem( KCal::Attachment*att, QIconView *parent ) :
00076         KIconViewItem( parent )
00077     {
00078       if ( att ) {
00079         mAttachment = new KCal::Attachment( *att );
00080       } else {
00081         mAttachment = new KCal::Attachment( '\0' ); //use the non-uri constructor
00082                                                     //as we want inline by default
00083       }
00084       readAttachment();
00085       setDragEnabled( true );
00086     }
00087     ~AttachmentListItem() { delete mAttachment; }
00088     KCal::Attachment *attachment() const { return mAttachment; }
00089 
00090     const QString uri() const
00091     {
00092       return mAttachment->uri();
00093     }
00094     void setUri( const QString &uri )
00095     {
00096       mAttachment->setUri( uri );
00097       readAttachment();
00098     }
00099     void setData( const QByteArray data )
00100     {
00101       mAttachment->setDecodedData( data );
00102       readAttachment();
00103     }
00104     const QString mimeType() const
00105     {
00106       return mAttachment->mimeType();
00107     }
00108     void setMimeType( const QString &mime )
00109     {
00110       mAttachment->setMimeType( mime );
00111       readAttachment();
00112     }
00113     const QString label() const
00114     {
00115       return mAttachment->label();
00116     }
00117     void setLabel( const QString &label )
00118     {
00119       mAttachment->setLabel( label );
00120       readAttachment();
00121     }
00122     bool isBinary() const
00123     {
00124       return mAttachment->isBinary();
00125     }
00126     QPixmap icon() const
00127     {
00128       return icon( KMimeType::mimeType( mAttachment->mimeType() ),
00129                    mAttachment->uri() );
00130     }
00131     static QPixmap icon( KMimeType::Ptr mimeType, const QString &uri )
00132     {
00133       QString iconStr = mimeType->icon( uri, false );
00134       return KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Small );
00135     }
00136     void readAttachment()
00137     {
00138       if ( mAttachment->label().isEmpty() ) {
00139         if ( mAttachment->isUri() ) {
00140           setText( mAttachment->uri() );
00141         } else {
00142           setText( i18n( "[Binary data]" ) );
00143         }
00144       } else {
00145         setText( mAttachment->label() );
00146       }
00147       if ( mAttachment->mimeType().isEmpty() ||
00148            !( KMimeType::mimeType( mAttachment->mimeType() ) ) ) {
00149         KMimeType::Ptr mimeType;
00150         if ( mAttachment->isUri() ) {
00151           mimeType = KMimeType::findByURL( mAttachment->uri() );
00152         } else {
00153           mimeType = KMimeType::findByContent( mAttachment->decodedData() );
00154         }
00155         mAttachment->setMimeType( mimeType->name() );
00156       }
00157 
00158       setPixmap( icon() );
00159     }
00160 
00161   private:
00162     KCal::Attachment *mAttachment;
00163 };
00164 
00165 AttachmentEditDialog::AttachmentEditDialog( AttachmentListItem *item,
00166                                             QWidget *parent )
00167   : KDialogBase ( Plain, i18n( "Add Attachment" ), Ok|Cancel, Ok, parent, 0, false, false ),
00168     mItem( item ), mURLRequester( 0 )
00169 {
00170   QFrame *topFrame = plainPage();
00171   QVBoxLayout *vbl = new QVBoxLayout( topFrame, 0, spacingHint() );
00172 
00173   QGridLayout *grid = new QGridLayout();
00174   grid->setColStretch( 0, 0 );
00175   grid->setColStretch( 1, 0 );
00176   grid->setColStretch( 2, 1 );
00177   vbl->addLayout( grid );
00178 
00179   mIcon = new QLabel( topFrame );
00180   mIcon->setPixmap( item->icon() );
00181   grid->addWidget( mIcon, 0, 0 );
00182 
00183   mLabelEdit = new KLineEdit( topFrame );
00184   mLabelEdit->setText( item->label().isEmpty() ? item->uri() : item->label() );
00185   mLabelEdit->setClickMessage( i18n( "Attachment name" ) );
00186   QToolTip::add( mLabelEdit, i18n( "Give the attachment a name" ) );
00187   QWhatsThis::add( mLabelEdit,
00188                    i18n( "Type any string you desire here for the name of the attachment" ) );
00189   grid->addMultiCellWidget( mLabelEdit, 0, 0, 1, 2 );
00190 
00191   KSeparator *sep = new KSeparator( Qt::Horizontal, topFrame );
00192   grid->addMultiCellWidget( sep, 1, 1, 0, 2 );
00193 
00194   QLabel *label = new QLabel( i18n( "Type:" ), topFrame );
00195   grid->addWidget( label, 2, 0 );
00196   QString typecomment = item->mimeType().isEmpty() ?
00197                         i18n( "Unknown" ) :
00198                         KMimeType::mimeType( item->mimeType() )->comment();
00199   mTypeLabel = new QLabel( typecomment, topFrame );
00200   grid->addWidget( mTypeLabel, 2, 1 );
00201   mMimeType = KMimeType::mimeType( item->mimeType() );
00202 
00203   mInline = new QCheckBox( i18n( "Store attachment inline" ), topFrame );
00204   grid->addMultiCellWidget( mInline, 3, 3, 0, 2 );
00205   mInline->setChecked( item->isBinary() );
00206   QToolTip::add( mInline, i18n( "Store the attachment file inside the calendar" ) );
00207   QWhatsThis::add(
00208     mInline,
00209     i18n( "Checking this option will cause the attachment to be stored inside "
00210           "your calendar, which can take a lot of space depending on the size "
00211           "of the attachment. If this option is not checked, then only a link "
00212           "pointing to the attachment will be stored.  Do not use a link for "
00213           "attachments that change often or may be moved (or removed) from "
00214           "their current location." ) );
00215 
00216   if ( item->attachment()->isUri() || !item->attachment()->data() ) {
00217     label = new QLabel( i18n( "Location:" ), topFrame );
00218     grid->addWidget( label, 4, 0 );
00219     mURLRequester = new KURLRequester( item->uri(), topFrame );
00220     QToolTip::add( mURLRequester, i18n( "Provide a location for the attachment file" ) );
00221     QWhatsThis::add(
00222       mURLRequester,
00223       i18n( "Enter the path to the attachment file or use the "
00224             "file browser by pressing the adjacent button" ) );
00225     grid->addMultiCellWidget( mURLRequester, 4, 4, 1, 2 );
00226     connect( mURLRequester, SIGNAL(urlSelected(const QString &)),
00227              SLOT(urlSelected(const QString &)) );
00228     connect( mURLRequester, SIGNAL( textChanged( const QString& ) ),
00229              SLOT( urlChanged( const QString& ) ) );
00230     urlChanged( item->uri() );
00231   } else {
00232     uint size = item->attachment()->size();
00233     grid->addWidget( new QLabel( i18n( "Size:" ), topFrame ), 4, 0 );
00234     grid->addWidget( new QLabel( QString::fromLatin1( "%1 (%2)" ).
00235                                  arg( KIO::convertSize( size ) ).
00236                                  arg( KGlobal::locale()->formatNumber(
00237                                         size, 0 ) ), topFrame ), 4, 2 );
00238   }
00239   vbl->addStretch( 10 );
00240 }
00241 
00242 void AttachmentEditDialog::slotApply()
00243 {
00244   if ( !mLabelEdit->text().isEmpty() ) {
00245     mItem->setLabel( mLabelEdit->text() );
00246   } else {
00247     if ( mURLRequester ) {
00248       KURL url( mURLRequester->url() );
00249       if ( url.isLocalFile() ) {
00250         mItem->setLabel( url.fileName() );
00251       } else {
00252         mItem->setLabel( url.url() );
00253       }
00254     }
00255   }
00256   if ( mItem->label().isEmpty() ) {
00257     mItem->setLabel( i18n( "New attachment" ) );
00258   }
00259   mItem->setMimeType( mMimeType->name() );
00260   if ( mURLRequester ) {
00261     KURL url( mURLRequester->url() );
00262 
00263     QString correctedUrl = mURLRequester->url();
00264     if ( !url.isValid() ) {
00265       // If the user used KURLRequester's KURLCompletion
00266       // (used the line edit instead of the file dialog)
00267       // the returned url is not absolute and is always relative
00268       // to the home directory (not pwd), so we must prepend home
00269 
00270       correctedUrl = QDir::home().filePath( mURLRequester->url() );
00271       url = KURL( correctedUrl );
00272       if ( url.isValid() ) {
00273         urlSelected( correctedUrl );
00274         mItem->setMimeType( mMimeType->name() );
00275       }
00276     }
00277 
00278     if ( mInline->isChecked() ) {
00279       QString tmpFile;
00280       if ( KIO::NetAccess::download( correctedUrl, tmpFile, this ) ) {
00281         QFile f( tmpFile );
00282         if ( !f.open( IO_ReadOnly ) ) {
00283           return;
00284         }
00285         QByteArray data = f.readAll();
00286         f.close();
00287         mItem->setData( data );
00288       }
00289       KIO::NetAccess::removeTempFile( tmpFile );
00290     } else {
00291       mItem->setUri( url.url() );
00292     }
00293   }
00294 }
00295 
00296 void AttachmentEditDialog::accept()
00297 {
00298   slotApply();
00299   KDialog::accept();
00300 }
00301 
00302 void AttachmentEditDialog::urlChanged( const QString &url )
00303 {
00304   enableButton( Ok, !url.isEmpty() );
00305 }
00306 
00307 void AttachmentEditDialog::urlSelected( const QString &url )
00308 {
00309   KURL kurl( url );
00310   mMimeType = KMimeType::findByURL( kurl );
00311   mTypeLabel->setText( mMimeType->comment() );
00312   mIcon->setPixmap( AttachmentListItem::icon( mMimeType, kurl.path() ) );
00313 }
00314 
00315 AttachmentIconView::AttachmentIconView( KOEditorAttachments* parent )
00316   : KIconView( parent ),
00317     mParent( parent )
00318 {
00319   setSelectionMode( QIconView::Extended );
00320   setMode( KIconView::Select );
00321   setItemTextPos( QIconView::Right );
00322   setArrangement( QIconView::LeftToRight );
00323   setMaxItemWidth( QMAX(maxItemWidth(), 250) );
00324   setMinimumHeight( QMAX(fontMetrics().height(), 16) + 12 );
00325 
00326   connect( this, SIGNAL( dropped ( QDropEvent *, const QValueList<QIconDragItem> & ) ),
00327            this, SLOT( handleDrop( QDropEvent *, const QValueList<QIconDragItem> & ) ) );
00328 }
00329 
00330 KURL AttachmentIconView::tempFileForAttachment( KCal::Attachment *attachment )
00331 {
00332   if ( mTempFiles.contains( attachment ) ) {
00333     return mTempFiles[attachment];
00334   }
00335   QStringList patterns = KMimeType::mimeType( attachment->mimeType() )->patterns();
00336 
00337   KTempFile *file;
00338   if ( !patterns.empty() ) {
00339     file = new KTempFile( QString::null,
00340                           QString( patterns.first() ).remove( '*' ),0600 );
00341   } else {
00342     file = new KTempFile( QString::null, QString::null, 0600 );
00343   }
00344   file->setAutoDelete( true );
00345   file->file()->open( IO_WriteOnly );
00346   QTextStream stream( file->file() );
00347   stream.writeRawBytes( attachment->decodedData().data(), attachment->size() );
00348   KURL url( file->name() );
00349   mTempFiles.insert( attachment, url );
00350   file->close();
00351   return mTempFiles[attachment];
00352 }
00353 
00354 QDragObject *AttachmentIconView::mimeData()
00355 {
00356   // create a list of the URL:s that we want to drag
00357   KURL::List urls;
00358   QStringList labels;
00359   for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
00360     if ( it->isSelected() ) {
00361       AttachmentListItem *item = static_cast<AttachmentListItem *>( it );
00362       if ( item->isBinary() ) {
00363         urls.append( tempFileForAttachment( item->attachment() ) );
00364       } else {
00365         urls.append( item->uri() );
00366       }
00367       labels.append( KURL::encode_string( item->label() ) );
00368     }
00369   }
00370   if ( selectionMode() == QIconView::NoSelection ) {
00371     AttachmentListItem *item = static_cast<AttachmentListItem *>( currentItem() );
00372     if ( item ) {
00373       urls.append( item->uri() );
00374       labels.append( KURL::encode_string( item->label() ) );
00375     }
00376   }
00377 
00378   QMap<QString, QString> metadata;
00379   metadata["labels"] = labels.join( ":" );
00380 
00381   KURLDrag *drag = new KURLDrag( urls, metadata );
00382   return drag;
00383 }
00384 
00385 AttachmentIconView::~AttachmentIconView()
00386 {
00387   for ( std::set<KTempDir*>::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) {
00388     delete *it;
00389   }
00390 }
00391 
00392 QDragObject * AttachmentIconView::dragObject()
00393 {
00394   KURL::List urls;
00395   for ( QIconViewItem *it = firstItem( ); it; it = it->nextItem( ) ) {
00396     if ( !it->isSelected() ) continue;
00397     AttachmentListItem * item = dynamic_cast<AttachmentListItem*>( it );
00398     if ( !item ) return 0;
00399     KCal::Attachment * att = item->attachment();
00400     assert( att );
00401     KURL url;
00402     if ( att->isUri() ) {
00403       url.setPath( att->uri() );
00404     } else {
00405       KTempDir *tempDir = new KTempDir(); // will be deleted on editor close
00406       tempDir->setAutoDelete( true );
00407       mTempDirs.insert( tempDir );
00408       QByteArray encoded;
00409       encoded.duplicate( att->data(), strlen( att->data() ) );
00410       QByteArray decoded;
00411       KCodecs::base64Decode( encoded, decoded );
00412       // base64Decode() sometimes appends a null byte when called so
00413       // if the last byte is 0 remove it (this can happen sometimes)
00414       unsigned int len = decoded.size();
00415       if ( len > 0 && decoded[len - 1] == 0 ) {
00416         decoded.truncate( len - 1 );
00417       }
00418       const QString fileName = tempDir->name( ) + '/' + att->label();
00419       KPIM::kByteArrayToFile( decoded, fileName, false, false, false );
00420       url.setPath( fileName );
00421     }
00422     urls << url;
00423   }
00424   KURLDrag *drag  = new KURLDrag( urls, this );
00425   return drag;
00426 }
00427 
00428 void AttachmentIconView::handleDrop( QDropEvent *event, const QValueList<QIconDragItem> & list )
00429 {
00430   Q_UNUSED( list );
00431   mParent->handlePasteOrDrop( event );
00432 }
00433 
00434 
00435 void AttachmentIconView::dragMoveEvent( QDragMoveEvent *event )
00436 {
00437   mParent->dragMoveEvent( event );
00438 }
00439 
00440 void AttachmentIconView::contentsDragMoveEvent( QDragMoveEvent *event )
00441 {
00442   mParent->dragMoveEvent( event );
00443 }
00444 
00445 void AttachmentIconView::contentsDragEnterEvent( QDragEnterEvent *event )
00446 {
00447   mParent->dragMoveEvent( event );
00448 }
00449 
00450 void AttachmentIconView::dragEnterEvent( QDragEnterEvent *event )
00451 {
00452   mParent->dragEnterEvent( event );
00453 }
00454 
00455 KOEditorAttachments::KOEditorAttachments( int spacing, QWidget *parent,
00456                                           const char *name )
00457   : QWidget( parent, name )
00458 {
00459   QBoxLayout *topLayout = new QHBoxLayout( this );
00460   topLayout->setSpacing( spacing );
00461 
00462   QLabel *label = new QLabel( i18n("Attachments:"), this );
00463   topLayout->addWidget( label );
00464 
00465   mAttachments = new AttachmentIconView( this );
00466   QWhatsThis::add( mAttachments,
00467                    i18n("Displays a list of current items (files, mail, etc.) "
00468                         "that have been associated with this event or to-do. ") );
00469   topLayout->addWidget( mAttachments );
00470   connect( mAttachments, SIGNAL( doubleClicked( QIconViewItem * ) ),
00471            SLOT( showAttachment( QIconViewItem * ) ) );
00472   connect( mAttachments, SIGNAL(selectionChanged()),
00473            SLOT(selectionChanged()) );
00474   connect( mAttachments, SIGNAL(contextMenuRequested(QIconViewItem*,const QPoint&)),
00475            SLOT(contextMenu(QIconViewItem*,const QPoint&)) );
00476 
00477     QPushButton *addButton = new QPushButton( this );
00478   addButton->setIconSet( SmallIconSet( "add" ) );
00479   QToolTip::add( addButton, i18n( "Add an attachment" ) );
00480   QWhatsThis::add( addButton,
00481                    i18n( "Shows a dialog used to select an attachment "
00482                          "to add to this event or to-do as link or as "
00483                          "inline data." ) );
00484   topLayout->addWidget( addButton );
00485   connect( addButton, SIGNAL(clicked()), SLOT(slotAdd()) );
00486 
00487   mRemoveBtn = new QPushButton( this );
00488   mRemoveBtn->setIconSet( SmallIconSet( "remove" ) );
00489   QToolTip::add( mRemoveBtn, i18n("&Remove") );
00490   QWhatsThis::add( mRemoveBtn,
00491                    i18n("Removes the attachment selected in the list above "
00492                         "from this event or to-do.") );
00493   topLayout->addWidget( mRemoveBtn );
00494   connect( mRemoveBtn, SIGNAL(clicked()), SLOT(slotRemove()) );
00495 
00496   mContextMenu = new KPopupMenu( this );
00497 
00498   KActionCollection* ac = new KActionCollection( this, this );
00499 
00500   mOpenAction = new KAction( i18n("Open"), 0, this, SLOT(slotShow()), ac );
00501   mOpenAction->plug( mContextMenu );
00502 
00503   mSaveAsAction = new KAction( i18n( "Save As..." ), 0, this, SLOT(slotSaveAs()), ac );
00504   mSaveAsAction->plug( mContextMenu );
00505   mContextMenu->insertSeparator();
00506 
00507   mCopyAction = KStdAction::copy(this, SLOT(slotCopy()), ac );
00508   mCopyAction->plug( mContextMenu );
00509   mCutAction = KStdAction::cut(this, SLOT(slotCut()), ac );
00510   mCutAction->plug( mContextMenu );
00511   KAction *action = KStdAction::paste(this, SLOT(slotPaste()), ac );
00512   action->plug( mContextMenu );
00513   mContextMenu->insertSeparator();
00514 
00515   mDeleteAction = new KAction( i18n( "&Remove" ), 0, this, SLOT(slotRemove()),  ac );
00516   mDeleteAction->plug( mContextMenu );
00517   mDeleteAction->setShortcut( Key_Delete );
00518   mContextMenu->insertSeparator();
00519 
00520   mEditAction = new KAction( i18n( "&Properties..." ), 0, this, SLOT(slotEdit()), ac );
00521   mEditAction->plug( mContextMenu );
00522 
00523   selectionChanged();
00524   setAcceptDrops( true );
00525 }
00526 
00527 KOEditorAttachments::~KOEditorAttachments()
00528 {
00529 }
00530 
00531 bool KOEditorAttachments::hasAttachments()
00532 {
00533   return mAttachments->count() != 0;
00534 }
00535 
00536 void KOEditorAttachments::dragMoveEvent( QDragMoveEvent *event )
00537 {
00538   event->accept( KURLDrag::canDecode( event ) ||
00539                  QTextDrag::canDecode( event ) ||
00540                  KPIM::MailListDrag::canDecode( event ) ||
00541                  KVCardDrag::canDecode( event ) );
00542 }
00543 
00544 void KOEditorAttachments::dragEnterEvent( QDragEnterEvent* event )
00545 {
00546   dragMoveEvent( event );
00547 }
00548 
00549 void KOEditorAttachments::handlePasteOrDrop( QMimeSource* source )
00550 {
00551   KURL::List urls;
00552   bool probablyWeHaveUris = false;
00553   bool weCanCopy = true;
00554   QStringList labels;
00555 
00556   if ( KVCardDrag::canDecode( source ) ) {
00557     KABC::Addressee::List addressees;
00558     KVCardDrag::decode( source, addressees );
00559     for ( KABC::Addressee::List::ConstIterator it = addressees.constBegin();
00560           it != addressees.constEnd(); ++it ) {
00561       urls.append( KDEPIMPROTOCOL_CONTACT + ( *it ).uid() );
00562       // there is some weirdness about realName(), hence fromUtf8
00563       labels.append( QString::fromUtf8( ( *it ).realName().latin1() ) );
00564     }
00565     probablyWeHaveUris = true;
00566   } else if ( KURLDrag::canDecode( source ) ) {
00567     QMap<QString,QString> metadata;
00568     if ( KURLDrag::decode( source, urls, metadata ) ) {
00569       probablyWeHaveUris = true;
00570       labels = QStringList::split( ':', metadata["labels"], FALSE );
00571       for ( QStringList::Iterator it = labels.begin(); it != labels.end(); ++it ) {
00572         *it = KURL::decode_string( (*it).latin1() );
00573       }
00574 
00575     }
00576   } else if ( QTextDrag::canDecode( source ) ) {
00577     QString text;
00578     QTextDrag::decode( source, text );
00579     QStringList lst = QStringList::split( '\n', text, FALSE );
00580     for ( QStringList::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) {
00581       urls.append( *it );
00582       labels.append( QString::null );
00583     }
00584     probablyWeHaveUris = true;
00585   }
00586 
00587   KPopupMenu menu;
00588   int items=0;
00589   if ( probablyWeHaveUris ) {
00590     menu.insertItem( i18n( "&Link here" ), DRAG_LINK, items++ );
00591     // we need to check if we can reasonably expect to copy the objects
00592     for ( KURL::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it ) {
00593       if ( !( weCanCopy = KProtocolInfo::supportsReading( *it ) ) ) {
00594         break; // either we can copy them all, or no copying at all
00595       }
00596     }
00597     if ( weCanCopy ) {
00598       menu.insertItem( SmallIcon( "editcopy" ), i18n( "&Copy Here" ), DRAG_COPY, items++ );
00599     }
00600   } else {
00601       menu.insertItem( SmallIcon( "editcopy" ), i18n( "&Copy Here" ), DRAG_COPY, items++ );
00602   }
00603 
00604   menu.insertSeparator();
00605   items++;
00606   menu.insertItem( SmallIcon( "cancel" ), i18n( "C&ancel" ), DRAG_CANCEL, items );
00607   int action = menu.exec( QCursor::pos(), 0 );
00608 
00609   if ( action == DRAG_LINK ) {
00610     QStringList::ConstIterator jt = labels.constBegin();
00611     for ( KURL::List::ConstIterator it = urls.constBegin();
00612           it != urls.constEnd(); ++it ) {
00613       QString label = (*jt++);
00614       if ( mAttachments->findItem( label ) ) {
00615         label += '~' + randomString( 3 );
00616       }
00617       addUriAttachment( (*it).url(), QString::null, label, true );
00618     }
00619   } else if ( action != DRAG_CANCEL ) {
00620     if ( probablyWeHaveUris ) {
00621       for ( KURL::List::ConstIterator it = urls.constBegin();
00622             it != urls.constEnd(); ++it ) {
00623         QString label = (*it).fileName();
00624         if ( label.isEmpty() ) {
00625           label = (*it).prettyURL();
00626         }
00627         if ( mAttachments->findItem( label ) ) {
00628           label += '~' + randomString( 3 );
00629         }
00630         addUriAttachment( (*it).url(), QString::null, label, true );
00631       }
00632     } else { // we take anything
00633       addDataAttachment( source->encodedData( source->format() ),
00634                          source->format(),
00635                          KMimeType::mimeType( source->format() )->name() );
00636     }
00637   }
00638 }
00639 
00640 void KOEditorAttachments::dropEvent( QDropEvent* event )
00641 {
00642     handlePasteOrDrop( event );
00643 }
00644 
00645 void KOEditorAttachments::showAttachment( QIconViewItem *item )
00646 {
00647   AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
00648   if ( !attitem || !attitem->attachment() ) return;
00649 
00650   KCal::Attachment *att = attitem->attachment();
00651   if ( att->isUri() ) {
00652     emit openURL( att->uri() );
00653   } else {
00654     KRun::runURL( mAttachments->tempFileForAttachment( att ), att->mimeType(), 0, true );
00655   }
00656 }
00657 
00658 void KOEditorAttachments::saveAttachment( QIconViewItem *item )
00659 {
00660   AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
00661   if ( !attitem || !attitem->attachment() ) return;
00662 
00663   KCal::Attachment *att = attitem->attachment();
00664 
00665   // get the saveas file name
00666   QString saveAsFile =
00667     KFileDialog::getSaveFileName( att->label(),
00668                                   QString::null, 0,
00669                                   i18n( "Save  Attachment" ) );
00670   if ( saveAsFile.isEmpty() ||
00671        ( QFile( saveAsFile ).exists() &&
00672          ( KMessageBox::warningYesNo(
00673            0,
00674            i18n( "%1 already exists. Do you want to overwrite it?").
00675            arg( saveAsFile ) ) == KMessageBox::No ) ) ) {
00676     return;
00677   }
00678 
00679   KURL sourceUrl;
00680   if ( att->isUri() ) {
00681     sourceUrl = att->uri();
00682   } else {
00683     sourceUrl = mAttachments->tempFileForAttachment( att );
00684   }
00685   // save the attachment url
00686   if ( !KIO::NetAccess::file_copy( sourceUrl, KURL( saveAsFile ), -1, true ) &&
00687        KIO::NetAccess::lastError() ) {
00688     KMessageBox::error( this, KIO::NetAccess::lastErrorString() );
00689   }
00690 }
00691 
00692 void KOEditorAttachments::slotAdd()
00693 {
00694   AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );
00695 
00696   AttachmentEditDialog *dlg = new AttachmentEditDialog( item, mAttachments )
00697 ;
00698   if ( dlg->exec() == KDialog::Rejected ) {
00699     delete item;
00700   }
00701   delete dlg;
00702 }
00703 
00704 void KOEditorAttachments::slotAddData()
00705 {
00706   KURL uri = KFileDialog::getOpenFileName( QString(), QString(), this, i18n("Add Attachment") );
00707   if ( !uri.isEmpty() ) {
00708     QString label = uri.fileName();
00709     if ( label.isEmpty() ) {
00710       label = uri.prettyURL();
00711     }
00712     addUriAttachment( uri.url(), QString::null, label, true );
00713   }
00714 }
00715 
00716 void KOEditorAttachments::slotEdit()
00717 {
00718   for ( QIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00719     if ( item->isSelected() ) {
00720       AttachmentListItem *attitem = static_cast<AttachmentListItem*>( item );
00721       if ( !attitem || !attitem->attachment() ) {
00722         return;
00723       }
00724 
00725       AttachmentEditDialog *dialog = new AttachmentEditDialog( attitem, mAttachments );
00726       dialog->mInline->setEnabled( false );
00727       dialog->setModal( false );
00728       connect( dialog, SIGNAL(hidden()), dialog, SLOT(delayedDestruct()) );
00729       dialog->show();
00730     }
00731   }
00732 }
00733 
00734 void KOEditorAttachments::slotRemove()
00735 {
00736   QValueList<QIconViewItem*> selected;
00737   QStringList labels;
00738   for ( QIconViewItem *it = mAttachments->firstItem( ); it; it = it->nextItem( ) ) {
00739     if ( !it->isSelected() ) continue;
00740     selected << it;
00741 
00742     AttachmentListItem *attitem = static_cast<AttachmentListItem*>(it);
00743     KCal::Attachment *att = attitem->attachment();
00744     labels << att->label();
00745   }
00746 
00747   if ( selected.isEmpty() ) {
00748     return;
00749   }
00750 
00751   QString labelsStr = labels.join( "<br>" );
00752 
00753   if ( KMessageBox::questionYesNo(
00754          this,
00755          i18n( "<qt>Do you really want to remove these attachments?<p>%1</qt>" ).arg( labelsStr ),
00756          i18n( "Remove Attachment?" ),
00757          KStdGuiItem::yes(), KStdGuiItem::no(),
00758          "calendarRemoveAttachments" ) != KMessageBox::Yes ) {
00759     return;
00760   }
00761 
00762   for ( QValueList<QIconViewItem*>::iterator it( selected.begin() ), end( selected.end() );
00763         it != end ; ++it ) {
00764     if ( (*it)->nextItem() ) {
00765       (*it)->nextItem()->setSelected( true );
00766     } else if ( (*it)->prevItem() ) {
00767       (*it)->prevItem()->setSelected( true );
00768     }
00769     delete *it;
00770   }
00771   mAttachments->slotUpdate();
00772 }
00773 
00774 void KOEditorAttachments::slotShow()
00775 {
00776   for ( QIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) {
00777     if ( !it->isSelected() )
00778       continue;
00779     showAttachment( it );
00780   }
00781 }
00782 
00783 void KOEditorAttachments::slotSaveAs()
00784 {
00785   for ( QIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) {
00786     if ( !it->isSelected() )
00787       continue;
00788     saveAttachment( it );
00789   }
00790 }
00791 
00792 void KOEditorAttachments::setDefaults()
00793 {
00794   mAttachments->clear();
00795 }
00796 
00797 QString KOEditorAttachments::randomString(int length) const
00798 {
00799    if (length <=0 ) return QString();
00800 
00801    QString str; str.setLength( length );
00802    int i = 0;
00803    while (length--)
00804    {
00805       int r=random() % 62;
00806       r+=48;
00807       if (r>57) r+=7;
00808       if (r>90) r+=6;
00809       str[i++] =  char(r);
00810       // so what if I work backwards?
00811    }
00812    return str;
00813 }
00814 
00815 void KOEditorAttachments::addUriAttachment( const QString &uri,
00816                                             const QString &mimeType,
00817                                             const QString &label,
00818                                             bool inLine )
00819 {
00820   if ( !inLine ) {
00821     AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );
00822     item->setUri( uri );
00823     item->setLabel( label );
00824     if ( mimeType.isEmpty() ) {
00825       if ( uri.startsWith( KDEPIMPROTOCOL_CONTACT ) ) {
00826         item->setMimeType( "text/directory" );
00827       } else if ( uri.startsWith( KDEPIMPROTOCOL_EMAIL ) ) {
00828         item->setMimeType( "message/rfc822" );
00829       } else if ( uri.startsWith( KDEPIMPROTOCOL_INCIDENCE ) ) {
00830         item->setMimeType( "text/calendar" );
00831       } else if ( uri.startsWith( KDEPIMPROTOCOL_NEWSARTICLE ) ) {
00832         item->setMimeType( "message/news" );
00833       } else {
00834         item->setMimeType( KMimeType::findByURL( uri )->name() );
00835       }
00836     }
00837   } else {
00838     QString tmpFile;
00839     if ( KIO::NetAccess::download( uri, tmpFile, this ) ) {
00840       QFile f( tmpFile );
00841       if ( !f.open( IO_ReadOnly ) ) {
00842         return;
00843       }
00844       const QByteArray data = f.readAll();
00845       f.close();
00846       addDataAttachment( data, mimeType, label );
00847     }
00848     KIO::NetAccess::removeTempFile( tmpFile );
00849   }
00850 }
00851 
00852 void KOEditorAttachments::addDataAttachment( const QByteArray &data,
00853                                              const QString &mimeType,
00854                                              const QString &label )
00855 {
00856   AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );
00857 
00858   QString nlabel = label;
00859   if ( mimeType == "message/rfc822" ) {
00860     // mail message. try to set the label from the mail Subject:
00861     KMime::Message msg;
00862     msg.setContent( data.data() );
00863     msg.parse();
00864     nlabel = msg.subject()->asUnicodeString();
00865   }
00866 
00867   item->setData( data );
00868   item->setLabel( nlabel );
00869   if ( mimeType.isEmpty() ) {
00870     item->setMimeType( KMimeType::findByContent( data )->name() );
00871   } else {
00872     item->setMimeType( mimeType );
00873   }
00874 }
00875 
00876 void KOEditorAttachments::addAttachment( KCal::Attachment *attachment )
00877 {
00878   new AttachmentListItem( attachment, mAttachments );
00879 }
00880 
00881 void KOEditorAttachments::readIncidence( KCal::Incidence *i )
00882 {
00883   mAttachments->clear();
00884 
00885   KCal::Attachment::List attachments = i->attachments();
00886   KCal::Attachment::List::ConstIterator it;
00887   for( it = attachments.begin(); it != attachments.end(); ++it ) {
00888     addAttachment( (*it) );
00889   }
00890   if ( mAttachments->count() > 0 ) {
00891     QTimer::singleShot( 0, mAttachments, SLOT(arrangeItemsInGrid()) );
00892   }
00893 }
00894 
00895 void KOEditorAttachments::writeIncidence( KCal::Incidence *i )
00896 {
00897   i->clearAttachments();
00898 
00899   QIconViewItem *item;
00900   AttachmentListItem *attitem;
00901   for( item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00902     attitem = static_cast<AttachmentListItem*>(item);
00903     if ( attitem )
00904       i->addAttachment( new KCal::Attachment( *(attitem->attachment() ) ) );
00905   }
00906 }
00907 
00908 
00909 void KOEditorAttachments::slotCopy()
00910 {
00911     QApplication::clipboard()->setData( mAttachments->mimeData(), QClipboard::Clipboard );
00912 }
00913 
00914 void KOEditorAttachments::slotCut()
00915 {
00916     slotCopy();
00917     slotRemove();
00918 }
00919 
00920 void KOEditorAttachments::slotPaste()
00921 {
00922     handlePasteOrDrop( QApplication::clipboard()->data() );
00923 }
00924 
00925 void KOEditorAttachments::selectionChanged()
00926 {
00927   bool selected = false;
00928   for ( QIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00929     if ( item->isSelected() ) {
00930       selected = true;
00931       break;
00932     }
00933   }
00934   mRemoveBtn->setEnabled( selected );
00935 }
00936 
00937 void KOEditorAttachments::contextMenu(QIconViewItem * item, const QPoint & pos)
00938 {
00939   const bool enable = item != 0;
00940 
00941   int numSelected = 0;
00942   for ( QIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
00943     if ( item->isSelected() ) {
00944       numSelected++;
00945     }
00946   }
00947 
00948   mOpenAction->setEnabled( enable );
00949   //TODO: support saving multiple attachments into a directory
00950   mSaveAsAction->setEnabled( enable && numSelected == 1 );
00951   mCopyAction->setEnabled( enable && numSelected == 1 );
00952   mCutAction->setEnabled( enable && numSelected == 1 );
00953   mDeleteAction->setEnabled( enable );
00954   mEditAction->setEnabled( enable );
00955   mContextMenu->exec( pos );
00956 }
00957 
00958 #include "koeditorattachments.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys