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/copyjob.h" 00023 #include "kio/deletejob.h" 00024 #include "kio/global.h" 00025 #include "kio/netaccess.h" 00026 #include "kio/renamedialog.h" 00027 #include "kio/kprotocolmanager.h" 00028 #include "jobuidelegate.h" 00029 00030 #include <kurl.h> 00031 #include <kdebug.h> 00032 #include <klocale.h> 00033 #include <kinputdialog.h> 00034 #include <kmessagebox.h> 00035 #include <kmimetype.h> 00036 #include <ktemporaryfile.h> 00037 00038 #include <QtGui/QApplication> 00039 #include <QtGui/QClipboard> 00040 #include <QMimeData> 00041 #include <QtCore/QTextIStream> 00042 00043 static KUrl getNewFileName( const KUrl &u, const QString& text, const QString& suggestedFileName, QWidget *widget ) 00044 { 00045 bool ok; 00046 QString dialogText( text ); 00047 if ( dialogText.isEmpty() ) 00048 dialogText = i18n( "Filename for clipboard content:" ); 00049 QString file = KInputDialog::getText( QString(), dialogText, suggestedFileName, &ok, widget ); 00050 if ( !ok ) 00051 return KUrl(); 00052 00053 KUrl myurl(u); 00054 myurl.addPath( file ); 00055 00056 // Check for existing destination file. If we let CopyJob do it then we expose 00057 // an ugly tempfile name as the source URL... 00058 if (KIO::NetAccess::exists(myurl, KIO::NetAccess::DestinationSide, widget)) 00059 { 00060 kDebug(7007) << "Paste will overwrite file. Prompting..."; 00061 KIO::RenameDialog_Result res = KIO::R_OVERWRITE; 00062 00063 KIO::RenameDialog dlg( widget, 00064 i18n("File Already Exists"), 00065 u.pathOrUrl(), 00066 myurl.pathOrUrl(), 00067 (KIO::RenameDialog_Mode) (KIO::M_OVERWRITE | KIO::M_SINGLE) ); 00068 res = static_cast<KIO::RenameDialog_Result>(dlg.exec()); 00069 00070 if ( res == KIO::R_RENAME ) 00071 { 00072 myurl = dlg.newDestUrl(); 00073 } 00074 else if ( res == KIO::R_CANCEL ) 00075 { 00076 return KUrl(); 00077 } else if (res == KIO::R_OVERWRITE) 00078 { 00079 // Ideally we would just pass KIO::Overwrite to the job in pasteDataAsyncTo. 00080 // But 1) CopyJob doesn't support that (it wouldn't really apply to multiple files) 00081 // 2) we can't use file_move because CopyJob* is everywhere in the API (see TODO) 00082 // As solution 1bis) we could use a PredefinedAnswerJobUiDelegate like in kiotesthelper.h... 00083 // But well the simpler is really to delete the dest: 00084 KIO::Job* delJob = KIO::del(myurl); 00085 delJob->exec(); 00086 } 00087 } 00088 00089 return myurl; 00090 } 00091 00092 // The final step: write _data to tempfile and move it to newUrl 00093 static KIO::CopyJob* pasteDataAsyncTo( const KUrl& newUrl, const QByteArray& _data ) 00094 { 00095 // ### Bug: because we move from a tempfile to the destination, 00096 // if the user does "Undo" then we won't ask for confirmation, and we'll 00097 // move back to a tempfile, instead of just deleting. 00098 // I guess we need a macro job which does copy+del? 00099 // A KIO::storedPut would be better but FileUndoManager would need to support it first. 00100 KTemporaryFile tempFile; 00101 tempFile.setAutoRemove(false); 00102 tempFile.open(); 00103 tempFile.write(_data.data(), _data.size()); 00104 tempFile.flush(); 00105 00106 KUrl origUrl(tempFile.fileName()); 00107 00108 return KIO::move(origUrl, newUrl); 00109 } 00110 00111 #ifndef QT_NO_MIMECLIPBOARD 00112 static KIO::CopyJob* chooseAndPaste( const KUrl& u, const QMimeData* mimeData, 00113 const QStringList& formats, 00114 const QString& text, 00115 const QString& suggestedFileName, 00116 QWidget* widget, 00117 bool clipboard ) 00118 { 00119 QStringList formatLabels; 00120 for ( int i = 0; i < formats.size(); ++i ) { 00121 const QString& fmt = formats[i]; 00122 KMimeType::Ptr mime = KMimeType::mimeType(fmt, KMimeType::ResolveAliases); 00123 if (mime) 00124 formatLabels.append( i18n("%1 (%2)", mime->comment(), fmt) ); 00125 else 00126 formatLabels.append( fmt ); 00127 } 00128 00129 QString dialogText( text ); 00130 if ( dialogText.isEmpty() ) 00131 dialogText = i18n( "Filename for clipboard content:" ); 00132 //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 00133 KIO::PasteDialog dlg( QString::null, dialogText, suggestedFileName, formatLabels, widget, clipboard ); //krazy:exclude=nullstrassign 00134 00135 if ( dlg.exec() != KDialog::Accepted ) 00136 return 0; 00137 00138 if ( clipboard && dlg.clipboardChanged() ) { 00139 KMessageBox::sorry( widget, 00140 i18n( "The clipboard has changed since you used 'paste': " 00141 "the chosen data format is no longer applicable. " 00142 "Please copy again what you wanted to paste." ) ); 00143 return 0; 00144 } 00145 00146 const QString result = dlg.lineEditText(); 00147 const QString chosenFormat = formats[ dlg.comboItem() ]; 00148 00149 kDebug() << " result=" << result << " chosenFormat=" << chosenFormat; 00150 KUrl newUrl( u ); 00151 newUrl.addPath( result ); 00152 // if "data" came from QClipboard, then it was deleted already - by a nice 0-seconds timer 00153 // In that case, get it again. Let's hope the user didn't copy something else meanwhile :/ 00154 // #### QT4/KDE4 TODO: check that this is still the case 00155 if ( clipboard ) { 00156 mimeData = QApplication::clipboard()->mimeData(); 00157 } 00158 const QByteArray ba = mimeData->data( chosenFormat ); 00159 KIO::CopyJob* job = pasteDataAsyncTo( newUrl, ba ); 00160 job->ui()->setWindow(widget); 00161 return job; 00162 } 00163 #endif 00164 00165 00166 #ifndef QT_NO_MIMECLIPBOARD 00167 00168 static QStringList extractFormats(const QMimeData* mimeData) 00169 { 00170 QStringList formats; 00171 const QStringList allFormats = mimeData->formats(); 00172 Q_FOREACH(const QString& format, allFormats) { 00173 if (format == QLatin1String("application/x-qiconlist")) // see QIconDrag 00174 continue; 00175 if (format == QLatin1String("application/x-kde-cutselection")) // see KonqDrag 00176 continue; 00177 if (format == QLatin1String("application/x-kde-suggestedfilename")) 00178 continue; 00179 if (format.startsWith(QLatin1String("application/x-qt-"))) // Qt-internal 00180 continue; 00181 if (format.startsWith(QLatin1String("x-kmail-drag/"))) // app-internal 00182 continue; 00183 if (!format.contains(QLatin1Char('/'))) // e.g. TARGETS, MULTIPLE, TIMESTAMP 00184 continue; 00185 formats.append(format); 00186 } 00187 return formats; 00188 } 00189 00190 // The main method for dropping 00191 KIO::CopyJob* KIO::pasteMimeSource( const QMimeData* mimeData, const KUrl& destUrl, 00192 const QString& dialogText, QWidget* widget, bool clipboard ) 00193 { 00194 QByteArray ba; 00195 00196 const QString suggestedFilename = QString::fromUtf8(mimeData->data("application/x-kde-suggestedfilename")); 00197 00198 // Now check for plain text 00199 // We don't want to display a mimetype choice for a QTextDrag, those mimetypes look ugly. 00200 QString text; 00201 if ( mimeData->hasText() ) 00202 { 00203 ba = mimeData->text().toLocal8Bit(); // encoding OK? 00204 } 00205 else 00206 { 00207 const QStringList formats = extractFormats(mimeData); 00208 if ( formats.size() == 0 ) 00209 return 0; 00210 00211 if ( formats.size() > 1 ) { 00212 return chooseAndPaste(destUrl, mimeData, formats, dialogText, suggestedFilename, widget, clipboard); 00213 } 00214 ba = mimeData->data( formats.first() ); 00215 } 00216 if ( ba.isEmpty() ) 00217 { 00218 KMessageBox::sorry( widget, i18n("The clipboard is empty") ); 00219 return 0; 00220 } 00221 00222 const KUrl newUrl = getNewFileName(destUrl, dialogText, suggestedFilename, widget); 00223 if (newUrl.isEmpty()) 00224 return 0; 00225 00226 KIO::CopyJob* job = pasteDataAsyncTo(newUrl, ba); 00227 job->ui()->setWindow(widget); 00228 return job; 00229 } 00230 00231 KIO_EXPORT bool KIO::canPasteMimeSource(const QMimeData* data) 00232 { 00233 return data->hasText() || !extractFormats(data).isEmpty(); 00234 } 00235 00236 #endif 00237 00238 // The main method for pasting 00239 KIO_EXPORT KIO::Job *KIO::pasteClipboard( const KUrl& destUrl, QWidget* widget, bool move ) 00240 { 00241 if ( !destUrl.isValid() ) { 00242 KMessageBox::error( widget, i18n( "Malformed URL\n%1", destUrl.prettyUrl() ) ); 00243 return 0; 00244 } 00245 00246 #ifndef QT_NO_MIMECLIPBOARD 00247 const QMimeData *mimeData = QApplication::clipboard()->mimeData(); 00248 00249 // First check for URLs. 00250 const KUrl::List urls = KUrl::List::fromMimeData(mimeData, KUrl::List::PreferLocalUrls); 00251 if ( !urls.isEmpty() ) { 00252 KIO::Job *res = 0; 00253 if ( move ) 00254 res = KIO::move( urls, destUrl ); 00255 else 00256 res = KIO::copy( urls, destUrl ); 00257 res->ui()->setWindow(widget); 00258 00259 // If moving, update the clipboard contents with the new locations 00260 if ( move ) { 00261 QApplication::clipboard()->clear(); 00262 00263 KUrl::List newUrls; 00264 for (int i = 0; i < urls.size(); i++) { 00265 KUrl dUrl = destUrl; 00266 dUrl.addPath(urls.at(i).fileName()); 00267 newUrls.append(dUrl); 00268 } 00269 00270 QMimeData* mime = new QMimeData(); 00271 newUrls.populateMimeData(mime); 00272 QApplication::clipboard()->setMimeData(mime); 00273 } 00274 00275 return res; 00276 } 00277 return pasteMimeSource( mimeData, destUrl, QString(), widget, true /*clipboard*/ ); 00278 #else 00279 QByteArray ba; 00280 QTextStream txtStream( ba, QIODevice::WriteOnly ); 00281 00282 const QStringList data = QApplication::clipboard()->text().split('\n', QString::SkipEmptyParts); 00283 00284 KUrl::List urls; 00285 KURLDrag::decode(data, urls); 00286 QStringList::const_iterator end(data.end()); 00287 for(QStringList::const_iterator it=data.begin(); it!=end; ++it) 00288 txtStream << *it; 00289 if ( ba.size() == 0 ) 00290 { 00291 KMessageBox::sorry(widget, i18n("The clipboard is empty")); 00292 return 0; 00293 } 00294 return pasteDataAsync( destUrl, ba, widget ); 00295 #endif 00296 } 00297 00298 00299 KIO_EXPORT void KIO::pasteData( const KUrl& u, const QByteArray& _data, QWidget* widget ) 00300 { 00301 const KUrl newUrl = getNewFileName( u, QString(), QString(), widget ); 00302 // We could use KIO::put here, but that would require a class 00303 // for the slotData call. With NetAccess, we can do a synchronous call. 00304 00305 if (newUrl.isEmpty()) 00306 return; 00307 00308 KTemporaryFile tempFile; 00309 tempFile.open(); 00310 tempFile.write( _data.data(), _data.size() ); 00311 tempFile.flush(); 00312 00313 (void) KIO::NetAccess::upload( tempFile.fileName(), newUrl, widget ); 00314 } 00315 00316 // KDE5 TODO: return a KIO::Job*, not a CopyJob*, in case we want to use file_move or a macro job... 00317 // But then the caller needs to know the destUrl too. Return a QPair? 00318 // KDE5: Inline in the only caller, and remove from public API; it's not used anywhere else anymore. 00319 KIO_EXPORT KIO::CopyJob* KIO::pasteDataAsync( const KUrl& u, const QByteArray& _data, QWidget *widget, const QString& text ) 00320 { 00321 KUrl newUrl = getNewFileName( u, text, QString(), widget ); 00322 00323 if (newUrl.isEmpty()) 00324 return 0; 00325 00326 KIO::CopyJob* job = pasteDataAsyncTo( newUrl, _data ); 00327 job->ui()->setWindow(widget); 00328 return job; 00329 } 00330 00331 // NOTE: DolphinView::pasteInfo() has a better version of this 00332 // (but which requires KonqFileItemCapabilities) 00333 KIO_EXPORT QString KIO::pasteActionText() 00334 { 00335 const QMimeData *mimeData = QApplication::clipboard()->mimeData(); 00336 const KUrl::List urls = KUrl::List::fromMimeData( mimeData ); 00337 if ( !urls.isEmpty() ) { 00338 if ( urls.first().isLocalFile() ) 00339 return i18np( "&Paste File", "&Paste %1 Files", urls.count() ); 00340 else 00341 return i18np( "&Paste URL", "&Paste %1 URLs", urls.count() ); 00342 } else if ( !mimeData->formats().isEmpty() ) { 00343 return i18n( "&Paste Clipboard Contents" ); 00344 } else { 00345 return QString(); 00346 } 00347 }
KDE 4.6 API Reference