|
-
- /*
- Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
- #define DEBUG_KP_DOCUMENT 0
-
-
- #include <kpdocument.h>
-
- #include <math.h>
-
- #include <tqcolor.h>
- #include <tqbitmap.h>
- #include <tqbrush.h>
- #include <tqfile.h>
- #include <tqimage.h>
- #include <tqpixmap.h>
- #include <tqpainter.h>
- #include <tqrect.h>
- #include <tqsize.h>
- #include <tqvaluelist.h>
- #include <tqwmatrix.h>
-
- #include <kdebug.h>
- #include <tdeglobal.h>
- #include <kimageio.h>
- #include <tdeio/netaccess.h>
- #include <tdelocale.h>
- #include <tdemessagebox.h>
- #include <kmimetype.h>
- #include <ksavefile.h>
- #include <tdetempfile.h>
-
- #include <kpcolor.h>
- #include <kpcolortoolbar.h>
- #include <kpdefs.h>
- #include <kpdocumentsaveoptions.h>
- #include <kpdocumentmetainfo.h>
- #include <kpeffectreducecolors.h>
- #include <kpmainwindow.h>
- #include <kppixmapfx.h>
- #include <kpselection.h>
- #include <kptool.h>
- #include <kptooltoolbar.h>
- #include <kpviewmanager.h>
-
-
- struct kpDocumentPrivate
- {
- kpDocumentPrivate ()
- {
- }
- };
-
-
- kpDocument::kpDocument (int w, int h, kpMainWindow *mainWindow)
- : m_constructorWidth (w), m_constructorHeight (h),
- m_mainWindow (mainWindow),
- m_isFromURL (false),
- m_savedAtLeastOnceBefore (false),
- m_saveOptions (new kpDocumentSaveOptions ()),
- m_metaInfo (new kpDocumentMetaInfo ()),
- m_modified (false),
- m_selection (0),
- m_oldWidth (-1), m_oldHeight (-1),
- d (new kpDocumentPrivate ())
- {
- #if DEBUG_KP_DOCUMENT && 0
- kdDebug () << "kpDocument::kpDocument (" << w << "," << h << ")" << endl;
- #endif
-
- m_pixmap = new TQPixmap (w, h);
- m_pixmap->fill (TQt::white);
- }
-
- kpDocument::~kpDocument ()
- {
- delete d;
-
- delete m_pixmap;
-
- delete m_saveOptions;
- delete m_metaInfo;
-
- delete m_selection;
- }
-
-
- kpMainWindow *kpDocument::mainWindow () const
- {
- return m_mainWindow;
- }
-
- void kpDocument::setMainWindow (kpMainWindow *mainWindow)
- {
- m_mainWindow = mainWindow;
- }
-
-
- /*
- * File I/O
- */
-
- // public static
- TQPixmap kpDocument::convertToPixmapAsLosslessAsPossible (
- const TQImage &image,
- const kpPixmapFX::WarnAboutLossInfo &wali,
-
- kpDocumentSaveOptions *saveOptions,
- kpDocumentMetaInfo *metaInfo)
- {
- if (image.isNull ())
- return TQPixmap ();
-
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\timage: depth=" << image.depth ()
- << " (X display=" << TQColor::numBitPlanes () << ")"
- << " hasAlphaBuffer=" << image.hasAlphaBuffer ()
- << endl;
- #endif
-
- if (saveOptions)
- {
- saveOptions->setColorDepth (image.depth ());
- saveOptions->setDither (false); // avoid double dithering when saving
- }
-
- if (metaInfo)
- {
- metaInfo->setDotsPerMeterX (image.dotsPerMeterX ());
- metaInfo->setDotsPerMeterY (image.dotsPerMeterY ());
- metaInfo->setOffset (image.offset ());
-
- TQValueList <TQImageTextKeyLang> keyList = image.textList ();
- for (TQValueList <TQImageTextKeyLang>::const_iterator it = keyList.begin ();
- it != keyList.end ();
- it++)
- {
- metaInfo->setText (*it, image.text (*it));
- }
-
- #if DEBUG_KP_DOCUMENT
- metaInfo->printDebug ("\tmetaInfo");
- #endif
- }
-
- #if DEBUG_KP_DOCUMENT && 1
- {
- if (image.width () <= 16 && image.height () <= 16)
- {
- kdDebug () << "Image dump:" << endl;
-
- for (int y = 0; y < image.height (); y++)
- {
- for (int x = 0; x < image.width (); x++)
- {
- const TQRgb rgb = image.pixel (x, y);
- fprintf (stderr, " %08X", rgb);
- }
- fprintf (stderr, "\n");
- }
- }
- }
- #endif
-
-
- TQPixmap newPixmap = kpPixmapFX::convertToPixmapAsLosslessAsPossible (image, wali);
-
-
- #if DEBUG_KP_DOCUMENT && 1
- {
- const TQImage image2 = kpPixmapFX::convertToImage (newPixmap);
- kdDebug () << "(Converted to pixmap) Image dump:" << endl;
-
- bool differsFromOrgImage = false;
- unsigned long hash = 0;
- int numDiff = 0;
- for (int y = 0; y < image2.height (); y++)
- {
- for (int x = 0; x < image2.width (); x++)
- {
- const TQRgb rgb = image2.pixel (x, y);
- hash += ((x % 2) + 1) * rgb;
- if (rgb != image.pixel (x, y))
- {
- differsFromOrgImage = true;
- numDiff++;
- }
- if (image2.width () <= 16 && image2.height () <= 16)
- fprintf (stderr, " %08X", rgb);
- }
- if (image2.width () <= 16 && image2.height () <= 16)
- fprintf (stderr, "\n");
- }
-
- kdDebug () << "\tdiffersFromOrgImage="
- << differsFromOrgImage
- << " numDiff="
- << numDiff
- << " hash=" << hash << endl;
- }
- #endif
-
- return newPixmap;
- }
-
- // public static
- TQPixmap kpDocument::getPixmapFromFile (const KURL &url, bool suppressDoesntExistDialog,
- TQWidget *parent,
- kpDocumentSaveOptions *saveOptions,
- kpDocumentMetaInfo *metaInfo)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::getPixmapFromFile(" << url << "," << parent << ")" << endl;
- #endif
-
- if (saveOptions)
- *saveOptions = kpDocumentSaveOptions ();
-
- if (metaInfo)
- *metaInfo = kpDocumentMetaInfo ();
-
-
- TQString tempFile;
- if (url.isEmpty () || !TDEIO::NetAccess::download (url, tempFile, parent))
- {
- if (!suppressDoesntExistDialog)
- {
- KMessageBox::sorry (parent,
- i18n ("Could not open \"%1\".")
- .arg (kpDocument::prettyFilenameForURL (url)));
- }
-
- return TQPixmap ();
- }
-
-
- TQImage image;
-
- // sync: remember to "TDEIO::NetAccess::removeTempFile (tempFile)" in all exit paths
- {
- TQString detectedMimeType = KImageIO::mimeType (tempFile);
- if (saveOptions)
- saveOptions->setMimeType (detectedMimeType);
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\ttempFile=" << tempFile << endl;
- kdDebug () << "\tmimetype=" << detectedMimeType << endl;
- kdDebug () << "\tsrc=" << url.path () << endl;
- kdDebug () << "\tmimetype of src=" << KImageIO::mimeType (url.path ()) << endl;
- #endif
-
- if (detectedMimeType.isEmpty ())
- {
- KMessageBox::sorry (parent,
- i18n ("Could not open \"%1\" - unknown mimetype.")
- .arg (kpDocument::prettyFilenameForURL (url)));
- TDEIO::NetAccess::removeTempFile (tempFile);
- return TQPixmap ();
- }
-
-
- image = TQImage (tempFile);
- TDEIO::NetAccess::removeTempFile (tempFile);
- }
-
-
- if (image.isNull ())
- {
- KMessageBox::sorry (parent,
- i18n ("Could not open \"%1\" - unsupported image format.\n"
- "The file may be corrupt.")
- .arg (kpDocument::prettyFilenameForURL (url)));
- return TQPixmap ();
- }
-
- const TQPixmap newPixmap = kpDocument::convertToPixmapAsLosslessAsPossible (image,
- kpPixmapFX::WarnAboutLossInfo (
- i18n ("The image \"%1\""
- " may have more colors than the current screen mode."
- " In order to display it, some colors may be changed."
- " Try increasing your screen depth to at least %2bpp."
-
- "\nIt also"
-
- " contains translucency which is not fully"
- " supported. The translucency data will be"
- " approximated with a 1-bit transparency mask.")
- .arg (prettyFilenameForURL (url)),
- i18n ("The image \"%1\""
- " may have more colors than the current screen mode."
- " In order to display it, some colors may be changed."
- " Try increasing your screen depth to at least %2bpp.")
- .arg (prettyFilenameForURL (url)),
- i18n ("The image \"%1\""
- " contains translucency which is not fully"
- " supported. The translucency data will be"
- " approximated with a 1-bit transparency mask.")
- .arg (prettyFilenameForURL (url)),
- "docOpen",
- parent),
- saveOptions,
- metaInfo);
-
- if (newPixmap.isNull ())
- {
- KMessageBox::sorry (parent,
- i18n ("Could not open \"%1\" - out of graphics memory.")
- .arg (kpDocument::prettyFilenameForURL (url)));
- return TQPixmap ();
- }
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tpixmap: depth=" << newPixmap.depth ()
- << " hasAlphaChannelOrMask=" << newPixmap.hasAlpha ()
- << " hasAlphaChannel=" << newPixmap.hasAlphaChannel ()
- << endl;
- #endif
-
-
- return newPixmap;
- }
-
- void kpDocument::openNew (const KURL &url)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "KpDocument::openNew (" << url << ")" << endl;
- #endif
-
- m_pixmap->fill (TQt::white);
-
- setURL (url, false/*not from url*/);
- *m_saveOptions = kpDocumentSaveOptions ();
- *m_metaInfo = kpDocumentMetaInfo ();
- m_modified = false;
-
- emit documentOpened ();
- }
-
- bool kpDocument::open (const KURL &url, bool newDocSameNameIfNotExist)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::open (" << url << ")" << endl;
- #endif
-
- kpDocumentSaveOptions newSaveOptions;
- kpDocumentMetaInfo newMetaInfo;
- TQPixmap newPixmap = kpDocument::getPixmapFromFile (url,
- newDocSameNameIfNotExist/*suppress "doesn't exist" dialog*/,
- m_mainWindow,
- &newSaveOptions,
- &newMetaInfo);
-
- if (!newPixmap.isNull ())
- {
- delete m_pixmap;
- m_pixmap = new TQPixmap (newPixmap);
-
- setURL (url, true/*is from url*/);
- *m_saveOptions = newSaveOptions;
- *m_metaInfo = newMetaInfo;
- m_modified = false;
-
- emit documentOpened ();
- return true;
- }
-
- if (newDocSameNameIfNotExist)
- {
- if (!url.isEmpty () &&
- // not just a permission error?
- !TDEIO::NetAccess::exists (url, true/*open*/, m_mainWindow))
- {
- openNew (url);
- }
- else
- {
- openNew (KURL ());
- }
-
- return true;
- }
- else
- {
- return false;
- }
- }
-
- bool kpDocument::save (bool overwritePrompt, bool lossyPrompt)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::save("
- << "overwritePrompt=" << overwritePrompt
- << ",lossyPrompt=" << lossyPrompt
- << ") url=" << m_url
- << " savedAtLeastOnceBefore=" << savedAtLeastOnceBefore ()
- << endl;
- #endif
-
- // TODO: check feels weak
- if (m_url.isEmpty () || m_saveOptions->mimeType ().isEmpty ())
- {
- KMessageBox::detailedError (m_mainWindow,
- i18n ("Could not save image - insufficient information."),
- i18n ("URL: %1\n"
- "Mimetype: %2")
- .arg (prettyURL ())
- .arg (m_saveOptions->mimeType ().isEmpty () ?
- i18n ("<empty>") :
- m_saveOptions->mimeType ()),
- i18n ("Internal Error"));
- return false;
- }
-
- return saveAs (m_url, *m_saveOptions,
- overwritePrompt,
- lossyPrompt);
- }
-
-
- // public static
- bool kpDocument::lossyPromptContinue (const TQPixmap &pixmap,
- const kpDocumentSaveOptions &saveOptions,
- TQWidget *parent)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::lossyPromptContinue()" << endl;
- #endif
-
- #define QUIT_IF_CANCEL(messageBoxCommand) \
- { \
- if (messageBoxCommand != KMessageBox::Continue) \
- { \
- return false; \
- } \
- }
-
- const int lossyType = saveOptions.isLossyForSaving (pixmap);
- if (lossyType & (kpDocumentSaveOptions::MimeTypeMaximumColorDepthLow |
- kpDocumentSaveOptions::Quality))
- {
- QUIT_IF_CANCEL (
- KMessageBox::warningContinueCancel (parent,
- i18n ("<qt><p>The <b>%1</b> format may not be able"
- " to preserve all of the image's color information.</p>"
-
- "<p>Are you sure you want to save in this format?</p></qt>")
- .arg (KMimeType::mimeType (saveOptions.mimeType ())->comment ()),
- // TODO: caption misleading for lossless formats that have
- // low maximum colour depth
- i18n ("Lossy File Format"),
- KStdGuiItem::save (),
- TQString::fromLatin1 ("SaveInLossyMimeTypeDontAskAgain")));
- }
- else if (lossyType & kpDocumentSaveOptions::ColorDepthLow)
- {
- QUIT_IF_CANCEL (
- KMessageBox::warningContinueCancel (parent,
- i18n ("<qt><p>Saving the image at the low color depth of %1-bit"
- " may result in the loss of color information."
-
- " Any transparency will also be removed.</p>"
-
- "<p>Are you sure you want to save at this color depth?</p></qt>")
- .arg (saveOptions.colorDepth ()),
- i18n ("Low Color Depth"),
- KStdGuiItem::save (),
- TQString::fromLatin1 ("SaveAtLowColorDepthDontAskAgain")));
- }
- #undef QUIT_IF_CANCEL
-
- return true;
- }
-
- // public static
- bool kpDocument::savePixmapToDevice (const TQPixmap &pixmap,
- TQIODevice *device,
- const kpDocumentSaveOptions &saveOptions,
- const kpDocumentMetaInfo &metaInfo,
- bool lossyPrompt,
- TQWidget *parent,
- bool *userCancelled)
- {
- if (userCancelled)
- *userCancelled = false;
-
- TQString type = KImageIO::typeForMime (saveOptions.mimeType ());
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tmimeType=" << saveOptions.mimeType ()
- << " type=" << type << endl;
- #endif
-
- if (lossyPrompt && !lossyPromptContinue (pixmap, saveOptions, parent))
- {
- if (userCancelled)
- *userCancelled = true;
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because of lossyPrompt" << endl;
- #endif
- return false;
- }
-
-
- TQPixmap pixmapToSave =
- kpPixmapFX::pixmapWithDefinedTransparentPixels (pixmap,
- TQt::white); // CONFIG
- TQImage imageToSave = kpPixmapFX::convertToImage (pixmapToSave);
-
-
- // TODO: fix dup with kpDocumentSaveOptions::isLossyForSaving()
- const bool useSaveOptionsColorDepth =
- (saveOptions.mimeTypeHasConfigurableColorDepth () &&
- !saveOptions.colorDepthIsInvalid ());
- const bool useSaveOptionsQuality =
- (saveOptions.mimeTypeHasConfigurableQuality () &&
- !saveOptions.qualityIsInvalid ());
-
-
- //
- // Reduce colors if required
- //
-
- if (useSaveOptionsColorDepth &&
- imageToSave.depth () != saveOptions.colorDepth ())
- {
- imageToSave = ::convertImageDepth (imageToSave,
- saveOptions.colorDepth (),
- saveOptions.dither ());
- }
-
-
- //
- // Write Meta Info
- //
-
- imageToSave.setDotsPerMeterX (metaInfo.dotsPerMeterX ());
- imageToSave.setDotsPerMeterY (metaInfo.dotsPerMeterY ());
- imageToSave.setOffset (metaInfo.offset ());
-
- TQValueList <TQImageTextKeyLang> keyList = metaInfo.textList ();
- for (TQValueList <TQImageTextKeyLang>::const_iterator it = keyList.begin ();
- it != keyList.end ();
- it++)
- {
- imageToSave.setText ((*it).key, (*it).lang, metaInfo.text (*it));
- }
-
-
- //
- // Save at required quality
- //
-
- int quality = -1; // default
-
- if (useSaveOptionsQuality)
- quality = saveOptions.quality ();
-
- if (!imageToSave.save (device, type.latin1 (), quality))
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tTQImage::save() returned false" << endl;
- #endif
- return false;
- }
-
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tsave OK" << endl;
- #endif
- return true;
- }
-
- static void CouldNotCreateTemporaryFileDialog (TQWidget *parent)
- {
- KMessageBox::error (parent,
- i18n ("Could not save image - unable to create temporary file."));
- }
-
- static void CouldNotSaveDialog (const KURL &url, TQWidget *parent)
- {
- // TODO: use file.errorString()
- KMessageBox::error (parent,
- i18n ("Could not save as \"%1\".")
- .arg (kpDocument::prettyFilenameForURL (url)));
- }
-
- // public static
- bool kpDocument::savePixmapToFile (const TQPixmap &pixmap,
- const KURL &url,
- const kpDocumentSaveOptions &saveOptions,
- const kpDocumentMetaInfo &metaInfo,
- bool overwritePrompt,
- bool lossyPrompt,
- TQWidget *parent)
- {
- // TODO: Use TDEIO::NetAccess:mostLocalURL() for accessing home:/ (and other
- // such local URLs) for efficiency and because only local writes
- // are atomic.
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::savePixmapToFile ("
- << url
- << ",overwritePrompt=" << overwritePrompt
- << ",lossyPrompt=" << lossyPrompt
- << ")" << endl;
- saveOptions.printDebug (TQString::fromLatin1 ("\tsaveOptions"));
- metaInfo.printDebug (TQString::fromLatin1 ("\tmetaInfo"));
- #endif
-
- if (overwritePrompt && TDEIO::NetAccess::exists (url, false/*write*/, parent))
- {
- int result = KMessageBox::warningContinueCancel (parent,
- i18n ("A document called \"%1\" already exists.\n"
- "Do you want to overwrite it?")
- .arg (prettyFilenameForURL (url)),
- TQString(),
- i18n ("Overwrite"));
-
- if (result != KMessageBox::Continue)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tuser doesn't want to overwrite" << endl;
- #endif
-
- return false;
- }
- }
-
-
- if (lossyPrompt && !lossyPromptContinue (pixmap, saveOptions, parent))
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because of lossyPrompt" << endl;
- #endif
- return false;
- }
-
-
- // Local file?
- if (url.isLocalFile ())
- {
- const TQString filename = url.path ();
-
- // sync: All failure exit paths _must_ call KSaveFile::abort() or
- // else, the KSaveFile destructor will overwrite the file,
- // <filename>, despite the failure.
- KSaveFile atomicFileWriter (filename);
- {
- if (atomicFileWriter.status () != 0)
- {
- // We probably don't need this as <filename> has not been
- // opened.
- atomicFileWriter.abort ();
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because could not open KSaveFile"
- << " status=" << atomicFileWriter.status () << endl;
- #endif
- ::CouldNotCreateTemporaryFileDialog (parent);
- return false;
- }
-
- // Write to local temporary file.
- if (!savePixmapToDevice (pixmap, TQT_TQIODEVICE(atomicFileWriter.file ()),
- saveOptions, metaInfo,
- false/*no lossy prompt*/,
- parent))
- {
- atomicFileWriter.abort ();
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because could not save pixmap to device"
- << endl;
- #endif
- ::CouldNotSaveDialog (url, parent);
- return false;
- }
-
- // Atomically overwrite local file with the temporary file
- // we saved to.
- if (!atomicFileWriter.close ())
- {
- atomicFileWriter.abort ();
-
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\tcould not close KSaveFile" << endl;
- #endif
- ::CouldNotSaveDialog (url, parent);
- return false;
- }
- } // sync KSaveFile.abort()
- }
- // Remote file?
- else
- {
- // Create temporary file that is deleted when the variable goes
- // out of scope.
- KTempFile tempFile;
- tempFile.setAutoDelete (true);
-
- TQString filename = tempFile.name ();
- if (filename.isEmpty ())
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because tempFile empty" << endl;
- #endif
- ::CouldNotCreateTemporaryFileDialog (parent);
- return false;
- }
-
- // Write to local temporary file.
- TQFile file (filename);
- {
- if (!file.open (IO_WriteOnly))
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because can't open file"
- << " errorString=" << file.errorString () << endl;
- #endif
- ::CouldNotCreateTemporaryFileDialog (parent);
- return false;
- }
-
- if (!savePixmapToDevice (pixmap, TQT_TQIODEVICE(&file),
- saveOptions, metaInfo,
- false/*no lossy prompt*/,
- parent))
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because could not save pixmap to device"
- << endl;
- #endif
- ::CouldNotSaveDialog (url, parent);
- return false;
- }
- }
- file.close ();
- if (file.status () != IO_Ok)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because could not close" << endl;
- #endif
- ::CouldNotSaveDialog (url, parent);
- return false;
- }
-
- // Copy local temporary file to overwrite remote.
- // TODO: No one seems to know how to do this atomically
- // [http://lists.kde.org/?l=kde-core-devel&m=117845162728484&w=2].
- // At least, fish:// (ssh) is definitely not atomic.
- if (!TDEIO::NetAccess::upload (filename, url, parent))
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "\treturning false because could not upload" << endl;
- #endif
- KMessageBox::error (parent,
- i18n ("Could not save image - failed to upload."));
- return false;
- }
- }
-
-
- return true;
- }
-
- bool kpDocument::saveAs (const KURL &url,
- const kpDocumentSaveOptions &saveOptions,
- bool overwritePrompt,
- bool lossyPrompt)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::saveAs (" << url << ","
- << saveOptions.mimeType () << ")" << endl;
- #endif
-
- if (kpDocument::savePixmapToFile (pixmapWithSelection (),
- url,
- saveOptions, *metaInfo (),
- overwritePrompt,
- lossyPrompt,
- m_mainWindow))
- {
- setURL (url, true/*is from url*/);
- *m_saveOptions = saveOptions;
- m_modified = false;
-
- m_savedAtLeastOnceBefore = true;
-
- emit documentSaved ();
- return true;
- }
- else
- {
- return false;
- }
- }
-
- // public
- bool kpDocument::savedAtLeastOnceBefore () const
- {
- return m_savedAtLeastOnceBefore;
- }
-
- // public
- KURL kpDocument::url () const
- {
- return m_url;
- }
-
- // public
- void kpDocument::setURL (const KURL &url, bool isFromURL)
- {
- m_url = url;
- m_isFromURL = isFromURL;
- }
-
- // public
- bool kpDocument::isFromURL (bool checkURLStillExists) const
- {
- if (!m_isFromURL)
- return false;
-
- if (!checkURLStillExists)
- return true;
-
- return (!m_url.isEmpty () &&
- TDEIO::NetAccess::exists (m_url, true/*open*/, m_mainWindow));
- }
-
-
- // static
- TQString kpDocument::prettyURLForURL (const KURL &url)
- {
- if (url.isEmpty ())
- return i18n ("Untitled");
- else
- return url.prettyURL (0, KURL::StripFileProtocol);
- }
-
- TQString kpDocument::prettyURL () const
- {
- return prettyURLForURL (m_url);
- }
-
-
- // static
- TQString kpDocument::prettyFilenameForURL (const KURL &url)
- {
- if (url.isEmpty ())
- return i18n ("Untitled");
- else if (url.fileName ().isEmpty ())
- return prettyURLForURL (url); // better than the name ""
- else
- return url.fileName ();
- }
-
- TQString kpDocument::prettyFilename () const
- {
- return prettyFilenameForURL (m_url);
- }
-
-
- // public
- const kpDocumentSaveOptions *kpDocument::saveOptions () const
- {
- return m_saveOptions;
- }
-
- // public
- void kpDocument::setSaveOptions (const kpDocumentSaveOptions &saveOptions)
- {
- *m_saveOptions = saveOptions;
- }
-
-
- // public
- const kpDocumentMetaInfo *kpDocument::metaInfo () const
- {
- return m_metaInfo;
- }
-
- // public
- void kpDocument::setMetaInfo (const kpDocumentMetaInfo &metaInfo)
- {
- *m_metaInfo = metaInfo;
- }
-
-
- /*
- * Properties
- */
-
- void kpDocument::setModified (bool yes)
- {
- if (yes == m_modified)
- return;
-
- m_modified = yes;
-
- if (yes)
- emit documentModified ();
- }
-
- bool kpDocument::isModified () const
- {
- return m_modified;
- }
-
- bool kpDocument::isEmpty () const
- {
- return url ().isEmpty () && !isModified ();
- }
-
-
- int kpDocument::constructorWidth () const
- {
- return m_constructorWidth;
- }
-
- int kpDocument::width (bool ofSelection) const
- {
- if (ofSelection && m_selection)
- return m_selection->width ();
- else
- return m_pixmap->width ();
- }
-
- int kpDocument::oldWidth () const
- {
- return m_oldWidth;
- }
-
- void kpDocument::setWidth (int w, const kpColor &backgroundColor)
- {
- resize (w, height (), backgroundColor);
- }
-
-
- int kpDocument::constructorHeight () const
- {
- return m_constructorHeight;
- }
-
- int kpDocument::height (bool ofSelection) const
- {
- if (ofSelection && m_selection)
- return m_selection->height ();
- else
- return m_pixmap->height ();
- }
-
- int kpDocument::oldHeight () const
- {
- return m_oldHeight;
- }
-
- void kpDocument::setHeight (int h, const kpColor &backgroundColor)
- {
- resize (width (), h, backgroundColor);
- }
-
- TQRect kpDocument::rect (bool ofSelection) const
- {
- if (ofSelection && m_selection)
- return m_selection->boundingRect ();
- else
- return m_pixmap->rect ();
- }
-
-
- /*
- * Pixmap access
- */
-
- // public
- TQPixmap kpDocument::getPixmapAt (const TQRect &rect) const
- {
- return kpPixmapFX::getPixmapAt (*m_pixmap, rect);
- }
-
- // public
- void kpDocument::setPixmapAt (const TQPixmap &pixmap, const TQPoint &at)
- {
- #if DEBUG_KP_DOCUMENT && 0
- kdDebug () << "kpDocument::setPixmapAt (pixmap (w="
- << pixmap.width ()
- << ",h=" << pixmap.height ()
- << "), x=" << at.x ()
- << ",y=" << at.y ()
- << endl;
- #endif
-
- kpPixmapFX::setPixmapAt (m_pixmap, at, pixmap);
- slotContentsChanged (TQRect (at.x (), at.y (), pixmap.width (), pixmap.height ()));
- }
-
- // public
- void kpDocument::paintPixmapAt (const TQPixmap &pixmap, const TQPoint &at)
- {
- kpPixmapFX::paintPixmapAt (m_pixmap, at, pixmap);
- slotContentsChanged (TQRect (at.x (), at.y (), pixmap.width (), pixmap.height ()));
- }
-
-
- // public
- TQPixmap *kpDocument::pixmap (bool ofSelection) const
- {
- if (ofSelection)
- {
- if (m_selection && m_selection->pixmap ())
- return m_selection->pixmap ();
- else
- return 0;
- }
- else
- return m_pixmap;
- }
-
- // public
- void kpDocument::setPixmap (const TQPixmap &pixmap)
- {
- m_oldWidth = width (), m_oldHeight = height ();
-
- *m_pixmap = pixmap;
-
- if (m_oldWidth == width () && m_oldHeight == height ())
- slotContentsChanged (pixmap.rect ());
- else
- slotSizeChanged (width (), height ());
- }
-
- // public
- void kpDocument::setPixmap (bool ofSelection, const TQPixmap &pixmap)
- {
- if (ofSelection)
- {
- if (!m_selection)
- {
- kdError () << "kpDocument::setPixmap(ofSelection=true) without sel" << endl;
- return;
- }
-
- m_selection->setPixmap (pixmap);
- }
- else
- setPixmap (pixmap);
- }
-
-
- // private
- void kpDocument::updateToolsSingleKeyTriggersEnabled ()
- {
- if (m_mainWindow)
- {
- // Disable single key shortcuts when the user is editing text
- m_mainWindow->enableActionsSingleKeyTriggers (!m_selection || !m_selection->isText ());
- }
- }
-
-
- // public
- kpSelection *kpDocument::selection () const
- {
- return m_selection;
- }
-
- // public
- void kpDocument::setSelection (const kpSelection &selection)
- {
- #if DEBUG_KP_DOCUMENT && 0
- kdDebug () << "kpDocument::setSelection() sel boundingRect="
- << selection.boundingRect ()
- << endl;
- #endif
-
- kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
- if (vm)
- vm->setQueueUpdates ();
-
- bool hadSelection = (bool) m_selection;
-
-
- const bool isTextChanged = (m_mainWindow->toolIsTextTool () !=
- (selection.type () == kpSelection::Text));
-
- // We don't change the Selection Tool if the new selection's
- // shape is merely different to the current tool's (e.g. rectangular
- // vs elliptical) because:
- //
- // 1. All image selection tools support editing selections of all the
- // different shapes anyway.
- // 2. Suppose the user is trying out different drags of selection borders
- // and then decides to paste a differently shaped selection before continuing
- // to try out different borders. If the pasting were to switch to
- // a differently shaped tool, the borders drawn after the paste would
- // be using a new shape rather than the shape before the paste. This
- // could get irritating so we don't do the switch.
- //
- if (m_mainWindow &&
- (!m_mainWindow->toolIsASelectionTool () || isTextChanged))
- {
- // Switch to the appropriately shaped selection tool
- // _before_ we change the selection
- // (all selection tool's ::end() functions nuke the current selection)
- switch (selection.type ())
- {
- case kpSelection::Rectangle:
- m_mainWindow->slotToolRectSelection ();
- break;
- case kpSelection::Ellipse:
- m_mainWindow->slotToolEllipticalSelection ();
- break;
- case kpSelection::Points:
- m_mainWindow->slotToolFreeFormSelection ();
- break;
- case kpSelection::Text:
- m_mainWindow->slotToolText ();
- break;
- default:
- break;
- }
- }
-
-
- if (m_selection)
- {
- // TODO: Emitting this, before setting the new selection, is bogus
- // since it would redraw the old selection.
- //
- // Luckily, this doesn't matter thanks to the
- // kpViewManager::setQueueUpdates() call above.
- if (m_selection->pixmap ())
- slotContentsChanged (m_selection->boundingRect ());
- else
- // TODO: Should emit contentsChanged() instead?
- // I don't think it matters since contentsChanged() is
- // connected to updateViews() anyway (see
- // kpMainWindow::setDocument ()).
- vm->updateViews (m_selection->boundingRect ());
-
- delete m_selection;
- }
-
- m_selection = new kpSelection (selection);
-
- // TODO: this coupling is bad, careless and lazy
- if (m_mainWindow)
- {
- if (!m_selection->isText ())
- {
- if (m_selection->transparency () != m_mainWindow->selectionTransparency ())
- {
- kdDebug () << "kpDocument::setSelection() sel's transparency differs "
- "from mainWindow's transparency - setting mainWindow's transparency "
- "to sel"
- << endl;
- kdDebug () << "\tisOpaque: sel=" << m_selection->transparency ().isOpaque ()
- << " mainWindow=" << m_mainWindow->selectionTransparency ().isOpaque ()
- << endl;
- m_mainWindow->setSelectionTransparency (m_selection->transparency ());
- }
- }
- else
- {
- if (m_selection->textStyle () != m_mainWindow->textStyle ())
- {
- kdDebug () << "kpDocument::setSelection() sel's textStyle differs "
- "from mainWindow's textStyle - setting mainWindow's textStyle "
- "to sel"
- << endl;
- m_mainWindow->setTextStyle (m_selection->textStyle ());
- }
- }
- }
-
- updateToolsSingleKeyTriggersEnabled ();
-
- #if DEBUG_KP_DOCUMENT && 0
- kdDebug () << "\tcheck sel " << (int *) m_selection
- << " boundingRect=" << m_selection->boundingRect ()
- << endl;
- #endif
- if (m_selection->pixmap ())
- slotContentsChanged (m_selection->boundingRect ());
- else
- // TODO: Should emit contentsChanged() instead?
- // I don't think it matters since contentsChanged() is
- // connected to updateViews() anyway (see
- // kpMainWindow::setDocument ()).
- vm->updateViews (m_selection->boundingRect ());
-
- // There's no need to disconnect() the old selection since we:
- //
- // 1. Connect our _copy_ of the given selection.
- // 2. We delete our copy when setSelection() is called again.
- //
- // See code above for both.
- connect (m_selection, TQT_SIGNAL (changed (const TQRect &)),
- this, TQT_SLOT (slotContentsChanged (const TQRect &)));
-
-
- if (!hadSelection)
- emit selectionEnabled (true);
-
- if (isTextChanged)
- emit selectionIsTextChanged (selection.type () == kpSelection::Text);
-
- if (vm)
- vm->restoreQueueUpdates ();
- }
-
- // public
- TQPixmap kpDocument::getSelectedPixmap (const TQBitmap &maskBitmap_) const
- {
- kpSelection *sel = selection ();
-
- // must have a selection region
- if (!sel)
- {
- kdError () << "kpDocument::getSelectedPixmap() no sel region" << endl;
- return TQPixmap ();
- }
-
- // easy if we already have it :)
- if (sel->pixmap ())
- return *sel->pixmap ();
-
-
- const TQRect boundingRect = sel->boundingRect ();
- if (!boundingRect.isValid ())
- {
- kdError () << "kpDocument::getSelectedPixmap() boundingRect invalid" << endl;
- return TQPixmap ();
- }
-
-
- TQBitmap maskBitmap = maskBitmap_;
- if (maskBitmap.isNull () &&
- !sel->isRectangular ())
- {
- maskBitmap = sel->maskForOwnType ();
-
- if (maskBitmap.isNull ())
- {
- kdError () << "kpDocument::getSelectedPixmap() could not get mask" << endl;
- return TQPixmap ();
- }
- }
-
-
- TQPixmap selPixmap = getPixmapAt (boundingRect);
-
- if (!maskBitmap.isNull ())
- {
- // Src Dest = Result
- // -----------------
- // 0 0 0
- // 0 1 0
- // 1 0 0
- // 1 1 1
- TQBitmap selMaskBitmap = kpPixmapFX::getNonNullMask (selPixmap);
- bitBlt (&selMaskBitmap,
- TQPoint (0, 0),
- &maskBitmap,
- TQRect (0, 0, maskBitmap.width (), maskBitmap.height ()),
- TQt::AndROP);
- selPixmap.setMask (selMaskBitmap);
- }
-
- return selPixmap;
- }
-
- // public
- bool kpDocument::selectionPullFromDocument (const kpColor &backgroundColor)
- {
- kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
-
- kpSelection *sel = selection ();
-
- // must have a selection region
- if (!sel)
- {
- kdError () << "kpDocument::selectionPullFromDocument() no sel region" << endl;
- return false;
- }
-
- // should not already have a pixmap
- if (sel->pixmap ())
- {
- kdError () << "kpDocument::selectionPullFromDocument() already has pixmap" << endl;
- return false;
- }
-
- const TQRect boundingRect = sel->boundingRect ();
- if (!boundingRect.isValid ())
- {
- kdError () << "kpDocument::selectionPullFromDocument() boundingRect invalid" << endl;
- return false;
- }
-
-
- //
- // Figure out mask for non-rectangular selections
- //
-
- TQBitmap maskBitmap = sel->maskForOwnType (true/*return null bitmap for rectangular*/);
-
-
- //
- // Get selection pixmap from document
- //
-
- TQPixmap selPixmap = getSelectedPixmap (maskBitmap);
-
- if (vm)
- vm->setQueueUpdates ();
-
- sel->setPixmap (selPixmap);
-
-
- //
- // Fill opaque bits of the hole in the document
- //
-
- // TODO: this assumes backgroundColor == sel->transparency ().transparentColor()
- const TQPixmap selTransparentPixmap = sel->transparentPixmap ();
-
- if (backgroundColor.isOpaque ())
- {
- TQPixmap erasePixmap (boundingRect.width (), boundingRect.height ());
- erasePixmap.fill (backgroundColor.toTQColor ());
-
- if (selTransparentPixmap.mask ())
- erasePixmap.setMask (*selTransparentPixmap.mask ());
-
- paintPixmapAt (erasePixmap, boundingRect.topLeft ());
- }
- else
- {
- kpPixmapFX::paintMaskTransparentWithBrush (m_pixmap,
- boundingRect.topLeft (),
- kpPixmapFX::getNonNullMask (selTransparentPixmap));
- slotContentsChanged (boundingRect);
- }
-
- if (vm)
- vm->restoreQueueUpdates ();
-
- return true;
- }
-
- // public
- bool kpDocument::selectionDelete ()
- {
- kpSelection *sel = selection ();
-
- if (!sel)
- return false;
-
- const TQRect boundingRect = sel->boundingRect ();
- if (!boundingRect.isValid ())
- return false;
-
- bool selectionHadPixmap = m_selection ? (bool) m_selection->pixmap () : false;
-
- delete m_selection;
- m_selection = 0;
-
-
- // HACK to prevent document from being modified when
- // user cancels dragging out a new selection
- if (selectionHadPixmap)
- slotContentsChanged (boundingRect);
- else
- emit contentsChanged (boundingRect);
-
- emit selectionEnabled (false);
-
-
- updateToolsSingleKeyTriggersEnabled ();
-
- return true;
- }
-
- // public
- bool kpDocument::selectionCopyOntoDocument (bool useTransparentPixmap)
- {
- kpSelection *sel = selection ();
-
- // must have a pixmap already
- if (!sel)
- return false;
-
- // hasn't actually been lifted yet
- if (!sel->pixmap ())
- return true;
-
- const TQRect boundingRect = sel->boundingRect ();
- if (!boundingRect.isValid ())
- return false;
-
- if (!sel->isText ())
- {
- // We can't use kpSelection::paint() since that always uses the
- // transparent pixmap.
- paintPixmapAt (useTransparentPixmap ? sel->transparentPixmap () : sel->opaquePixmap (),
- boundingRect.topLeft ());
- }
- else
- {
- // (for antialiasing with background)
- sel->paint (m_pixmap, rect ());
- }
-
- slotContentsChanged (boundingRect);
-
- return true;
- }
-
- // public
- bool kpDocument::selectionPushOntoDocument (bool useTransparentPixmap)
- {
- return (selectionCopyOntoDocument (useTransparentPixmap) && selectionDelete ());
- }
-
- // public
- TQPixmap kpDocument::pixmapWithSelection () const
- {
- #if DEBUG_KP_DOCUMENT && 1
- kdDebug () << "kpDocument::pixmapWithSelection()" << endl;
- #endif
-
- // Have floating selection?
- if (m_selection && m_selection->pixmap ())
- {
- #if DEBUG_KP_DOCUMENT && 1
- kdDebug () << "\tselection @ " << m_selection->boundingRect () << endl;
- #endif
- TQPixmap output = *m_pixmap;
-
- m_selection->paint (&output, rect ());
-
- return output;
- }
- else
- {
- #if DEBUG_KP_DOCUMENT && 1
- kdDebug () << "\tno selection" << endl;
- #endif
- return *m_pixmap;
- }
- }
-
-
- /*
- * Transformations
- */
-
- void kpDocument::fill (const kpColor &color)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::fill ()" << endl;
- #endif
-
- kpPixmapFX::fill (m_pixmap, color);
- slotContentsChanged (m_pixmap->rect ());
- }
-
- void kpDocument::resize (int w, int h, const kpColor &backgroundColor, bool fillNewAreas)
- {
- #if DEBUG_KP_DOCUMENT
- kdDebug () << "kpDocument::resize (" << w << "," << h << "," << fillNewAreas << ")" << endl;
- #endif
-
- m_oldWidth = width (), m_oldHeight = height ();
-
- #if DEBUG_KP_DOCUMENT && 1
- kdDebug () << "\toldWidth=" << m_oldWidth
- << " oldHeight=" << m_oldHeight
- << endl;
- #endif
-
- if (w == m_oldWidth && h == m_oldHeight)
- return;
-
- kpPixmapFX::resize (m_pixmap, w, h, backgroundColor, fillNewAreas);
-
- slotSizeChanged (width (), height ());
- }
-
-
- /*
- * Slots
- */
-
- void kpDocument::slotContentsChanged (const TQRect &rect)
- {
- setModified ();
- emit contentsChanged (rect);
- }
-
- void kpDocument::slotSizeChanged (int newWidth, int newHeight)
- {
- setModified ();
- emit sizeChanged (newWidth, newHeight);
- emit sizeChanged (TQSize (newWidth, newHeight));
- }
-
- void kpDocument::slotSizeChanged (const TQSize &newSize)
- {
- slotSizeChanged (newSize.width (), newSize.height ());
- }
-
- #include <kpdocument.moc>
|