File indexing completed on 2024-12-22 04:13:13
0001 /* This file is part of the KDE project 0002 * Copyright 2010 (C) Boudewijn Rempt <boud@valdyas.org> 0003 * Copyright 2011 (C) Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 #include "kis_scratch_pad.h" 0008 0009 #include <QApplication> 0010 #include <QDesktopWidget> 0011 #include <QMutex> 0012 0013 #include <KoColorSpace.h> 0014 #include <KoColorProfile.h> 0015 #include <KoColorSpaceRegistry.h> 0016 #include <KoPointerEvent.h> 0017 #include <resources/KoAbstractGradient.h> 0018 0019 #include <kis_cursor.h> 0020 #include <kis_tool_utils.h> 0021 #include <kis_paint_layer.h> 0022 #include <kis_paint_device.h> 0023 #include <kis_gradient_painter.h> 0024 #include <kis_default_bounds.h> 0025 #include <kis_canvas_resource_provider.h> 0026 0027 #include "kis_config.h" 0028 #include "kis_image.h" 0029 #include "kis_undo_stores.h" 0030 #include "kis_update_scheduler.h" 0031 #include "kis_post_execution_undo_adapter.h" 0032 #include "kis_scratch_pad_event_filter.h" 0033 #include "kis_painting_information_builder.h" 0034 #include "kis_tool_freehand_helper.h" 0035 #include "kis_image_patch.h" 0036 #include "kis_canvas_widget_base.h" 0037 #include "kis_layer_projection_plane.h" 0038 #include "kis_node_graph_listener.h" 0039 #include "kis_transaction.h" 0040 0041 class KisScratchPadNodeListener : public KisNodeGraphListener 0042 { 0043 public: 0044 KisScratchPadNodeListener(KisScratchPad *scratchPad) 0045 : m_scratchPad(scratchPad) 0046 { 0047 } 0048 0049 void requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache) override { 0050 KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache); 0051 0052 QMutexLocker locker(&m_lock); 0053 0054 Q_FOREACH (const QRect &rc, rects) { 0055 m_scratchPad->imageUpdated(rc); 0056 } 0057 } 0058 0059 private: 0060 KisScratchPad *m_scratchPad; 0061 QMutex m_lock; 0062 }; 0063 0064 0065 class KisScratchPadDefaultBounds : public KisDefaultBounds 0066 { 0067 public: 0068 0069 KisScratchPadDefaultBounds(KisScratchPad *scratchPad) 0070 : m_scratchPad(scratchPad) 0071 { 0072 } 0073 0074 ~KisScratchPadDefaultBounds() override {} 0075 0076 QRect bounds() const override { 0077 return m_scratchPad->imageBounds(); 0078 } 0079 0080 void * sourceCookie() const override { 0081 return m_scratchPad; 0082 } 0083 0084 private: 0085 Q_DISABLE_COPY(KisScratchPadDefaultBounds) 0086 0087 KisScratchPad *m_scratchPad; 0088 }; 0089 0090 class KisScratchPadPaintingInformationBuilder : public KisPaintingInformationBuilder 0091 { 0092 Q_OBJECT 0093 0094 public: 0095 KisScratchPadPaintingInformationBuilder(KisScratchPad *scratchPad) 0096 : m_scratchPad(scratchPad) 0097 { 0098 } 0099 0100 protected: 0101 QPointF imageToView(const QPointF &point) override { 0102 return m_scratchPad->documentToWidget().map(point); 0103 } 0104 0105 private: 0106 const KisScratchPad *m_scratchPad; 0107 }; 0108 0109 0110 KisScratchPad::KisScratchPad(QWidget *parent) 0111 : QWidget(parent) 0112 , m_toolMode(HOVERING) 0113 , isModeManuallySet(false) 0114 , isMouseDown(false) 0115 , linkCanvasZoomLevel(true) 0116 , m_paintLayer(0) 0117 , m_displayProfile(0) 0118 , m_resourceProvider(0) 0119 { 0120 setAutoFillBackground(false); 0121 setMouseTracking(true); 0122 0123 m_cursor = KisCursor::load("tool_freehand_cursor.xpm", 2, 2); 0124 m_colorSamplerCursor = KisCursor::load("tool_color_sampler_cursor.xpm", 2, 2); 0125 setCursor(m_cursor); 0126 0127 0128 KisConfig cfg(true); 0129 QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize()); 0130 m_checkBrush = QBrush(checkImage); 0131 0132 0133 // We are not supposed to use updates here, 0134 // so just set the listener to null 0135 m_updateScheduler = new KisUpdateScheduler(0); 0136 m_undoStore = new KisSurrogateUndoStore(); 0137 m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler); 0138 m_nodeListener = new KisScratchPadNodeListener(this); 0139 0140 connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection); 0141 0142 // filter will be deleted by the QObject hierarchy 0143 m_eventFilter = new KisScratchPadEventFilter(this); 0144 0145 m_infoBuilder = new KisScratchPadPaintingInformationBuilder(this); 0146 0147 m_scaleBorderWidth = 1; 0148 } 0149 0150 KisScratchPad::~KisScratchPad() 0151 { 0152 delete m_infoBuilder; 0153 0154 delete m_undoAdapter; 0155 delete m_undoStore; 0156 delete m_updateScheduler; 0157 delete m_nodeListener; 0158 } 0159 0160 KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const 0161 { 0162 return 0163 button == Qt::NoButton ? HOVERING : 0164 button == Qt::MiddleButton ? PANNING : 0165 button == Qt::RightButton ? SAMPLING : 0166 PAINTING; 0167 } 0168 0169 void KisScratchPad::pointerPress(KoPointerEvent *event) 0170 { 0171 if(!isEnabled()) return; 0172 0173 if (isModeManuallySet == false) { 0174 m_toolMode = modeFromButton(event->button()); 0175 } 0176 0177 // see if we are pressing down with a button 0178 if (event->button() == Qt::LeftButton || 0179 event->button() == Qt::MiddleButton || 0180 event->button() == Qt::RightButton) { 0181 isMouseDown = true; 0182 } else { 0183 isMouseDown = false; 0184 } 0185 0186 // if mouse is down, we are doing one of three things 0187 if(isMouseDown) { 0188 if (m_toolMode == PAINTING) { 0189 beginStroke(event); 0190 event->accept(); 0191 } 0192 else if (m_toolMode == PANNING) { 0193 beginPan(event); 0194 event->accept(); 0195 } 0196 else if (m_toolMode == SAMPLING) { 0197 sample(event); 0198 event->accept(); 0199 } 0200 } 0201 0202 } 0203 0204 void KisScratchPad::pointerRelease(KoPointerEvent *event) 0205 { 0206 if(!isEnabled()) return; 0207 isMouseDown = false; 0208 0209 if (isModeManuallySet == false) { 0210 if (modeFromButton(event->button()) != m_toolMode) return; 0211 0212 if (m_toolMode == PAINTING) { 0213 endStroke(event); 0214 m_toolMode = HOVERING; 0215 event->accept(); 0216 } 0217 else if (m_toolMode == PANNING) { 0218 endPan(event); 0219 m_toolMode = HOVERING; 0220 event->accept(); 0221 } 0222 else if (m_toolMode == SAMPLING) { 0223 event->accept(); 0224 m_toolMode = HOVERING; 0225 } 0226 0227 } else { 0228 if (m_toolMode == PAINTING) { 0229 endStroke(event); 0230 } 0231 else if (m_toolMode == PANNING) { 0232 endPan(event); 0233 } 0234 0235 event->accept(); 0236 } 0237 0238 0239 } 0240 0241 void KisScratchPad::pointerMove(KoPointerEvent *event) 0242 { 0243 if(!isEnabled()) return; 0244 KIS_SAFE_ASSERT_RECOVER_RETURN(event); 0245 0246 if(event->point.isNull() == false) { 0247 m_helper->cursorMoved(documentToWidget().map(event->point)); 0248 } 0249 0250 0251 if (isMouseDown) { 0252 if (m_toolMode == PAINTING) { 0253 doStroke(event); 0254 event->accept(); 0255 } 0256 else if (m_toolMode == PANNING) { 0257 doPan(event); 0258 event->accept(); 0259 } 0260 else if (m_toolMode == SAMPLING) { 0261 sample(event); 0262 event->accept(); 0263 } 0264 } 0265 } 0266 0267 void KisScratchPad::beginStroke(KoPointerEvent *event) 0268 { 0269 0270 m_helper->initPaint(event, 0271 documentToWidget().map(event->point), 0272 0, 0273 0, 0274 m_updateScheduler, 0275 m_paintLayer, 0276 m_paintLayer->paintDevice()->defaultBounds()); 0277 0278 0279 } 0280 0281 void KisScratchPad::doStroke(KoPointerEvent *event) 0282 { 0283 m_helper->paintEvent(event); 0284 } 0285 0286 void KisScratchPad::endStroke(KoPointerEvent *event) 0287 { 0288 Q_UNUSED(event); 0289 m_helper->endPaint(); 0290 } 0291 0292 void KisScratchPad::beginPan(KoPointerEvent *event) 0293 { 0294 setCursor(QCursor(Qt::ClosedHandCursor)); 0295 m_panDocPoint = event->point; 0296 } 0297 0298 void KisScratchPad::doPan(KoPointerEvent *event) 0299 { 0300 QPointF docOffset = event->point - m_panDocPoint; 0301 0302 m_translateTransform.translate(-docOffset.x(), -docOffset.y()); 0303 updateTransformations(); 0304 update(); 0305 } 0306 0307 void KisScratchPad::endPan(KoPointerEvent *event) 0308 { 0309 Q_UNUSED(event); 0310 0311 // the normal brush editor scratchpad reverts back to paint mode when done 0312 if(isModeManuallySet) { 0313 setCursor(QCursor(Qt::OpenHandCursor)); 0314 } else { 0315 setCursor(m_cursor); 0316 } 0317 0318 } 0319 0320 void KisScratchPad::sample(KoPointerEvent *event) 0321 { 0322 KoColor color; 0323 if (KisToolUtils::sampleColor(color, m_paintLayer->projection(), event->point.toPoint())) { 0324 emit colorSelected(color); 0325 } 0326 } 0327 0328 void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY) 0329 { 0330 m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY)); 0331 0332 // the scratchpad will use the canvas zoom level...or not 0333 if(linkCanvasZoomLevel) { 0334 m_scaleTransform = QTransform::fromScale(scaleX, scaleY); 0335 } else { 0336 m_scaleTransform = QTransform::fromScale(1, 1); 0337 } 0338 0339 updateTransformations(); 0340 update(); 0341 } 0342 0343 QTransform KisScratchPad::documentToWidget() const 0344 { 0345 return m_translateTransform.inverted() * m_scaleTransform; 0346 } 0347 0348 QTransform KisScratchPad::widgetToDocument() const 0349 { 0350 return m_scaleTransform.inverted() * m_translateTransform; 0351 } 0352 0353 void KisScratchPad::updateTransformations() 0354 { 0355 m_eventFilter->setWidgetToDocumentTransform(widgetToDocument()); 0356 } 0357 0358 QRect KisScratchPad::imageBounds() const 0359 { 0360 return widgetToDocument().mapRect(rect()); 0361 } 0362 0363 void KisScratchPad::imageUpdated(const QRect &rect) 0364 { 0365 emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect()); 0366 } 0367 0368 void KisScratchPad::slotUpdateCanvas(const QRect &rect) 0369 { 0370 update(rect); 0371 } 0372 0373 void KisScratchPad::paintEvent ( QPaintEvent * event ) { 0374 if(!m_paintLayer) return; 0375 0376 QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect())); 0377 0378 QRect alignedImageRect = 0379 imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth, 0380 m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect(); 0381 0382 QPointF offset = alignedImageRect.topLeft(); 0383 0384 m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer); 0385 KisPaintDeviceSP projection = m_paintLayer->projection(); 0386 0387 0388 0389 QImage image = projection->convertToQImage(m_displayProfile, 0390 alignedImageRect.x(), 0391 alignedImageRect.y(), 0392 alignedImageRect.width(), 0393 alignedImageRect.height(), 0394 KoColorConversionTransformation::internalRenderingIntent(), 0395 KoColorConversionTransformation::internalConversionFlags()); 0396 0397 0398 QPainter gc(this); 0399 gc.fillRect(event->rect(), m_checkBrush); 0400 0401 // if we scale down, it should use Smooth 0402 // if we scale up, it should use Fast (nearest Neighbour) to show pixels 0403 if (event->rect().width() < image.rect().width()) { 0404 gc.setRenderHints(QPainter::SmoothPixmapTransform); 0405 } else { 0406 gc.setRenderHints(0); // that will use NN 0407 } 0408 0409 gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset)); 0410 0411 QBrush brush(Qt::lightGray); 0412 QPen pen(brush, 1, Qt::DotLine); 0413 gc.setPen(pen); 0414 if (m_cutoutOverlay.isValid()) { 0415 gc.drawRect(m_cutoutOverlay); 0416 } 0417 0418 if(!isEnabled()) { 0419 QColor color(Qt::lightGray); 0420 color.setAlphaF(0.5); 0421 QBrush disabledBrush(color); 0422 gc.fillRect(event->rect(), disabledBrush); 0423 } 0424 gc.end(); 0425 } 0426 0427 void KisScratchPad::resetState() 0428 { 0429 if (m_helper->isRunning()) { 0430 m_helper->endPaint(); 0431 } 0432 0433 m_toolMode = HOVERING; 0434 setCursor(m_cursor); 0435 } 0436 0437 void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider, 0438 const QColor &defaultColor) 0439 { 0440 m_resourceProvider = resourceProvider; 0441 KisConfig cfg(true); 0442 setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(QApplication::activeWindow()))); 0443 connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)), 0444 SLOT(setDisplayProfile(const KoColorProfile*))); 0445 0446 connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)), 0447 SLOT(setOnScreenResolution(qreal,qreal))); 0448 connect(this, SIGNAL(colorSelected(KoColor)), 0449 m_resourceProvider, SLOT(slotSetFGColor(KoColor))); 0450 0451 m_helper.reset(new KisToolFreehandHelper(m_infoBuilder, m_resourceProvider->resourceManager())); 0452 0453 setFillColor(defaultColor); 0454 0455 KisPaintDeviceSP paintDevice = 0456 new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad"); 0457 0458 m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice); 0459 m_paintLayer->setGraphListener(m_nodeListener); 0460 m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this)); 0461 0462 fillDefault(); 0463 } 0464 0465 void KisScratchPad::setCutoutOverlayRect(const QRect& rc) 0466 { 0467 m_cutoutOverlay = rc; 0468 } 0469 0470 void KisScratchPad::setModeManually(bool value) 0471 { 0472 isModeManuallySet = value; 0473 } 0474 0475 void KisScratchPad::setModeType(QString mode) 0476 { 0477 if (mode.toLower() == "painting") { 0478 m_toolMode = PAINTING; 0479 setCursor(m_cursor); 0480 } 0481 else if (mode.toLower() == "panning") { 0482 m_toolMode = PANNING; 0483 setCursor(Qt::OpenHandCursor); 0484 } 0485 else if (mode.toLower() == "colorsampling") { 0486 m_toolMode = SAMPLING; 0487 setCursor(m_colorSamplerCursor); 0488 } 0489 } 0490 0491 void KisScratchPad::linkCanvasToZoomLevel(bool value) 0492 { 0493 linkCanvasZoomLevel = value; 0494 } 0495 0496 QImage KisScratchPad::cutoutOverlay() const 0497 { 0498 if(!m_paintLayer) return QImage(); 0499 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0500 0501 0502 QRect rc = widgetToDocument().mapRect(m_cutoutOverlay); 0503 QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); 0504 0505 QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(), 0506 Qt::IgnoreAspectRatio, 0507 Qt::SmoothTransformation); 0508 0509 return scaledImage; 0510 } 0511 0512 void KisScratchPad::setPresetImage(const QImage& image) 0513 { 0514 m_presetImage = image; 0515 } 0516 0517 0518 void KisScratchPad::paintCustomImage(const QImage& loadedImage) 0519 { 0520 // this is 99% copied from the normal paintPresetImage() 0521 // we don't want to save over the preset image, so we don't 0522 // want to store it in the m_presetImage 0523 if(!m_paintLayer) return; 0524 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0525 0526 0527 QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay); 0528 QRect imageRect(QPoint(), overlayRect.size()); 0529 0530 QImage scaledImage = loadedImage.scaled(overlayRect.size(), 0531 Qt::IgnoreAspectRatio, 0532 Qt::SmoothTransformation); 0533 KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace()); 0534 device->convertFromQImage(scaledImage, 0); 0535 0536 KisPainter painter(paintDevice); 0537 painter.beginTransaction(); 0538 painter.bitBlt(overlayRect.topLeft(), device, imageRect); 0539 painter.deleteTransaction(); 0540 update(); 0541 } 0542 0543 void KisScratchPad::loadScratchpadImage(QImage image) 0544 { 0545 if(!m_paintLayer) return; 0546 0547 m_translateTransform.reset(); // image will be loaded at 0,0, so reset panning location 0548 updateTransformations(); 0549 0550 fillDefault(); // wipes out whatever was there before 0551 0552 QRect imageSize = image.rect(); 0553 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0554 0555 KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace()); 0556 device->convertFromQImage(image, 0); 0557 0558 KisPainter painter(paintDevice); 0559 painter.beginTransaction(); 0560 painter.bitBlt(imageSize.topLeft(), device, imageSize); 0561 painter.deleteTransaction(); 0562 update(); 0563 } 0564 0565 QImage KisScratchPad::copyScratchpadImageData() 0566 { 0567 const QRect paintingBounds = m_paintLayer.data()->exactBounds(); 0568 QImage imageData = m_paintLayer->paintDevice()->convertToQImage(0, paintingBounds.x(), paintingBounds.y(), paintingBounds.width(), paintingBounds.height(), 0569 KoColorConversionTransformation::internalRenderingIntent(), 0570 KoColorConversionTransformation::internalConversionFlags()); 0571 return imageData; 0572 } 0573 0574 void KisScratchPad::paintPresetImage() 0575 { 0576 if(!m_paintLayer) return; 0577 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0578 0579 0580 QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay); 0581 QRect imageRect(QPoint(), overlayRect.size()); 0582 0583 QImage scaledImage = m_presetImage.scaled(overlayRect.size(), 0584 Qt::IgnoreAspectRatio, 0585 Qt::SmoothTransformation); 0586 KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace()); 0587 device->convertFromQImage(scaledImage, 0); 0588 0589 KisPainter painter(paintDevice); 0590 painter.beginTransaction(); 0591 painter.bitBlt(overlayRect.topLeft(), device, imageRect); 0592 painter.deleteTransaction(); 0593 update(); 0594 } 0595 0596 void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile) 0597 { 0598 if (colorProfile) { 0599 m_displayProfile = colorProfile; 0600 QWidget::update(); 0601 } 0602 } 0603 0604 void KisScratchPad::fillDefault() 0605 { 0606 if(!m_paintLayer) return; 0607 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0608 0609 KisTransaction t(paintDevice); 0610 paintDevice->setDefaultPixel(m_defaultColor); 0611 paintDevice->clear(); 0612 t.end(); 0613 update(); 0614 } 0615 0616 void KisScratchPad::fillTransparent() { 0617 if(!m_paintLayer) return; 0618 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0619 0620 QColor transQColor(0,0,0,0); 0621 KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8()); 0622 transparentColor.setOpacity(0.0); 0623 0624 KisTransaction t(paintDevice); 0625 paintDevice->setDefaultPixel(transparentColor); 0626 paintDevice->clear(); 0627 t.end(); 0628 update(); 0629 } 0630 0631 void KisScratchPad::setFillColor(QColor newColor) 0632 { 0633 m_defaultColor = KoColor(newColor, KoColorSpaceRegistry::instance()->rgb8()); 0634 } 0635 0636 void KisScratchPad::fillGradient() 0637 { 0638 if(!m_paintLayer) return; 0639 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0640 0641 KoAbstractGradientSP gradient = m_resourceProvider->currentGradient(); 0642 QRect gradientRect = widgetToDocument().mapRect(rect()); 0643 0644 KisTransaction t(paintDevice); 0645 0646 paintDevice->clear(); 0647 0648 KisGradientPainter painter(paintDevice); 0649 painter.setGradient(gradient); 0650 painter.setGradientShape(KisGradientPainter::GradientShapeLinear); 0651 painter.paintGradient(gradientRect.topLeft(), 0652 gradientRect.bottomRight(), 0653 KisGradientPainter::GradientRepeatNone, 0654 0.2, false, 0655 gradientRect.left(), gradientRect.top(), 0656 gradientRect.width(), gradientRect.height()); 0657 0658 t.end(); 0659 update(); 0660 } 0661 0662 void KisScratchPad::fillBackground() 0663 { 0664 if(!m_paintLayer) return; 0665 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0666 0667 KisTransaction t(paintDevice); 0668 paintDevice->setDefaultPixel(m_resourceProvider->bgColor()); 0669 paintDevice->clear(); 0670 t.end(); 0671 update(); 0672 } 0673 0674 void KisScratchPad::fillLayer() 0675 { 0676 if(!m_paintLayer) return; 0677 KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); 0678 0679 QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height()); 0680 0681 KisPainter painter(paintDevice); 0682 painter.beginTransaction(); 0683 painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect); 0684 painter.deleteTransaction(); 0685 update(); 0686 } 0687 0688 #include "kis_scratch_pad.moc"