/*
    This file is part of KOrganizer.

    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
    Copyright (c) 2005 Reinhold Kainhofer <reinhold@kainhofer.com>

    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 TQt, and distribute the resulting executable,
    without including the source code for TQt in the source distribution.
*/

#include "koeditorattachments.h"

#include <libkcal/attachmenthandler.h>
#include <libkcal/incidence.h>
#include <libtdepim/kpimurlrequesterdlg.h>
#include <libtdepim/tdefileio.h>
#include <libtdepim/tdepimprotocols.h>
#include <libtdepim/maillistdrag.h>
#include <libtdepim/kvcarddrag.h>
#include <libtdepim/tdepimprotocols.h>

#include <tdelocale.h>
#include <kdebug.h>
#include <kmdcodec.h>
#include <tdemessagebox.h>
#include <krun.h>
#include <kurldrag.h>
#include <tdetempfile.h>
#include <ktempdir.h>
#include <tdeio/netaccess.h>
#include <kmimetype.h>
#include <kiconloader.h>
#include <tdefiledialog.h>
#include <kstdaction.h>
#include <tdeactioncollection.h>
#include <tdepopupmenu.h>
#include <kprotocolinfo.h>
#include <klineedit.h>
#include <kseparator.h>
#include <kurlrequester.h>
#include <libkmime/kmime_message.h>

#include <tqcheckbox.h>
#include <tqfile.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlistview.h>
#include <tqpushbutton.h>
#include <tqdragobject.h>
#include <tqtooltip.h>
#include <tqwhatsthis.h>
#include <tqapplication.h>
#include <tqclipboard.h>

#include <cassert>
#include <cstdlib>

class AttachmentListItem : public TDEIconViewItem
{
  public:
    AttachmentListItem( KCal::Attachment*att, TQIconView *parent ) :
        TDEIconViewItem( parent )
    {
      if ( att ) {
        mAttachment = new KCal::Attachment( *att );
      } else {
        mAttachment = new KCal::Attachment( TQChar('\0') ); //use the non-uri constructor
                                                    //as we want inline by default
      }
      readAttachment();
      setDragEnabled( true );
    }
    ~AttachmentListItem() { delete mAttachment; }
    KCal::Attachment *attachment() const { return mAttachment; }

    const TQString uri() const
    {
      return mAttachment->uri();
    }
    void setUri( const TQString &uri )
    {
      mAttachment->setUri( uri );
      readAttachment();
    }
    void setData( const TQByteArray data )
    {
      mAttachment->setDecodedData( data );
      readAttachment();
    }
    const TQString mimeType() const
    {
      return mAttachment->mimeType();
    }
    void setMimeType( const TQString &mime )
    {
      mAttachment->setMimeType( mime );
      readAttachment();
    }
    const TQString label() const
    {
      return mAttachment->label();
    }
    void setLabel( const TQString &label )
    {
      mAttachment->setLabel( label );
      readAttachment();
    }
    bool isBinary() const
    {
      return mAttachment->isBinary();
    }
    TQPixmap icon() const
    {
      return icon( KMimeType::mimeType( mAttachment->mimeType() ),
                   mAttachment->uri() );
    }
    static TQPixmap icon( KMimeType::Ptr mimeType, const TQString &uri )
    {
      TQString iconStr = mimeType->icon( uri, false );
      return TDEGlobal::iconLoader()->loadIcon( iconStr, TDEIcon::Small );
    }
    void readAttachment()
    {
      if ( mAttachment->label().isEmpty() ) {
        if ( mAttachment->isUri() ) {
          setText( mAttachment->uri() );
        } else {
          setText( i18n( "[Binary data]" ) );
        }
      } else {
        setText( mAttachment->label() );
      }
      if ( mAttachment->mimeType().isEmpty() ||
           !( KMimeType::mimeType( mAttachment->mimeType() ) ) ) {
        KMimeType::Ptr mimeType;
        if ( mAttachment->isUri() ) {
          mimeType = KMimeType::findByURL( mAttachment->uri() );
        } else {
          mimeType = KMimeType::findByContent( mAttachment->decodedData() );
        }
        mAttachment->setMimeType( mimeType->name() );
      }

      setPixmap( icon() );
    }

  private:
    KCal::Attachment *mAttachment;
};

AttachmentEditDialog::AttachmentEditDialog( AttachmentListItem *item,
                                            TQWidget *parent )
  : KDialogBase ( Plain, i18n( "Add Attachment" ), Ok|Cancel, Ok, parent, 0, false, false ),
    mItem( item ), mURLRequester( 0 )
{
  TQFrame *topFrame = plainPage();
  TQVBoxLayout *vbl = new TQVBoxLayout( topFrame, 0, spacingHint() );

  TQGridLayout *grid = new TQGridLayout();
  grid->setColStretch( 0, 0 );
  grid->setColStretch( 1, 0 );
  grid->setColStretch( 2, 1 );
  vbl->addLayout( grid );

  mIcon = new TQLabel( topFrame );
  mIcon->setPixmap( item->icon() );
  grid->addWidget( mIcon, 0, 0 );

  mLabelEdit = new KLineEdit( topFrame );
  mLabelEdit->setText( item->label().isEmpty() ? item->uri() : item->label() );
  mLabelEdit->setClickMessage( i18n( "Attachment name" ) );
  TQToolTip::add( mLabelEdit, i18n( "Give the attachment a name" ) );
  TQWhatsThis::add( mLabelEdit,
                   i18n( "Type any string you desire here for the name of the attachment" ) );
  grid->addMultiCellWidget( mLabelEdit, 0, 0, 1, 2 );

  KSeparator *sep = new KSeparator( TQt::Horizontal, topFrame );
  grid->addMultiCellWidget( sep, 1, 1, 0, 2 );

  TQLabel *label = new TQLabel( i18n( "Type:" ), topFrame );
  grid->addWidget( label, 2, 0 );
  TQString typecomment = item->mimeType().isEmpty() ?
                        i18n( "Unknown" ) :
                        KMimeType::mimeType( item->mimeType() )->comment();
  mTypeLabel = new TQLabel( typecomment, topFrame );
  grid->addWidget( mTypeLabel, 2, 1 );
  mMimeType = KMimeType::mimeType( item->mimeType() );

  mInline = new TQCheckBox( i18n( "Store attachment inline" ), topFrame );
  grid->addMultiCellWidget( mInline, 3, 3, 0, 2 );
  mInline->setChecked( item->isBinary() );
  TQToolTip::add( mInline, i18n( "Store the attachment file inside the calendar" ) );
  TQWhatsThis::add(
    mInline,
    i18n( "Checking this option will cause the attachment to be stored inside "
          "your calendar, which can take a lot of space depending on the size "
          "of the attachment. If this option is not checked, then only a link "
          "pointing to the attachment will be stored.  Do not use a link for "
          "attachments that change often or may be moved (or removed) from "
          "their current location." ) );

  if ( item->attachment()->isUri() || !item->attachment()->data() ) {
    label = new TQLabel( i18n( "Location:" ), topFrame );
    grid->addWidget( label, 4, 0 );
    mURLRequester = new KURLRequester( item->uri(), topFrame );
    TQToolTip::add( mURLRequester, i18n( "Provide a location for the attachment file" ) );
    TQWhatsThis::add(
      mURLRequester,
      i18n( "Enter the path to the attachment file or use the "
            "file browser by pressing the adjacent button" ) );
    grid->addMultiCellWidget( mURLRequester, 4, 4, 1, 2 );
    connect( mURLRequester, TQ_SIGNAL(urlSelected(const TQString &)),
             TQ_SLOT(urlSelected(const TQString &)) );
    connect( mURLRequester, TQ_SIGNAL( textChanged( const TQString& ) ),
             TQ_SLOT( urlChanged( const TQString& ) ) );
    urlChanged( item->uri() );
  } else {
    uint size = item->attachment()->size();
    grid->addWidget( new TQLabel( i18n( "Size:" ), topFrame ), 4, 0 );
    grid->addWidget( new TQLabel( TQString::fromLatin1( "%1 (%2)" ).
                                 arg( TDEIO::convertSize( size ) ).
                                 arg( TDEGlobal::locale()->formatNumber(
                                        size, 0 ) ), topFrame ), 4, 2 );
  }
  vbl->addStretch( 10 );
}

void AttachmentEditDialog::slotApply()
{
  if ( !mLabelEdit->text().isEmpty() ) {
    mItem->setLabel( mLabelEdit->text() );
  } else {
    if ( mURLRequester ) {
      KURL url( mURLRequester->url() );
      if ( url.isLocalFile() ) {
        mItem->setLabel( url.fileName() );
      } else {
        mItem->setLabel( url.url() );
      }
    }
  }
  if ( mItem->label().isEmpty() ) {
    mItem->setLabel( i18n( "New attachment" ) );
  }
  mItem->setMimeType( mMimeType->name() );
  if ( mURLRequester ) {
    KURL url( mURLRequester->url() );

    TQString correctedUrl = mURLRequester->url();
    if ( !url.isValid() ) {
      // If the user used KURLRequester's KURLCompletion
      // (used the line edit instead of the file dialog)
      // the returned url is not absolute and is always relative
      // to the home directory (not pwd), so we must prepend home

      correctedUrl = TQDir::home().filePath( mURLRequester->url() );
      url = KURL( correctedUrl );
      if ( url.isValid() ) {
        urlSelected( correctedUrl );
        mItem->setMimeType( mMimeType->name() );
      }
    }

    if ( mInline->isChecked() ) {
      TQString tmpFile;
      if ( TDEIO::NetAccess::download( correctedUrl, tmpFile, this ) ) {
        TQFile f( tmpFile );
        if ( !f.open( IO_ReadOnly ) ) {
          return;
        }
        TQByteArray data = f.readAll();
        f.close();
        mItem->setData( data );
      }
      TDEIO::NetAccess::removeTempFile( tmpFile );
    } else {
      mItem->setUri( url.url() );
    }
  }
}

void AttachmentEditDialog::accept()
{
  slotApply();
  KDialog::accept();
}

void AttachmentEditDialog::urlChanged( const TQString &url )
{
  enableButton( Ok, !url.isEmpty() );
}

void AttachmentEditDialog::urlSelected( const TQString &url )
{
  KURL kurl( url );
  mMimeType = KMimeType::findByURL( kurl );
  mTypeLabel->setText( mMimeType->comment() );
  mIcon->setPixmap( AttachmentListItem::icon( mMimeType, kurl.path() ) );
}

AttachmentIconView::AttachmentIconView( KOEditorAttachments* parent )
  : TDEIconView( parent ),
    mParent( parent )
{
  setSelectionMode( TQIconView::Extended );
  setMode( TDEIconView::Select );
  setItemTextPos( TQIconView::Right );
  setArrangement( TQIconView::LeftToRight );
  setMaxItemWidth( TQMAX(maxItemWidth(), 250) );
  setMinimumHeight( TQMAX(fontMetrics().height(), 16) + 12 );

  connect( this, TQ_SIGNAL( dropped ( TQDropEvent *, const TQValueList<TQIconDragItem> & ) ),
           this, TQ_SLOT( handleDrop( TQDropEvent *, const TQValueList<TQIconDragItem> & ) ) );
}

KURL AttachmentIconView::tempFileForAttachment( KCal::Attachment *attachment )
{
  if ( mTempFiles.contains( attachment ) ) {
    return mTempFiles[attachment];
  }
  TQStringList patterns = KMimeType::mimeType( attachment->mimeType() )->patterns();

  KTempFile *file;
  if ( !patterns.empty() ) {
    file = new KTempFile( TQString(),
                          TQString( patterns.first() ).remove( '*' ),0600 );
  } else {
    file = new KTempFile( TQString(), TQString(), 0600 );
  }
  file->setAutoDelete( true );
  file->file()->open( IO_WriteOnly );
  TQTextStream stream( file->file() );
  stream.writeRawBytes( attachment->decodedData().data(), attachment->size() );
  KURL url( file->name() );
  mTempFiles.insert( attachment, url );
  file->close();
  return mTempFiles[attachment];
}

TQDragObject *AttachmentIconView::mimeData()
{
  // create a list of the URL:s that we want to drag
  KURL::List urls;
  TQStringList labels;
  for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
    if ( it->isSelected() ) {
      AttachmentListItem *item = static_cast<AttachmentListItem *>( it );
      if ( item->isBinary() ) {
        urls.append( tempFileForAttachment( item->attachment() ) );
      } else {
        urls.append( item->uri() );
      }
      labels.append( KURL::encode_string( item->label() ) );
    }
  }
  if ( selectionMode() == TQIconView::NoSelection ) {
    AttachmentListItem *item = static_cast<AttachmentListItem *>( currentItem() );
    if ( item ) {
      urls.append( item->uri() );
      labels.append( KURL::encode_string( item->label() ) );
    }
  }

  TQMap<TQString, TQString> metadata;
  metadata["labels"] = labels.join( ":" );

  KURLDrag *drag = new KURLDrag( urls, metadata );
  return drag;
}

AttachmentIconView::~AttachmentIconView()
{
  for ( std::set<KTempDir*>::iterator it = mTempDirs.begin() ; it != mTempDirs.end() ; ++it ) {
    delete *it;
  }
}

TQDragObject * AttachmentIconView::dragObject()
{
  KURL::List urls;
  for ( TQIconViewItem *it = firstItem( ); it; it = it->nextItem( ) ) {
    if ( !it->isSelected() ) continue;
    AttachmentListItem * item = dynamic_cast<AttachmentListItem*>( 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 );
      TQByteArray encoded;
      encoded.duplicate( att->data(), strlen( att->data() ) );
      TQByteArray decoded;
      KCodecs::base64Decode( encoded, decoded );
      const TQString 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 AttachmentIconView::handleDrop( TQDropEvent *event, const TQValueList<TQIconDragItem> & list )
{
  Q_UNUSED( list );
  mParent->handlePasteOrDrop( event );
}


void AttachmentIconView::dragMoveEvent( TQDragMoveEvent *event )
{
  mParent->dragMoveEvent( event );
}

void AttachmentIconView::contentsDragMoveEvent( TQDragMoveEvent *event )
{
  mParent->dragMoveEvent( event );
}

void AttachmentIconView::contentsDragEnterEvent( TQDragEnterEvent *event )
{
  mParent->dragMoveEvent( event );
}

void AttachmentIconView::dragEnterEvent( TQDragEnterEvent *event )
{
  mParent->dragEnterEvent( event );
}

KOEditorAttachments::KOEditorAttachments( int spacing, TQWidget *parent,
                                          const char *name )
  : TQWidget( parent, name )
{
  TQBoxLayout *topLayout = new TQHBoxLayout( this );
  topLayout->setSpacing( spacing );

  TQLabel *label = new TQLabel( i18n("Attachments:"), this );
  topLayout->addWidget( label );

  mAttachments = new AttachmentIconView( this );
  TQWhatsThis::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, TQ_SIGNAL( doubleClicked( TQIconViewItem * ) ),
           TQ_SLOT( showAttachment( TQIconViewItem * ) ) );
  connect( mAttachments, TQ_SIGNAL(selectionChanged()),
           TQ_SLOT(selectionChanged()) );
  connect( mAttachments, TQ_SIGNAL(contextMenuRequested(TQIconViewItem*,const TQPoint&)),
           TQ_SLOT(contextMenu(TQIconViewItem*,const TQPoint&)) );

    TQPushButton *addButton = new TQPushButton( this );
  addButton->setIconSet( SmallIconSet( "add" ) );
  TQToolTip::add( addButton, i18n( "Add an attachment" ) );
  TQWhatsThis::add( addButton,
                   i18n( "Shows a dialog used to select an attachment "
                         "to add to this event or to-do as link or as "
                         "inline data." ) );
  topLayout->addWidget( addButton );
  connect( addButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotAdd()) );

  mRemoveBtn = new TQPushButton( this );
  mRemoveBtn->setIconSet( SmallIconSet( "remove" ) );
  TQToolTip::add( mRemoveBtn, i18n("&Remove") );
  TQWhatsThis::add( mRemoveBtn,
                   i18n("Removes the attachment selected in the list above "
                        "from this event or to-do.") );
  topLayout->addWidget( mRemoveBtn );
  connect( mRemoveBtn, TQ_SIGNAL(clicked()), TQ_SLOT(slotRemove()) );

  mContextMenu = new TDEPopupMenu( this );

  TDEActionCollection* ac = new TDEActionCollection( this, this );

  mOpenAction = new TDEAction( i18n("Open"), 0, this, TQ_SLOT(slotShow()), ac );
  mOpenAction->plug( mContextMenu );

  mSaveAsAction = new TDEAction( i18n( "Save As..." ), 0, this, TQ_SLOT(slotSaveAs()), ac );
  mSaveAsAction->plug( mContextMenu );
  mContextMenu->insertSeparator();

  mCopyAction = KStdAction::copy(this, TQ_SLOT(slotCopy()), ac );
  mCopyAction->plug( mContextMenu );
  mCutAction = KStdAction::cut(this, TQ_SLOT(slotCut()), ac );
  mCutAction->plug( mContextMenu );
  TDEAction *action = KStdAction::paste(this, TQ_SLOT(slotPaste()), ac );
  action->plug( mContextMenu );
  mContextMenu->insertSeparator();

  mDeleteAction = new TDEAction( i18n( "&Remove" ), 0, this, TQ_SLOT(slotRemove()),  ac );
  mDeleteAction->plug( mContextMenu );
  mDeleteAction->setShortcut( Key_Delete );
  mContextMenu->insertSeparator();

  mEditAction = new TDEAction( i18n( "&Properties..." ), 0, this, TQ_SLOT(slotEdit()), ac );
  mEditAction->plug( mContextMenu );

  selectionChanged();
  setAcceptDrops( true );
}

KOEditorAttachments::~KOEditorAttachments()
{
}

bool KOEditorAttachments::hasAttachments()
{
  return mAttachments->count() != 0;
}

void KOEditorAttachments::dragMoveEvent( TQDragMoveEvent *event )
{
  event->accept( KURLDrag::canDecode( event ) ||
                 TQTextDrag::canDecode( event ) ||
                 KPIM::MailListDrag::canDecode( event ) ||
                 KVCardDrag::canDecode( event ) );
}

void KOEditorAttachments::dragEnterEvent( TQDragEnterEvent* event )
{
  dragMoveEvent( event );
}

void KOEditorAttachments::handlePasteOrDrop( TQMimeSource* source )
{
  KURL::List urls;
  bool probablyWeHaveUris = false;
  bool weCanCopy = true;
  TQStringList labels;

  if ( KVCardDrag::canDecode( source ) ) {
    TDEABC::Addressee::List addressees;
    KVCardDrag::decode( source, addressees );
    for ( TDEABC::Addressee::List::ConstIterator it = addressees.constBegin();
          it != addressees.constEnd(); ++it ) {
      urls.append( TDEPIMPROTOCOL_CONTACT + ( *it ).uid() );
      // there is some weirdness about realName(), hence fromUtf8
      labels.append( TQString::fromUtf8( ( *it ).realName().latin1() ) );
    }
    probablyWeHaveUris = true;
  } else if ( KURLDrag::canDecode( source ) ) {
    TQMap<TQString,TQString> metadata;
    if ( KURLDrag::decode( source, urls, metadata ) ) {
      probablyWeHaveUris = true;
      labels = TQStringList::split( ':', metadata["labels"], false );
      for ( TQStringList::Iterator it = labels.begin(); it != labels.end(); ++it ) {
        *it = KURL::decode_string( (*it).latin1() );
      }

    }
  } else if ( TQTextDrag::canDecode( source ) ) {
    TQString text;
    TQTextDrag::decode( source, text );
    TQStringList lst = TQStringList::split( '\n', text, false );
    for ( TQStringList::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) {
      urls.append( *it );
      labels.append( TQString() );
    }
    probablyWeHaveUris = true;
  }

  TDEPopupMenu menu;
  int items=0;
  if ( probablyWeHaveUris ) {
    menu.insertItem( i18n( "&Link here" ), DRAG_LINK, items++ );
    // we need to check if we can reasonably expect to copy the objects
    for ( KURL::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it ) {
      if ( !( weCanCopy = KProtocolInfo::supportsReading( *it ) ) ) {
        break; // either we can copy them all, or no copying at all
      }
    }
    if ( weCanCopy ) {
      menu.insertItem( SmallIcon( "edit-copy" ), i18n( "&Copy Here" ), DRAG_COPY, items++ );
    }
  } else {
      menu.insertItem( SmallIcon( "edit-copy" ), i18n( "&Copy Here" ), DRAG_COPY, items++ );
  }

  menu.insertSeparator();
  items++;
  menu.insertItem( SmallIcon( "cancel" ), i18n( "C&ancel" ), DRAG_CANCEL, items );
  int action = menu.exec( TQCursor::pos(), 0 );

  if ( action == DRAG_LINK ) {
    TQStringList::ConstIterator jt = labels.constBegin();
    for ( KURL::List::ConstIterator it = urls.constBegin();
          it != urls.constEnd(); ++it ) {
      TQString label = (*jt++);
      if ( mAttachments->findItem( label ) ) {
        label += '~' + randomString( 3 );
      }
      addUriAttachment( (*it).url(), TQString(), label, true );
    }
  } else if ( action != DRAG_CANCEL ) {
    if ( probablyWeHaveUris ) {
      for ( KURL::List::ConstIterator it = urls.constBegin();
            it != urls.constEnd(); ++it ) {
        TQString label = (*it).fileName();
        if ( label.isEmpty() ) {
          label = (*it).prettyURL();
        }
        if ( mAttachments->findItem( label ) ) {
          label += '~' + randomString( 3 );
        }
        addUriAttachment( (*it).url(), TQString(), label, true );
      }
    } else { // we take anything
      addDataAttachment( source->encodedData( source->format() ),
                         source->format(),
                         KMimeType::mimeType( source->format() )->name() );
    }
  }
}

void KOEditorAttachments::dropEvent( TQDropEvent* event )
{
    handlePasteOrDrop( event );
}

void KOEditorAttachments::showAttachment( TQIconViewItem *item )
{
  AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
  if ( !attitem || !attitem->attachment() ) return;

  KCal::Attachment *att = attitem->attachment();
  KCal::AttachmentHandler::view( this, att );
}

void KOEditorAttachments::saveAttachment( TQIconViewItem *item )
{
  AttachmentListItem *attitem = static_cast<AttachmentListItem*>(item);
  if ( !attitem || !attitem->attachment() ) return;

  KCal::Attachment *att = attitem->attachment();
  KCal::AttachmentHandler::saveAs( this, att );
}

void KOEditorAttachments::slotAdd()
{
  AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );

  AttachmentEditDialog *dlg = new AttachmentEditDialog( item, mAttachments )
;
  if ( dlg->exec() == KDialog::Rejected ) {
    delete item;
  }
  delete dlg;
}

void KOEditorAttachments::slotAddData()
{
  KURL uri = KFileDialog::getOpenFileName( TQString(), TQString(), this, i18n("Add Attachment") );
  if ( !uri.isEmpty() ) {
    TQString label = uri.fileName();
    if ( label.isEmpty() ) {
      label = uri.prettyURL();
    }
    addUriAttachment( uri.url(), TQString(), label, true );
  }
}

void KOEditorAttachments::slotEdit()
{
  for ( TQIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
    if ( item->isSelected() ) {
      AttachmentListItem *attitem = static_cast<AttachmentListItem*>( item );
      if ( !attitem || !attitem->attachment() ) {
        return;
      }

      AttachmentEditDialog *dialog = new AttachmentEditDialog( attitem, mAttachments );
      dialog->mInline->setEnabled( false );
      dialog->setModal( false );
      connect( dialog, TQ_SIGNAL(hidden()), dialog, TQ_SLOT(delayedDestruct()) );
      dialog->show();
    }
  }
}

void KOEditorAttachments::slotRemove()
{
  TQValueList<TQIconViewItem*> selected;
  TQStringList labels;
  for ( TQIconViewItem *it = mAttachments->firstItem( ); it; it = it->nextItem( ) ) {
    if ( !it->isSelected() ) continue;
    selected << it;

    AttachmentListItem *attitem = static_cast<AttachmentListItem*>(it);
    KCal::Attachment *att = attitem->attachment();
    labels << att->label();
  }

  if ( selected.isEmpty() ) {
    return;
  }

  TQString labelsStr = labels.join( "<br>" );

  if ( KMessageBox::questionYesNo(
         this,
         i18n( "<qt>Do you really want to remove these attachments?<p>%1</qt>" ).arg( labelsStr ),
         i18n( "Remove Attachment?" ),
         KStdGuiItem::yes(), KStdGuiItem::no(),
         "calendarRemoveAttachments" ) != KMessageBox::Yes ) {
    return;
  }

  for ( TQValueList<TQIconViewItem*>::iterator it( selected.begin() ), end( selected.end() );
        it != end ; ++it ) {
    if ( (*it)->nextItem() ) {
      (*it)->nextItem()->setSelected( true );
    } else if ( (*it)->prevItem() ) {
      (*it)->prevItem()->setSelected( true );
    }
    delete *it;
  }
  mAttachments->slotUpdate();
}

void KOEditorAttachments::slotShow()
{
  for ( TQIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) {
    if ( !it->isSelected() )
      continue;
    showAttachment( it );
  }
}

void KOEditorAttachments::slotSaveAs()
{
  for ( TQIconViewItem *it = mAttachments->firstItem(); it; it = it->nextItem() ) {
    if ( !it->isSelected() )
      continue;
    saveAttachment( it );
  }
}

void KOEditorAttachments::setDefaults()
{
  mAttachments->clear();
}

TQString KOEditorAttachments::randomString(int length) const
{
   if (length <=0 ) return TQString();

   TQString str; str.setLength( length );
   int i = 0;
   while (length--)
   {
      int r=random() % 62;
      r+=48;
      if (r>57) r+=7;
      if (r>90) r+=6;
      str[i++] =  char(r);
      // so what if I work backwards?
   }
   return str;
}

void KOEditorAttachments::addUriAttachment( const TQString &uri,
                                            const TQString &mimeType,
                                            const TQString &label,
                                            bool inLine )
{
  if ( !inLine ) {
    AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );
    item->setUri( uri );
    item->setLabel( label );
    if ( mimeType.isEmpty() ) {
      if ( uri.startsWith( TDEPIMPROTOCOL_CONTACT ) ) {
        item->setMimeType( "text/directory" );
      } else if ( uri.startsWith( TDEPIMPROTOCOL_EMAIL ) ) {
        item->setMimeType( "message/rfc822" );
      } else if ( uri.startsWith( TDEPIMPROTOCOL_INCIDENCE ) ) {
        item->setMimeType( "text/calendar" );
      } else if ( uri.startsWith( TDEPIMPROTOCOL_NEWSARTICLE ) ) {
        item->setMimeType( "message/news" );
      } else {
        item->setMimeType( KMimeType::findByURL( uri )->name() );
      }
    }
  } else {
    TQString tmpFile;
    if ( TDEIO::NetAccess::download( uri, tmpFile, this ) ) {
      TQFile f( tmpFile );
      if ( !f.open( IO_ReadOnly ) ) {
        return;
      }
      const TQByteArray data = f.readAll();
      f.close();
      addDataAttachment( data, mimeType, label );
    }
    TDEIO::NetAccess::removeTempFile( tmpFile );
  }
}

void KOEditorAttachments::addDataAttachment( const TQByteArray &data,
                                             const TQString &mimeType,
                                             const TQString &label )
{
  AttachmentListItem *item = new AttachmentListItem( 0, mAttachments );

  TQString nlabel = label;
  if ( mimeType == "message/rfc822" ) {
    // mail message. try to set the label from the mail Subject:
    KMime::Message msg;
    msg.setContent( data.data() );
    msg.parse();
    nlabel = msg.subject()->asUnicodeString();
  }

  item->setData( data );
  item->setLabel( nlabel );
  if ( mimeType.isEmpty() ) {
    item->setMimeType( KMimeType::findByContent( data )->name() );
  } else {
    item->setMimeType( mimeType );
  }
}

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 ) {
    TQTimer::singleShot( 0, mAttachments, TQ_SLOT(arrangeItemsInGrid()) );
  }
}

void KOEditorAttachments::writeIncidence( KCal::Incidence *i )
{
  i->clearAttachments();

  TQIconViewItem *item;
  AttachmentListItem *attitem;
  for( item = mAttachments->firstItem(); item; item = item->nextItem() ) {
    attitem = static_cast<AttachmentListItem*>(item);
    if ( attitem )
      i->addAttachment( new KCal::Attachment( *(attitem->attachment() ) ) );
  }
}


void KOEditorAttachments::slotCopy()
{
    TQApplication::clipboard()->setData( mAttachments->mimeData(), TQClipboard::Clipboard );
}

void KOEditorAttachments::slotCut()
{
    slotCopy();
    slotRemove();
}

void KOEditorAttachments::slotPaste()
{
    handlePasteOrDrop( TQApplication::clipboard()->data() );
}

void KOEditorAttachments::selectionChanged()
{
  bool selected = false;
  for ( TQIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
    if ( item->isSelected() ) {
      selected = true;
      break;
    }
  }
  mRemoveBtn->setEnabled( selected );
}

void KOEditorAttachments::contextMenu(TQIconViewItem * item, const TQPoint & pos)
{
  const bool enable = item != 0;

  int numSelected = 0;
  for ( TQIconViewItem *item = mAttachments->firstItem(); item; item = item->nextItem() ) {
    if ( item->isSelected() ) {
      numSelected++;
    }
  }

  mOpenAction->setEnabled( enable );
  //TODO: support saving multiple attachments into a directory
  mSaveAsAction->setEnabled( enable && numSelected == 1 );
  mCopyAction->setEnabled( enable && numSelected == 1 );
  mCutAction->setEnabled( enable && numSelected == 1 );
  mDeleteAction->setEnabled( enable );
  mEditAction->setEnabled( enable );
  mContextMenu->exec( pos );
}

#include "koeditorattachments.moc"
