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 }
KDE 4.7 API Reference