Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_guides_manager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QMenu>
10#include <QGuiApplication>
12#include <KoRuler.h>
13#include "kis_guides_config.h"
14#include "kis_action_manager.h"
15#include "kis_action.h"
16#include "kis_signals_blocker.h"
19#include "kis_zoom_manager.h"
21#include "KisViewManager.h"
22#include "KisDocument.h"
23#include "kis_algebra_2d.h"
24#include <KoSnapGuide.h>
27#include "kis_snap_config.h"
28#include "kis_canvas2.h"
31
33{
35 : q(_q),
36 decoration(0),
37 invalidGuide(Qt::Horizontal, -1),
39 cursorSwitched(false),
41 shouldSetModified(false) {}
42
44
50
51 typedef QPair<Qt::Orientation, int> GuideHandle;
52
53 GuideHandle findGuide(const QPointF &docPos);
54 bool isGuideValid(const GuideHandle &h);
55 qreal guideValue(const GuideHandle &h);
56 void setGuideValue(const GuideHandle &h, qreal value);
57 void deleteGuide(const GuideHandle &h);
59
60 bool updateCursor(const QPointF &docPos, bool forceDisableCursor = false);
61
62 void initDragStart(const GuideHandle &guide,
63 const QPointF &dragStart,
64 qreal guideValue,
65 bool snapToStart);
66 bool mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers);
67 bool mouseReleaseHandler(const QPointF &docPos);
68
70 QPointF alignToPixels(const QPointF docPoint);
71 QPointF getDocPointFromEvent(QEvent *event);
72 Qt::MouseButton getButtonFromEvent(QEvent *event);
73 QAction* createShortenedAction(const QString &text, const QString &parentId, QObject *parent);
74 void syncAction(const QString &actionName, bool value);
75 bool needsUndoCommand();
77
79
81 QCursor oldCursor;
82
83 QPointF dragStartDoc;
86
88
90};
91
93 : QObject(parent),
94 m_d(new Private(this))
95{
96}
97
101
103{
104 if (config == m_d->guidesConfig) return;
105 setGuidesConfigImpl(config, !config.hasSamePositionAs(m_d->guidesConfig));
107}
108
110{
111 if (config == m_d->guidesConfig) return;
112 setGuidesConfigImpl(config, false);
113}
114
116{
117 const KisGuidesConfig &value = m_d->guidesConfig;
118
119 KisDocument *doc = m_d->view ? m_d->view->document() : 0;
120 if (doc) {
121 KisSignalsBlocker b(doc);
122
123 // we've made KisChangeGuidesCommand post-exec, so in all situations
124 // we will replace the whole config
126
127 value.saveStaticData();
128 }
129
130 m_d->shouldSetModified = false;
131}
132
134{
135 m_d->guidesConfig = value;
136
137 if (m_d->decoration && value != m_d->decoration->guidesConfig()) {
138 m_d->decoration->setVisible(value.showGuides());
139 m_d->decoration->setGuidesConfig(value);
140 }
141
142 m_d->shouldSetModified |= emitModified;
143
144 const bool shouldFilterEvent =
145 value.showGuides() && !value.lockGuides() && value.hasGuides();
146
147 attachEventFilterImpl(shouldFilterEvent);
149
150 if (!m_d->isGuideValid(m_d->currentGuide)) {
151 m_d->updateSnappingStatus(value);
152 }
153
154 if (m_d->view) {
155 m_d->view->document()->setUnit(KoUnit(m_d->guidesConfig.unitType()));
156 m_d->view->viewManager()->actionManager()->actionByName("ruler_pixel_multiple2")->setChecked(value.rulersMultiple2());
157 }
158
159 Q_EMIT sigRequestUpdateGuidesConfig(m_d->guidesConfig);
160}
161
163{
164 if (!m_d->view) return;
165
166 KisInputManager *inputManager = m_d->view->globalInputManager();
167 if (inputManager) {
168 if (value) {
169 inputManager->attachPriorityEventFilter(this, 100);
170 } else {
171 inputManager->detachPriorityEventFilter(this);
172 }
173 }
174}
175
176void KisGuidesManager::Private::syncAction(const QString &actionName, bool value)
177{
178 KisActionManager *actionManager = view->viewManager()->actionManager();
179 KisAction *action = actionManager->actionByName(actionName);
181 KisSignalsBlocker b(action);
182 action->setChecked(value);
183}
184
186{
187 return !(oldGuidesConfig.hasSamePositionAs(guidesConfig));
188}
189
191{
192 KisDocument *doc = view ? view->document() : 0;
193 if (doc && needsUndoCommand()) {
194 KUndo2Command *cmd = new KisChangeGuidesCommand(doc, oldGuidesConfig, guidesConfig);
195 view->canvasBase()->addCommand(cmd);
196 }
197}
198
200{
201 if (!m_d->view) return;
202
203 m_d->syncAction("view_show_guides", m_d->guidesConfig.showGuides());
204 m_d->syncAction("view_lock_guides", m_d->guidesConfig.lockGuides());
205 m_d->syncAction("view_snap_to_guides", m_d->guidesConfig.snapToGuides());
206
207 m_d->syncAction("view_snap_orthogonal", m_d->snapConfig.orthogonal());
208 m_d->syncAction("view_snap_node", m_d->snapConfig.node());
209 m_d->syncAction("view_snap_extension", m_d->snapConfig.extension());
210 m_d->syncAction("view_snap_intersection", m_d->snapConfig.intersection());
211 m_d->syncAction("view_snap_bounding_box", m_d->snapConfig.boundingBox());
212 m_d->syncAction("view_snap_image_bounds", m_d->snapConfig.imageBounds());
213 m_d->syncAction("view_snap_image_center", m_d->snapConfig.imageCenter());
214 m_d->syncAction("view_snap_to_pixel",m_d->snapConfig.toPixel());
215}
216
218{
219 if (!view) return;
220
221 KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
222 KisSnapLineStrategy *guidesSnap = 0;
223
224 if (value.snapToGuides()) {
226 guidesSnap->setHorizontalLines(value.horizontalGuideLines());
227 guidesSnap->setVerticalLines(value.verticalGuideLines());
228 }
229
232
233 snapGuide->enableSnapStrategy(KoSnapGuide::OrthogonalSnapping, snapConfig.orthogonal());
234 snapGuide->enableSnapStrategy(KoSnapGuide::NodeSnapping, snapConfig.node());
235 snapGuide->enableSnapStrategy(KoSnapGuide::ExtensionSnapping, snapConfig.extension());
236 snapGuide->enableSnapStrategy(KoSnapGuide::IntersectionSnapping, snapConfig.intersection());
237 snapGuide->enableSnapStrategy(KoSnapGuide::BoundingBoxSnapping, snapConfig.boundingBox());
238 snapGuide->enableSnapStrategy(KoSnapGuide::DocumentBoundsSnapping, snapConfig.imageBounds());
239 snapGuide->enableSnapStrategy(KoSnapGuide::DocumentCenterSnapping, snapConfig.imageCenter());
240 snapGuide->enableSnapStrategy(KoSnapGuide::PixelSnapping, snapConfig.toPixel());
241
242 snapConfig.saveStaticData();
243}
244
246{
247 return m_d->guidesConfig.showGuides();
248}
249
251{
252 m_d->guidesConfig.setShowGuides(value);
253 setGuidesConfigImpl(m_d->guidesConfig);
255}
256
258{
259 return m_d->guidesConfig.lockGuides();
260}
261
263{
264 m_d->guidesConfig.setLockGuides(value);
265 setGuidesConfigImpl(m_d->guidesConfig);
267}
268
270{
271 return m_d->guidesConfig.snapToGuides();
272}
273
275{
276 m_d->guidesConfig.setSnapToGuides(value);
277 setGuidesConfigImpl(m_d->guidesConfig);
279}
280
282{
283 return m_d->guidesConfig.rulersMultiple2();
284}
285
287{
288 m_d->guidesConfig.setRulersMultiple2(value);
289 setGuidesConfigImpl(m_d->guidesConfig);
291}
292
294{
295 return m_d->guidesConfig.unitType();
296}
297
299{
300 m_d->guidesConfig.setUnitType(type);
301 setGuidesConfigImpl(m_d->guidesConfig, false);
303}
304
306{
307 KisAction *action = 0;
308
309 action = actionManager->createAction("view_show_guides");
310 connect(action, SIGNAL(toggled(bool)), this, SLOT(setShowGuides(bool)));
311
312 action = actionManager->createAction("view_lock_guides");
313 connect(action, SIGNAL(toggled(bool)), this, SLOT(setLockGuides(bool)));
314
315 action = actionManager->createAction("view_snap_to_guides");
316 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapToGuides(bool)));
317
318 action = actionManager->createAction("show_snap_options_popup");
319 connect(action, SIGNAL(triggered()), this, SLOT(slotShowSnapOptions()));
320
321 action = actionManager->createAction("view_snap_orthogonal");
322 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapOrthogonal(bool)));
323
324 action = actionManager->createAction("view_snap_node");
325 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapNode(bool)));
326
327 action = actionManager->createAction("view_snap_extension");
328 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapExtension(bool)));
329
330 action = actionManager->createAction("view_snap_intersection");
331 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapIntersection(bool)));
332
333 action = actionManager->createAction("view_snap_bounding_box");
334 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapBoundingBox(bool)));
335
336 action = actionManager->createAction("view_snap_image_bounds");
337 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageBounds(bool)));
338
339 action = actionManager->createAction("view_snap_image_center");
340 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageCenter(bool)));
341
342 action = actionManager->createAction("view_snap_to_pixel");
343 connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapToPixel(bool)));
344
345 m_d->updateSnappingStatus(m_d->guidesConfig);
347}
348
350{
351 if (m_d->view) {
352 KoSnapGuide *snapGuide = m_d->view->canvasBase()->snapGuide();
355
357
358 m_d->decoration = 0;
359 m_d->viewConnections.clear();
361 }
362
363 m_d->view = view;
364
365 if (m_d->view) {
366 KisGuidesDecoration* decoration = qobject_cast<KisGuidesDecoration*>(m_d->view->canvasBase()->decoration(GUIDES_DECORATION_ID).data());
367 if (!decoration) {
368 decoration = new KisGuidesDecoration(m_d->view);
369 m_d->view->canvasBase()->addDecoration(decoration);
370 }
371 m_d->decoration = decoration;
372
373 m_d->guidesConfig = m_d->view->document()->guidesConfig();
374 setGuidesConfigImpl(m_d->guidesConfig, false);
375
376 m_d->viewConnections.addUniqueConnection(
377 m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation,QPoint)),
378 this, SLOT(slotGuideCreationInProgress(Qt::Orientation,QPoint)));
379
380 m_d->viewConnections.addUniqueConnection(
381 m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation,QPoint)),
382 this, SLOT(slotGuideCreationFinished(Qt::Orientation,QPoint)));
383
384 m_d->viewConnections.addUniqueConnection(
385 m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation,QPoint)),
386 this, SLOT(slotGuideCreationInProgress(Qt::Orientation,QPoint)));
387
388 m_d->viewConnections.addUniqueConnection(
389 m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation,QPoint)),
390 this, SLOT(slotGuideCreationFinished(Qt::Orientation,QPoint)));
391
392 m_d->viewConnections.addUniqueConnection(
393 m_d->view->document(), SIGNAL(sigGuidesConfigChanged(KisGuidesConfig)),
395 }
396}
397
400{
401 const int snapRadius = 16;
402 const KoViewConverter *converter = view->canvasBase()->viewConverter();
403 const QPointF docPosView = converter->documentToView(docPos);
404
405 GuideHandle nearestGuide = invalidGuide;
406 qreal nearestRadius = std::numeric_limits<int>::max();
407
408
409 for (int i = 0; i < guidesConfig.horizontalGuideLines().size(); i++) {
410 const QPointF guideCoord = {0, guidesConfig.horizontalGuideLines()[i]};
411 const qreal guide = converter->documentToView(guideCoord).y();
412 const qreal radius = qAbs(docPosView.y() - guide);
413 if (radius < snapRadius && radius < nearestRadius) {
414 nearestGuide = GuideHandle(Qt::Horizontal, i);
415 nearestRadius = radius;
416 }
417 }
418
419 for (int i = 0; i < guidesConfig.verticalGuideLines().size(); i++) {
420 const QPointF guideCoord = {guidesConfig.verticalGuideLines()[i], 0};
421 const qreal guide = converter->documentToView(guideCoord).x();
422 const qreal radius = qAbs(docPosView.x() - guide);
423 if (radius < snapRadius && radius < nearestRadius) {
424 nearestGuide = GuideHandle(Qt::Vertical, i);
425 nearestRadius = radius;
426 }
427 }
428
429 return nearestGuide;
430}
431
433{
434 return h.second >= 0;
435}
436
438{
439 return h.first == Qt::Horizontal ?
440 guidesConfig.horizontalGuideLines()[h.second] :
441 guidesConfig.verticalGuideLines()[h.second];
442}
443
445{
446 if (h.first == Qt::Horizontal) {
447 QList<qreal> guides = guidesConfig.horizontalGuideLines();
448 guides[h.second] = value;
449 guidesConfig.setHorizontalGuideLines(guides);
450 } else {
451 QList<qreal> guides = guidesConfig.verticalGuideLines();
452 guides[h.second] = value;
453 guidesConfig.setVerticalGuideLines(guides);
454 }
455}
456
458{
459 if (h.first == Qt::Horizontal) {
460 QList<qreal> guides = guidesConfig.horizontalGuideLines();
461 guides.removeAt(h.second);
462 guidesConfig.setHorizontalGuideLines(guides);
463 } else {
464 QList<qreal> guides = guidesConfig.verticalGuideLines();
465 guides.removeAt(h.second);
466 guidesConfig.setVerticalGuideLines(guides);
467 }
468}
469
470bool KisGuidesManager::Private::updateCursor(const QPointF &docPos, bool forceDisableCursor)
471{
472 KisCanvas2 *canvas = view->canvasBase();
473
474 const GuideHandle guide = findGuide(docPos);
475 const bool guideValid = isGuideValid(guide) && !forceDisableCursor;
476
477 if (guideValid && !cursorSwitched) {
478 oldCursor = canvas->canvasWidget()->cursor();
479 }
480
481 if (guideValid) {
482 cursorSwitched = true;
483 QCursor newCursor = guide.first == Qt::Horizontal ?
484 Qt::SizeVerCursor : Qt::SizeHorCursor;
485 canvas->canvasWidget()->setCursor(newCursor);
486 }
487
488 if (!guideValid && cursorSwitched) {
489 canvas->canvasWidget()->setCursor(oldCursor);
490 cursorSwitched = false;
491 }
492
493 return guideValid;
494}
495
497 const QPointF &dragStart,
498 qreal guideValue,
499 bool snapToStart)
500{
501 currentGuide = guide;
502 dragStartDoc = dragStart;
503 dragStartGuidePos = guideValue;
504 dragPointerOffset =
505 guide.first == Qt::Horizontal ?
506 QPointF(0, dragStartGuidePos - dragStartDoc.y()) :
507 QPointF(dragStartGuidePos - dragStartDoc.x(), 0);
508
509 KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
510 snapGuide->reset();
511
512 if (snapToStart) {
514 strategy->addLine(guide.first, guideValue);
515 snapGuide->addCustomSnapStrategy(strategy);
516 }
517}
518
519QPointF KisGuidesManager::Private::alignToPixels(const QPointF docPoint)
520{
521 KisCanvas2 *canvas = view->canvasBase();
522 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
523 QPoint imagePoint = converter->documentToImage(docPoint).toPoint();
524 return converter->imageToDocument(imagePoint);
525}
526
527bool KisGuidesManager::Private::mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers)
528{
529 if (isGuideValid(currentGuide)) {
530 KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
531 const QPointF snappedPos = snapGuide->snap(docPos, dragPointerOffset, modifiers);
532 const QPointF offset = snappedPos - dragStartDoc;
533 const qreal newValue = dragStartGuidePos +
534 (currentGuide.first == Qt::Horizontal ?
535 offset.y() : offset.x());
536
537 setGuideValue(currentGuide, newValue);
538 q->setGuidesConfigImpl(guidesConfig);
539
540 const KisCoordinatesConverter *converter = view->canvasBase()->coordinatesConverter();
541 if(currentGuide.first == Qt::Horizontal) {
542 view->canvasBase()->viewManager()->showFloatingMessage(
543 i18n("Y: %1 px", converter->documentToImage(docPos).toPoint().y()), QIcon(), 1000
544 , KisFloatingMessage::High, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignVCenter);
545 }
546 else {
547 view->canvasBase()->viewManager()->showFloatingMessage(
548 i18n("X: %1 px", converter->documentToImage(docPos).toPoint().x()), QIcon(), 1000
549 , KisFloatingMessage::High, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignVCenter);
550 }
551 }
552
553 return updateCursor(docPos);
554}
555
557{
558 bool result = false;
559 KisCanvas2 *canvas = view->canvasBase();
560 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
561
562 if (isGuideValid(currentGuide)) {
563 const QRectF docRect = converter->imageRectInDocumentPixels();
564 // TODO: enable work rect after we fix painting guides
565 // outside canvas in openGL mode
566 const QRectF workRect = KisAlgebra2D::blowRect(docRect, 0 /*0.2*/);
567 if (!workRect.contains(docPos)) {
568 deleteGuide(currentGuide);
569 q->setGuidesConfigImpl(guidesConfig);
570
577 result = true;
578 }
579
580 currentGuide = invalidGuide;
581 dragStartDoc = QPointF();
582 dragPointerOffset = QPointF();
583 dragStartGuidePos = 0;
584
585 KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
586 snapGuide->reset();
587
588 updateSnappingStatus(guidesConfig);
589 }
590
591 q->slotUploadConfigToDocument();
592 createUndoCommandIfNeeded();
593
594 return updateCursor(docPos) | result;
595}
596
598{
599 QPointF result;
600
601 KisCanvas2 *canvas = view->canvasBase();
602 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
603
604 if (event->type() == QEvent::Enter) {
605 QEnterEvent *enterEvent = static_cast<QEnterEvent*>(event);
606 result = alignToPixels(converter->widgetToDocument(enterEvent->pos()));
607 } else if (event->type() == QEvent::MouseMove ||
608 event->type() == QEvent::MouseButtonPress ||
609 event->type() == QEvent::MouseButtonRelease) {
610
611 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
612 result = alignToPixels(converter->widgetToDocument(mouseEvent->pos()));
613
614 } else if (event->type() == QEvent::TabletMove ||
615 event->type() == QEvent::TabletPress ||
616 event->type() == QEvent::TabletRelease) {
617
618 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
619 result = alignToPixels(converter->widgetToDocument(tabletEvent->pos()));
620 } else {
621 // we shouldn't silently return QPointF(0,0), higher level code may
622 // snap to some unexpected guide
623 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "event type is not supported!");
624 }
625
626 return result;
627}
628
630{
631 Qt::MouseButton button = Qt::NoButton;
632
633 if (event->type() == QEvent::MouseMove ||
634 event->type() == QEvent::MouseButtonPress ||
635 event->type() == QEvent::MouseButtonRelease) {
636
637 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
638 button = mouseEvent->button();
639
640 } else if (event->type() == QEvent::TabletMove ||
641 event->type() == QEvent::TabletPress ||
642 event->type() == QEvent::TabletRelease) {
643
644 QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
645 button = tabletEvent->button();
646 }
647
648 return button;
649}
650
651bool KisGuidesManager::eventFilter(QObject *obj, QEvent *event)
652{
653 if (!m_d->view || obj != m_d->view->canvasBase()->canvasWidget()) return false;
654
655 bool retval = false;
656
657 switch (event->type()) {
658 case QEvent::Leave:
659 m_d->updateCursor(QPointF(), true);
660 break;
661 case QEvent::Enter:
662 case QEvent::TabletMove:
663 case QEvent::MouseMove: {
664 const QPointF docPos = m_d->getDocPointFromEvent(event);
665 const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers();
666
667 // we should never eat Enter events, input manager may get crazy about it
668 retval = m_d->mouseMoveHandler(docPos, modifiers) && event->type() != QEvent::Enter;
669
670 break;
671 }
672 case QEvent::TabletPress:
673 case QEvent::MouseButtonPress: {
674 if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break;
675
676 const QPointF docPos = m_d->getDocPointFromEvent(event);
677 const Private::GuideHandle guide = m_d->findGuide(docPos);
678 const bool guideValid = m_d->isGuideValid(guide);
679
680 if (guideValid) {
681 m_d->oldGuidesConfig = m_d->guidesConfig;
682 m_d->initDragStart(guide, docPos, m_d->guideValue(guide), true);
683 }
684
685 retval = m_d->updateCursor(docPos);
686
687 break;
688 }
689 case QEvent::TabletRelease:
690 case QEvent::MouseButtonRelease: {
691 if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break;
692
693 const QPointF docPos = m_d->getDocPointFromEvent(event);
694 retval = m_d->mouseReleaseHandler(docPos);
695
696 break;
697 }
698 default:
699 break;
700 }
701
702 return !retval ? QObject::eventFilter(obj, event) : true;
703}
704
705void KisGuidesManager::slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos)
706{
707 if (m_d->guidesConfig.lockGuides()) return;
708
709 KisCanvas2 *canvas = m_d->view->canvasBase();
710 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
711 const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos);
712 const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos));
713
714 if (m_d->isGuideValid(m_d->currentGuide)) {
715 const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers();
716 m_d->mouseMoveHandler(docPos, modifiers);
717 } else {
718 m_d->guidesConfig.setShowGuides(true);
719
720 m_d->oldGuidesConfig = m_d->guidesConfig;
721
722 if (orientation == Qt::Horizontal) {
723 QList<qreal> guides = m_d->guidesConfig.horizontalGuideLines();
724 guides.append(docPos.y());
725 m_d->currentGuide.first = orientation;
726 m_d->currentGuide.second = guides.size() - 1;
727 m_d->guidesConfig.setHorizontalGuideLines(guides);
728 m_d->initDragStart(m_d->currentGuide, docPos, docPos.y(), false);
729 } else {
730 QList<qreal> guides = m_d->guidesConfig.verticalGuideLines();
731 guides.append(docPos.x());
732 m_d->currentGuide.first = orientation;
733 m_d->currentGuide.second = guides.size() - 1;
734 m_d->guidesConfig.setVerticalGuideLines(guides);
735 m_d->initDragStart(m_d->currentGuide, docPos, docPos.x(), false);
736 }
737
738 setGuidesConfigImpl(m_d->guidesConfig);
739 }
740}
741
742void KisGuidesManager::slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos)
743{
744 Q_UNUSED(orientation);
745 if (m_d->guidesConfig.lockGuides()) return;
746
747 KisCanvas2 *canvas = m_d->view->canvasBase();
748 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
749 const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos);
750 const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos));
751
752 m_d->mouseReleaseHandler(docPos);
753}
754
755QAction* KisGuidesManager::Private::createShortenedAction(const QString &text, const QString &parentId, QObject *parent)
756{
757 KisActionManager *actionManager = view->viewManager()->actionManager();
758 QAction *action = 0;
759 KisAction *parentAction = 0;
760
761 action = new QAction(text, parent);
762 action->setCheckable(true);
763 parentAction = actionManager->actionByName(parentId);
764 action->setChecked(parentAction->isChecked());
765 connect(action, SIGNAL(toggled(bool)), parentAction, SLOT(setChecked(bool)));
766
767 return action;
768}
769
771{
772 const QPoint pos = QCursor::pos();
773 QMenu menu;
774
775 menu.addSection(i18n("Snap to:"));
776 menu.addAction(m_d->createShortenedAction(i18n("Grid"), "view_snap_to_grid", &menu));
777 menu.addAction(m_d->createShortenedAction(i18n("Guides"), "view_snap_to_guides", &menu));
778 menu.addAction(m_d->createShortenedAction(i18n("Pixel"), "view_snap_to_pixel", &menu));
779 menu.addAction(m_d->createShortenedAction(i18n("Orthogonal"), "view_snap_orthogonal", &menu));
780
781 menu.addAction(m_d->createShortenedAction(i18n("Node"), "view_snap_node", &menu));
782 menu.addAction(m_d->createShortenedAction(i18n("Extension"), "view_snap_extension", &menu));
783 menu.addAction(m_d->createShortenedAction(i18n("Intersection"), "view_snap_intersection", &menu));
784
785 menu.addAction(m_d->createShortenedAction(i18n("Bounding Box"), "view_snap_bounding_box", &menu));
786 menu.addAction(m_d->createShortenedAction(i18n("Image Bounds"), "view_snap_image_bounds", &menu));
787 menu.addAction(m_d->createShortenedAction(i18n("Image Center"), "view_snap_image_center", &menu));
788
789 menu.exec(pos);
790}
791
793{
794 m_d->snapConfig.setOrthogonal(value);
795 m_d->updateSnappingStatus(m_d->guidesConfig);
796}
797
799{
800 m_d->snapConfig.setNode(value);
801 m_d->updateSnappingStatus(m_d->guidesConfig);
802}
803
805{
806 m_d->snapConfig.setExtension(value);
807 m_d->updateSnappingStatus(m_d->guidesConfig);
808}
809
811{
812 m_d->snapConfig.setIntersection(value);
813 m_d->updateSnappingStatus(m_d->guidesConfig);
814}
815
817{
818 m_d->snapConfig.setBoundingBox(value);
819 m_d->updateSnappingStatus(m_d->guidesConfig);
820}
821
823{
824 m_d->snapConfig.setImageBounds(value);
825 m_d->updateSnappingStatus(m_d->guidesConfig);
826}
827
829{
830 m_d->snapConfig.setImageCenter(value);
831 m_d->updateSnappingStatus(m_d->guidesConfig);
832}
833
835{
836 m_d->snapConfig.setToPixel(value);
837 m_d->updateSnappingStatus(m_d->guidesConfig);
838}
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
KisAction * actionByName(const QString &name) const
KisViewManager * viewManager
QPointer< KisView > view
KisCoordinatesConverter * coordinatesConverter
KisAbstractCanvasWidget * canvasWidget
_Private::Traits< T >::Result widgetToDocument(const T &obj) const
_Private::Traits< T >::Result documentToImage(const T &obj) const
_Private::Traits< T >::Result imageToDocument(const T &obj) const
void setGuidesConfig(const KisGuidesConfig &data)
bool hasSamePositionAs(const KisGuidesConfig &rhs) const
const KisGuidesConfig & guidesConfig() const
void setView(QPointer< KisView > view)
void setRulersMultiple2(bool value)
const QScopedPointer< Private > m_d
void slotDocumentRequestedConfig(const KisGuidesConfig &config)
void setSnapBoundingBox(bool value)
KoUnit::Type unitType() const
void setSnapToGuides(bool value)
void setShowGuides(bool value)
void setSnapImageCenter(bool value)
bool eventFilter(QObject *obj, QEvent *event) override
void setSnapOrthogonal(bool value)
void setGuidesConfigImpl(const KisGuidesConfig &value, bool emitModified=true)
void setGuidesConfig(const KisGuidesConfig &config)
void setLockGuides(bool value)
KisGuidesManager(QObject *parent=0)
void setSnapNode(bool value)
void attachEventFilterImpl(bool value)
void setUnitType(KoUnit::Type type)
void setSnapExtension(bool value)
void slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos)
void sigRequestUpdateGuidesConfig(const KisGuidesConfig &config)
void setSnapImageBounds(bool value)
void setSnapIntersection(bool value)
void setup(KisActionManager *actionManager)
void slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos)
void setSnapToPixel(bool value)
Central object to manage canvas input.
void detachPriorityEventFilter(QObject *filter)
detachPriorityEventFilter
void attachPriorityEventFilter(QObject *filter, int priority=0)
attachPriorityEventFilter
void setVerticalLines(const QList< qreal > &lines)
void addLine(Qt::Orientation orientation, qreal pos)
void setHorizontalLines(const QList< qreal > &lines)
KisActionManager * actionManager() const
bool addCustomSnapStrategy(KoSnapStrategy *customStrategy)
void reset()
Resets the snap guide.
@ DocumentBoundsSnapping
Definition KoSnapGuide.h:57
@ DocumentCenterSnapping
Definition KoSnapGuide.h:58
@ IntersectionSnapping
Definition KoSnapGuide.h:53
@ BoundingBoxSnapping
Definition KoSnapGuide.h:55
QPointF snap(const QPointF &mousePosition, Qt::KeyboardModifiers modifiers)
snaps the mouse position, returns if mouse was snapped
void enableSnapStrategy(Strategy type, bool value)
void overrideSnapStrategy(Strategy type, KoSnapStrategy *strategy)
virtual QPointF documentToView(const QPointF &documentPoint) const
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
static const QString GUIDES_DECORATION_ID
QString button(const QWheelEvent &ev)
Rect blowRect(const Rect &rect, qreal coeff)
QAction * createShortenedAction(const QString &text, const QString &parentId, QObject *parent)
KisGuidesDecoration * decoration
bool mouseReleaseHandler(const QPointF &docPos)
QPointF getDocPointFromEvent(QEvent *event)
bool isGuideValid(const GuideHandle &h)
bool updateCursor(const QPointF &docPos, bool forceDisableCursor=false)
void updateSnappingStatus(const KisGuidesConfig &value)
void syncAction(const QString &actionName, bool value)
Qt::MouseButton getButtonFromEvent(QEvent *event)
QPair< Qt::Orientation, int > GuideHandle
bool mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers)
qreal guideValue(const GuideHandle &h)
QPointF alignToPixels(const QPointF docPoint)
void deleteGuide(const GuideHandle &h)
KisSignalAutoConnectionsStore viewConnections
Private(KisGuidesManager *_q)
void setGuideValue(const GuideHandle &h, qreal value)
void initDragStart(const GuideHandle &guide, const QPointF &dragStart, qreal guideValue, bool snapToStart)
GuideHandle findGuide(const QPointF &docPos)