diff --git a/libs/ui/KisView.cpp b/libs/ui/KisView.cpp index d56bd46246..95b3ec4433 100644 --- a/libs/ui/KisView.cpp +++ b/libs/ui/KisView.cpp @@ -1,938 +1,943 @@ /* * 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(); } } } +bool KisView::canvasIsMirrored() const +{ + return d->canvas.xAxisMirrored() || d->canvas.yAxisMirrored(); +} + 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("Soft Proofing doesn't work in floating point."); viewManager()->showFloatingMessage(message,QIcon()); return; } if (softProofing){ message = i18n("Soft Proofing turned on."); } else { message = i18n("Soft Proofing 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("Gamut Warnings don't work in floating point."); viewManager()->showFloatingMessage(message,QIcon()); return; } if (gamutCheck){ message = i18n("Gamut Warnings turned on."); if (!d->softProofing){ message += "\n "+i18n("But Soft Proofing is still off."); } } else { message = i18n("Gamut Warnings 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/KisView.h b/libs/ui/KisView.h index 03902f35f1..7cf9883c7e 100644 --- a/libs/ui/KisView.h +++ b/libs/ui/KisView.h @@ -1,286 +1,288 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2007 Thomas Zander Copyright (C) 2010 Benjamin Port 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. */ #ifndef KIS_VIEW_H #define KIS_VIEW_H #include #include #include #include #include "kritaui_export.h" #include "widgets/kis_floating_message.h" class KisDocument; class KisMainWindow; class KisPrintJob; class KisCanvasController; class KisZoomManager; class KisCanvas2; class KisViewManager; class KisDocument; class KisCanvasResourceProvider; class KisCoordinatesConverter; class KisInputManager; class KoZoomController; class KoZoomController; struct KoPageLayout; class KoCanvasResourceManager; // KDE classes class QAction; class KActionCollection; // Qt classes class QDragEnterEvent; class QDropEvent; class QPrintDialog; class QCloseEvent; class QStatusBar; /** * This class is used to display a @ref KisDocument. * * Multiple views can be attached to one document at a time. */ class KRITAUI_EXPORT KisView : public QWidget { Q_OBJECT public: /** * Creates a new view for the document. */ KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent = 0); ~KisView(); QAction *undoAction() const; QAction *redoAction() const; // Temporary while teasing apart view and mainwindow void setViewManager(KisViewManager *view); KisViewManager *viewManager() const; public: /** * Retrieves the document object of this view. */ KisDocument *document() const; /** * Reset the view to show the given document. */ void setDocument(KisDocument *document); /** * Tells this view that its document has got deleted (called internally) */ void setDocumentDeleted(); /** * In order to print the document represented by this view a new print job should * be constructed that is capable of doing the printing. * The default implementation returns 0, which silently cancels printing. */ KisPrintJob * createPrintJob(); /** * @return the page layout to be used for printing. * Default is the documents layout. * Reimplement if your application needs to use a different layout. */ KoPageLayout pageLayout() const; /** * Create a QPrintDialog based on the @p printJob */ QPrintDialog *createPrintDialog(KisPrintJob *printJob, QWidget *parent); /** * @return the KisMainWindow in which this view is currently. */ KisMainWindow * mainWindow() const; /** * @return the statusbar of the KisMainWindow in which this view is currently. */ QStatusBar * statusBar() const; /** * This adds a widget to the statusbar for this view. * If you use this method instead of using statusBar() directly, * KisView will take care of removing the items when the view GUI is deactivated * and readding them when it is reactivated. * The parameters are the same as QStatusBar::addWidget(). */ void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false); /** * Remove a widget from the statusbar for this view. */ void removeStatusBarItem(QWidget * widget); /** * Return the zoomController for this view. */ KoZoomController *zoomController() const; /// create a list of actions that when activated will change the unit on the document. QList createChangeUnitActions(bool addPixelUnit = false); public: /** * The zoommanager handles everything action-related to zooming */ KisZoomManager *zoomManager() const; /** * The CanvasController decorates the canvas with scrollbars * and knows where to start painting on the canvas widget, i.e., * the document offset. */ KisCanvasController *canvasController() const; KisCanvasResourceProvider *resourceProvider() const; /** * Filters events and sends them to canvas actions. Shared * among all the views/canvases * * NOTE: May be null while initialization! */ KisInputManager* globalInputManager() const; /** * @return the canvas object */ KisCanvas2 *canvasBase() const; /// @return the image this view is displaying KisImageWSP image() const; KisCoordinatesConverter *viewConverter() const; void resetImageSizeAndScroll(bool changeCentering, const QPointF &oldImageStillPoint = QPointF(), const QPointF &newImageStillPoint = QPointF()); void setCurrentNode(KisNodeSP node); KisNodeSP currentNode() const; KisLayerSP currentLayer() const; KisMaskSP currentMask() const; /** * @brief softProofing * @return whether or not we're softproofing in this view. */ bool softProofing(); /** * @brief gamutCheck * @return whether or not we're using gamut warnings in this view. */ bool gamutCheck(); /// Convenience method to get at the active selection (the /// selection of the current layer, or, if that does not exist, /// the global selection. KisSelectionSP selection(); void notifyCurrentStateChanged(bool isCurrent); void setShowFloatingMessage(bool show); void showFloatingMessageImpl(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment); + bool canvasIsMirrored() const; + public Q_SLOTS: /** * Display a message in the status bar (calls QStatusBar::message()) * @todo rename to something more generic */ void slotActionStatusText(const QString &text); /** * End of the message in the status bar (calls QStatusBar::clear()) * @todo rename to something more generic */ void slotClearStatusText(); /** * @brief slotSoftProofing set whether or not we're softproofing in this view. * Will be setting the same in the canvas belonging to the view. */ void slotSoftProofing(bool softProofing); /** * @brief slotGamutCheck set whether or not we're gamutchecking in this view. * Will be setting the same in the vans belonging to the view. */ void slotGamutCheck(bool gamutCheck); bool queryClose(); private Q_SLOTS: void slotImageNodeAdded(KisNodeSP node); void slotContinueAddNode(KisNodeSP newActiveNode); void slotImageNodeRemoved(KisNodeSP node); void slotContinueRemoveNode(KisNodeSP newActiveNode); Q_SIGNALS: // From KisImage void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint); void sigProfileChanged(const KoColorProfile * profile); void sigColorSpaceChanged(const KoColorSpace* cs); void titleModified(QString,bool); void sigContinueAddNode(KisNodeSP newActiveNode); void sigContinueRemoveNode(KisNodeSP newActiveNode); protected: // QWidget overrides void dragEnterEvent(QDragEnterEvent * event); void dropEvent(QDropEvent * event); void closeEvent(QCloseEvent *event); /** * Generate a name for this view. */ QString newObjectName(); public Q_SLOTS: void slotLoadingFinished(); void slotSavingFinished(); void slotImageResolutionChanged(); void slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint); private: class Private; Private * const d; static bool s_firstView; }; #endif diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp index 608cb02b39..2ff3fc1340 100644 --- a/libs/ui/canvas/kis_canvas_controller.cpp +++ b/libs/ui/canvas/kis_canvas_controller.cpp @@ -1,296 +1,290 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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_canvas_controller.h" #include #include #include #include "kis_canvas_decoration.h" #include "kis_paintop_transformation_connector.h" #include "kis_coordinates_converter.h" #include "kis_canvas2.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisView.h" #include "krita_utils.h" #include "kis_signal_compressor_with_param.h" static const int gRulersUpdateDelay = 80 /* ms */; struct KisCanvasController::Private { Private(KisCanvasController *qq) : q(qq), paintOpTransformationConnector(0) { using namespace std::placeholders; std::function callback( std::bind(&KisCanvasController::Private::emitPointerPositionChangedSignals, this, _1)); mousePositionCompressor.reset( new KisSignalCompressorWithParam( gRulersUpdateDelay, callback, KisSignalCompressor::FIRST_ACTIVE)); } QPointer view; KisCoordinatesConverter *coordinatesConverter; KisCanvasController *q; KisPaintopTransformationConnector *paintOpTransformationConnector; QScopedPointer > mousePositionCompressor; void emitPointerPositionChangedSignals(QPoint pointerPos); void updateDocumentSizeAfterTransform(); void showRotationValueOnCanvas(); void showMirrorStateOnCanvas(); }; void KisCanvasController::Private::emitPointerPositionChangedSignals(QPoint pointerPos) { if (!coordinatesConverter) return; QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos); q->proxyObject->emitDocumentMousePositionChanged(documentPos); q->proxyObject->emitCanvasMousePositionChanged(pointerPos); } void KisCanvasController::Private::updateDocumentSizeAfterTransform() { // round the size of the area to the nearest integer instead of getting aligned rect QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size(); q->updateDocumentSize(widgetSize, true); KisCanvas2 *kritaCanvas = dynamic_cast(q->canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->notifyZoomChanged(); } KisCanvasController::KisCanvasController(QPointerparent, KActionCollection * actionCollection) : KoCanvasControllerWidget(actionCollection, parent), m_d(new Private(this)) { m_d->view = parent; } KisCanvasController::~KisCanvasController() { delete m_d; } void KisCanvasController::setCanvas(KoCanvasBase *canvas) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas); Q_ASSERT(kritaCanvas); m_d->coordinatesConverter = const_cast(kritaCanvas->coordinatesConverter()); KoCanvasControllerWidget::setCanvas(canvas); m_d->paintOpTransformationConnector = new KisPaintopTransformationConnector(kritaCanvas, this); } void KisCanvasController::changeCanvasWidget(QWidget *widget) { KoCanvasControllerWidget::changeCanvasWidget(widget); } void KisCanvasController::activate() { KoCanvasControllerWidget::activate(); } void KisCanvasController::keyPressEvent(QKeyEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::keyPressEvent() * to avoid activation of Pan and Default tool activation shortcuts */ Q_UNUSED(event); } void KisCanvasController::wheelEvent(QWheelEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::wheelEvent() * to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea */ Q_UNUSED(event); } bool KisCanvasController::eventFilter(QObject *watched, QEvent *event) { KoCanvasBase *canvas = this->canvas(); if (!canvas || !canvas->canvasWidget() || canvas->canvasWidget() != watched) return false; if (event->type() == QEvent::MouseMove) { QMouseEvent *mevent = static_cast(event); m_d->mousePositionCompressor->start(mevent->pos()); } else if (event->type() == QEvent::TabletMove) { QTabletEvent *tevent = static_cast(event); m_d->mousePositionCompressor->start(tevent->pos()); } return false; } void KisCanvasController::updateDocumentSize(const QSize &sz, bool recalculateCenter) { KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter); emit documentSizeChanged(); } void KisCanvasController::Private::showMirrorStateOnCanvas() { bool isXMirrored = coordinatesConverter->xAxisMirrored(); view->viewManager()-> showFloatingMessage( i18nc("floating message about mirroring", "Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } void KisCanvasController::mirrorCanvas(bool enable) { - KisCanvasDecorationSP decorator = dynamic_cast(this->canvas())->decoration("mirror_axis"); - KIS_ASSERT_RECOVER_RETURN(decorator); - decorator->setVisible(enable); - QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showMirrorStateOnCanvas(); } void KisCanvasController::Private::showRotationValueOnCanvas() { qreal rotationAngle = coordinatesConverter->rotationAngle(); - - view->viewManager()-> showFloatingMessage( i18nc("floating message about rotation", "Rotation: %1° ", KritaUtils::prettyFormatReal(rotationAngle)), QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter); } void KisCanvasController::rotateCanvas(qreal angle) { QPoint newOffset = m_d->coordinatesConverter->rotate(m_d->coordinatesConverter->widgetCenterPoint(), angle); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::rotateCanvasRight15() { rotateCanvas(15.0); } void KisCanvasController::rotateCanvasLeft15() { rotateCanvas(-15.0); } void KisCanvasController::resetCanvasRotation() { QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint()); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::slotToggleWrapAroundMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); if (!canvas()->canvasIsOpenGL() && value) { m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n" "To visualize wrap-around mode, enable OpenGL."), QIcon()); } kritaCanvas->setWrapAroundViewingMode(value); kritaCanvas->image()->setWrapAroundModePermitted(value); } bool KisCanvasController::wrapAroundMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->wrapAroundViewingMode(); } void KisCanvasController::slotToggleLevelOfDetailMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->setLodAllowedInCanvas(value); bool result = levelOfDetailMode(); if (!value || result) { m_d->view->viewManager()->showFloatingMessage( i18n("Instant Preview Mode: %1", result ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } else { QString reason; if (!kritaCanvas->canvasIsOpenGL()) { reason = i18n("Instant Preview is only supported with OpenGL activated"); } else if (kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode || kritaCanvas->openGLFilterMode() == KisOpenGL::NearestFilterMode) { QString filteringMode = kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ? i18n("Bilinear") : i18n("Nearest Neighbour"); reason = i18n("Instant Preview is supported\n in Trilinear or High Quality filtering modes.\nCurrent mode is %1", filteringMode); } m_d->view->viewManager()->showFloatingMessage( i18n("Failed activating Instant Preview mode!\n\n%1", reason), QIcon(), 5000, KisFloatingMessage::Low); } } bool KisCanvasController::levelOfDetailMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->lodAllowedInCanvas(); } diff --git a/libs/ui/canvas/kis_coordinates_converter.cpp b/libs/ui/canvas/kis_coordinates_converter.cpp index 5a34332399..b8329a97a6 100644 --- a/libs/ui/canvas/kis_coordinates_converter.cpp +++ b/libs/ui/canvas/kis_coordinates_converter.cpp @@ -1,435 +1,441 @@ /* * Copyright (c) 2010 Dmitry Kazakov * 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 #include "kis_coordinates_converter.h" #include #include #include #include struct KisCoordinatesConverter::Private { Private(): isXAxisMirrored(false), isYAxisMirrored(false), rotationAngle(0.0) { } - + KisImageWSP image; - + bool isXAxisMirrored; bool isYAxisMirrored; qreal rotationAngle; QSizeF canvasWidgetSize; QPointF documentOffset; - + QTransform flakeToWidget; QTransform imageToDocument; QTransform documentToFlake; QTransform widgetToViewport; }; /** * When vastScrolling value is less than 0.5 it is possible * that the whole scrolling area (viewport) will be smaller than * the size of the widget. In such cases the image should be * centered in the widget. Previously we used a special parameter * documentOrigin for this purpose, now the value for this * centering is calculated dynamically, helping the offset to * center the image inside the widget * * Note that the correction is null when the size of the document * plus vast scrolling reserve is larger than the widget. This * is always true for vastScrolling parameter > 0.5. */ QPointF KisCoordinatesConverter::centeringCorrection() const { KisConfig cfg; QSize documentSize = imageRectInWidgetPixels().toAlignedRect().size(); QPointF dPoint(documentSize.width(), documentSize.height()); QPointF wPoint(m_d->canvasWidgetSize.width(), m_d->canvasWidgetSize.height()); QPointF minOffset = -cfg.vastScrolling() * wPoint; QPointF maxOffset = dPoint - wPoint + cfg.vastScrolling() * wPoint; QPointF range = maxOffset - minOffset; range.rx() = qMin(range.x(), (qreal)0.0); range.ry() = qMin(range.y(), (qreal)0.0); range /= 2; return -range; } /** * The document offset and the position of the top left corner of the * image must always coincide, that is why we need to correct them to * and fro. * * When we change zoom level, the calculation of the new offset is * done by KoCanvasControllerWidget, that is why we just passively fix * the flakeToWidget transform to conform the offset and wait until * the canvas controller will recenter us. * * But when we do our own transformations of the canvas, like rotation * and mirroring, we cannot rely on the centering of the canvas * controller and we do it ourselves. Then we just set new offset and * return its value to be set in the canvas controller explicitly. */ void KisCoordinatesConverter::correctOffsetToTransformation() { m_d->documentOffset = -(imageRectInWidgetPixels().topLeft() - centeringCorrection()).toPoint(); } void KisCoordinatesConverter::correctTransformationToOffset() { QPointF topLeft = imageRectInWidgetPixels().topLeft(); QPointF diff = (-topLeft) - m_d->documentOffset; diff += centeringCorrection(); m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y()); } void KisCoordinatesConverter::recalculateTransformations() { if(!m_d->image) return; m_d->imageToDocument = QTransform::fromScale(1 / m_d->image->xRes(), 1 / m_d->image->yRes()); qreal zoomX, zoomY; KoZoomHandler::zoom(&zoomX, &zoomY); m_d->documentToFlake = QTransform::fromScale(zoomX, zoomY); correctTransformationToOffset(); QRectF irect = imageRectInWidgetPixels(); QRectF wrect = QRectF(QPoint(0,0), m_d->canvasWidgetSize); QRectF rrect = irect & wrect; QTransform reversedTransform = flakeToWidgetTransform().inverted(); QRectF canvasBounds = reversedTransform.mapRect(rrect); QPointF offset = canvasBounds.topLeft(); m_d->widgetToViewport = reversedTransform * QTransform::fromTranslate(-offset.x(), -offset.y()); } KisCoordinatesConverter::KisCoordinatesConverter() : m_d(new Private) { } KisCoordinatesConverter::~KisCoordinatesConverter() { delete m_d; } void KisCoordinatesConverter::setCanvasWidgetSize(QSize size) { m_d->canvasWidgetSize = size; recalculateTransformations(); } void KisCoordinatesConverter::setImage(KisImageWSP image) { m_d->image = image; recalculateTransformations(); } void KisCoordinatesConverter::setDocumentOffset(const QPoint& offset) { QPointF diff = m_d->documentOffset - offset; - + m_d->documentOffset = offset; m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y()); recalculateTransformations(); } QPoint KisCoordinatesConverter::documentOffset() const { return QPoint(int(m_d->documentOffset.x()), int(m_d->documentOffset.y())); } qreal KisCoordinatesConverter::rotationAngle() const { return m_d->rotationAngle; } void KisCoordinatesConverter::setZoom(qreal zoom) { KoZoomHandler::setZoom(zoom); recalculateTransformations(); } qreal KisCoordinatesConverter::effectiveZoom() const { qreal scaleX, scaleY; this->imageScale(&scaleX, &scaleY); if (scaleX != scaleY) { qWarning() << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY)); } // zoom by average of x and y return 0.5 * (scaleX + scaleY); } QPoint KisCoordinatesConverter::rotate(QPointF center, qreal angle) { QTransform rot; rot.rotate(angle); m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y()); m_d->flakeToWidget *= rot; m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y()); m_d->rotationAngle = std::fmod(m_d->rotationAngle + angle, 360.0); correctOffsetToTransformation(); recalculateTransformations(); return m_d->documentOffset.toPoint(); } -QPoint KisCoordinatesConverter::mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis, bool keepOrientation) +QPoint KisCoordinatesConverter::mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis) { + bool keepOrientation = false; // XXX: Keep here for now, maybe some day we can restore the parameter again. + bool doXMirroring = m_d->isXAxisMirrored ^ mirrorXAxis; bool doYMirroring = m_d->isYAxisMirrored ^ mirrorYAxis; qreal scaleX = doXMirroring ? -1.0 : 1.0; qreal scaleY = doYMirroring ? -1.0 : 1.0; QTransform mirror = QTransform::fromScale(scaleX, scaleY); - + QTransform rot; rot.rotate(m_d->rotationAngle); - + m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y()); - - if(keepOrientation) + + if (keepOrientation) { m_d->flakeToWidget *= rot.inverted(); - + } + m_d->flakeToWidget *= mirror; - - if(keepOrientation) + + if (keepOrientation) { m_d->flakeToWidget *= rot; - + } + m_d->flakeToWidget *= QTransform::fromTranslate(center.x(),center.y()); - - if(!keepOrientation && (doXMirroring ^ doYMirroring)) + + + if (!keepOrientation && (doXMirroring ^ doYMirroring)) { m_d->rotationAngle = -m_d->rotationAngle; - + } + m_d->isXAxisMirrored = mirrorXAxis; m_d->isYAxisMirrored = mirrorYAxis; correctOffsetToTransformation(); recalculateTransformations(); return m_d->documentOffset.toPoint(); } bool KisCoordinatesConverter::xAxisMirrored() const { return m_d->isXAxisMirrored; } bool KisCoordinatesConverter::yAxisMirrored() const { return m_d->isYAxisMirrored; } QPoint KisCoordinatesConverter::resetRotation(QPointF center) { QTransform rot; rot.rotate(-m_d->rotationAngle); - + m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(), -center.y()); m_d->flakeToWidget *= rot; m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y()); m_d->rotationAngle = 0.0; correctOffsetToTransformation(); recalculateTransformations(); return m_d->documentOffset.toPoint(); } QTransform KisCoordinatesConverter::imageToWidgetTransform() const{ return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget; } QTransform KisCoordinatesConverter::imageToDocumentTransform() const { return m_d->imageToDocument; } QTransform KisCoordinatesConverter::documentToFlakeTransform() const { return m_d->documentToFlake; } QTransform KisCoordinatesConverter::flakeToWidgetTransform() const { return m_d->flakeToWidget; } QTransform KisCoordinatesConverter::documentToWidgetTransform() const { return m_d->documentToFlake * m_d->flakeToWidget; } QTransform KisCoordinatesConverter::viewportToWidgetTransform() const { return m_d->widgetToViewport.inverted(); } QTransform KisCoordinatesConverter::imageToViewportTransform() const { return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget * m_d->widgetToViewport; } void KisCoordinatesConverter::getQPainterCheckersInfo(QTransform *transform, QPointF *brushOrigin, QPolygonF *polygon) const { /** * Qt has different rounding for QPainter::drawRect/drawImage. * The image is rounded mathematically, while rect in aligned * to the next integer. That causes transparent line appear on * the canvas. * * See: https://bugreports.qt.nokia.com/browse/QTBUG-22827 */ QRectF imageRect = imageRectInViewportPixels(); imageRect.adjust(0,0,-0.5,-0.5); KisConfig cfg; if (cfg.scrollCheckers()) { *transform = viewportToWidgetTransform(); *polygon = imageRect; *brushOrigin = imageToViewport(QPointF(0,0)); } else { *transform = QTransform(); *polygon = viewportToWidgetTransform().map(imageRect); *brushOrigin = QPoint(0,0); } } void KisCoordinatesConverter::getOpenGLCheckersInfo(const QRectF &viewportRect, QTransform *textureTransform, QTransform *modelTransform, QRectF *textureRect, QRectF *modelRect, const bool scrollCheckers) const { if(scrollCheckers) { *textureTransform = QTransform(); *textureRect = QRectF(0, 0, viewportRect.width(),viewportRect.height()); } else { *textureTransform = viewportToWidgetTransform(); *textureRect = viewportRect; } *modelTransform = viewportToWidgetTransform(); *modelRect = viewportRect; } QPointF KisCoordinatesConverter::imageCenterInWidgetPixel() const { if(!m_d->image) return QPointF(); - + QPolygonF poly = imageToWidget(QPolygon(m_d->image->bounds())); return (poly[0] + poly[1] + poly[2] + poly[3]) / 4.0; } // these functions return a bounding rect if the canvas is rotated QRectF KisCoordinatesConverter::imageRectInWidgetPixels() const { if(!m_d->image) return QRectF(); return imageToWidget(m_d->image->bounds()); } QRectF KisCoordinatesConverter::imageRectInViewportPixels() const { if(!m_d->image) return QRectF(); return imageToViewport(m_d->image->bounds()); } QRect KisCoordinatesConverter::imageRectInImagePixels() const { if(!m_d->image) return QRect(); return m_d->image->bounds(); } QRectF KisCoordinatesConverter::imageRectInDocumentPixels() const { if(!m_d->image) return QRectF(); return imageToDocument(m_d->image->bounds()); } QSizeF KisCoordinatesConverter::imageSizeInFlakePixels() const { if(!m_d->image) return QSizeF(); qreal scaleX, scaleY; imageScale(&scaleX, &scaleY); QSize imageSize = m_d->image->size(); return QSizeF(imageSize.width() * scaleX, imageSize.height() * scaleY); } QRectF KisCoordinatesConverter::widgetRectInFlakePixels() const { return widgetToFlake(QRectF(QPoint(0,0), m_d->canvasWidgetSize)); } QPointF KisCoordinatesConverter::flakeCenterPoint() const { QRectF widgetRect = widgetRectInFlakePixels(); return QPointF(widgetRect.left() + widgetRect.width() / 2, widgetRect.top() + widgetRect.height() / 2); } QPointF KisCoordinatesConverter::widgetCenterPoint() const { return QPointF(m_d->canvasWidgetSize.width() / 2.0, m_d->canvasWidgetSize.height() / 2.0); } void KisCoordinatesConverter::imageScale(qreal *scaleX, qreal *scaleY) const { if(!m_d->image) { *scaleX = 1.0; *scaleY = 1.0; return; } // get the x and y zoom level of the canvas qreal zoomX, zoomY; KoZoomHandler::zoom(&zoomX, &zoomY); // Get the KisImage resolution qreal resX = m_d->image->xRes(); qreal resY = m_d->image->yRes(); // Compute the scale factors *scaleX = zoomX / resX; *scaleY = zoomY / resY; } diff --git a/libs/ui/canvas/kis_coordinates_converter.h b/libs/ui/canvas/kis_coordinates_converter.h index a016e37c57..ba02a66d66 100644 --- a/libs/ui/canvas/kis_coordinates_converter.h +++ b/libs/ui/canvas/kis_coordinates_converter.h @@ -1,162 +1,162 @@ /* * Copyright (c) 2010 Dmitry Kazakov * 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. */ #ifndef KIS_COORDINATES_CONVERTER_H #define KIS_COORDINATES_CONVERTER_H #include #include #include "kritaui_export.h" #include "kis_types.h" #define EPSILON 1e-6 #define SCALE_LESS_THAN(scX, scY, value) \ (scX < (value) - EPSILON && scY < (value) - EPSILON) #define SCALE_MORE_OR_EQUAL_TO(scX, scY, value) \ (scX > (value) - EPSILON && scY > (value) - EPSILON) namespace _Private { template struct Traits { typedef T Result; static T map(const QTransform& transform, const T& obj) { return transform.map(obj); } }; template<> struct Traits { typedef QRectF Result; static QRectF map(const QTransform& transform, const QRectF& rc) { return transform.mapRect(rc); } }; - + template<> struct Traits: public Traits { }; template<> struct Traits: public Traits { }; template<> struct Traits: public Traits { }; template<> struct Traits: public Traits { }; } class KRITAUI_EXPORT KisCoordinatesConverter: public KoZoomHandler { public: KisCoordinatesConverter(); ~KisCoordinatesConverter(); void setCanvasWidgetSize(QSize size); void setImage(KisImageWSP image); void setDocumentOffset(const QPoint &offset); QPoint documentOffset() const; qreal rotationAngle() const; - + QPoint rotate(QPointF center, qreal angle); - QPoint mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis, bool keepOrientation=false); + QPoint mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis); bool xAxisMirrored() const; bool yAxisMirrored() const; QPoint resetRotation(QPointF center); - + virtual void setZoom(qreal zoom); /** * A composition of to scale methods: zoom level + image resolution */ qreal effectiveZoom() const; template typename _Private::Traits::Result imageToViewport(const T& obj) const { return _Private::Traits::map(imageToViewportTransform(), obj); } template typename _Private::Traits::Result viewportToImage(const T& obj) const { return _Private::Traits::map(imageToViewportTransform().inverted(), obj); } - + template typename _Private::Traits::Result flakeToWidget(const T& obj) const { return _Private::Traits::map(flakeToWidgetTransform(), obj); } template typename _Private::Traits::Result widgetToFlake(const T& obj) const { return _Private::Traits::map(flakeToWidgetTransform().inverted(), obj); } - + template typename _Private::Traits::Result widgetToViewport(const T& obj) const { return _Private::Traits::map(viewportToWidgetTransform().inverted(), obj); } template typename _Private::Traits::Result viewportToWidget(const T& obj) const { return _Private::Traits::map(viewportToWidgetTransform(), obj); } - + template typename _Private::Traits::Result documentToWidget(const T& obj) const { return _Private::Traits::map(documentToWidgetTransform(), obj); } template typename _Private::Traits::Result widgetToDocument(const T& obj) const { return _Private::Traits::map(documentToWidgetTransform().inverted(), obj); } - + template typename _Private::Traits::Result imageToDocument(const T& obj) const { return _Private::Traits::map(imageToDocumentTransform(), obj); } template typename _Private::Traits::Result documentToImage(const T& obj) const { return _Private::Traits::map(imageToDocumentTransform().inverted(), obj); } template typename _Private::Traits::Result documentToFlake(const T& obj) const { return _Private::Traits::map(documentToFlakeTransform(), obj); } template typename _Private::Traits::Result flakeToDocument(const T& obj) const { return _Private::Traits::map(documentToFlakeTransform().inverted(), obj); } - + template typename _Private::Traits::Result imageToWidget(const T& obj) const { return _Private::Traits::map(imageToWidgetTransform(), obj); } template typename _Private::Traits::Result widgetToImage(const T& obj) const { return _Private::Traits::map(imageToWidgetTransform().inverted(), obj); } QTransform imageToWidgetTransform() const; QTransform imageToDocumentTransform() const; QTransform documentToFlakeTransform() const; QTransform imageToViewportTransform() const; QTransform viewportToWidgetTransform() const; QTransform flakeToWidgetTransform() const; QTransform documentToWidgetTransform() const; void getQPainterCheckersInfo(QTransform *transform, QPointF *brushOrigin, QPolygonF *poligon) const; void getOpenGLCheckersInfo(const QRectF &viewportRect, QTransform *textureTransform, QTransform *modelTransform, QRectF *textureRect, QRectF *modelRect, bool scrollCheckers) const; QPointF imageCenterInWidgetPixel() const; QRectF imageRectInWidgetPixels() const; QRectF imageRectInViewportPixels() const; QSizeF imageSizeInFlakePixels() const; QRectF widgetRectInFlakePixels() const; QRect imageRectInImagePixels() const; QRectF imageRectInDocumentPixels() const; QPointF flakeCenterPoint() const; QPointF widgetCenterPoint() const; void imageScale(qreal *scaleX, qreal *scaleY) const; private: friend class KisZoomAndPanTest; QPointF centeringCorrection() const; void correctOffsetToTransformation(); void correctTransformationToOffset(); void recalculateTransformations(); private: struct Private; Private * const m_d; }; #endif /* KIS_COORDINATES_CONVERTER_H */ diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h index 4d1c4a1883..3dc9ac95ab 100644 --- a/libs/ui/kis_canvas_resource_provider.h +++ b/libs/ui/kis_canvas_resource_provider.h @@ -1,250 +1,248 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * 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. */ #ifndef KIS_CANVAS_RESOURCE_PROVIDER_H_ #define KIS_CANVAS_RESOURCE_PROVIDER_H_ #include #include #include #include #include "kis_types.h" #include "kritaui_export.h" class KisWorkspaceResource; class KoColorProfile; class KoAbstractGradient; class KoResource; class KoCanvasBase; class KisViewManager; class KoPattern; class KisFilterConfiguration; #include /** * KisCanvasResourceProvider contains the per-window current settings that * influence painting, like paintop, color, gradients and so on. */ class KRITAUI_EXPORT KisCanvasResourceProvider : public QObject { Q_OBJECT public: enum Resources { HdrExposure = KoCanvasResourceManager::KritaStart + 1, CurrentPattern, CurrentGradient, CurrentDisplayProfile, CurrentImage, CurrentKritaNode, CurrentPaintOpPreset, CurrentGeneratorConfiguration, CurrentCompositeOp, CurrentEffectiveCompositeOp, LodAvailability, EraserMode, MirrorHorizontal, MirrorVertical, MirrorHorizontalLock, MirrorVerticalLock, MirrorVerticalHideDecorations, MirrorHorizontalHideDecorations, MirrorAxesCenter, Opacity, Flow, Size, HdrGamma, GlobalAlphaLock, DisablePressure, PreviousPaintOpPreset, EffectiveZoom, ///<-Used only by painting tools for non-displaying purposes PresetAllowsLod, SelectionAction, SelectionMode }; KisCanvasResourceProvider(KisViewManager * view); ~KisCanvasResourceProvider(); void setResourceManager(KoCanvasResourceManager *resourceManager); KoCanvasResourceManager* resourceManager(); KoCanvasBase * canvas() const; KoColor bgColor() const; void setBGColor(const KoColor& c); KoColor fgColor() const; void setFGColor(const KoColor& c); float HDRExposure() const; void setHDRExposure(float exposure); float HDRGamma() const; void setHDRGamma(float gamma); bool eraserMode() const; void setEraserMode(bool value); KoPattern *currentPattern() const; KoAbstractGradient *currentGradient() const; KisImageWSP currentImage() const; KisNodeSP currentNode() const; KisPaintOpPresetSP currentPreset() const; void setPaintOpPreset(const KisPaintOpPresetSP preset); KisPaintOpPresetSP previousPreset() const; void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset); KisFilterConfiguration* currentGeneratorConfiguration() const; void setCurrentCompositeOp(const QString& compositeOp); QString currentCompositeOp() const; QList > perspectiveGrids() const; void addPerspectiveGrid(KisAbstractPerspectiveGrid*); void removePerspectiveGrid(KisAbstractPerspectiveGrid*); void clearPerspectiveGrids(); void setMirrorHorizontal(bool mirrorHorizontal); bool mirrorHorizontal() const; void setMirrorVertical(bool mirrorVertical); bool mirrorVertical() const; // options for horizontal and vertical mirror toolbar void setMirrorHorizontalLock(bool isLocked); bool mirrorHorizontalLock(); void setMirrorVerticalLock(bool isLocked); bool mirrorVerticalLock(); void setMirrorVerticalHideDecorations(bool hide); bool mirrorVerticalHideDecorations(); void setMirrorHorizontalHideDecorations(bool hide); bool mirrorHorizontalHideDecorations(); void mirrorVerticalMoveCanvasToCenter(); void mirrorHorizontalMoveCanvasToCenter(); - - void setOpacity(qreal opacity); qreal opacity() const; void setFlow(qreal opacity); qreal flow() const; void setSize(qreal size); qreal size() const; void setGlobalAlphaLock(bool lock); bool globalAlphaLock() const; void setDisablePressure(bool value); bool disablePressure() const; ///Notify that the workspace is saved and settings should be saved to it void notifySavingWorkspace(KisWorkspaceResource* workspace); ///Notify that the workspace is loaded and settings can be read void notifyLoadingWorkspace(KisWorkspaceResource* workspace); int selectionAction(); void setSelectionAction(int action); int selectionMode(); void setSelectionMode(int mode); public Q_SLOTS: void slotSetFGColor(const KoColor& c); void slotSetBGColor(const KoColor& c); void slotPatternActivated(KoResource *pattern); void slotGradientActivated(KoResource *gradient); void slotNodeActivated(const KisNodeSP node); void slotGeneratorConfigurationActivated(KisFilterConfiguration * generatorConfiguration); void slotPainting(); /** * Set the image size in pixels. The resource provider will store * the image size in postscript points. */ // FIXME: this slot doesn't catch the case when image resolution is changed void slotImageSizeChanged(); void slotOnScreenResolutionChanged(); // This is a flag to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible void slotResetEnableFGChange(bool); private Q_SLOTS: void slotCanvasResourceChanged(int key, const QVariant & res); Q_SIGNALS: void sigFGColorChanged(const KoColor &); void sigBGColorChanged(const KoColor &); void sigGradientChanged(KoAbstractGradient *); void sigPatternChanged(KoPattern *); void sigNodeChanged(const KisNodeSP); void sigDisplayProfileChanged(const KoColorProfile *); void sigFGColorUsed(const KoColor&); void sigOnScreenResolutionChanged(qreal scaleX, qreal scaleY); void sigOpacityChanged(qreal); void sigSavingWorkspace(KisWorkspaceResource* workspace); void sigLoadingWorkspace(KisWorkspaceResource* workspace); void sigSelectionActionChanged(const int); void sigSelectionModeChanged(const int); void mirrorModeChanged(); void moveMirrorVerticalCenter(); void moveMirrorHorizontalCenter(); private: KisViewManager * m_view; KoCanvasResourceManager *m_resourceManager; bool m_fGChanged; QList > m_perspectiveGrids; // This is a flag to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible bool m_enablefGChange; }; #endif diff --git a/libs/ui/kis_mirror_manager.cpp b/libs/ui/kis_mirror_manager.cpp index 84fe2b33f7..63b09c1fb3 100644 --- a/libs/ui/kis_mirror_manager.cpp +++ b/libs/ui/kis_mirror_manager.cpp @@ -1,89 +1,75 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * 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_mirror_manager.h" #include "KisViewManager.h" #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_mirror_axis.h" KisMirrorManager::KisMirrorManager(KisViewManager* view) : QObject(view) , m_imageView(0) { - } KisMirrorManager::~KisMirrorManager() { - } void KisMirrorManager::setup(KActionCollection * collection) { m_mirrorCanvas = new KToggleAction(i18n("Mirror View"), this); m_mirrorCanvas->setChecked(false); - //m_mirrorCanvas->setShortcut(QKeySequence(Qt::Key_M)); collection->addAction("mirror_canvas", m_mirrorCanvas); collection->setDefaultShortcut(m_mirrorCanvas, QKeySequence(Qt::Key_M)); updateAction(); } void KisMirrorManager::setView(QPointer imageView) { if (m_imageView) { m_mirrorCanvas->disconnect(); } m_imageView = imageView; - if (m_imageView && !decoration()) { - - m_imageView->canvasBase()->addDecoration(new KisMirrorAxis(m_imageView->viewManager()->resourceProvider(), m_imageView)); - } - if (m_imageView && decoration()) { + if (m_imageView) { connect(m_mirrorCanvas, SIGNAL(toggled(bool)), dynamic_cast(m_imageView->canvasController()), SLOT(mirrorCanvas(bool))); } updateAction(); } void KisMirrorManager::updateAction() { - if (decoration()) { - m_mirrorCanvas->setChecked(decoration()->visible()); + if (m_imageView) { m_mirrorCanvas->setEnabled(true); - } else { - m_mirrorCanvas->setEnabled(false); + m_mirrorCanvas->setChecked(m_imageView->canvasIsMirrored()); } -} - -KisMirrorAxis* KisMirrorManager::decoration() -{ - if (m_imageView && m_imageView->canvasBase()) { - return dynamic_cast(m_imageView->canvasBase()->decoration("mirror_axis").data()); + else { + m_mirrorCanvas->setEnabled(false); + m_mirrorCanvas->setChecked(false); } - return 0; } - diff --git a/libs/ui/kis_mirror_manager.h b/libs/ui/kis_mirror_manager.h index 40524803cc..d8db64d56e 100644 --- a/libs/ui/kis_mirror_manager.h +++ b/libs/ui/kis_mirror_manager.h @@ -1,55 +1,54 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * 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. */ #ifndef KIS_MIRROR_MANAGER_H #define KIS_MIRROR_MANAGER_H #include #include #include "KisView.h" class KisViewManager; class KActionCollection; class KisMirrorAxis; class KisMirrorManager : public QObject { Q_OBJECT public: KisMirrorManager(KisViewManager* view); virtual ~KisMirrorManager(); void setup(KActionCollection* collection); void setView(QPointer imageView); private Q_SLOTS: void updateAction(); private: - KisMirrorAxis* decoration(); - + QPointer m_imageView; QAction *m_mirrorCanvas; }; #endif // KIS_PAINTING_ASSISTANTS_MANAGER_H diff --git a/libs/ui/tests/kis_coordinates_converter_test.cpp b/libs/ui/tests/kis_coordinates_converter_test.cpp index 89efd135d5..ffd079f162 100644 --- a/libs/ui/tests/kis_coordinates_converter_test.cpp +++ b/libs/ui/tests/kis_coordinates_converter_test.cpp @@ -1,273 +1,273 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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_coordinates_converter_test.h" #include #include #include #include #include #include #include "kis_coordinates_converter.h" void initImage(KisImageSP *image, KoZoomHandler *zoomHandler) { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); *image = new KisImage(0, 1000, 1000, cs, "projection test"); (*image)->setResolution(100, 100); - + zoomHandler->setResolution(100, 100); } void KisCoordinatesConverterTest::testConversion() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); converter.setImage(image); converter.setDocumentOffset(QPoint(20,20)); converter.setCanvasWidgetSize(QSize(500,500)); converter.setZoom(1.); - + QRectF testRect(100,100,100,100); QCOMPARE(converter.imageToViewport(testRect), QRectF(80,80,100,100)); QCOMPARE(converter.viewportToImage(testRect), QRectF(120,120,100,100)); QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100)); QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100)); QCOMPARE(converter.widgetToDocument(testRect), QRectF(1.20,1.20,1,1)); QCOMPARE(converter.documentToWidget(testRect), QRectF(9980,9980,10000,10000)); QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1)); QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000)); converter.setZoom(0.5); QCOMPARE(converter.imageToViewport(testRect), QRectF(30,30,50,50)); QCOMPARE(converter.viewportToImage(testRect), QRectF(240,240,200,200)); QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100)); QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100)); QCOMPARE(converter.widgetToDocument(testRect), QRectF(2.4,2.4,2,2)); QCOMPARE(converter.documentToWidget(testRect), QRectF(4980,4980,5000,5000)); QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1)); QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000)); } void KisCoordinatesConverterTest::testImageCropping() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); converter.setImage(image); converter.setDocumentOffset(QPoint(0,0)); converter.setCanvasWidgetSize(QSize(500,500)); converter.setZoom(1.); // we do NOT crop here QCOMPARE(converter.viewportToImage(QRectF(900,900,200,200)), QRectF(900,900,200,200)); } #define CHECK_TRANSFORM(trans,test,ref) QCOMPARE(trans.map(test).boundingRect(), ref) //#define CHECK_TRANSFORM(trans,test,ref) dbgKrita << trans.map(test).boundingRect() void KisCoordinatesConverterTest::testTransformations() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); converter.setImage(image); converter.setDocumentOffset(QPoint(20,30)); converter.setCanvasWidgetSize(QSize(500,500)); QRectF testRect(100,100,100,100); QTransform imageToWidget; QTransform documentToWidget; QTransform flakeToWidget; QTransform viewportToWidget; converter.setZoom(1.); imageToWidget = converter.imageToWidgetTransform(); documentToWidget = converter.documentToWidgetTransform(); flakeToWidget = converter.flakeToWidgetTransform(); viewportToWidget = converter.viewportToWidgetTransform(); CHECK_TRANSFORM(imageToWidget, testRect, QRectF(80,70,100,100)); CHECK_TRANSFORM(documentToWidget, testRect, QRectF(9980,9970,10000,10000)); CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100)); CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100)); converter.setZoom(0.5); imageToWidget = converter.imageToWidgetTransform(); documentToWidget = converter.documentToWidgetTransform(); flakeToWidget = converter.flakeToWidgetTransform(); viewportToWidget = converter.viewportToWidgetTransform(); CHECK_TRANSFORM(imageToWidget, testRect, QRectF(30,20,50,50)); CHECK_TRANSFORM(documentToWidget, testRect, QRectF(4980,4970,5000,5000)); CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100)); CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100)); } void KisCoordinatesConverterTest::testConsistency() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); converter.setImage(image); converter.setDocumentOffset(QPoint(20,30)); converter.setCanvasWidgetSize(QSize(500,500)); QRectF testRect(100,100,100,100); QTransform imageToWidget; QTransform documentToWidget; QTransform viewportToWidget; converter.setZoom(0.5); imageToWidget = converter.imageToWidgetTransform(); documentToWidget = converter.documentToWidgetTransform(); viewportToWidget = converter.viewportToWidgetTransform(); QRectF fromImage = converter.viewportToWidget(converter.imageToViewport(testRect)); QRectF fromDocument = converter.documentToWidget(testRect); QRectF fromViewport = converter.viewportToWidget(testRect); CHECK_TRANSFORM(imageToWidget, testRect, fromImage); CHECK_TRANSFORM(documentToWidget, testRect, fromDocument); CHECK_TRANSFORM(viewportToWidget, testRect, fromViewport); } void KisCoordinatesConverterTest::testRotation() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); QSize widgetSize(1000,500); QRectF testRect(800, 100, 300, 300); converter.setImage(image); converter.setDocumentOffset(QPoint(0,0)); converter.setCanvasWidgetSize(widgetSize); converter.rotate(converter.widgetCenterPoint(), 30); converter.setZoom(1.); QTransform viewportToWidget = converter.viewportToWidgetTransform(); QRectF boundingRect = viewportToWidget.mapRect(testRect); QRectF directRect = converter.viewportToWidget(testRect); QCOMPARE(boundingRect, directRect); QRectF referenceRect(QPointF(742.82,53.5898), QSizeF(409.808,409.808)); #define FUZZY(a,b) ((a)-(b) < 0.01) QVERIFY(FUZZY(boundingRect.top(), referenceRect.top())); QVERIFY(FUZZY(boundingRect.left(), referenceRect.left())); QVERIFY(FUZZY(boundingRect.width(), referenceRect.width())); QVERIFY(FUZZY(boundingRect.height(), referenceRect.height())); } void KisCoordinatesConverterTest::testMirroring() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); QSize widgetSize(500,400); - QSize flakeSize(1000,1000); +// QSize flakeSize(1000,1000); QRectF testRect(300, 100, 200, 200); converter.setImage(image); converter.setDocumentOffset(QPoint(200,100)); converter.setCanvasWidgetSize(widgetSize); - QTransform imageToWidget; - QTransform documentToWidget; - QTransform flakeToWidget; - QTransform viewportToWidget; +// QTransform imageToWidget; +// QTransform documentToWidget; +// QTransform flakeToWidget; +// QTransform viewportToWidget; converter.mirror(converter.imageCenterInWidgetPixel(), true, false); converter.setZoom(1.); // image pixels == flake pixels QRectF viewportRect = converter.imageToViewport(testRect); QRectF widgetRect = converter.viewportToWidget(viewportRect); QCOMPARE(widgetRect, QRectF(300,0,200,200)); QCOMPARE(viewportRect, QRectF(0,0,200,200)); QRectF roundTrip = converter.viewportToWidget(widgetRect); QCOMPARE(roundTrip, viewportRect); } void KisCoordinatesConverterTest::testMirroringCanvasBiggerThanImage() { KisImageSP image; KisCoordinatesConverter converter; initImage(&image, &converter); QSize widgetSize(2000,2000); - QSize flakeSize(1000,1000); +// QSize flakeSize(1000,1000); QRectF testRect(300, 100, 200, 200); converter.setImage(image); converter.setDocumentOffset(QPoint(-50,-50)); converter.setCanvasWidgetSize(widgetSize); - QTransform imageToWidget; - QTransform documentToWidget; - QTransform flakeToWidget; - QTransform viewportToWidget; +// QTransform imageToWidget; +// QTransform documentToWidget; +// QTransform flakeToWidget; +// QTransform viewportToWidget; converter.mirror(converter.imageCenterInWidgetPixel(), true, false); converter.setZoom(1.); // image pixels == flake pixels QRectF viewportRect = converter.imageToViewport(testRect); QRectF widgetRect = converter.viewportToWidget(viewportRect); QCOMPARE(widgetRect, QRectF(550,150,200,200)); QCOMPARE(viewportRect, QRectF(300,100,200,200)); } QTEST_MAIN(KisCoordinatesConverterTest)