File indexing completed on 2024-05-12 16:01:56
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KisView.h" 0009 0010 #include "KisView_p.h" 0011 0012 #include <KoDockFactoryBase.h> 0013 #include <KoDockRegistry.h> 0014 #include <KoDocumentInfo.h> 0015 #include <KoToolManager.h> 0016 0017 #include <kis_icon.h> 0018 0019 #include <kactioncollection.h> 0020 #include <klocalizedstring.h> 0021 #include <kis_debug.h> 0022 #include <kselectaction.h> 0023 #include <kconfiggroup.h> 0024 0025 #include <QMenu> 0026 #include <QMessageBox> 0027 #include <QUrl> 0028 #include <QTemporaryFile> 0029 #include <QApplication> 0030 #include <QDesktopWidget> 0031 #include <QDockWidget> 0032 #include <QDragEnterEvent> 0033 #include <QDropEvent> 0034 #include <QImage> 0035 #include <QList> 0036 #include <QPrintDialog> 0037 #include <QToolBar> 0038 #include <QStatusBar> 0039 #include <QMoveEvent> 0040 #include <QMdiSubWindow> 0041 #include <QFileInfo> 0042 0043 #include <kis_image.h> 0044 #include <kis_node.h> 0045 0046 #include <kis_group_layer.h> 0047 #include <kis_layer.h> 0048 #include <kis_mask.h> 0049 #include <kis_selection.h> 0050 0051 #include "KisDocument.h" 0052 #include "KisImageSignals.h" 0053 #include "KisImportExportManager.h" 0054 #include "KisMainWindow.h" 0055 #include "KisMimeDatabase.h" 0056 #include "KisPart.h" 0057 #include "KisReferenceImagesDecoration.h" 0058 #include "KisRemoteFileFetcher.h" 0059 #include "KisSynchronizedConnection.h" 0060 #include "KisViewManager.h" 0061 #include "dialogs/kis_dlg_paste_format.h" 0062 #include "input/kis_input_manager.h" 0063 #include "kis_canvas2.h" 0064 #include "kis_canvas_controller.h" 0065 #include "kis_canvas_resource_provider.h" 0066 #include "kis_clipboard.h" 0067 #include "kis_config.h" 0068 #include "kis_file_layer.h" 0069 #include "kis_fill_painter.h" 0070 #include "kis_filter_manager.h" 0071 #include "kis_image_manager.h" 0072 #include "kis_import_catcher.h" 0073 #include "kis_mimedata.h" 0074 #include "kis_mirror_axis.h" 0075 #include "kis_node_commands_adapter.h" 0076 #include "kis_node_manager.h" 0077 #include "kis_paint_layer.h" 0078 #include "kis_painting_assistants_decoration.h" 0079 #include "kis_processing_applicator.h" 0080 #include "kis_progress_widget.h" 0081 #include "kis_resources_snapshot.h" 0082 #include "kis_selection_manager.h" 0083 #include "kis_shape_controller.h" 0084 #include "kis_signal_compressor.h" 0085 #include "kis_statusbar.h" 0086 #include "kis_tool_freehand.h" 0087 #include "kis_zoom_manager.h" 0088 #include "krita_utils.h" 0089 #include "processing/fill_processing_visitor.h" 0090 #include "widgets/kis_canvas_drop.h" 0091 0092 //static 0093 QString KisView::newObjectName() 0094 { 0095 static int s_viewIFNumber = 0; 0096 QString name; name.setNum(s_viewIFNumber++); name.prepend("view_"); 0097 return name; 0098 } 0099 0100 bool KisView::s_firstView = true; 0101 0102 class Q_DECL_HIDDEN KisView::Private 0103 { 0104 public: 0105 Private(KisView *_q, 0106 KisDocument *document, 0107 KisViewManager *viewManager) 0108 : actionCollection(viewManager->actionCollection()) 0109 , viewConverter() 0110 , canvasController(_q, viewManager->mainWindow(), viewManager->actionCollection()) 0111 , canvas(&viewConverter, viewManager->canvasResourceProvider()->resourceManager(), viewManager->mainWindow(), _q, document->shapeController()) 0112 , zoomManager(_q, &this->viewConverter, &this->canvasController) 0113 , viewManager(viewManager) 0114 , floatingMessageCompressor(100, KisSignalCompressor::POSTPONE) 0115 { 0116 } 0117 0118 bool inOperation {false}; //in the middle of an operation (no screen refreshing)? 0119 0120 QPointer<KisDocument> document; // our KisDocument 0121 QWidget *tempActiveWidget {nullptr}; 0122 0123 KisKActionCollection* actionCollection {nullptr}; 0124 KisCoordinatesConverter viewConverter; 0125 KisCanvasController canvasController; 0126 KisCanvas2 canvas; 0127 KisZoomManager zoomManager; 0128 KisViewManager *viewManager {nullptr}; 0129 KisNodeSP currentNode; 0130 KisPaintingAssistantsDecorationSP paintingAssistantsDecoration; 0131 KisReferenceImagesDecorationSP referenceImagesDecoration; 0132 bool isCurrent {false}; 0133 bool showFloatingMessage {true}; 0134 QPointer<KisFloatingMessage> savedFloatingMessage; 0135 KisSignalCompressor floatingMessageCompressor; 0136 QMdiSubWindow *subWindow {nullptr}; 0137 0138 bool softProofing {false}; 0139 bool gamutCheck {false}; 0140 0141 KisSynchronizedConnection<KisNodeSP> addNodeConnection; 0142 KisSynchronizedConnection<KisNodeSP> removeNodeConnection; 0143 0144 // Hmm sorry for polluting the private class with such a big inner class. 0145 // At the beginning it was a little struct :) 0146 class StatusBarItem 0147 { 0148 public: 0149 0150 StatusBarItem(QWidget * widget, int stretch, bool permanent) 0151 : m_widget(widget), 0152 m_stretch(stretch), 0153 m_permanent(permanent), 0154 m_connected(false), 0155 m_hidden(false) {} 0156 0157 bool operator==(const StatusBarItem& rhs) { 0158 return m_widget == rhs.m_widget; 0159 } 0160 0161 bool operator!=(const StatusBarItem& rhs) { 0162 return m_widget != rhs.m_widget; 0163 } 0164 0165 QWidget * widget() const { 0166 return m_widget; 0167 } 0168 0169 void ensureItemShown(QStatusBar * sb) { 0170 Q_ASSERT(m_widget); 0171 if (!m_connected) { 0172 if (m_permanent) 0173 sb->addPermanentWidget(m_widget, m_stretch); 0174 else 0175 sb->addWidget(m_widget, m_stretch); 0176 0177 if(!m_hidden) 0178 m_widget->show(); 0179 0180 m_connected = true; 0181 } 0182 } 0183 void ensureItemHidden(QStatusBar * sb) { 0184 if (m_connected) { 0185 m_hidden = m_widget->isHidden(); 0186 sb->removeWidget(m_widget); 0187 m_widget->hide(); 0188 m_connected = false; 0189 } 0190 } 0191 private: 0192 QWidget * m_widget = 0; 0193 int m_stretch; 0194 bool m_permanent; 0195 bool m_connected = false; 0196 bool m_hidden = false; 0197 0198 }; 0199 0200 }; 0201 0202 KisView::KisView(KisDocument *document, KisViewManager *viewManager, QWidget *parent) 0203 : QWidget(parent) 0204 , d(new Private(this, document, viewManager)) 0205 { 0206 Q_ASSERT(document); 0207 connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool))); 0208 setObjectName(newObjectName()); 0209 0210 d->document = document; 0211 0212 setFocusPolicy(Qt::StrongFocus); 0213 0214 QStatusBar * sb = statusBar(); 0215 if (sb) { // No statusbar in e.g. konqueror 0216 connect(d->document, SIGNAL(statusBarMessage(QString,int)), 0217 this, SLOT(slotSavingStatusMessage(QString,int))); 0218 connect(d->document, SIGNAL(clearStatusBarMessage()), 0219 this, SLOT(slotClearStatusText())); 0220 } 0221 0222 d->canvas.setup(); 0223 0224 KisConfig cfg(false); 0225 0226 d->canvasController.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0227 d->canvasController.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0228 d->canvasController.setVastScrolling(cfg.vastScrolling()); 0229 d->canvasController.setCanvas(&d->canvas); 0230 0231 d->zoomManager.setup(d->actionCollection); 0232 0233 0234 connect(&d->canvasController, SIGNAL(documentSizeChanged()), &d->zoomManager, SLOT(slotScrollAreaSizeChanged())); 0235 setAcceptDrops(true); 0236 0237 connect(d->document, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); 0238 connect(d->document, SIGNAL(sigSavingFinished(QString)), this, SLOT(slotSavingFinished())); 0239 0240 d->referenceImagesDecoration = new KisReferenceImagesDecoration(this, document, /* viewReady = */ false); 0241 d->canvas.addDecoration(d->referenceImagesDecoration); 0242 d->referenceImagesDecoration->setVisible(true); 0243 0244 d->paintingAssistantsDecoration = new KisPaintingAssistantsDecoration(this); 0245 d->canvas.addDecoration(d->paintingAssistantsDecoration); 0246 d->paintingAssistantsDecoration->setVisible(true); 0247 0248 d->showFloatingMessage = cfg.showCanvasMessages(); 0249 d->zoomManager.updateScreenResolution(this); 0250 } 0251 0252 KisView::~KisView() 0253 { 0254 if (d->viewManager) { 0255 if (d->viewManager->filterManager()->isStrokeRunning()) { 0256 d->viewManager->filterManager()->cancelDialog(); 0257 } 0258 0259 d->viewManager->mainWindow()->notifyChildViewDestroyed(this); 0260 } 0261 0262 image()->requestStrokeCancellation(); 0263 0264 /** 0265 * KisCanvas2 maintains direct connections to the image, so we should 0266 * disconnect it from the image before the destruction process starts 0267 */ 0268 d->canvas.disconnectImage(); 0269 0270 KoToolManager::instance()->removeCanvasController(&d->canvasController); 0271 d->canvasController.setCanvas(0); 0272 0273 KisPart::instance()->removeView(this); 0274 delete d; 0275 } 0276 0277 void KisView::notifyCurrentStateChanged(bool isCurrent) 0278 { 0279 d->isCurrent = isCurrent; 0280 0281 if (!d->isCurrent && d->savedFloatingMessage) { 0282 d->savedFloatingMessage->removeMessage(); 0283 } 0284 0285 KisInputManager *inputManager = globalInputManager(); 0286 if (d->isCurrent) { 0287 inputManager->attachPriorityEventFilter(&d->canvasController); 0288 } else { 0289 inputManager->detachPriorityEventFilter(&d->canvasController); 0290 } 0291 0292 /** 0293 * When current view is changed, currently selected node is also changed, 0294 * therefore we should update selection overlay mask 0295 */ 0296 viewManager()->selectionManager()->selectionChanged(); 0297 } 0298 0299 bool KisView::isCurrent() const 0300 { 0301 return d->isCurrent; 0302 } 0303 0304 void KisView::setShowFloatingMessage(bool show) 0305 { 0306 d->showFloatingMessage = show; 0307 } 0308 0309 void KisView::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) 0310 { 0311 if (!d->viewManager) return; 0312 0313 if(d->isCurrent && d->showFloatingMessage && d->viewManager->qtMainWindow()) { 0314 if (d->savedFloatingMessage) { 0315 d->savedFloatingMessage->tryOverrideMessage(message, icon, timeout, priority, alignment); 0316 } else { 0317 d->savedFloatingMessage = new KisFloatingMessage(message, this->canvasBase()->canvasWidget(), false, timeout, priority, alignment); 0318 d->savedFloatingMessage->setShowOverParent(true); 0319 d->savedFloatingMessage->setIcon(icon); 0320 0321 connect(&d->floatingMessageCompressor, SIGNAL(timeout()), d->savedFloatingMessage, SLOT(showMessage())); 0322 d->floatingMessageCompressor.start(); 0323 } 0324 } 0325 } 0326 0327 bool KisView::canvasIsMirrored() const 0328 { 0329 return d->canvas.xAxisMirrored() || d->canvas.yAxisMirrored(); 0330 } 0331 0332 void KisView::setViewManager(KisViewManager *view) 0333 { 0334 d->viewManager = view; 0335 0336 KoToolManager::instance()->addController(&d->canvasController); 0337 KoToolManager::instance()->registerToolActions(d->actionCollection, &d->canvasController); 0338 dynamic_cast<KisShapeController*>(d->document->shapeController())->setInitialShapeForCanvas(&d->canvas); 0339 0340 if (resourceProvider()) { 0341 resourceProvider()->slotImageSizeChanged(); 0342 } 0343 0344 if (d->viewManager && d->viewManager->nodeManager()) { 0345 d->viewManager->nodeManager()->nodesUpdated(); 0346 } 0347 0348 connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(slotImageSizeChanged(QPointF,QPointF))); 0349 connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged())); 0350 0351 0352 d->addNodeConnection.connectSync(image(), &KisImage::sigNodeAddedAsync, 0353 this, &KisView::slotContinueAddNode); 0354 0355 // executed in a context of an image thread 0356 connect(image(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), 0357 SLOT(slotImageNodeRemoved(KisNodeSP)), 0358 Qt::DirectConnection); 0359 0360 d->removeNodeConnection.connectOutputSlot(this, &KisView::slotContinueRemoveNode); 0361 0362 d->viewManager->updateGUI(); 0363 0364 KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); 0365 } 0366 0367 KisViewManager* KisView::viewManager() const 0368 { 0369 return d->viewManager; 0370 } 0371 0372 void KisView::slotContinueAddNode(KisNodeSP newActiveNode) 0373 { 0374 /** 0375 * When deleting the last layer, root node got selected. We should 0376 * fix it when the first layer is added back. 0377 * 0378 * Here we basically reimplement what Qt's view/model do. But 0379 * since they are not connected, we should do it manually. 0380 */ 0381 0382 if (!d->isCurrent && 0383 (!d->currentNode || !d->currentNode->parent())) { 0384 0385 d->currentNode = newActiveNode; 0386 } 0387 } 0388 0389 0390 void KisView::slotImageNodeRemoved(KisNodeSP node) 0391 { 0392 d->removeNodeConnection.start(KritaUtils::nearestNodeAfterRemoval(node)); 0393 } 0394 0395 void KisView::slotContinueRemoveNode(KisNodeSP newActiveNode) 0396 { 0397 if (!d->isCurrent) { 0398 d->currentNode = newActiveNode; 0399 } 0400 } 0401 0402 KoZoomController *KisView::zoomController() const 0403 { 0404 return d->zoomManager.zoomController(); 0405 } 0406 0407 KisZoomManager *KisView::zoomManager() const 0408 { 0409 return &d->zoomManager; 0410 } 0411 0412 KisCanvasController *KisView::canvasController() const 0413 { 0414 return &d->canvasController; 0415 } 0416 0417 KisCanvasResourceProvider *KisView::resourceProvider() const 0418 { 0419 if (d->viewManager) { 0420 return d->viewManager->canvasResourceProvider(); 0421 } 0422 return 0; 0423 } 0424 0425 KisInputManager* KisView::globalInputManager() const 0426 { 0427 return d->viewManager ? d->viewManager->inputManager() : 0; 0428 } 0429 0430 KisCanvas2 *KisView::canvasBase() const 0431 { 0432 return &d->canvas; 0433 } 0434 0435 KisImageWSP KisView::image() const 0436 { 0437 if (d->document) { 0438 return d->document->image(); 0439 } 0440 return 0; 0441 } 0442 0443 0444 KisCoordinatesConverter *KisView::viewConverter() const 0445 { 0446 return &d->viewConverter; 0447 } 0448 0449 void KisView::dragEnterEvent(QDragEnterEvent *event) 0450 { 0451 dbgUI << Q_FUNC_INFO 0452 << "Formats: " << event->mimeData()->formats() 0453 << "Urls: " << event->mimeData()->urls() 0454 << "Has images: " << event->mimeData()->hasImage(); 0455 if (event->mimeData()->hasImage() 0456 || event->mimeData()->hasUrls() 0457 || event->mimeData()->hasFormat("application/x-krita-node-internal-pointer") 0458 || event->mimeData()->hasFormat("krita/x-colorsetentry") 0459 || event->mimeData()->hasColor()) { 0460 event->accept(); 0461 0462 // activate view if it should accept the drop 0463 this->setFocus(); 0464 } else { 0465 event->ignore(); 0466 } 0467 } 0468 0469 void KisView::dropEvent(QDropEvent *event) 0470 { 0471 KisImageWSP kisimage = image(); 0472 Q_ASSERT(kisimage); 0473 0474 QPoint imgCursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint(); 0475 QRect imageBounds = kisimage->bounds(); 0476 boost::optional<QPoint> forcedCenter; 0477 0478 if (event->keyboardModifiers() & Qt::ShiftModifier && imageBounds.contains(imgCursorPos)) { 0479 forcedCenter = imgCursorPos; 0480 } 0481 0482 dbgUI << Q_FUNC_INFO; 0483 dbgUI << "\t Formats: " << event->mimeData()->formats(); 0484 dbgUI << "\t Urls: " << event->mimeData()->urls(); 0485 dbgUI << "\t Has images: " << event->mimeData()->hasImage(); 0486 0487 if (event->mimeData()->hasFormat("application/x-krita-node-internal-pointer")) { 0488 KisShapeController *kritaShapeController = 0489 dynamic_cast<KisShapeController*>(d->document->shapeController()); 0490 0491 bool copyNode = true; 0492 QList<KisNodeSP> nodes; 0493 0494 if (forcedCenter) { 0495 nodes = KisMimeData::loadNodesFastAndRecenter(*forcedCenter, event->mimeData(), kisimage, kritaShapeController, copyNode); 0496 } else { 0497 nodes = KisMimeData::loadNodesFast(event->mimeData(), kisimage, kritaShapeController, copyNode); 0498 } 0499 0500 Q_FOREACH (KisNodeSP node, nodes) { 0501 if (node) { 0502 KisNodeCommandsAdapter adapter(viewManager()); 0503 if (!viewManager()->nodeManager()->activeLayer()) { 0504 adapter.addNode(node, kisimage->rootLayer() , 0); 0505 } else { 0506 adapter.addNode(node, 0507 viewManager()->nodeManager()->activeLayer()->parent(), 0508 viewManager()->nodeManager()->activeLayer()); 0509 } 0510 } 0511 } 0512 } else if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) { 0513 const auto *data = event->mimeData(); 0514 0515 KisCanvasDrop dlgAction; 0516 0517 const auto callPos = QCursor::pos(); 0518 0519 const KisCanvasDrop::Action action = dlgAction.dropAs(*data, callPos); 0520 0521 if (action == KisCanvasDrop::INSERT_AS_NEW_LAYER) { 0522 const QPair<bool, KisClipboard::PasteFormatBehaviour> source = 0523 KisClipboard::instance()->askUserForSource(data); 0524 0525 if (!source.first) { 0526 dbgUI << "Paste event cancelled"; 0527 return; 0528 } 0529 0530 if (source.second != KisClipboard::PASTE_FORMAT_CLIP) { 0531 const QList<QUrl> &urls = data->urls(); 0532 const auto url = std::find_if( 0533 urls.constBegin(), 0534 urls.constEnd(), 0535 [&](const QUrl &url) { 0536 if (source.second 0537 == KisClipboard::PASTE_FORMAT_DOWNLOAD) { 0538 return !url.isLocalFile(); 0539 } else if (source.second 0540 == KisClipboard::PASTE_FORMAT_LOCAL) { 0541 return url.isLocalFile(); 0542 } else { 0543 return false; 0544 } 0545 }); 0546 0547 if (url != urls.constEnd()) { 0548 QScopedPointer<QTemporaryFile> tmp(new QTemporaryFile()); 0549 tmp->setAutoRemove(true); 0550 0551 const QUrl localUrl = [&]() -> QUrl { 0552 if (!url->isLocalFile()) { 0553 // download the file and substitute the url 0554 KisRemoteFileFetcher fetcher; 0555 tmp->setFileName(url->fileName()); 0556 0557 if (!fetcher.fetchFile(*url, tmp.data())) { 0558 warnUI << "Fetching" << *url << "failed"; 0559 return {}; 0560 } 0561 return QUrl::fromLocalFile(tmp->fileName()); 0562 } 0563 return *url; 0564 }(); 0565 0566 if (localUrl.isLocalFile()) { 0567 this->mainWindow() 0568 ->viewManager() 0569 ->imageManager() 0570 ->importImage(localUrl); 0571 this->activateWindow(); 0572 return; 0573 } 0574 } 0575 } 0576 0577 KisPaintDeviceSP clip = 0578 KisClipboard::instance()->clipFromBoardContents(data, 0579 QRect(), 0580 true, 0581 -1, 0582 false, 0583 source); 0584 if (clip) { 0585 const auto pos = this->viewConverter() 0586 ->imageToDocument(imgCursorPos) 0587 .toPoint(); 0588 0589 clip->moveTo(pos.x(), pos.y()); 0590 0591 KisImportCatcher::adaptClipToImageColorSpace(clip, 0592 this->image()); 0593 0594 KisPaintLayerSP layer = new KisPaintLayer( 0595 this->image(), 0596 this->image()->nextLayerName() + " " + i18n("(pasted)"), 0597 OPACITY_OPAQUE_U8, 0598 clip); 0599 KisNodeCommandsAdapter adapter( 0600 this->mainWindow()->viewManager()); 0601 adapter.addNode( 0602 layer, 0603 this->mainWindow()->viewManager()->activeNode()->parent(), 0604 this->mainWindow()->viewManager()->activeNode()); 0605 this->activateWindow(); 0606 return; 0607 } 0608 } else if (action == KisCanvasDrop::INSERT_AS_REFERENCE_IMAGE) { 0609 KisPaintDeviceSP clip = 0610 KisClipboard::instance()->clipFromMimeData(data, QRect(), true); 0611 if (clip) { 0612 KisImportCatcher::adaptClipToImageColorSpace(clip, 0613 this->image()); 0614 0615 auto *reference = 0616 KisReferenceImage::fromPaintDevice(clip, 0617 *this->viewConverter(), 0618 this); 0619 0620 if (reference) { 0621 const auto pos = this->canvasBase() 0622 ->coordinatesConverter() 0623 ->widgetToImage(event->pos()); 0624 reference->setPosition( 0625 (*this->viewConverter()).imageToDocument(pos)); 0626 this->canvasBase() 0627 ->referenceImagesDecoration() 0628 ->addReferenceImage(reference); 0629 KoToolManager::instance()->switchToolRequested( 0630 "ToolReferenceImages"); 0631 return; 0632 } 0633 } 0634 } else if (action != KisCanvasDrop::NONE) { 0635 // multiple URLs detected OR about to open a document 0636 0637 for (QUrl url : data->urls()) { // do copy it 0638 QScopedPointer<QTemporaryFile> tmp(new QTemporaryFile()); 0639 tmp->setAutoRemove(true); 0640 0641 if (!url.isLocalFile()) { 0642 // download the file and substitute the url 0643 KisRemoteFileFetcher fetcher; 0644 tmp->setFileName(url.fileName()); 0645 0646 if (!fetcher.fetchFile(url, tmp.data())) { 0647 qWarning() << "Fetching" << url << "failed"; 0648 continue; 0649 } 0650 url = QUrl::fromLocalFile(tmp->fileName()); 0651 } 0652 0653 if (url.isLocalFile()) { 0654 if (action == KisCanvasDrop::INSERT_MANY_LAYERS) { 0655 this->mainWindow() 0656 ->viewManager() 0657 ->imageManager() 0658 ->importImage(url); 0659 this->activateWindow(); 0660 } else if (action == KisCanvasDrop::INSERT_MANY_FILE_LAYERS 0661 || action 0662 == KisCanvasDrop::INSERT_AS_NEW_FILE_LAYER) { 0663 KisNodeCommandsAdapter adapter( 0664 this->mainWindow()->viewManager()); 0665 QFileInfo fileInfo(url.toLocalFile()); 0666 0667 QString type = 0668 KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); 0669 QStringList mimes = 0670 KisImportExportManager::supportedMimeTypes( 0671 KisImportExportManager::Import); 0672 0673 if (!mimes.contains(type)) { 0674 QString msg = 0675 KisImportExportErrorCode( 0676 ImportExportCodes::FileFormatNotSupported) 0677 .errorMessage(); 0678 QMessageBox::warning( 0679 this, 0680 i18nc("@title:window", "Krita"), 0681 i18n("Could not open %2.\nReason: %1.", 0682 msg, 0683 url.toDisplayString())); 0684 continue; 0685 } 0686 0687 KisFileLayer *fileLayer = 0688 new KisFileLayer(this->image(), 0689 "", 0690 url.toLocalFile(), 0691 KisFileLayer::None, 0692 fileInfo.fileName(), 0693 OPACITY_OPAQUE_U8); 0694 0695 KisLayerSP above = 0696 this->mainWindow()->viewManager()->activeLayer(); 0697 KisNodeSP parent = above ? above->parent() 0698 : this->mainWindow() 0699 ->viewManager() 0700 ->image() 0701 ->root(); 0702 0703 adapter.addNode(fileLayer, parent, above); 0704 } else if (action == KisCanvasDrop::OPEN_IN_NEW_DOCUMENT 0705 || action 0706 == KisCanvasDrop::OPEN_MANY_DOCUMENTS) { 0707 if (this->mainWindow()) { 0708 this->mainWindow()->openDocument( 0709 url.toLocalFile(), 0710 KisMainWindow::None); 0711 } 0712 } else if (action 0713 == KisCanvasDrop::INSERT_AS_REFERENCE_IMAGES 0714 || action 0715 == KisCanvasDrop:: 0716 INSERT_AS_REFERENCE_IMAGE) { 0717 auto *reference = 0718 KisReferenceImage::fromFile(url.toLocalFile(), 0719 *this->viewConverter(), 0720 this); 0721 0722 if (reference) { 0723 const auto pos = this->canvasBase() 0724 ->coordinatesConverter() 0725 ->widgetToImage(event->pos()); 0726 reference->setPosition( 0727 (*this->viewConverter()).imageToDocument(pos)); 0728 this->canvasBase() 0729 ->referenceImagesDecoration() 0730 ->addReferenceImage(reference); 0731 0732 KoToolManager::instance()->switchToolRequested( 0733 "ToolReferenceImages"); 0734 } 0735 } 0736 } 0737 } 0738 } 0739 } else if (event->mimeData()->hasColor() || event->mimeData()->hasFormat("krita/x-colorsetentry")) { 0740 if (!image()) { 0741 return; 0742 } 0743 0744 // Cannot fill on non-painting layers (vector layer, clone layer, file layer, group layer) 0745 if (d->viewManager->activeNode().isNull() || 0746 d->viewManager->activeNode()->inherits("KisShapeLayer") || 0747 d->viewManager->activeNode()->inherits("KisCloneLayer") || 0748 !d->viewManager->activeDevice()) { 0749 showFloatingMessage(i18n("You cannot drag and drop colors on the selected layer type."), QIcon()); 0750 return; 0751 } 0752 0753 // Cannot fill if the layer is not editable 0754 if (!d->viewManager->activeNode()->isEditable()) { 0755 QString message; 0756 if (!d->viewManager->activeNode()->visible() && d->viewManager->activeNode()->userLocked()) { 0757 message = i18n("Layer is locked and invisible."); 0758 } else if (d->viewManager->activeNode()->userLocked()) { 0759 message = i18n("Layer is locked."); 0760 } else if(!d->viewManager->activeNode()->visible()) { 0761 message = i18n("Layer is invisible."); 0762 } 0763 showFloatingMessage(message, KisIconUtils::loadIcon("object-locked")); 0764 return; 0765 } 0766 0767 // The cursor is outside the image 0768 if (!image()->wrapAroundModePermitted() && !image()->bounds().contains(imgCursorPos)) { 0769 return; 0770 } 0771 0772 KisProcessingApplicator applicator(image(), d->viewManager->activeNode(), 0773 KisProcessingApplicator::NONE, 0774 KisImageSignalVector(), 0775 kundo2_i18n("Flood Fill Layer")); 0776 0777 KisResourcesSnapshotSP resources = 0778 new KisResourcesSnapshot(image(), d->viewManager->activeNode(), d->viewManager->canvasResourceProvider()->resourceManager()); 0779 0780 if (event->mimeData()->hasColor()) { 0781 resources->setFGColorOverride(KoColor(event->mimeData()->colorData().value<QColor>(), image()->colorSpace())); 0782 } else { 0783 QByteArray byteData = event->mimeData()->data("krita/x-colorsetentry"); 0784 KisSwatch s = KisSwatch::fromByteArray(byteData); 0785 resources->setFGColorOverride(s.color()); 0786 } 0787 0788 // Use same options as the fill tool 0789 KConfigGroup configGroup = KSharedConfig::openConfig()->group("KritaFill/KisToolFill"); 0790 const bool isAltPressed = event->keyboardModifiers() & Qt::AltModifier; 0791 const bool fillSelectionOnly = configGroup.readEntry("fillSelection", false) != isAltPressed; 0792 const int thresholdAmount = configGroup.readEntry("thresholdAmount", 8); 0793 const int opacitySpread = configGroup.readEntry("opacitySpread", 100); 0794 const bool useSelectionAsBoundary = configGroup.readEntry("useSelectionAsBoundary", false); 0795 const bool antiAlias = configGroup.readEntry("antiAlias", true); 0796 const int growSelection = configGroup.readEntry("growSelection", 0); 0797 const int featherAmount = configGroup.readEntry("featherAmount", 0); 0798 const QString SAMPLE_LAYERS_MODE_CURRENT = {"currentLayer"}; 0799 const QString SAMPLE_LAYERS_MODE_ALL = {"allLayers"}; 0800 const QString SAMPLE_LAYERS_MODE_COLOR_LABELED = {"colorLabeledLayers"}; 0801 QString sampleLayersMode; 0802 if (configGroup.hasKey("sampleLayersMode")) { 0803 sampleLayersMode = configGroup.readEntry("sampleLayersMode", SAMPLE_LAYERS_MODE_CURRENT); 0804 } else { // if neither option is present in the configuration, it will fall back to CURRENT 0805 bool sampleMerged = configGroup.readEntry("sampleMerged", false); 0806 sampleLayersMode = sampleMerged ? SAMPLE_LAYERS_MODE_ALL : SAMPLE_LAYERS_MODE_CURRENT; 0807 } 0808 const bool useFastMode = !resources->activeSelection() && 0809 opacitySpread == 100 && 0810 useSelectionAsBoundary == false && 0811 !antiAlias && growSelection == 0 && featherAmount == 0 && 0812 sampleLayersMode == SAMPLE_LAYERS_MODE_CURRENT; 0813 // If the sample layer mode is other than SAMPLE_LAYERS_MODE_ALL just 0814 // default to SAMPLE_LAYERS_MODE_CURRENT. This means that 0815 // SAMPLE_LAYERS_MODE_COLOR_LABELED is not supported yet since the color 0816 // labels are not stored in the config 0817 // TODO: make this work with color labels or reference layers in the future 0818 if (sampleLayersMode != SAMPLE_LAYERS_MODE_ALL) { 0819 sampleLayersMode = SAMPLE_LAYERS_MODE_CURRENT; 0820 } 0821 0822 KisPaintDeviceSP referencePaintDevice = nullptr; 0823 if (sampleLayersMode == SAMPLE_LAYERS_MODE_ALL) { 0824 referencePaintDevice = image()->projection(); 0825 } else if (sampleLayersMode == SAMPLE_LAYERS_MODE_CURRENT) { 0826 referencePaintDevice = d->viewManager->activeNode()->paintDevice(); 0827 } 0828 KIS_ASSERT(referencePaintDevice); 0829 0830 FillProcessingVisitor *visitor = new FillProcessingVisitor(referencePaintDevice, 0831 selection(), 0832 resources); 0833 visitor->setSeedPoint(imgCursorPos); 0834 visitor->setUseFastMode(useFastMode); 0835 visitor->setSelectionOnly(fillSelectionOnly); 0836 visitor->setUseSelectionAsBoundary(useSelectionAsBoundary); 0837 visitor->setFeather(featherAmount); 0838 visitor->setSizeMod(growSelection); 0839 visitor->setFillThreshold(thresholdAmount); 0840 visitor->setOpacitySpread(opacitySpread); 0841 visitor->setAntiAlias(antiAlias); 0842 0843 applicator.applyVisitor(visitor, 0844 KisStrokeJobData::SEQUENTIAL, 0845 KisStrokeJobData::EXCLUSIVE); 0846 0847 applicator.end(); 0848 } 0849 } 0850 0851 void KisView::dragMoveEvent(QDragMoveEvent *event) 0852 { 0853 dbgUI << Q_FUNC_INFO 0854 << "Formats: " << event->mimeData()->formats() 0855 << "Urls: " << event->mimeData()->urls() 0856 << "Has images: " << event->mimeData()->hasImage(); 0857 if (event->mimeData()->hasImage() 0858 || event->mimeData()->hasUrls() 0859 || event->mimeData()->hasFormat("application/x-krita-node-internal-pointer") 0860 || event->mimeData()->hasFormat("krita/x-colorsetentry") 0861 || event->mimeData()->hasColor()) { 0862 return event->accept(); 0863 } 0864 0865 return event->ignore(); 0866 } 0867 0868 KisDocument *KisView::document() const 0869 { 0870 return d->document; 0871 } 0872 0873 KisView *KisView::replaceBy(KisDocument *document) 0874 { 0875 KisMainWindow *window = mainWindow(); 0876 QMdiSubWindow *subWindow = d->subWindow; 0877 delete this; 0878 return window->newView(document, subWindow); 0879 } 0880 0881 KisMainWindow * KisView::mainWindow() const 0882 { 0883 return d->viewManager->mainWindow(); 0884 } 0885 0886 void KisView::setSubWindow(QMdiSubWindow *subWindow) 0887 { 0888 d->subWindow = subWindow; 0889 } 0890 0891 QStatusBar * KisView::statusBar() const 0892 { 0893 KisMainWindow *mw = mainWindow(); 0894 return mw ? mw->statusBar() : 0; 0895 } 0896 0897 void KisView::slotSavingStatusMessage(const QString &text, int timeout, bool isAutoSaving) 0898 { 0899 QStatusBar *sb = statusBar(); 0900 if (sb) { 0901 sb->showMessage(text, timeout); 0902 } 0903 0904 KisConfig cfg(true); 0905 0906 if (!sb || sb->isHidden() || 0907 (!isAutoSaving && cfg.forceShowSaveMessages()) || 0908 (cfg.forceShowAutosaveMessages() && isAutoSaving)) { 0909 0910 viewManager()->showFloatingMessage(text, QIcon()); 0911 } 0912 } 0913 0914 void KisView::slotClearStatusText() 0915 { 0916 QStatusBar *sb = statusBar(); 0917 if (sb) { 0918 sb->clearMessage(); 0919 } 0920 } 0921 0922 QList<QAction*> KisView::createChangeUnitActions(bool addPixelUnit) 0923 { 0924 UnitActionGroup* unitActions = new UnitActionGroup(d->document, addPixelUnit, this); 0925 return unitActions->actions(); 0926 } 0927 0928 void KisView::closeEvent(QCloseEvent *event) 0929 { 0930 // Check whether we're the last (user visible) view 0931 int viewCount = KisPart::instance()->viewCount(document()); 0932 if (viewCount > 1 || !isVisible()) { 0933 // there are others still, so don't bother the user 0934 event->accept(); 0935 return; 0936 } 0937 0938 if (queryClose()) { 0939 event->accept(); 0940 return; 0941 } 0942 0943 event->ignore(); 0944 0945 } 0946 0947 bool KisView::queryClose() 0948 { 0949 if (!document()) 0950 return true; 0951 0952 document()->waitForSavingToComplete(); 0953 0954 if (document()->isModified()) { 0955 QString name; 0956 name = QFileInfo(document()->path()).fileName(); 0957 0958 if (name.isEmpty()) 0959 name = i18n("Untitled"); 0960 0961 int res = QMessageBox::warning(this, 0962 i18nc("@title:window", "Krita"), 0963 i18n("<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", name), 0964 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); 0965 0966 switch (res) { 0967 case QMessageBox::Yes : { 0968 bool isNative = (document()->mimeType() == document()->nativeFormatMimeType()); 0969 if (!viewManager()->mainWindow()->saveDocument(document(), !isNative, false)) 0970 return false; 0971 break; 0972 } 0973 case QMessageBox::No : { 0974 KisImageSP image = document()->image(); 0975 image->requestStrokeCancellation(); 0976 viewManager()->blockUntilOperationsFinishedForced(image); 0977 0978 document()->removeAutoSaveFiles(document()->localFilePath(), document()->isRecovered()); 0979 document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. 0980 break; 0981 } 0982 default : // case QMessageBox::Cancel : 0983 return false; 0984 } 0985 } 0986 0987 return true; 0988 0989 } 0990 0991 void KisView::slotScreenChanged() 0992 { 0993 d->zoomManager.updateScreenResolution(this); 0994 } 0995 0996 void KisView::slotThemeChanged(QPalette pal) 0997 { 0998 this->setPalette(pal); 0999 for (int i=0; i<this->children().size();i++) { 1000 QWidget *w = qobject_cast<QWidget*> ( this->children().at(i)); 1001 if (w) { 1002 w->setPalette(pal); 1003 } 1004 } 1005 if (canvasBase()) { 1006 canvasBase()->canvasWidget()->setPalette(pal); 1007 } 1008 if (canvasController()) { 1009 canvasController()->setPalette(pal); 1010 } 1011 } 1012 1013 void KisView::resetImageSizeAndScroll(bool changeCentering, 1014 const QPointF &oldImageStillPoint, 1015 const QPointF &newImageStillPoint) 1016 { 1017 const KisCoordinatesConverter *converter = d->canvas.coordinatesConverter(); 1018 1019 QPointF oldPreferredCenter = d->canvasController.preferredCenter(); 1020 1021 /** 1022 * Calculating the still point in old coordinates depending on the 1023 * parameters given 1024 */ 1025 1026 QPointF oldStillPoint; 1027 1028 if (changeCentering) { 1029 oldStillPoint = 1030 converter->imageToWidget(oldImageStillPoint) + 1031 converter->documentOffset(); 1032 } else { 1033 QSizeF oldDocumentSize = d->canvasController.documentSize(); 1034 oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height()); 1035 } 1036 1037 /** 1038 * Updating the document size 1039 */ 1040 1041 QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes()); 1042 KoZoomController *zc = d->zoomManager.zoomController(); 1043 zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom(), 1044 d->zoomManager.resolutionX(), d->zoomManager.resolutionY()); 1045 zc->setPageSize(size); 1046 zc->setDocumentSize(size, true); 1047 1048 /** 1049 * Calculating the still point in new coordinates depending on the 1050 * parameters given 1051 */ 1052 1053 QPointF newStillPoint; 1054 1055 if (changeCentering) { 1056 newStillPoint = 1057 converter->imageToWidget(newImageStillPoint) + 1058 converter->documentOffset(); 1059 } else { 1060 QSizeF newDocumentSize = d->canvasController.documentSize(); 1061 newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height()); 1062 } 1063 1064 d->canvasController.setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint); 1065 } 1066 1067 void KisView::syncLastActiveNodeToDocument() 1068 { 1069 KisDocument *doc = document(); 1070 if (doc) { 1071 doc->setPreActivatedNode(d->currentNode); 1072 } 1073 } 1074 1075 void KisView::saveViewState(KisPropertiesConfiguration &config) const 1076 { 1077 config.setProperty("file", d->document->path()); 1078 config.setProperty("window", mainWindow()->windowStateConfig().name()); 1079 1080 if (d->subWindow) { 1081 config.setProperty("geometry", d->subWindow->saveGeometry().toBase64()); 1082 } 1083 1084 config.setProperty("zoomMode", (int)zoomController()->zoomMode()); 1085 config.setProperty("zoom", d->canvas.coordinatesConverter()->zoom()); 1086 1087 d->canvasController.saveCanvasState(config); 1088 } 1089 1090 void KisView::restoreViewState(const KisPropertiesConfiguration &config) 1091 { 1092 if (d->subWindow) { 1093 QByteArray geometry = QByteArray::fromBase64(config.getString("geometry", "").toLatin1()); 1094 d->subWindow->restoreGeometry(QByteArray::fromBase64(geometry)); 1095 } 1096 1097 qreal zoom = config.getFloat("zoom", 1.0f); 1098 int zoomMode = config.getInt("zoomMode", (int)KoZoomMode::ZOOM_PAGE); 1099 d->zoomManager.zoomController()->setZoom((KoZoomMode::Mode)zoomMode, zoom); 1100 d->canvasController.restoreCanvasState(config); 1101 } 1102 1103 void KisView::setCurrentNode(KisNodeSP node) 1104 { 1105 d->currentNode = node; 1106 d->canvas.slotTrySwitchShapeManager(); 1107 1108 syncLastActiveNodeToDocument(); 1109 } 1110 1111 KisNodeSP KisView::currentNode() const 1112 { 1113 return d->currentNode; 1114 } 1115 1116 KisLayerSP KisView::currentLayer() const 1117 { 1118 KisNodeSP node; 1119 KisMaskSP mask = currentMask(); 1120 if (mask) { 1121 node = mask->parent(); 1122 } 1123 else { 1124 node = d->currentNode; 1125 } 1126 return qobject_cast<KisLayer*>(node.data()); 1127 } 1128 1129 KisMaskSP KisView::currentMask() const 1130 { 1131 return dynamic_cast<KisMask*>(d->currentNode.data()); 1132 } 1133 1134 KisSelectionSP KisView::selection() 1135 { 1136 KisLayerSP layer = currentLayer(); 1137 if (layer) 1138 return layer->selection(); // falls through to the global 1139 // selection, or 0 in the end 1140 if (image()) { 1141 return image()->globalSelection(); 1142 } 1143 return 0; 1144 } 1145 1146 void KisView::slotSoftProofing(bool softProofing) 1147 { 1148 d->softProofing = softProofing; 1149 QString message; 1150 if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F")) 1151 { 1152 message = i18n("Soft Proofing doesn't work in floating point."); 1153 viewManager()->showFloatingMessage(message,QIcon()); 1154 return; 1155 } 1156 if (softProofing){ 1157 message = i18n("Soft Proofing turned on."); 1158 } else { 1159 message = i18n("Soft Proofing turned off."); 1160 } 1161 viewManager()->showFloatingMessage(message,QIcon()); 1162 canvasBase()->slotSoftProofing(); 1163 } 1164 1165 void KisView::slotGamutCheck(bool gamutCheck) 1166 { 1167 d->gamutCheck = gamutCheck; 1168 QString message; 1169 if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F")) 1170 { 1171 message = i18n("Gamut Warnings don't work in floating point."); 1172 viewManager()->showFloatingMessage(message,QIcon()); 1173 return; 1174 } 1175 1176 if (gamutCheck){ 1177 message = i18n("Gamut Warnings turned on."); 1178 if (!d->softProofing){ 1179 message += "\n "+i18n("But Soft Proofing is still off."); 1180 } 1181 } else { 1182 message = i18n("Gamut Warnings turned off."); 1183 } 1184 viewManager()->showFloatingMessage(message,QIcon()); 1185 canvasBase()->slotGamutCheck(); 1186 } 1187 1188 bool KisView::softProofing() 1189 { 1190 return d->softProofing; 1191 } 1192 1193 bool KisView::gamutCheck() 1194 { 1195 return d->gamutCheck; 1196 } 1197 1198 void KisView::slotLoadingFinished() 1199 { 1200 if (!document()) return; 1201 1202 /** 1203 * Cold-start of image size/resolution signals 1204 */ 1205 slotImageResolutionChanged(); 1206 1207 if (image()->locked()) { 1208 // If this is the first view on the image, the image will have been locked 1209 // so unlock it. 1210 image()->blockSignals(false); 1211 image()->unlock(); 1212 } 1213 1214 canvasBase()->initializeImage(); 1215 1216 /** 1217 * Dirty hack alert 1218 */ 1219 d->zoomManager.zoomController()->setCanvasMappingMode(false); 1220 1221 if (viewConverter()) { 1222 viewConverter()->setZoomMode(KoZoomMode::ZOOM_PAGE); 1223 } 1224 1225 connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SIGNAL(sigColorSpaceChanged(const KoColorSpace*))); 1226 connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), this, SIGNAL(sigProfileChanged(const KoColorProfile*))); 1227 connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SIGNAL(sigSizeChanged(QPointF,QPointF))); 1228 1229 KisNodeSP activeNode = document()->preActivatedNode(); 1230 1231 if (!activeNode) { 1232 activeNode = image()->rootLayer()->lastChild(); 1233 } 1234 1235 while (activeNode && !activeNode->inherits("KisLayer")) { 1236 activeNode = activeNode->prevSibling(); 1237 } 1238 1239 setCurrentNode(activeNode); 1240 connect(d->viewManager->mainWindow(), SIGNAL(screenChanged()), SLOT(slotScreenChanged())); 1241 zoomManager()->updateImageBoundsSnapping(); 1242 } 1243 1244 void KisView::slotSavingFinished() 1245 { 1246 if (d->viewManager && d->viewManager->mainWindow()) { 1247 d->viewManager->mainWindow()->updateCaption(); 1248 } 1249 } 1250 1251 void KisView::slotImageResolutionChanged() 1252 { 1253 resetImageSizeAndScroll(false); 1254 zoomManager()->updateImageBoundsSnapping(); 1255 zoomManager()->updateGuiAfterDocumentSize(); 1256 1257 // update KoUnit value for the document 1258 if (resourceProvider()) { 1259 resourceProvider()->resourceManager()-> 1260 setResource(KoCanvasResource::Unit, d->canvas.unit()); 1261 } 1262 } 1263 1264 void KisView::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint) 1265 { 1266 resetImageSizeAndScroll(true, oldStillPoint, newStillPoint); 1267 zoomManager()->updateImageBoundsSnapping(); 1268 zoomManager()->updateGuiAfterDocumentSize(); 1269 } 1270 1271 void KisView::closeView() 1272 { 1273 d->subWindow->close(); 1274 }