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 //---------------------------------------------------------------------