/* Copyright (c) 2003-2007 Clarence Dang 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include // public kpAbstractSelection *kpDocument::selection () const { return m_selection; } //--------------------------------------------------------------------- // public kpAbstractImageSelection *kpDocument::imageSelection () const { return dynamic_cast (m_selection); } //--------------------------------------------------------------------- // public kpTextSelection *kpDocument::textSelection () const { return dynamic_cast (m_selection); } //--------------------------------------------------------------------- // public void kpDocument::setSelection (const kpAbstractSelection &selection) { #if DEBUG_KP_DOCUMENT && 1 kDebug () << "kpDocument::setSelection() sel boundingRect=" << selection.boundingRect () << endl; #endif d->environ->setQueueViewUpdates (); { const bool hadSelection = (bool) m_selection; kpAbstractSelection *oldSelection = m_selection; // (must be called before giving the document a new selection, to // avoid a potential mess where switchToCompatibleTool() ends // the current selection tool, killing the new selection) bool isTextChanged = false; d->environ->switchToCompatibleTool (selection, &isTextChanged); Q_ASSERT (m_selection == oldSelection); m_selection = selection.clone (); // There's no need to uninitialize the old selection // (e.g. call disconnect()) since we delete it later. connect (m_selection, SIGNAL (changed (const QRect &)), this, SLOT (slotContentsChanged (const QRect &))); // // Now all kpDocument state has been set. // We can _only_ change the environment after that, as the environment // may access the document. Exception is above with // switchToCompatibleTool(). // d->environ->assertMatchingUIState (selection); // // Now all kpDocument and environment state has been set. // We can _only_ fire signals after that, as the signal receivers (the // "wider environment") may access the document and the environment. // #if DEBUG_KP_DOCUMENT && 1 kDebug () << "\tcheck sel " << (int *) m_selection << " boundingRect=" << m_selection->boundingRect () << endl; #endif if (oldSelection) { if (oldSelection->hasContent ()) slotContentsChanged (oldSelection->boundingRect ()); else emit contentsChanged (oldSelection->boundingRect ()); delete oldSelection; oldSelection = 0; } if (m_selection->hasContent ()) slotContentsChanged (m_selection->boundingRect ()); else emit contentsChanged (m_selection->boundingRect ()); if (!hadSelection) emit selectionEnabled (true); if (isTextChanged) emit selectionIsTextChanged (textSelection ()); } d->environ->restoreQueueViewUpdates (); #if DEBUG_KP_DOCUMENT && 1 kDebug () << "\tkpDocument::setSelection() ended"; #endif } //--------------------------------------------------------------------- // public kpImage kpDocument::getSelectedBaseImage () const { kpAbstractImageSelection *imageSel = imageSelection (); Q_ASSERT (imageSel); // Easy if we already have it :) const kpImage image = imageSel->baseImage (); if (!image.isNull ()) return image; const QRect boundingRect = imageSel->boundingRect (); Q_ASSERT (boundingRect.isValid ()); // OPT: This is very slow. Image / More Effects ... calls us twice // unnecessarily. return imageSel->givenImageMaskedByShape (getImageAt (boundingRect)); } //--------------------------------------------------------------------- // public void kpDocument::imageSelectionPullFromDocument (const kpColor &backgroundColor) { kpAbstractImageSelection *imageSel = imageSelection (); Q_ASSERT (imageSel); // Should not already have an image or we would not be pulling. Q_ASSERT (!imageSel->hasContent ()); const QRect boundingRect = imageSel->boundingRect (); Q_ASSERT (boundingRect.isValid ()); // // Get selection image from document // kpImage selectedImage = getSelectedBaseImage (); d->environ->setQueueViewUpdates (); imageSel->setBaseImage (selectedImage); // // Fill opaque bits of the hole in the document // #if !defined (QT_NO_DEBUG) && !defined (NDEBUG) if (imageSel->transparency ().isTransparent ()) { Q_ASSERT (backgroundColor == imageSel->transparency ().transparentColor ()); } else { // If this method is begin called by a tool, the assert does not // have to hold since transparentColor() might not be defined in Opaque // Mode. // // If we were called by a tricky sequence of undo/redo commands, the assert // does not have to hold for additional reason, which is that // kpMainWindow::setImageSelectionTransparency() does not have to // set in Opaque Mode. // // In practice, it probably does hold but I wouldn't bet on it. } #endif kpImage eraseImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied); eraseImage.fill(backgroundColor.toQRgb()); // only paint the region of the shape of the selection QPainter painter(m_image); painter.setClipRegion(imageSel->shapeRegion()); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(boundingRect.topLeft(), eraseImage); slotContentsChanged(boundingRect); d->environ->restoreQueueViewUpdates (); } //--------------------------------------------------------------------- // public void kpDocument::selectionDelete () { if ( !m_selection ) return; const QRect boundingRect = m_selection->boundingRect (); Q_ASSERT (boundingRect.isValid ()); const bool selectionHadContent = m_selection->hasContent (); delete m_selection; m_selection = 0; // HACK to prevent document from being modified when // user cancels dragging out a new selection // REFACTOR: Extract this out into a method. if (selectionHadContent) slotContentsChanged (boundingRect); else emit contentsChanged (boundingRect); emit selectionEnabled (false); } //--------------------------------------------------------------------- // public void kpDocument::selectionCopyOntoDocument (bool applySelTransparency) { // Empty selection, just doing nothing if ( !m_selection || !m_selection->hasContent() ) return; const QRect boundingRect = m_selection->boundingRect (); Q_ASSERT (boundingRect.isValid ()); if (imageSelection ()) { if (applySelTransparency) imageSelection ()->paint (m_image, rect ()); else imageSelection ()->paintWithBaseImage (m_image, rect ()); } else { // (for antialiasing with background) m_selection->paint (m_image, rect ()); } slotContentsChanged (boundingRect); } //--------------------------------------------------------------------- // public void kpDocument::selectionPushOntoDocument (bool applySelTransparency) { selectionCopyOntoDocument (applySelTransparency); selectionDelete (); } //--------------------------------------------------------------------- // public kpImage kpDocument::imageWithSelection () const { #if DEBUG_KP_DOCUMENT && 1 kDebug () << "kpDocument::imageWithSelection()"; #endif // Have selection? // // It need not have any content because e.g. a text box with an opaque // background, but no content, is still visually there. if (m_selection) { #if DEBUG_KP_DOCUMENT && 1 kDebug () << "\tselection @ " << m_selection->boundingRect (); #endif kpImage output = *m_image; // (this is a NOP for image selections without content) m_selection->paint (&output, rect ()); return output; } else { #if DEBUG_KP_DOCUMENT && 1 kDebug () << "\tno selection"; #endif return *m_image; } } //---------------------------------------------------------------------