Version: 1.3.91 (using KDE KDE 3.5.2) Installed from: Compiled From Sources Compiler: gcc (GCC) 3.4.5 (Gentoo 3.4.5-r1, ssp-3.4.5-1.0, pie-8.7.9) OS: Linux GwenView refuses to save animated GIFs. This is highly counter-productive when using GwenView as the image-viewing KPart for HTTP URLs in Konqueror. It should instead only allow saving in original GIF format as a byte-for-byte copy of the original file.
SVN commit 606305 by gateau: Instead of "re-saving" the image, do a copy of the data if possible. BUG: 131833 M +3 -0 NEWS M +116 -3 gvimagepart/gvimagepart.cpp M +24 -0 gvimagepart/gvimagepart.h --- trunk/extragear/graphics/gwenview/NEWS #606304:606305 @@ -22,6 +22,9 @@ (Bug 126218). - In KPart, do not loose selection when starting a drag. - Fixed behavior of the middle-mouse button (Bug 134590). + - Saving in KPart now does a copy of the image instead of recompressing it, + unless the user rotated the image, this ensures the KPart won't fail to + save certain files (Bug 131833). 2006.09.16 - v1.4.0 - Fixes: --- trunk/extragear/graphics/gwenview/gvimagepart/gvimagepart.cpp #606304:606305 @@ -20,6 +20,7 @@ #include <qapplication.h> #include <qcursor.h> +#include <qfile.h> #include <qpoint.h> #include <kaction.h> @@ -27,13 +28,16 @@ #include <kconfig.h> #include <kdebug.h> #include <kdirlister.h> +#include <kfiledialog.h> #include <kiconloader.h> #include <klocale.h> +#include <kmessagebox.h> #include <kmimetype.h> #include <kparts/genericfactory.h> #include <gvcore/cache.h> #include <gvcore/document.h> +#include <gvcore/fileoperation.h> #include <gvcore/printdialog.h> #include <gvcore/imageview.h> #include <gvcore/imageloader.h> @@ -47,7 +51,7 @@ #undef ENABLE_LOG #undef LOG -//#define ENABLE_LOG +#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) kdDebug() << k_funcinfo << x << endl #else @@ -55,6 +59,18 @@ #endif +static bool storeData(QWidget* parent, QFile* file, const QByteArray& data) { + uint sizeWritten = file->writeBlock(data); + if (sizeWritten != data.size()) { + KMessageBox::error( + parent, + i18n("Could not save image to a temporary file")); + return false; + } + return true; +} + + //Factory Code typedef KParts::GenericFactory<GVImagePart> GVImageFactory; K_EXPORT_COMPONENT_FACTORY( libgvimagepart /*library name*/, GVImageFactory ) @@ -98,7 +114,7 @@ this,SLOT(slotSelectNext()), actionCollection(), "next"); updateNextPrevious(); - KStdAction::saveAs( mDocument, SLOT(saveAs()), actionCollection(), "saveAs" ); + KStdAction::saveAs( this, SLOT(saveAs()), actionCollection(), "saveAs" ); new KAction(i18n("Rotate &Left"), "rotate_ccw", CTRL + Key_L, this, SLOT(rotateLeft()), actionCollection(), "rotate_left"); new KAction(i18n("Rotate &Right"), "rotate_cw", CTRL + Key_R, this, SLOT(rotateRight()), actionCollection(), "rotate_right"); @@ -288,6 +304,103 @@ } +void GVImagePart::saveAs() { + if (!mDocument->isModified()) { + saveOriginalAs(); + return; + } + + if (mDocument->canBeSaved()) { + mDocument->saveAs(); + return; + } + + KGuiItem saveItem(i18n("&Save Original"), "filesaveas"); + int result = KMessageBox::warningContinueCancel( + widget(), + i18n("Gwenview KPart can't save the modifications you made. Do you want to save the original image?"), + i18n("Warning"), + saveItem); + + if (result == KMessageBox::Cancel) return; + + saveOriginalAs(); +} + + +void GVImagePart::showJobError(KIO::Job* job) { + if (job->error() != 0) { + job->showErrorDialog(widget()); + } +} + + +void GVImagePart::saveOriginalAs() { + KURL srcURL = mDocument->url(); + KURL dstURL = KFileDialog::getSaveURL( + srcURL.fileName(), + QString::null, + widget()); + if (!dstURL.isValid()) return; + + // Try to get data from the cache to avoid downloading the image again. + QByteArray data = Cache::instance()->file(srcURL); + + if (data.size() == 0) { + // We need to read the image again. Let KIO::copy do the work. + KIO::Job* job = KIO::copy(srcURL, dstURL); + job->setWindow(widget()); + connect(job, SIGNAL(result(KIO::Job*)), + this, SLOT(showJobError(KIO::Job*)) ); + return; + } + + if (dstURL.isLocalFile()) { + // Destination is a local file, store it ourself + QString path = dstURL.path(); + QFile file(path); + if (!file.open(IO_WriteOnly)) { + KMessageBox::error( + widget(), + i18n("Could not open '%1' for writing.").arg(path)); + return; + } + storeData(widget(), &file, data); + return; + } + + // We need to send the data to a remote location + new DataUploader(widget(), data, dstURL); +} + + +DataUploader::DataUploader(QWidget* dialogParent, const QByteArray& data, const KURL& dstURL) +: mDialogParent(dialogParent) +{ + mTempFile.setAutoDelete(true); + + // Store it in a temp file + if (! storeData(dialogParent, mTempFile.file(), data) ) return; + + // Now upload it + KURL tmpURL; + tmpURL.setPath(mTempFile.name()); + KIO::Job* job = KIO::copy(tmpURL, dstURL); + job->setWindow(dialogParent); + connect(job, SIGNAL(result(KIO::Job*)), + this, SLOT(slotJobFinished(KIO::Job*)) ); +} + + +void DataUploader::slotJobFinished(KIO::Job* job) { + if (job->error() != 0) { + job->showErrorDialog(mDialogParent); + } + + delete this; +} + + /** * Overload KXMLGUIClient so that we can call setXML */ @@ -304,7 +417,7 @@ QString doc = KXMLGUIFactory::readConfigFile( "gvimagepartpopup.rc", true, instance() ); PopupGUIClient guiClient(instance(), doc); - KStdAction::saveAs( mDocument, SLOT(saveAs()), guiClient.actionCollection(), "saveAs" ); + KStdAction::saveAs( this, SLOT(saveAs()), guiClient.actionCollection(), "saveAs" ); KParts::URLArgs urlArgs; urlArgs.serviceType = mDocument->mimeType(); --- trunk/extragear/graphics/gwenview/gvimagepart/gvimagepart.h #606304:606305 @@ -21,8 +21,10 @@ #include <kparts/part.h> #include <kparts/browserextension.h> +#include <ktempfile.h> // Forward declarations +class QFile; class QPoint; class KAboutData; @@ -118,11 +120,16 @@ void openContextMenu(const QPoint&); + void saveAs(); + + void showJobError(KIO::Job* job); + private: void updateNextPrevious(); KURL nextURL() const; KURL previousURL() const; + void saveOriginalAs(); /** * The component's widget @@ -153,5 +160,22 @@ LastDirection mLastDirection; // used for prefetching }; + +/** + * This simple helper class uploads data to a remote URL asynchronously + */ +class DataUploader : public QObject { + Q_OBJECT +public: + DataUploader(QWidget* dialogParent, const QByteArray& data, const KURL& destURL); + +private slots: + void slotJobFinished(KIO::Job*); + +private: + KTempFile mTempFile; + QWidget* mDialogParent; +}; + } // namespace #endif