File indexing completed on 2024-04-28 04:20:14

0001 
0002 /*
0003    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 
0029 #define DEBUG_KP_DOCUMENT 0
0030 
0031 
0032 #include "kpDocument.h"
0033 #include "kpDocumentPrivate.h"
0034 
0035 
0036 #include <QImage>
0037 #include <QPainter>
0038 #include <QRect>
0039 
0040 #include "kpLogCategories.h"
0041 #include <KLocalizedString>
0042 
0043 #include "imagelib/kpColor.h"
0044 #include "kpDefs.h"
0045 #include "environments/document/kpDocumentEnvironment.h"
0046 #include "layers/selections/kpAbstractSelection.h"
0047 #include "layers/selections/image/kpAbstractImageSelection.h"
0048 #include "layers/selections/text/kpTextSelection.h"
0049 
0050 
0051 // public
0052 kpAbstractSelection *kpDocument::selection () const
0053 {
0054     return m_selection;
0055 }
0056 
0057 //---------------------------------------------------------------------
0058 
0059 // public
0060 kpAbstractImageSelection *kpDocument::imageSelection () const
0061 {
0062     return dynamic_cast <kpAbstractImageSelection *> (m_selection);
0063 }
0064 
0065 //---------------------------------------------------------------------
0066 
0067 // public
0068 kpTextSelection *kpDocument::textSelection () const
0069 {
0070     return dynamic_cast <kpTextSelection *> (m_selection);
0071 }
0072 
0073 //---------------------------------------------------------------------
0074 
0075 // public
0076 void kpDocument::setSelection (const kpAbstractSelection &selection)
0077 {
0078 #if DEBUG_KP_DOCUMENT && 1
0079     qCDebug(kpLogDocument) << "kpDocument::setSelection() sel boundingRect="
0080                << selection.boundingRect ();
0081 #endif
0082 
0083     d->environ->setQueueViewUpdates ();
0084     {
0085         const bool hadSelection = static_cast<bool> (m_selection);
0086         auto *oldSelection = m_selection;
0087 
0088 
0089         // (must be called before giving the document a new selection, to
0090         //  avoid a potential mess where switchToCompatibleTool() ends
0091         //  the current selection tool, killing the new selection)
0092         bool isTextChanged = false;
0093         d->environ->switchToCompatibleTool (selection, &isTextChanged);
0094         Q_ASSERT (m_selection == oldSelection);
0095 
0096 
0097         m_selection = selection.clone ();
0098 
0099         // There's no need to uninitialize the old selection
0100         // (e.g. call disconnect()) since we delete it later.
0101         connect (m_selection, &kpAbstractSelection::changed,
0102                  this, &kpDocument::slotContentsChanged);
0103 
0104 
0105         //
0106         // Now all kpDocument state has been set.
0107         // We can _only_ change the environment after that, as the environment
0108         // may access the document.  Exception is above with
0109         // switchToCompatibleTool().
0110         //
0111 
0112         d->environ->assertMatchingUIState (selection);
0113 
0114 
0115         //
0116         // Now all kpDocument and environment state has been set.
0117         // We can _only_ fire signals after that, as the signal receivers (the
0118         // "wider environment") may access the document and the environment.
0119         //
0120 
0121     #if DEBUG_KP_DOCUMENT && 1
0122         qCDebug(kpLogDocument) << "\tcheck sel " << (int *) m_selection
0123                    << " boundingRect=" << m_selection->boundingRect ();
0124     #endif
0125         if (oldSelection)
0126         {
0127             if (oldSelection->hasContent ()) {
0128                 slotContentsChanged (oldSelection->boundingRect ());
0129             }
0130             else {
0131                 Q_EMIT contentsChanged (oldSelection->boundingRect ());
0132             }
0133 
0134             delete oldSelection;
0135             oldSelection = nullptr;
0136         }
0137 
0138         if (m_selection->hasContent ()) {
0139             slotContentsChanged (m_selection->boundingRect ());
0140         }
0141         else {
0142             Q_EMIT contentsChanged (m_selection->boundingRect ());
0143         }
0144 
0145 
0146         if (!hadSelection) {
0147             Q_EMIT selectionEnabled (true);
0148         }
0149 
0150         if (isTextChanged) {
0151             Q_EMIT selectionIsTextChanged (textSelection ());
0152         }
0153     }
0154     d->environ->restoreQueueViewUpdates ();
0155 
0156 #if DEBUG_KP_DOCUMENT && 1
0157     qCDebug(kpLogDocument) << "\tkpDocument::setSelection() ended";
0158 #endif
0159 }
0160 
0161 //---------------------------------------------------------------------
0162 
0163 // public
0164 kpImage kpDocument::getSelectedBaseImage () const
0165 {
0166     auto *imageSel = imageSelection ();
0167     Q_ASSERT (imageSel);
0168 
0169     // Easy if we already have it :)
0170     const auto image = imageSel->baseImage ();
0171     if (!image.isNull ()) {
0172         return image;
0173     }
0174 
0175 
0176     const auto boundingRect = imageSel->boundingRect ();
0177     Q_ASSERT (boundingRect.isValid ());
0178 
0179     // OPT: This is very slow.  Image / More Effects ... calls us twice
0180     //      unnecessarily.
0181     return imageSel->givenImageMaskedByShape (getImageAt (boundingRect));
0182 }
0183 
0184 //---------------------------------------------------------------------
0185 
0186 // public
0187 void kpDocument::imageSelectionPullFromDocument (const kpColor &backgroundColor)
0188 {
0189     auto *imageSel = imageSelection ();
0190     Q_ASSERT (imageSel);
0191 
0192     // Should not already have an image or we would not be pulling.
0193     Q_ASSERT (!imageSel->hasContent ());
0194 
0195     const auto boundingRect = imageSel->boundingRect ();
0196     Q_ASSERT (boundingRect.isValid ());
0197 
0198     //
0199     // Get selection image from document
0200     //
0201 
0202     auto selectedImage = getSelectedBaseImage ();
0203 
0204     d->environ->setQueueViewUpdates ();
0205 
0206     imageSel->setBaseImage (selectedImage);
0207 
0208     //
0209     // Fill opaque bits of the hole in the document
0210     //
0211 
0212 #if !defined (QT_NO_DEBUG) && !defined (NDEBUG)
0213     if (imageSel->transparency ().isTransparent ())
0214     {
0215         Q_ASSERT (backgroundColor == imageSel->transparency ().transparentColor ());
0216     }
0217     else
0218     {
0219         // If this method is begin called by a tool, the assert does not
0220         // have to hold since transparentColor() might not be defined in Opaque
0221         // Mode.
0222         //
0223         // If we were called by a tricky sequence of undo/redo commands, the assert
0224         // does not have to hold for additional reason, which is that
0225         // kpMainWindow::setImageSelectionTransparency() does not have to
0226         // set <backgroundColor> in Opaque Mode.
0227         //
0228         // In practice, it probably does hold but I wouldn't bet on it.
0229     }
0230 #endif
0231 
0232     kpImage eraseImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
0233     eraseImage.fill(backgroundColor.toQRgb());
0234 
0235     // only paint the region of the shape of the selection
0236     QPainter painter(m_image);
0237     painter.setClipRegion(imageSel->shapeRegion());
0238     painter.setCompositionMode(QPainter::CompositionMode_Source);
0239     painter.drawImage(boundingRect.topLeft(), eraseImage);
0240     slotContentsChanged(boundingRect);
0241 
0242     d->environ->restoreQueueViewUpdates ();
0243 }
0244 
0245 //---------------------------------------------------------------------
0246 
0247 // public
0248 void kpDocument::selectionDelete ()
0249 {
0250     if ( !m_selection ) {
0251         return;
0252     }
0253 
0254     const auto boundingRect = m_selection->boundingRect ();
0255     Q_ASSERT (boundingRect.isValid ());
0256 
0257     const auto selectionHadContent = m_selection->hasContent ();
0258 
0259     delete m_selection;
0260     m_selection = nullptr;
0261 
0262 
0263     // HACK to prevent document from being modified when
0264     //      user cancels dragging out a new selection
0265     // REFACTOR: Extract this out into a method.
0266     if (selectionHadContent) {
0267         slotContentsChanged (boundingRect);
0268     }
0269     else {
0270         Q_EMIT contentsChanged (boundingRect);
0271     }
0272 
0273     Q_EMIT selectionEnabled (false);
0274 }
0275 
0276 //---------------------------------------------------------------------
0277 
0278 // public
0279 void kpDocument::selectionCopyOntoDocument (bool applySelTransparency)
0280 {
0281     // Empty selection, just doing nothing
0282     if ( !m_selection || !m_selection->hasContent() ) {
0283         return;
0284     }
0285 
0286     const QRect boundingRect = m_selection->boundingRect ();
0287     Q_ASSERT (boundingRect.isValid ());
0288 
0289     if (imageSelection ())
0290     {
0291         if (applySelTransparency) {
0292             imageSelection ()->paint (m_image, rect ());
0293         }
0294         else {
0295             imageSelection ()->paintWithBaseImage (m_image, rect ());
0296         }
0297     }
0298     else
0299     {
0300         // (for antialiasing with background)
0301         m_selection->paint (m_image, rect ());
0302     }
0303 
0304     slotContentsChanged (boundingRect);
0305 }
0306 
0307 //---------------------------------------------------------------------
0308 
0309 // public
0310 void kpDocument::selectionPushOntoDocument (bool applySelTransparency)
0311 {
0312     selectionCopyOntoDocument (applySelTransparency);
0313     selectionDelete ();
0314 }
0315 
0316 //---------------------------------------------------------------------
0317 
0318 // public
0319 kpImage kpDocument::imageWithSelection () const
0320 {
0321 #if DEBUG_KP_DOCUMENT && 1
0322     qCDebug(kpLogDocument) << "kpDocument::imageWithSelection()";
0323 #endif
0324 
0325     // Have selection?
0326     //
0327     // It need not have any content because e.g. a text box with an opaque
0328     // background, but no content, is still visually there.
0329     if (m_selection)
0330     {
0331     #if DEBUG_KP_DOCUMENT && 1
0332         qCDebug(kpLogDocument) << "\tselection @ " << m_selection->boundingRect ();
0333     #endif
0334         kpImage output = *m_image;
0335 
0336         // (this is a NOP for image selections without content)
0337         m_selection->paint (&output, rect ());
0338 
0339         return output;
0340     }
0341     else
0342     {
0343     #if DEBUG_KP_DOCUMENT && 1
0344         qCDebug(kpLogDocument) << "\tno selection";
0345     #endif
0346         return *m_image;
0347     }
0348 }
0349 
0350 //---------------------------------------------------------------------