QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgslayoutviewtooladdnodeitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutviewtooladdnodeitem.cpp
3 ----------------------------
4 Date : July 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsapplication.h"
19#include "qgsgui.h"
20#include "qgslayout.h"
25#include "qgslayoututils.h"
26#include "qgslayoutview.h"
29#include "qgslogger.h"
30#include "qgssettings.h"
31
32#include <QBrush>
33#include <QGraphicsRectItem>
34#include <QMouseEvent>
35#include <QPen>
36
37#include "moc_qgslayoutviewtooladdnodeitem.cpp"
38
45
47{
48 mItemMetadataId = metadataId;
49}
50
52{
53 if ( event->button() == Qt::LeftButton )
54 {
55 if ( !mRubberBand )
56 {
57 mPolygon.clear();
58 mRubberBand.reset( QgsGui::layoutItemGuiRegistry()->createNodeItemRubberBand( mItemMetadataId, view() ) );
59 if ( mRubberBand )
60 layout()->addItem( mRubberBand.get() );
61
62 // On first press, deselect the currently selected item, if any
63 view()->deselectAll();
64 }
65
66 if ( mRubberBand )
67 {
68 //add a new node
69 addNode( event->snappedPoint() );
70 }
71 }
72 else if ( event->button() == Qt::RightButton && mRubberBand )
73 {
74 // finish up
75
76 // last (temporary) point is removed
77 mPolygon.remove( mPolygon.count() - 1 );
78
79 std::unique_ptr<QgsLayoutItem> item( QgsGui::layoutItemGuiRegistry()->createItem( mItemMetadataId, layout() ) );
80 if ( !item )
81 return;
82
83 if ( QgsLayoutNodesItem *nodesItem = qobject_cast<QgsLayoutNodesItem *>( item.get() ) )
84 {
85 nodesItem->setNodes( mPolygon );
86 if ( !nodesItem->isValid() )
87 {
88 mRubberBand.reset();
89 return;
90 }
91 }
92 QgsLayoutItem *newItem = item.get();
93 layout()->addLayoutItem( item.release() );
94 layout()->setSelectedItem( newItem );
95 emit createdItem();
96 }
97 else
98 {
99 event->ignore();
100 mRubberBand.reset();
101 }
102}
103
105{
106 if ( mRubberBand )
107 {
108 moveTemporaryNode( event->snappedPoint(), event->modifiers() );
109 }
110 else
111 {
112 event->ignore();
113 }
114}
115
117{
118 if ( !mRubberBand )
119 {
120 event->ignore();
121 return;
122 }
123}
124
126{
127 if ( !mRubberBand || event->isAutoRepeat() )
128 {
129 event->ignore();
130 return;
131 }
132
133 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
134 {
135 if ( mPolygon.size() > 2 )
136 {
137 //remove last added vertex
138 mPolygon.pop_back();
139 setRubberBandNodes();
140 moveTemporaryNode( view()->mapToScene( view()->mapFromGlobal( QCursor::pos() ) ), event->modifiers() );
141 }
142 else
143 {
144 // all deleted, cancel
145 mRubberBand.reset();
146 }
147 }
148 else if ( event->key() == Qt::Key_Escape )
149 {
150 mRubberBand.reset();
151 }
152 else
153 {
154 event->ignore();
155 }
156}
157
159{
160 if ( mRubberBand )
161 {
162 // canceled mid operation
163 mRubberBand.reset();
164 }
166}
167
168void QgsLayoutViewToolAddNodeItem::addNode( QPointF scenePoint )
169{
170 mPolygon.append( scenePoint );
171
172 if ( mPolygon.size() == 1 )
173 mPolygon.append( scenePoint );
174
175 setRubberBandNodes();
176}
177
178void QgsLayoutViewToolAddNodeItem::moveTemporaryNode( QPointF scenePoint, Qt::KeyboardModifiers modifiers )
179{
180 if ( mPolygon.isEmpty() )
181 return;
182
183 if ( mPolygon.size() > 1 && ( modifiers & Qt::ShiftModifier ) )
184 {
185 const QPointF start = mPolygon.at( mPolygon.size() - 2 );
186 QLineF newLine = QLineF( start, scenePoint );
187
188 //movement is constrained to 45 degree angles
189 const double angle = QgsLayoutUtils::snappedAngle( newLine.angle() );
190 newLine.setAngle( angle );
191 scenePoint = newLine.p2();
192 }
193
194 mPolygon.replace( mPolygon.size() - 1, scenePoint );
195 setRubberBandNodes();
196}
197
198void QgsLayoutViewToolAddNodeItem::setRubberBandNodes()
199{
200 QList<QGraphicsItem *> items = mRubberBand->childItems();
201
202 // Rubber band is not a QGraphicsItem with children, but may be
203 // a custom QGraphicsPolygonItem / QGraphicsPathItem returned by a Python plugin
204 // In this case, directly append it to the list.
205 if ( items.isEmpty() )
206 {
207 items << mRubberBand.get();
208 }
209
210 if ( QGraphicsPolygonItem *polygonItem = dynamic_cast<QGraphicsPolygonItem *>( items[0] ) )
211 {
212 // The group contains two polygons
213 if ( items.size() == 2 && dynamic_cast<QGraphicsPolygonItem *>( items[1] ) != nullptr )
214 {
215 if ( mPolygon.size() > 3 )
216 {
217 polygonItem->setPolygon( QPolygonF( mPolygon.mid( 0, mPolygon.size() - 1 ) ) );
218 }
219 else
220 {
221 polygonItem->setPolygon( QPolygonF() );
222 }
223 dynamic_cast<QGraphicsPolygonItem *>( items[1] )->setPolygon( mPolygon );
224 }
225 // The group contains a single QGraphicsPolygonItem as rubberband
226 else
227 {
228 polygonItem->setPolygon( mPolygon );
229 }
230 }
231 else if ( QGraphicsPathItem *polylineItem = dynamic_cast<QGraphicsPathItem *>( items[0] ) )
232 {
233 // The group contains two polylines
234 if ( items.size() == 2 && dynamic_cast<QGraphicsPathItem *>( items[1] ) != nullptr )
235 {
236 if ( mPolygon.size() > 2 )
237 {
238 QPainterPath path;
239 path.addPolygon( QPolygonF( mPolygon.mid( 0, mPolygon.size() - 1 ) ) );
240 polylineItem->setPath( path );
241 }
242 else
243 {
244 polylineItem->setPath( QPainterPath() );
245 }
246 if ( mPolygon.size() > 1 )
247 {
248 QPainterPath path;
249 path.addPolygon( mPolygon.mid( mPolygon.size() - 2 ) );
250 dynamic_cast<QGraphicsPathItem *>( items[1] )->setPath( path );
251 }
252 }
253 // The group contains a single QGraphicsPathItem as rubberband
254 else
255 {
256 // rebuild a new qpainter path
257 QPainterPath path;
258 path.addPolygon( mPolygon );
259 polylineItem->setPath( path );
260 }
261 }
262}
263
265{
266 return mItemMetadataId;
267}
static QgsLayoutItemGuiRegistry * layoutItemGuiRegistry()
Returns the global layout item GUI registry, used for registering the GUI behavior of layout items.
Definition qgsgui.cpp:151
Base class for graphical items within a QgsLayout.
An abstract layout item that provides generic methods for node based shapes such as polygon or polyli...
static double snappedAngle(double angle)
Snaps an angle (in degrees) to its closest 45 degree angle.
A mouse event which is the result of a user interaction with a QgsLayoutView.
QPointF snappedPoint() const
Returns the snapped event point location in layout coordinates.
void layoutMoveEvent(QgsLayoutViewMouseEvent *event) override
Mouse move event for overriding.
int itemMetadataId() const
Returns the item metadata id for items created by the tool.
void createdItem()
Emitted when an item has been created using the tool.
void layoutReleaseEvent(QgsLayoutViewMouseEvent *event) override
Mouse release event for overriding.
QgsLayoutViewToolAddNodeItem(QgsLayoutView *view)
Constructs a QgsLayoutViewToolAddNodeItem for the given layout view.
void setItemMetadataId(int metadataId)
Sets the item metadata metadataId for items created by the tool.
void keyPressEvent(QKeyEvent *event) override
Key press event for overriding.
void layoutPressEvent(QgsLayoutViewMouseEvent *event) override
Mouse press event for overriding.
void deactivate() override
Called when tool is deactivated.
void setCursor(const QCursor &cursor)
Sets a user defined cursor for use when the tool is active.
QgsLayoutView * view() const
Returns the view associated with the tool.
virtual void deactivate()
Called when tool is deactivated.
void setFlags(QgsLayoutViewTool::Flags flags)
Sets the combination of flags that will be used for the tool.
@ FlagSnaps
Tool utilizes snapped coordinates.
QgsLayout * layout() const
Returns the layout associated with the tool.
QgsLayoutViewTool(QgsLayoutView *view, const QString &name)
Constructor for QgsLayoutViewTool, taking a layout view and tool name as parameters.
A graphical widget to display and interact with QgsLayouts.
void deselectAll()
Deselects all items in the view.
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
void addLayoutItem(QgsLayoutItem *item)
Adds an item to the layout.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).