diff --git a/libs/ui/KisView.cpp b/libs/ui/KisView.cpp index 55d26c4f02..35ec6f21d2 100644 --- a/libs/ui/KisView.cpp +++ b/libs/ui/KisView.cpp @@ -1,925 +1,938 @@ /* * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisView.h" #include "KisView_p.h" #include #include #include #include "KoDocumentInfo.h" #include "KoPageLayout.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "KisDocument.h" #include "kis_image_manager.h" #include "KisMainWindow.h" #include "kis_mimedata.h" #include "kis_mirror_axis.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_shape_controller.h" #include "kis_tool_freehand.h" #include "KisUndoStackAction.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "kis_composite_progress_proxy.h" #include "kis_statusbar.h" #include "kis_painting_assistants_decoration.h" #include "kis_progress_widget.h" #include "kis_signal_compressor.h" #include "kis_filter_manager.h" #include "krita_utils.h" #include "input/kis_input_manager.h" #include "KisRemoteFileFetcher.h" //static QString KisView::newObjectName() { static int s_viewIFNumber = 0; QString name; name.setNum(s_viewIFNumber++); name.prepend("view_"); return name; } bool KisView::s_firstView = true; class Q_DECL_HIDDEN KisView::Private { public: Private(KisView *_q, KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection) : actionCollection(actionCollection) , viewConverter() , canvasController(_q, actionCollection) , canvas(&viewConverter, resourceManager, _q, document->shapeController()) , zoomManager(_q, &this->viewConverter, &this->canvasController) , paintingAssistantsDecoration(new KisPaintingAssistantsDecoration(_q)) , floatingMessageCompressor(100, KisSignalCompressor::POSTPONE) { } KisUndoStackAction *undo = 0; KisUndoStackAction *redo = 0; bool inOperation; //in the middle of an operation (no screen refreshing)? QPointer document; // our KisDocument QWidget *tempActiveWidget = 0; /** * Signals the document has been deleted. Can't use document==0 since this * only happens in ~QObject, and views get deleted by ~KisDocument. * XXX: either provide a better justification to do things this way, or * rework the mechanism. */ bool documentDeleted = false; KActionCollection* actionCollection; KisCoordinatesConverter viewConverter; KisCanvasController canvasController; KisCanvas2 canvas; KisZoomManager zoomManager; KisViewManager *viewManager = 0; KisNodeSP currentNode; KisPaintingAssistantsDecorationSP paintingAssistantsDecoration; bool isCurrent = false; bool showFloatingMessage = false; QPointer savedFloatingMessage; KisSignalCompressor floatingMessageCompressor; bool softProofing = false; bool gamutCheck = false; // Hmm sorry for polluting the private class with such a big inner class. // At the beginning it was a little struct :) class StatusBarItem { public: StatusBarItem(QWidget * widget, int stretch, bool permanent) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_connected(false), m_hidden(false) {} bool operator==(const StatusBarItem& rhs) { return m_widget == rhs.m_widget; } bool operator!=(const StatusBarItem& rhs) { return m_widget != rhs.m_widget; } QWidget * widget() const { return m_widget; } void ensureItemShown(QStatusBar * sb) { Q_ASSERT(m_widget); if (!m_connected) { if (m_permanent) sb->addPermanentWidget(m_widget, m_stretch); else sb->addWidget(m_widget, m_stretch); if(!m_hidden) m_widget->show(); m_connected = true; } } void ensureItemHidden(QStatusBar * sb) { if (m_connected) { m_hidden = m_widget->isHidden(); sb->removeWidget(m_widget); m_widget->hide(); m_connected = false; } } private: QWidget * m_widget = 0; int m_stretch; bool m_permanent; bool m_connected = false; bool m_hidden = false; }; }; KisView::KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) : QWidget(parent) , d(new Private(this, document, resourceManager, actionCollection)) { Q_ASSERT(document); connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool))); setObjectName(newObjectName()); d->document = document; setFocusPolicy(Qt::StrongFocus); d->undo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::UNDO); d->redo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::RED0); QStatusBar * sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } d->canvas.setup(); KisConfig cfg; d->canvasController.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController.setDrawShadow(false); d->canvasController.setCanvasMode(KoCanvasController::Infinite); d->canvasController.setVastScrolling(cfg.vastScrolling()); d->canvasController.setCanvas(&d->canvas); d->zoomManager.setup(d->actionCollection); connect(&d->canvasController, SIGNAL(documentSizeChanged()), &d->zoomManager, SLOT(slotScrollAreaSizeChanged())); setAcceptDrops(true); connect(d->document, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); connect(d->document, SIGNAL(sigSavingFinished()), this, SLOT(slotSavingFinished())); d->canvas.addDecoration(d->paintingAssistantsDecoration); d->paintingAssistantsDecoration->setVisible(true); d->showFloatingMessage = cfg.showCanvasMessages(); } KisView::~KisView() { if (d->viewManager) { KoProgressProxy *proxy = d->viewManager->statusBar()->progress()->progressProxy(); KIS_ASSERT_RECOVER_NOOP(proxy); image()->compositeProgressProxy()->removeProxy(proxy); if (d->viewManager->filterManager()->isStrokeRunning()) { d->viewManager->filterManager()->cancel(); } } KisPart::instance()->removeView(this); KoToolManager::instance()->removeCanvasController(&d->canvasController); delete d; } void KisView::notifyCurrentStateChanged(bool isCurrent) { d->isCurrent = isCurrent; if (!d->isCurrent && d->savedFloatingMessage) { d->savedFloatingMessage->removeMessage(); } KisInputManager *inputManager = globalInputManager(); if (d->isCurrent) { inputManager->attachPriorityEventFilter(&d->canvasController); } else { inputManager->detachPriorityEventFilter(&d->canvasController); } } void KisView::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisView::showFloatingMessageImpl(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->viewManager) return; if(d->isCurrent && d->showFloatingMessage && d->viewManager->qtMainWindow()) { if (d->savedFloatingMessage) { d->savedFloatingMessage->tryOverrideMessage(message, icon, timeout, priority, alignment); } else { d->savedFloatingMessage = new KisFloatingMessage(message, this->canvasBase()->canvasWidget(), false, timeout, priority, alignment); d->savedFloatingMessage->setShowOverParent(true); d->savedFloatingMessage->setIcon(icon); connect(&d->floatingMessageCompressor, SIGNAL(timeout()), d->savedFloatingMessage, SLOT(showMessage())); d->floatingMessageCompressor.start(); } } } void KisView::setViewManager(KisViewManager *view) { d->viewManager = view; KoToolManager::instance()->addController(&d->canvasController); KoToolManager::instance()->registerToolActions(d->actionCollection, &d->canvasController); dynamic_cast(d->document->shapeController())->setInitialShapeForCanvas(&d->canvas); if (resourceProvider()) { resourceProvider()->slotImageSizeChanged(); } if (d->viewManager && d->viewManager->nodeManager()) { d->viewManager->nodeManager()->nodesUpdated(); } connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), this, SLOT(slotImageSizeChanged(const QPointF&, const QPointF&))); connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged())); // executed in a context of an image thread connect(image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), SLOT(slotImageNodeAdded(KisNodeSP)), Qt::DirectConnection); // executed in a context of the gui thread connect(this, SIGNAL(sigContinueAddNode(KisNodeSP)), SLOT(slotContinueAddNode(KisNodeSP)), Qt::AutoConnection); // executed in a context of an image thread connect(image(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(slotImageNodeRemoved(KisNodeSP)), Qt::DirectConnection); // executed in a context of the gui thread connect(this, SIGNAL(sigContinueRemoveNode(KisNodeSP)), SLOT(slotContinueRemoveNode(KisNodeSP)), Qt::AutoConnection); /* * WARNING: Currently we access the global progress bar in two ways: * connecting to composite progress proxy (strokes) and creating * progress updaters. The latter way should be deprecated in favour * of displaying the status of the global strokes queue */ image()->compositeProgressProxy()->addProxy(d->viewManager->statusBar()->progress()->progressProxy()); connect(d->viewManager->statusBar()->progress(), SIGNAL(sigCancellationRequested()), image(), SLOT(requestStrokeCancellation())); d->viewManager->updateGUI(); KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } KisViewManager* KisView::viewManager() const { return d->viewManager; } void KisView::slotImageNodeAdded(KisNodeSP node) { emit sigContinueAddNode(node); } void KisView::slotContinueAddNode(KisNodeSP newActiveNode) { /** * When deleting the last layer, root node got selected. We should * fix it when the first layer is added back. * * Here we basically reimplement what Qt's view/model do. But * since they are not connected, we should do it manually. */ if (!d->isCurrent && (!d->currentNode || !d->currentNode->parent())) { d->currentNode = newActiveNode; } } void KisView::slotImageNodeRemoved(KisNodeSP node) { emit sigContinueRemoveNode(KritaUtils::nearestNodeAfterRemoval(node)); } void KisView::slotContinueRemoveNode(KisNodeSP newActiveNode) { if (!d->isCurrent) { d->currentNode = newActiveNode; } } QAction *KisView::undoAction() const { return d->undo; } QAction *KisView::redoAction() const { return d->redo; } KoZoomController *KisView::zoomController() const { return d->zoomManager.zoomController(); } KisZoomManager *KisView::zoomManager() const { return &d->zoomManager; } KisCanvasController *KisView::canvasController() const { return &d->canvasController; } KisCanvasResourceProvider *KisView::resourceProvider() const { if (d->viewManager) { return d->viewManager->resourceProvider(); } return 0; } KisInputManager* KisView::globalInputManager() const { return d->viewManager ? d->viewManager->inputManager() : 0; } KisCanvas2 *KisView::canvasBase() const { return &d->canvas; } KisImageWSP KisView::image() const { if (d->document) { return d->document->image(); } return 0; } KisCoordinatesConverter *KisView::viewConverter() const { return &d->viewConverter; } void KisView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node")) { event->accept(); // activate view if it should accept the drop this->setFocus(); } else { event->ignore(); } } void KisView::dropEvent(QDropEvent *event) { KisImageWSP kisimage = image(); Q_ASSERT(kisimage); QPoint cursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint(); QRect imageBounds = kisimage->bounds(); QPoint pasteCenter; bool forceRecenter; if (event->keyboardModifiers() & Qt::ShiftModifier && imageBounds.contains(cursorPos)) { pasteCenter = cursorPos; forceRecenter = true; } else { pasteCenter = imageBounds.center(); forceRecenter = false; } if (event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasImage()) { KisShapeController *kritaShapeController = dynamic_cast(d->document->shapeController()); QList nodes = KisMimeData::loadNodes(event->mimeData(), imageBounds, pasteCenter, forceRecenter, kisimage, kritaShapeController); Q_FOREACH (KisNodeSP node, nodes) { if (node) { KisNodeCommandsAdapter adapter(viewManager()); if (!viewManager()->nodeManager()->activeLayer()) { adapter.addNode(node, kisimage->rootLayer() , 0); } else { adapter.addNode(node, viewManager()->nodeManager()->activeLayer()->parent(), viewManager()->nodeManager()->activeLayer()); } } } } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); if (urls.length() > 0) { QMenu popup; popup.setObjectName("drop_popup"); QAction *insertAsNewLayer = new QAction(i18n("Insert as New Layer"), &popup); QAction *insertManyLayers = new QAction(i18n("Insert Many Layers"), &popup); QAction *openInNewDocument = new QAction(i18n("Open in New Document"), &popup); QAction *openManyDocuments = new QAction(i18n("Open Many Documents"), &popup); QAction *cancel = new QAction(i18n("Cancel"), &popup); popup.addAction(insertAsNewLayer); popup.addAction(openInNewDocument); popup.addAction(insertManyLayers); popup.addAction(openManyDocuments); insertAsNewLayer->setEnabled(image() && urls.count() == 1); openInNewDocument->setEnabled(urls.count() == 1); insertManyLayers->setEnabled(image() && urls.count() > 1); openManyDocuments->setEnabled(urls.count() > 1); popup.addSeparator(); popup.addAction(cancel); QAction *action = popup.exec(QCursor::pos()); if (action != 0 && action != cancel) { QTemporaryFile *tmp = 0; Q_FOREACH (QUrl url, urls) { if (!url.isLocalFile()) { // download the file and substitute the url KisRemoteFileFetcher fetcher; tmp = new QTemporaryFile(); tmp->setAutoRemove(true); if (!fetcher.fetchFile(url, tmp)) { qDebug() << "Fetching" << url << "failed"; continue; } url = url.fromLocalFile(tmp->fileName()); } if (url.isLocalFile()) { if (action == insertAsNewLayer || action == insertManyLayers) { d->viewManager->imageManager()->importImage(url); activateWindow(); } else { Q_ASSERT(action == openInNewDocument || action == openManyDocuments); if (mainWindow()) { mainWindow()->openDocument(url); } } } delete tmp; tmp = 0; } } } } } KisDocument *KisView::document() const { return d->document; } void KisView::setDocument(KisDocument *document) { d->document->disconnect(this); d->document = document; QStatusBar *sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } } void KisView::setDocumentDeleted() { d->documentDeleted = true; } KoPageLayout KisView::pageLayout() const { return document()->pageLayout(); } QPrintDialog *KisView::createPrintDialog(KisPrintJob *printJob, QWidget *parent) { Q_UNUSED(parent); QPrintDialog *printDialog = new QPrintDialog(&printJob->printer(), this); printDialog->setMinMax(printJob->printer().fromPage(), printJob->printer().toPage()); printDialog->setEnabledOptions(printJob->printDialogOptions()); return printDialog; } KisMainWindow * KisView::mainWindow() const { return dynamic_cast(window()); } QStatusBar * KisView::statusBar() const { KisMainWindow *mw = mainWindow(); return mw ? mw->statusBar() : 0; } void KisView::slotActionStatusText(const QString &text) { QStatusBar *sb = statusBar(); if (sb) sb->showMessage(text); } void KisView::slotClearStatusText() { QStatusBar *sb = statusBar(); if (sb) sb->clearMessage(); } QList KisView::createChangeUnitActions(bool addPixelUnit) { UnitActionGroup* unitActions = new UnitActionGroup(d->document, addPixelUnit, this); return unitActions->actions(); } void KisView::closeEvent(QCloseEvent *event) { // Check whether we're the last view int viewCount = KisPart::instance()->viewCount(document()); if (viewCount > 1) { // there are others still, so don't bother the user event->accept(); return; } if (queryClose()) { d->viewManager->removeStatusBarItem(zoomManager()->zoomActionWidget()); event->accept(); return; } event->ignore(); } bool KisView::queryClose() { if (!document()) return true; if (document()->isModified()) { QString name; if (document()->documentInfo()) { name = document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : { bool isNative = (document()->outputMimeType() == document()->nativeFormatMimeType()); if (!viewManager()->mainWindow()->saveDocument(document(), !isNative)) return false; break; } case QMessageBox::No : document()->removeAutoSaveFiles(); document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case QMessageBox::Cancel : return false; } } return true; } void KisView::resetImageSizeAndScroll(bool changeCentering, const QPointF &oldImageStillPoint, const QPointF &newImageStillPoint) { const KisCoordinatesConverter *converter = d->canvas.coordinatesConverter(); QPointF oldPreferredCenter = d->canvasController.preferredCenter(); /** * Calculating the still point in old coordinates depending on the * parameters given */ QPointF oldStillPoint; if (changeCentering) { oldStillPoint = converter->imageToWidget(oldImageStillPoint) + converter->documentOffset(); } else { QSize oldDocumentSize = d->canvasController.documentSize(); oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height()); } /** * Updating the document size */ QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes()); KoZoomController *zc = d->zoomManager.zoomController(); zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom()); zc->setPageSize(size); zc->setDocumentSize(size, true); /** * Calculating the still point in new coordinates depending on the * parameters given */ QPointF newStillPoint; if (changeCentering) { newStillPoint = converter->imageToWidget(newImageStillPoint) + converter->documentOffset(); } else { QSize newDocumentSize = d->canvasController.documentSize(); newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height()); } d->canvasController.setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint); } void KisView::setCurrentNode(KisNodeSP node) { d->currentNode = node; } KisNodeSP KisView::currentNode() const { return d->currentNode; } KisLayerSP KisView::currentLayer() const { KisNodeSP node; KisMaskSP mask = currentMask(); if (mask) { node = mask->parent(); } else { node = d->currentNode; } return dynamic_cast(node.data()); } KisMaskSP KisView::currentMask() const { return dynamic_cast(d->currentNode.data()); } KisSelectionSP KisView::selection() { KisLayerSP layer = currentLayer(); if (layer) return layer->selection(); // falls through to the global // selection, or 0 in the end if (image()) { return image()->globalSelection(); } return 0; } void KisView::slotSoftProofing(bool softProofing) { d->softProofing = softProofing; QString message; + if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F")) + { + message = i18n("Softproofing doesn't work in floating point."); + viewManager()->showFloatingMessage(message,QIcon()); + return; + } if (softProofing){ message = i18n("Softproofing turned on."); } else { message = i18n("Softproofing turned off."); } viewManager()->showFloatingMessage(message,QIcon()); canvasBase()->slotSoftProofing(softProofing); } void KisView::slotGamutCheck(bool gamutCheck) { d->gamutCheck = gamutCheck; QString message; + if (canvasBase()->image()->colorSpace()->colorDepthId().id().contains("F")) + { + message = i18n("Gamutwarnings don't work in floating point."); + viewManager()->showFloatingMessage(message,QIcon()); + return; + } + if (gamutCheck){ message = i18n("Gamut alarms turned on."); if (!d->softProofing){ message += "\n "+i18n("But softproofing is still off."); } } else { message = i18n("Gamut alarms turned off."); } viewManager()->showFloatingMessage(message,QIcon()); canvasBase()->slotGamutCheck(gamutCheck); } bool KisView::softProofing() { return d->softProofing; } bool KisView::gamutCheck() { return d->gamutCheck; } void KisView::slotLoadingFinished() { if (!document()) return; /** * Cold-start of image size/resolution signals */ slotImageResolutionChanged(); if (image()->locked()) { // If this is the first view on the image, the image will have been locked // so unlock it. image()->blockSignals(false); image()->unlock(); } canvasBase()->initializeImage(); /** * Dirty hack alert */ d->zoomManager.zoomController()->setAspectMode(true); if (viewConverter()) { viewConverter()->setZoomMode(KoZoomMode::ZOOM_PAGE); } connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SIGNAL(sigColorSpaceChanged(const KoColorSpace*))); connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), this, SIGNAL(sigProfileChanged(const KoColorProfile*))); connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SIGNAL(sigSizeChanged(QPointF,QPointF))); KisNodeSP activeNode = document()->preActivatedNode(); document()->setPreActivatedNode(0); // to make sure that we don't keep a reference to a layer the user can later delete. if (!activeNode) { activeNode = image()->rootLayer()->lastChild(); } while (activeNode && !activeNode->inherits("KisLayer")) { activeNode = activeNode->prevSibling(); } setCurrentNode(activeNode); zoomManager()->updateImageBoundsSnapping(); } void KisView::slotSavingFinished() { if (d->viewManager && d->viewManager->mainWindow()) { d->viewManager->mainWindow()->updateCaption(); } } KisPrintJob * KisView::createPrintJob() { return new KisPrintJob(image()); } void KisView::slotImageResolutionChanged() { resetImageSizeAndScroll(false); zoomManager()->updateImageBoundsSnapping(); zoomManager()->updateGUI(); // update KoUnit value for the document if (resourceProvider()) { resourceProvider()->resourceManager()-> setResource(KoCanvasResourceManager::Unit, d->canvas.unit()); } } void KisView::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint) { resetImageSizeAndScroll(true, oldStillPoint, newStillPoint); zoomManager()->updateImageBoundsSnapping(); zoomManager()->updateGUI(); } diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp index 232fb915fd..e785ebc3e3 100644 --- a/libs/ui/canvas/kis_canvas2.cpp +++ b/libs/ui/canvas/kis_canvas2.cpp @@ -1,968 +1,968 @@ /* This file is part of the KDE project * * Copyright (C) 2006, 2010 Boudewijn Rempt * Copyright (C) Lukáš Tvrdý , (C) 2010 * Copyright (C) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.. */ #include "kis_canvas2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_tool_proxy.h" #include "kis_coordinates_converter.h" #include "kis_prescaled_projection.h" #include "kis_image.h" #include "kis_image_barrier_locker.h" #include "KisDocument.h" #include "flake/kis_shape_layer.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_abstract_canvas_widget.h" #include "kis_qpainter_canvas.h" #include "kis_group_layer.h" #include "flake/kis_shape_controller.h" #include "kis_node_manager.h" #include "kis_selection.h" #include "kis_selection_component.h" #include "flake/kis_shape_selection.h" #include "kis_image_config.h" #include "kis_infinity_manager.h" #include "kis_signal_compressor.h" #include "kis_display_color_converter.h" #include "kis_exposure_gamma_correction_interface.h" #include "KisView.h" #include "kis_canvas_controller.h" #include "kis_grid_config.h" #include "kis_animation_player.h" #include "kis_animation_frame_cache.h" #include "opengl/kis_opengl_canvas2.h" #include "opengl/kis_opengl.h" #include "kis_fps_decoration.h" #include "KoColorConversionTransformation.h" #include "KisProofingConfiguration.h" #include #include #include "input/kis_input_manager.h" #include "kis_painting_assistants_decoration.h" #include "kis_canvas_updates_compressor.h" class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private { public: KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer view, KoCanvasResourceManager* resourceManager) : coordinatesConverter(coordConverter) , view(view) , shapeManager(parent) , toolProxy(parent) , displayColorConverter(resourceManager, view) { } KisCoordinatesConverter *coordinatesConverter; QPointerview; KisAbstractCanvasWidget *canvasWidget = 0; KoShapeManager shapeManager; bool currentCanvasIsOpenGL; int openGLFilterMode; KisToolProxy toolProxy; KisPrescaledProjectionSP prescaledProjection; bool vastScrolling; KisSignalCompressor updateSignalCompressor; QRect savedUpdateRect; QBitArray channelFlags; KisProofingConfiguration *proofingConfig = 0; bool softProofing = false; bool gamutCheck = false; bool proofingConfigUpdated = false; KisPopupPalette *popupPalette = 0; KisDisplayColorConverter displayColorConverter; KisCanvasUpdatesCompressor projectionUpdatesCompressor; KisAnimationPlayer *animationPlayer; KisAnimationFrameCacheSP frameCache; bool lodAllowedInCanvas; bool bootstrapLodBlocked; bool effectiveLodAllowedInCanvas() { return lodAllowedInCanvas && !bootstrapLodBlocked; } }; KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, KisView *view, KoShapeBasedDocumentBase *sc) : KoCanvasBase(sc, resourceManager) , m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager)) { /** * While loading LoD should be blocked. Only when GUI has finished * loading and zoom level settled down, LoD is given a green * light. */ m_d->bootstrapLodBlocked = true; connect(view->mainWindow(), SIGNAL(guiLoadingFinished()), SLOT(bootstrapFinished())); m_d->updateSignalCompressor.setDelay(10); m_d->updateSignalCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE); } void KisCanvas2::setup() { // a bit of duplication from slotConfigChanged() KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); m_d->lodAllowedInCanvas = cfg.levelOfDetailEnabled(); createCanvas(cfg.useOpenGL()); setLodAllowedInCanvas(m_d->lodAllowedInCanvas); m_d->animationPlayer = new KisAnimationPlayer(this); connect(m_d->view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved(QPoint))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); /** * We switch the shape manager every time vector layer or * shape selection is activated. Flake does not expect this * and connects all the signals of the global shape manager * to the clients in the constructor. To workaround this we * forward the signals of local shape managers stored in the * vector layers to the signals of global shape manager. So the * sequence of signal deliveries is the following: * * shapeLayer.m_d.canvas.m_shapeManager.selection() -> * shapeLayer -> * shapeController -> * globalShapeManager.selection() */ KisShapeController *kritaShapeController = static_cast(shapeController()->documentBase()); connect(kritaShapeController, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); connect(kritaShapeController, SIGNAL(selectionContentChanged()), globalShapeManager(), SIGNAL(selectionContentChanged())); connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)), globalShapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*))); connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate())); } KisCanvas2::~KisCanvas2() { if (m_d->animationPlayer->isPlaying()) { m_d->animationPlayer->forcedStopOnExit(); } delete m_d; } void KisCanvas2::setCanvasWidget(QWidget * widget) { KisAbstractCanvasWidget *tmp = dynamic_cast(widget); Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget"); if (m_d->popupPalette) { m_d->popupPalette->setParent(widget); } if(m_d->canvasWidget != 0) { tmp->setDecorations(m_d->canvasWidget->decorations()); // Redundant check for the constructor case, see below if(viewManager() != 0) viewManager()->inputManager()->removeTrackedCanvas(this); } m_d->canvasWidget = tmp; // Either tmp was null or we are being called by KisCanvas2 constructor that is called by KisView // constructor, so the view manager still doesn't exists. if(m_d->canvasWidget != 0 && viewManager() != 0) viewManager()->inputManager()->addTrackedCanvas(this); if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) { KisInfinityManager *manager = new KisInfinityManager(m_d->view, this); manager->setVisible(true); m_d->canvasWidget->addDecoration(manager); } widget->setAutoFillBackground(false); widget->setAttribute(Qt::WA_OpaquePaintEvent); widget->setMouseTracking(true); widget->setAcceptDrops(true); KoCanvasControllerWidget *controller = dynamic_cast(canvasController()); if (controller) { Q_ASSERT(controller->canvas() == this); controller->changeCanvasWidget(widget); } } bool KisCanvas2::canvasIsOpenGL() const { return m_d->currentCanvasIsOpenGL; } KisOpenGL::FilterMode KisCanvas2::openGLFilterMode() const { return KisOpenGL::FilterMode(m_d->openGLFilterMode); } void KisCanvas2::gridSize(QPointF *offset, QSizeF *spacing) const { QTransform transform = coordinatesConverter()->imageToDocumentTransform(); const QPoint intSpacing = m_d->view->document()->gridConfig().spacing(); const QPoint intOffset = m_d->view->document()->gridConfig().offset(); QPointF size = transform.map(QPointF(intSpacing)); spacing->rwidth() = size.x(); spacing->rheight() = size.y(); *offset = transform.map(QPointF(intOffset)); } bool KisCanvas2::snapToGrid() const { return m_d->view->document()->gridConfig().snapToGrid(); } qreal KisCanvas2::rotationAngle() const { return m_d->coordinatesConverter->rotationAngle(); } bool KisCanvas2::xAxisMirrored() const { return m_d->coordinatesConverter->xAxisMirrored(); } bool KisCanvas2::yAxisMirrored() const { return m_d->coordinatesConverter->yAxisMirrored(); } void KisCanvas2::channelSelectionChanged() { KisImageWSP image = this->image(); m_d->channelFlags = image->rootLayer()->channelFlags(); image->barrierLock(); m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags); startUpdateInPatches(image->bounds()); image->unlock(); } void KisCanvas2::addCommand(KUndo2Command *command) { // This method exists to support flake-related operations m_d->view->document()->addCommand(command); } KoShapeManager* KisCanvas2::shapeManager() const { if (!viewManager()) return globalShapeManager(); if (!viewManager()->nodeManager()) return globalShapeManager(); KisLayerSP activeLayer = viewManager()->nodeManager()->activeLayer(); if (activeLayer && activeLayer->isEditable()) { KisShapeLayer * shapeLayer = dynamic_cast(activeLayer.data()); if (shapeLayer) { return shapeLayer->shapeManager(); } KisSelectionSP selection = activeLayer->selection(); if (selection && !selection.isNull()) { if (selection->hasShapeSelection()) { KoShapeManager* m = dynamic_cast(selection->shapeSelection())->shapeManager(); return m; } } } return globalShapeManager(); } KoShapeManager * KisCanvas2::globalShapeManager() const { return &m_d->shapeManager; } void KisCanvas2::updateInputMethodInfo() { // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget... } const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const { return m_d->coordinatesConverter; } KoViewConverter* KisCanvas2::viewConverter() const { return m_d->coordinatesConverter; } KisInputManager* KisCanvas2::globalInputManager() const { return m_d->view->globalInputManager(); } QWidget* KisCanvas2::canvasWidget() { return m_d->canvasWidget->widget(); } const QWidget* KisCanvas2::canvasWidget() const { return m_d->canvasWidget->widget(); } KoUnit KisCanvas2::unit() const { KoUnit unit(KoUnit::Pixel); KisImageWSP image = m_d->view->image(); if (image) { if (!qFuzzyCompare(image->xRes(), image->yRes())) { warnKrita << "WARNING: resolution of the image is anisotropic" << ppVar(image->xRes()) << ppVar(image->yRes()); } const qreal resolution = image->xRes(); unit.setFactor(resolution); } return unit; } KoToolProxy * KisCanvas2::toolProxy() const { return &m_d->toolProxy; } void KisCanvas2::createQPainterCanvas() { m_d->currentCanvasIsOpenGL = false; KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view); m_d->prescaledProjection = new KisPrescaledProjection(); m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter); m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter.monitorProfile(), m_d->displayColorConverter.renderingIntent(), m_d->displayColorConverter.conversionFlags()); m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter.displayFilter()); canvasWidget->setPrescaledProjection(m_d->prescaledProjection); setCanvasWidget(canvasWidget); } void KisCanvas2::createOpenGLCanvas() { KisConfig cfg; m_d->openGLFilterMode = cfg.openGLFilteringMode(); m_d->currentCanvasIsOpenGL = true; KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->view->image(), &m_d->displayColorConverter); m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures()); setCanvasWidget(canvasWidget); if (canvasWidget->needsFpsDebugging() && !decoration(KisFpsDecoration::idTag)) { addDecoration(new KisFpsDecoration(imageView())); } } void KisCanvas2::createCanvas(bool useOpenGL) { // deinitialize previous canvas structures m_d->prescaledProjection = 0; m_d->frameCache = 0; KisConfig cfg; QDesktopWidget dw; const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView())); m_d->displayColorConverter.setMonitorProfile(profile); if (useOpenGL) { if (KisOpenGL::hasOpenGL()) { createOpenGLCanvas(); if (cfg.canvasState() == "OPENGL_FAILED") { // Creating the opengl canvas failed, fall back warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter."; createQPainterCanvas(); } } else { warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; createQPainterCanvas(); } } else { createQPainterCanvas(); } if (m_d->popupPalette) { m_d->popupPalette->setParent(m_d->canvasWidget->widget()); } } void KisCanvas2::initializeImage() { KisImageWSP image = m_d->view->image(); m_d->coordinatesConverter->setImage(image); connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection); connect(this, SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection())); connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig())); connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection); connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32))); connectCurrentCanvas(); } void KisCanvas2::connectCurrentCanvas() { KisImageWSP image = m_d->view->image(); if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setImage(image); } startResizingImage(); emit imageChanged(image); setLodAllowedInCanvas(m_d->lodAllowedInCanvas); } void KisCanvas2::resetCanvas(bool useOpenGL) { // we cannot reset the canvas before it's created, but this method might be called, // for instance when setting the monitor profile. if (!m_d->canvasWidget) { return; } KisConfig cfg; bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) || (m_d->currentCanvasIsOpenGL && m_d->openGLFilterMode != cfg.openGLFilteringMode()); if (needReset) { createCanvas(useOpenGL); connectCurrentCanvas(); notifyZoomChanged(); } updateCanvasWidgetImpl(); } void KisCanvas2::startUpdateInPatches(const QRect &imageRect) { if (m_d->currentCanvasIsOpenGL) { startUpdateCanvasProjection(imageRect); } else { KisImageConfig imageConfig; int patchWidth = imageConfig.updatePatchWidth(); int patchHeight = imageConfig.updatePatchHeight(); for (int y = 0; y < imageRect.height(); y += patchHeight) { for (int x = 0; x < imageRect.width(); x += patchWidth) { QRect patchRect(x, y, patchWidth, patchHeight); startUpdateCanvasProjection(patchRect); } } } } void KisCanvas2::setDisplayFilter(KisDisplayFilter *displayFilter) { m_d->displayColorConverter.setDisplayFilter(displayFilter); KisImageWSP image = this->image(); image->barrierLock(); m_d->canvasWidget->setDisplayFilter(displayFilter); image->unlock(); } KisDisplayFilter *KisCanvas2::displayFilter() const { return m_d->displayColorConverter.displayFilter(); } KisDisplayColorConverter* KisCanvas2::displayColorConverter() const { return &m_d->displayColorConverter; } KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const { KisDisplayFilter *displayFilter = m_d->displayColorConverter.displayFilter(); return displayFilter ? displayFilter->correctionInterface() : KisDumbExposureGammaCorrectionInterface::instance(); } void KisCanvas2::setProofingOptions(bool softProof, bool gamutCheck) { m_d->proofingConfig = this->image()->proofingConfiguration(); if (!m_d->proofingConfig) { qDebug()<<"Canvas: No proofing config found, generating one."; m_d->proofingConfig = new KisProofingConfiguration(); } KoColorConversionTransformation::ConversionFlags conversionFlags = m_d->proofingConfig->conversionFlags; - if (softProof) { + if (softProof && this->image()->colorSpace()->colorDepthId().id().contains("U")) { conversionFlags |= KoColorConversionTransformation::SoftProofing; } else { conversionFlags = conversionFlags & ~KoColorConversionTransformation::SoftProofing; } - if (gamutCheck) { + if (gamutCheck && this->image()->colorSpace()->colorDepthId().id().contains("U")) { conversionFlags |= KoColorConversionTransformation::GamutCheck; } else { conversionFlags = conversionFlags & ~KoColorConversionTransformation::GamutCheck; } m_d->proofingConfig->conversionFlags = conversionFlags; qDebug()<<"setting softproofing in canvas: "<proofingConfigUpdated = true; startUpdateInPatches(this->image()->bounds()); } void KisCanvas2::slotSoftProofing(bool softProofing) { m_d->softProofing = softProofing; setProofingOptions(m_d->softProofing, m_d->gamutCheck); } void KisCanvas2::slotGamutCheck(bool gamutCheck) { m_d->gamutCheck = gamutCheck; setProofingOptions(m_d->softProofing, m_d->gamutCheck); } void KisCanvas2::slotChangeProofingConfig() { setProofingOptions(m_d->softProofing, m_d->gamutCheck); } void KisCanvas2::setProofingConfigUpdated(bool updated) { m_d->proofingConfigUpdated = updated; } bool KisCanvas2::proofingConfigUpdated() { return m_d->proofingConfigUpdated; } KisProofingConfiguration *KisCanvas2::proofingConfiguration() const { if (!m_d->proofingConfig) { qDebug()<<"No proofing config found, generating one"; m_d->proofingConfig = new KisProofingConfiguration(); } return m_d->proofingConfig; } void KisCanvas2::startResizingImage() { KisImageWSP image = this->image(); qint32 w = image->width(); qint32 h = image->height(); emit sigContinueResizeImage(w, h); QRect imageBounds(0, 0, w, h); startUpdateInPatches(imageBounds); } void KisCanvas2::finishResizingImage(qint32 w, qint32 h) { m_d->canvasWidget->finishResizingImage(w, h); } void KisCanvas2::startUpdateCanvasProjection(const QRect & rc) { KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc, m_d->channelFlags); if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) { emit sigCanvasCacheUpdated(); } } void KisCanvas2::updateCanvasProjection() { while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) { QRect vRect = m_d->canvasWidget->updateCanvasProjection(info); if (!vRect.isEmpty()) { updateCanvasWidgetImpl(m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect()); } } // TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas if (m_d->currentCanvasIsOpenGL) { updateCanvasWidgetImpl(); } } void KisCanvas2::slotDoCanvasUpdate() { if (m_d->canvasWidget->isBusy()) { // just restarting the timer updateCanvasWidgetImpl(m_d->savedUpdateRect); return; } if (m_d->savedUpdateRect.isEmpty()) { m_d->canvasWidget->widget()->update(); emit updateCanvasRequested(m_d->canvasWidget->widget()->rect()); } else { emit updateCanvasRequested(m_d->savedUpdateRect); m_d->canvasWidget->widget()->update(m_d->savedUpdateRect); } m_d->savedUpdateRect = QRect(); } void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc) { if (!m_d->updateSignalCompressor.isActive() || !m_d->savedUpdateRect.isEmpty()) { m_d->savedUpdateRect |= rc; } m_d->updateSignalCompressor.start(); } void KisCanvas2::updateCanvas() { updateCanvasWidgetImpl(); } void KisCanvas2::updateCanvas(const QRectF& documentRect) { if (m_d->currentCanvasIsOpenGL && m_d->canvasWidget->decorations().size() > 0) { updateCanvasWidgetImpl(); } else { // updateCanvas is called from tools, never from the projection // updates, so no need to prescale! QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect(); widgetRect.adjust(-2, -2, 2, 2); if (!widgetRect.isEmpty()) { updateCanvasWidgetImpl(widgetRect); } } } void KisCanvas2::disconnectCanvasObserver(QObject *object) { KoCanvasBase::disconnectCanvasObserver(object); m_d->view->disconnect(object); } void KisCanvas2::notifyZoomChanged() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->notifyZoomChanged(); } notifyLevelOfDetailChange(); updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction } void KisCanvas2::notifyLevelOfDetailChange() { if (!m_d->effectiveLodAllowedInCanvas()) return; KisImageSP image = this->image(); const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom(); KisConfig cfg; const int maxLod = cfg.numMipmapLevels(); int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod); image->setDesiredLevelOfDetail(lod); } void KisCanvas2::preScale() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->preScale(); } } const KoColorProfile * KisCanvas2::monitorProfile() { return m_d->displayColorConverter.monitorProfile(); } KisViewManager* KisCanvas2::viewManager() const { if (m_d->view) { return m_d->view->viewManager(); } return 0; } QPointerKisCanvas2::imageView() const { return m_d->view; } KisImageWSP KisCanvas2::image() const { return m_d->view->image(); } KisImageWSP KisCanvas2::currentImage() const { return m_d->view->image(); } void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset) { QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); m_d->coordinatesConverter->setDocumentOffset(documentOffset); QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); QPointF moveOffset = offsetAfter - offsetBefore; if (!m_d->currentCanvasIsOpenGL) m_d->prescaledProjection->viewportMoved(moveOffset); emit documentOffsetUpdateFinished(); updateCanvas(); } void KisCanvas2::slotConfigChanged() { KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); resetCanvas(cfg.useOpenGL()); slotSetDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget()))); } void KisCanvas2::refetchDataFromImage() { KisImageSP image = this->image(); KisImageBarrierLocker l(image); startUpdateInPatches(image->bounds()); } void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile) { if (m_d->displayColorConverter.monitorProfile() == monitorProfile) return; m_d->displayColorConverter.setMonitorProfile(monitorProfile); KisImageWSP image = this->image(); image->barrierLock(); m_d->canvasWidget->setDisplayProfile(&m_d->displayColorConverter); refetchDataFromImage(); } void KisCanvas2::addDecoration(KisCanvasDecorationSP deco) { m_d->canvasWidget->addDecoration(deco); } KisCanvasDecorationSP KisCanvas2::decoration(const QString& id) const { return m_d->canvasWidget->decoration(id); } QPoint KisCanvas2::documentOrigin() const { /** * In Krita we don't use document origin anymore. * All the centering when needed (vastScrolling < 0.5) is done * automatically by the KisCoordinatesConverter. */ return QPoint(); } QPoint KisCanvas2::documentOffset() const { return m_d->coordinatesConverter->documentOffset(); } void KisCanvas2::setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager) { m_d->popupPalette = new KisPopupPalette(favoriteResourceManager, displayColorConverter()->displayRendererInterface(), m_d->canvasWidget->widget()); m_d->popupPalette->showPopupPalette(false); } void KisCanvas2::setCursor(const QCursor &cursor) { canvasWidget()->setCursor(cursor); } KisAnimationFrameCacheSP KisCanvas2::frameCache() const { return m_d->frameCache; } KisAnimationPlayer *KisCanvas2::animationPlayer() const { return m_d->animationPlayer; } void KisCanvas2::slotSelectionChanged() { KisShapeLayer* shapeLayer = dynamic_cast(viewManager()->activeLayer().data()); if (!shapeLayer) { return; } m_d->shapeManager.selection()->deselectAll(); Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) { m_d->shapeManager.selection()->select(shape); } } bool KisCanvas2::isPopupPaletteVisible() const { if (!m_d->popupPalette) { return false; } return m_d->popupPalette->isVisible(); } void KisCanvas2::setWrapAroundViewingMode(bool value) { KisCanvasDecorationSP infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { infinityDecoration->setVisible(!value); } m_d->canvasWidget->setWrapAroundViewingMode(value); } bool KisCanvas2::wrapAroundViewingMode() const { KisCanvasDecorationSP infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { return !(infinityDecoration->visible()); } return false; } void KisCanvas2::bootstrapFinished() { if (!m_d->bootstrapLodBlocked) return; m_d->bootstrapLodBlocked = false; setLodAllowedInCanvas(m_d->lodAllowedInCanvas); } void KisCanvas2::setLodAllowedInCanvas(bool value) { if (!KisOpenGL::supportsGLSL13()) { qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support"; } m_d->lodAllowedInCanvas = value && m_d->currentCanvasIsOpenGL && KisOpenGL::supportsGLSL13() && (m_d->openGLFilterMode == KisOpenGL::TrilinearFilterMode || m_d->openGLFilterMode == KisOpenGL::HighQualityFiltering); KisImageSP image = this->image(); if (m_d->effectiveLodAllowedInCanvas() != !image->levelOfDetailBlocked()) { image->setLevelOfDetailBlocked(!m_d->effectiveLodAllowedInCanvas()); notifyLevelOfDetailChange(); } KisConfig cfg; cfg.setLevelOfDetailEnabled(m_d->lodAllowedInCanvas); } bool KisCanvas2::lodAllowedInCanvas() const { return m_d->lodAllowedInCanvas; } void KisCanvas2::slotShowPopupPalette(const QPoint &p) { if (!m_d->popupPalette) { return; } m_d->popupPalette->showPopupPalette(p); } KisPaintingAssistantsDecorationSP KisCanvas2::paintingAssistantsDecoration() const { KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration"); return qobject_cast(deco.data()); }