• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

paste.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
00017 */
00018 
00019 #include "paste.h"
00020 #include "pastedialog.h"
00021 
00022 #include "kio/job.h"
00023 #include "kio/copyjob.h"
00024 #include "kio/deletejob.h"
00025 #include "kio/global.h"
00026 #include "kio/netaccess.h"
00027 #include "kio/renamedialog.h"
00028 #include "kio/kprotocolmanager.h"
00029 #include "jobuidelegate.h"
00030 
00031 #include <kurl.h>
00032 #include <kdebug.h>
00033 #include <klocale.h>
00034 #include <kinputdialog.h>
00035 #include <kmessagebox.h>
00036 #include <kmimetype.h>
00037 #include <ktemporaryfile.h>
00038 
00039 #include <QtGui/QApplication>
00040 #include <QtGui/QClipboard>
00041 #include <QMimeData>
00042 
00043 static bool decodeIsCutSelection(const QMimeData *mimeData)
00044 {
00045     const QByteArray data = mimeData->data("application/x-kde-cutselection");
00046     return data.isEmpty() ? false : data.at(0) == '1';
00047 }
00048 
00049 // This could be made a public method, if there's a need for pasting only urls
00050 // and not random data.
00061 //KIO_EXPORT Job *pasteClipboardUrls(const KUrl& destDir, JobFlags flags = DefaultFlags);
00062 static KIO::Job *pasteClipboardUrls(const QMimeData* mimeData, const KUrl& destDir, KIO::JobFlags flags = KIO::DefaultFlags)
00063 {
00064     const KUrl::List urls = KUrl::List::fromMimeData(mimeData, KUrl::List::PreferLocalUrls);
00065     if (!urls.isEmpty()) {
00066         const bool move = decodeIsCutSelection(mimeData);
00067         KIO::Job *job = 0;
00068         if (move)
00069             job = KIO::move(urls, destDir, flags);
00070         else
00071             job = KIO::copy(urls, destDir, flags);
00072 
00073         // If moving, update the clipboard contents with the new locations
00074         if (move) {
00075             QApplication::clipboard()->clear();
00076 
00077             KUrl::List newUrls;
00078             Q_FOREACH(const KUrl& url, urls) {
00079                 KUrl dUrl = destDir;
00080                 dUrl.addPath(url.fileName());
00081                 newUrls.append(dUrl);
00082             }
00083 
00084             QMimeData* mime = new QMimeData();
00085             newUrls.populateMimeData(mime);
00086             QApplication::clipboard()->setMimeData(mime);
00087         }
00088         return job;
00089     }
00090     return 0;
00091 }
00092 
00093 static KUrl getNewFileName( const KUrl &u, const QString& text, const QString& suggestedFileName, QWidget *widget, bool delIfOverwrite )
00094 {
00095   bool ok;
00096   QString dialogText( text );
00097   if ( dialogText.isEmpty() )
00098     dialogText = i18n( "Filename for clipboard content:" );
00099   QString file = KInputDialog::getText( QString(), dialogText, suggestedFileName, &ok, widget );
00100   if ( !ok )
00101      return KUrl();
00102 
00103   KUrl myurl(u);
00104   myurl.addPath( file );
00105 
00106   // Check for existing destination file.
00107   // When we were using CopyJob, we couldn't let it do that (would expose
00108   // an ugly tempfile name as the source URL)
00109   // And now we're using a put job anyway, no destination checking included.
00110   if (KIO::NetAccess::exists(myurl, KIO::NetAccess::DestinationSide, widget))
00111   {
00112       kDebug(7007) << "Paste will overwrite file.  Prompting...";
00113       KIO::RenameDialog_Result res = KIO::R_OVERWRITE;
00114 
00115       KIO::RenameDialog dlg( widget,
00116                           i18n("File Already Exists"),
00117                           u.pathOrUrl(),
00118                           myurl.pathOrUrl(),
00119                           (KIO::RenameDialog_Mode) (KIO::M_OVERWRITE | KIO::M_SINGLE) );
00120       res = static_cast<KIO::RenameDialog_Result>(dlg.exec());
00121 
00122       if ( res == KIO::R_RENAME )
00123       {
00124           myurl = dlg.newDestUrl();
00125       }
00126       else if ( res == KIO::R_CANCEL )
00127       {
00128           return KUrl();
00129       } else if (res == KIO::R_OVERWRITE)
00130       {
00131           // Old hack. With the put job we just pass Overwrite.
00132           if (delIfOverwrite) {
00133               // Ideally we would just pass KIO::Overwrite to the job in pasteDataAsyncTo.
00134               // But 1) CopyJob doesn't support that (it wouldn't really apply to multiple files) [not true anymore]
00135               // 2) we can't use file_move because CopyJob* is everywhere in the API (see TODO)
00136               // But well the simpler is really to delete the dest:
00137               KIO::Job* delJob = KIO::del(myurl);
00138               delJob->exec();
00139           }
00140       }
00141   }
00142 
00143   return myurl;
00144 }
00145 
00146 // Old solution
00147 // The final step: write _data to tempfile and move it to newUrl
00148 static KIO::CopyJob* pasteDataAsyncTo( const KUrl& newUrl, const QByteArray& _data )
00149 {
00150     // ### Bug: because we move from a tempfile to the destination,
00151     // if the user does "Undo" then we won't ask for confirmation, and we'll
00152     // move back to a tempfile, instead of just deleting.
00153     // A KIO::storedPut would be better but FileUndoManager would need to support it first.
00154     KTemporaryFile tempFile;
00155     tempFile.setAutoRemove(false);
00156     tempFile.open();
00157     tempFile.write(_data.data(), _data.size());
00158     tempFile.flush();
00159     KUrl origUrl(tempFile.fileName());
00160     return KIO::move(origUrl, newUrl);
00161 }
00162 
00163 // New solution
00164 static KIO::Job* putDataAsyncTo(const KUrl& url, const QByteArray& data, QWidget* widget, KIO::JobFlags flags)
00165 {
00166     KIO::Job* job = KIO::storedPut(data, url, -1, flags);
00167     job->ui()->setWindow(widget);
00168     return job;
00169 }
00170 
00171 static QByteArray chooseFormatAndUrl(const KUrl& u, const QMimeData* mimeData,
00172                                      const QStringList& formats,
00173                                      const QString& text,
00174                                      const QString& suggestedFileName,
00175                                      QWidget* widget,
00176                                      bool clipboard,
00177                                      KUrl* newUrl)
00178 {
00179     QStringList formatLabels;
00180     for ( int i = 0; i < formats.size(); ++i ) {
00181         const QString& fmt = formats[i];
00182         KMimeType::Ptr mime = KMimeType::mimeType(fmt, KMimeType::ResolveAliases);
00183         if (mime)
00184             formatLabels.append( i18n("%1 (%2)", mime->comment(), fmt) );
00185         else
00186             formatLabels.append( fmt );
00187     }
00188 
00189     QString dialogText( text );
00190     if ( dialogText.isEmpty() )
00191         dialogText = i18n( "Filename for clipboard content:" );
00192     //using QString() instead of QString::null didn't compile (with gcc 3.2.3), because the ctor was mistaken as a function declaration, Alex //krazy:exclude=nullstrassign
00193     KIO::PasteDialog dlg( QString::null, dialogText, suggestedFileName, formatLabels, widget, clipboard ); //krazy:exclude=nullstrassign
00194 
00195     if ( dlg.exec() != KDialog::Accepted )
00196         return QByteArray();
00197 
00198     if ( clipboard && dlg.clipboardChanged() ) {
00199         KMessageBox::sorry( widget,
00200                             i18n( "The clipboard has changed since you used 'paste': "
00201                                   "the chosen data format is no longer applicable. "
00202                                   "Please copy again what you wanted to paste." ) );
00203         return QByteArray();
00204     }
00205 
00206     const QString result = dlg.lineEditText();
00207     const QString chosenFormat = formats[ dlg.comboItem() ];
00208 
00209     kDebug() << " result=" << result << " chosenFormat=" << chosenFormat;
00210     *newUrl = KUrl( u );
00211     newUrl->addPath( result );
00212     // if "data" came from QClipboard, then it was deleted already - by a nice 0-seconds timer
00213     // In that case, get it again. Let's hope the user didn't copy something else meanwhile :/
00214     // #### QT4/KDE4 TODO: check that this is still the case
00215     if ( clipboard ) {
00216         mimeData = QApplication::clipboard()->mimeData();
00217     }
00218     const QByteArray ba = mimeData->data( chosenFormat );
00219     return ba;
00220 }
00221 
00222 static QStringList extractFormats(const QMimeData* mimeData)
00223 {
00224     QStringList formats;
00225     const QStringList allFormats = mimeData->formats();
00226     Q_FOREACH(const QString& format, allFormats) {
00227         if (format == QLatin1String("application/x-qiconlist")) // see QIconDrag
00228             continue;
00229         if (format == QLatin1String("application/x-kde-cutselection")) // see KonqDrag
00230             continue;
00231         if (format == QLatin1String("application/x-kde-suggestedfilename"))
00232             continue;
00233         if (format.startsWith(QLatin1String("application/x-qt-"))) // Qt-internal
00234             continue;
00235         if (format.startsWith(QLatin1String("x-kmail-drag/"))) // app-internal
00236             continue;
00237         if (!format.contains(QLatin1Char('/'))) // e.g. TARGETS, MULTIPLE, TIMESTAMP
00238             continue;
00239         formats.append(format);
00240     }
00241     return formats;
00242 }
00243 
00244 // The [old] main method for dropping
00245 KIO::CopyJob* KIO::pasteMimeSource( const QMimeData* mimeData, const KUrl& destUrl,
00246                                     const QString& dialogText, QWidget* widget, bool clipboard )
00247 {
00248   QByteArray ba;
00249 
00250   const QString suggestedFilename = QString::fromUtf8(mimeData->data("application/x-kde-suggestedfilename"));
00251 
00252   // Now check for plain text
00253   // We don't want to display a mimetype choice for a QTextDrag, those mimetypes look ugly.
00254   if ( mimeData->hasText() )
00255   {
00256       ba = mimeData->text().toLocal8Bit(); // encoding OK?
00257   }
00258   else
00259   {
00260       const QStringList formats = extractFormats(mimeData);
00261       if ( formats.size() == 0 )
00262           return 0;
00263 
00264       if ( formats.size() > 1 ) {
00265           KUrl newUrl;
00266           ba = chooseFormatAndUrl(destUrl, mimeData, formats, dialogText, suggestedFilename, widget, clipboard, &newUrl);
00267           KIO::CopyJob* job = pasteDataAsyncTo(newUrl, ba);
00268           job->ui()->setWindow(widget);
00269           return job;
00270       }
00271       ba = mimeData->data( formats.first() );
00272   }
00273   if ( ba.isEmpty() )
00274   {
00275     KMessageBox::sorry( widget, i18n("The clipboard is empty") );
00276     return 0;
00277   }
00278 
00279     const KUrl newUrl = getNewFileName(destUrl, dialogText, suggestedFilename, widget, true);
00280     if (newUrl.isEmpty())
00281         return 0;
00282 
00283     KIO::CopyJob* job = pasteDataAsyncTo(newUrl, ba);
00284     job->ui()->setWindow(widget);
00285     return job;
00286 }
00287 
00288 KIO_EXPORT bool KIO::canPasteMimeSource(const QMimeData* data)
00289 {
00290     return data->hasText() || !extractFormats(data).isEmpty();
00291 }
00292 
00293 KIO::Job* pasteMimeDataImpl(const QMimeData* mimeData, const KUrl& destUrl,
00294                             const QString& dialogText, QWidget* widget,
00295                             bool clipboard)
00296 {
00297     QByteArray ba;
00298     const QString suggestedFilename = QString::fromUtf8(mimeData->data("application/x-kde-suggestedfilename"));
00299 
00300     // Now check for plain text
00301     // We don't want to display a mimetype choice for a QTextDrag, those mimetypes look ugly.
00302     if (mimeData->hasText()) {
00303         ba = mimeData->text().toLocal8Bit(); // encoding OK?
00304     } else {
00305         const QStringList formats = extractFormats(mimeData);
00306         if (formats.isEmpty()) {
00307             return 0;
00308         } else if (formats.size() > 1) {
00309             KUrl newUrl;
00310             ba = chooseFormatAndUrl(destUrl, mimeData, formats, dialogText, suggestedFilename, widget, clipboard, &newUrl);
00311             if (ba.isEmpty()) {
00312                 return 0;
00313             }
00314             return putDataAsyncTo(newUrl, ba, widget, KIO::Overwrite);
00315         }
00316         ba = mimeData->data(formats.first());
00317     }
00318     if (ba.isEmpty()) {
00319         return 0;
00320     }
00321 
00322     const KUrl newUrl = getNewFileName(destUrl, dialogText, suggestedFilename, widget, false);
00323     if (newUrl.isEmpty())
00324         return 0;
00325 
00326     return putDataAsyncTo(newUrl, ba, widget, KIO::Overwrite);
00327 }
00328 
00329 // The main method for pasting
00330 KIO_EXPORT KIO::Job *KIO::pasteClipboard( const KUrl& destUrl, QWidget* widget, bool move )
00331 {
00332     Q_UNUSED(move);
00333 
00334   if ( !destUrl.isValid() ) {
00335     KMessageBox::error( widget, i18n( "Malformed URL\n%1", destUrl.prettyUrl() ) );
00336     return 0;
00337   }
00338 
00339   // TODO: if we passed mimeData as argument, we could write unittests that don't
00340   // mess up the clipboard and that don't need QtGui.
00341   const QMimeData *mimeData = QApplication::clipboard()->mimeData();
00342 
00343   if (KUrl::List::canDecode(mimeData)) {
00344       // We can ignore the bool move, KIO::paste decodes it
00345       KIO::Job* job = pasteClipboardUrls(mimeData, destUrl);
00346       if (job) {
00347           job->ui()->setWindow(widget);
00348           return job;
00349       }
00350   }
00351 
00352   return pasteMimeDataImpl(mimeData, destUrl, QString(), widget, true /*clipboard*/);
00353 }
00354 
00355 
00356 KIO_EXPORT void KIO::pasteData(const KUrl& u, const QByteArray& data, QWidget* widget)
00357 {
00358     const KUrl newUrl = getNewFileName(u, QString(), QString(), widget, false);
00359     if (newUrl.isEmpty())
00360        return;
00361 
00362     KIO::Job* job = putDataAsyncTo(newUrl, data, widget, KIO::Overwrite);
00363     KIO::NetAccess::synchronousRun(job, widget);
00364 }
00365 
00366 // KDE5: remove
00367 KIO_EXPORT KIO::CopyJob* KIO::pasteDataAsync( const KUrl& u, const QByteArray& _data, QWidget *widget, const QString& text )
00368 {
00369     KUrl newUrl = getNewFileName(u, text, QString(), widget, true);
00370 
00371     if (newUrl.isEmpty())
00372        return 0;
00373 
00374     KIO::CopyJob* job = pasteDataAsyncTo( newUrl, _data );
00375     job->ui()->setWindow(widget);
00376     return job;
00377 }
00378 
00379 // NOTE: DolphinView::pasteInfo() has a better version of this
00380 // (but which requires KonqFileItemCapabilities)
00381 KIO_EXPORT QString KIO::pasteActionText()
00382 {
00383     const QMimeData *mimeData = QApplication::clipboard()->mimeData();
00384     const KUrl::List urls = KUrl::List::fromMimeData( mimeData );
00385     if ( !urls.isEmpty() ) {
00386         if ( urls.first().isLocalFile() )
00387             return i18np( "&Paste File", "&Paste %1 Files", urls.count() );
00388         else
00389             return i18np( "&Paste URL", "&Paste %1 URLs", urls.count() );
00390     } else if ( !mimeData->formats().isEmpty() ) {
00391         return i18n( "&Paste Clipboard Contents" );
00392     } else {
00393         return QString();
00394     }
00395 }
00396 
00397 // The [new] main method for dropping
00398 KIO_EXPORT KIO::Job* KIO::pasteMimeData(const QMimeData* mimeData, const KUrl& destUrl,
00399                                         const QString& dialogText, QWidget* widget)
00400 {
00401     return pasteMimeDataImpl(mimeData, destUrl, dialogText, widget, false /*not clipboard*/);
00402 }

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal