Bug 131833 - GwenView refuses to save animated GIFs
Summary: GwenView refuses to save animated GIFs
Status: RESOLVED FIXED
Alias: None
Product: gwenview
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: Compiled Sources Linux
: NOR normal
Target Milestone: ---
Assignee: Gwenview Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-08-04 08:52 UTC by Stephan Sokolow
Modified: 2012-10-19 13:26 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Stephan Sokolow 2006-08-04 08:52:29 UTC
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.
Comment 1 Aurelien Gateau 2006-11-20 00:20:05 UTC
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