QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgslayoutviewtooleditnodes.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutviewtooleditnodes.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 
18 #include "qgslayoutview.h"
19 #include "qgslayout.h"
20 #include "qgslayoutitemnodeitem.h"
21 #include "qgslayoutundostack.h"
22 
24  : QgsLayoutViewTool( view, tr( "Select" ) )
25 {
26  setCursor( Qt::CrossCursor );
28 }
29 
31 {
32  if ( mNodesItem && mNodesItemIndex != -1 )
33  {
34  layout()->undoStack()->beginCommand( mNodesItem, tr( "Remove Item Node" ) );
35  if ( mNodesItem->removeNode( mNodesItemIndex ) )
36  {
37  layout()->undoStack()->endCommand();
38  if ( mNodesItem->nodesSize() > 0 )
39  {
40  mNodesItemIndex = mNodesItem->selectedNode();
41  // setSelectedNode( mNodesItem, mNodesItemIndex );
42  }
43  else
44  {
45  mNodesItemIndex = -1;
46  mNodesItem = nullptr;
47  }
48  if ( mNodesItem )
49  mNodesItem->update();
50  }
51  else
52  {
54  }
55  }
56 }
57 
59 {
60  displayNodes( true );
62 }
63 
65 {
66  if ( event->button() != Qt::LeftButton )
67  {
68  event->ignore();
69  return;
70  }
71 
72  const QList<QGraphicsItem *> itemsAtCursorPos = view()->items( event->pos().x(), event->pos().y(),
73  mMoveContentSearchRadius,
74  mMoveContentSearchRadius );
75  if ( itemsAtCursorPos.isEmpty() )
76  return;
77 
78  mNodesItemIndex = -1;
79  mNodesItem = nullptr;
80  isMoving = false;
81 
82  for ( QGraphicsItem *graphicsItem : itemsAtCursorPos )
83  {
84  QgsLayoutNodesItem *item = dynamic_cast<QgsLayoutNodesItem *>( graphicsItem );
85 
86  if ( item && !item->isLocked() )
87  {
88  int index = item->nodeAtPosition( event->layoutPoint() );
89  if ( index != -1 )
90  {
91  mNodesItemIndex = index;
92  mNodesItem = item;
93  mMoveContentStartPos = event->layoutPoint();
94  }
95  }
96 
97  if ( mNodesItem && mNodesItemIndex != -1 )
98  {
99  layout()->undoStack()->beginCommand( mNodesItem, tr( "Move Item Node" ) );
100  setSelectedNode( mNodesItem, mNodesItemIndex );
101  isMoving = true;
102  break;
103  }
104  }
105 }
106 
108 {
109  if ( !isMoving )
110  {
111  event->ignore();
112  return;
113  }
114 
115  if ( mNodesItem && mNodesItemIndex != -1 && event->layoutPoint() != mMoveContentStartPos )
116  {
117  mNodesItem->moveNode( mNodesItemIndex, event->snappedPoint() );
118  }
119 }
120 
122 {
123  if ( event->button() != Qt::LeftButton || !isMoving )
124  {
125  event->ignore();
126  return;
127  }
128 
129  isMoving = false;
130  if ( mNodesItemIndex != -1 )
131  {
132  if ( event->layoutPoint() != mMoveContentStartPos )
133  {
134  layout()->undoStack()->endCommand();
135  }
136  else
137  {
139  }
140  }
141 }
142 
144 {
145  if ( event->button() != Qt::LeftButton )
146  {
147  event->ignore();
148  return;
149  }
150 
151  // erase status previously set by the mousePressEvent method
152  if ( mNodesItemIndex != -1 )
153  {
154  mNodesItem = nullptr;
155  mNodesItemIndex = -1;
156  deselectNodes();
157  }
158 
159  // search items in layout
160  const QList<QGraphicsItem *> itemsAtCursorPos = view()->items( event->pos().x(), event->pos().y(),
161  mMoveContentSearchRadius,
162  mMoveContentSearchRadius );
163 
164  if ( itemsAtCursorPos.isEmpty() )
165  return;
166 
167  bool rc = false;
168  for ( QGraphicsItem *graphicsItem : itemsAtCursorPos )
169  {
170  QgsLayoutNodesItem *item = dynamic_cast<QgsLayoutNodesItem *>( graphicsItem );
171 
172  if ( item && !item->isLocked() )
173  {
174  layout()->undoStack()->beginCommand( item, tr( "Add Item Node" ) );
175  rc = item->addNode( event->layoutPoint() );
176 
177  if ( rc )
178  {
179  layout()->undoStack()->endCommand();
180  mNodesItem = item;
181  mNodesItemIndex = mNodesItem->nodeAtPosition( event->layoutPoint() );
182  }
183  else
185  }
186 
187  if ( rc )
188  break;
189  }
190 
191  if ( rc )
192  {
193  setSelectedNode( mNodesItem, mNodesItemIndex );
194  mNodesItem->update();
195  }
196 }
197 
199 {
200  if ( mNodesItem && mNodesItemIndex != -1 && ( event->key() == Qt::Key_Left
201  || event->key() == Qt::Key_Right
202  || event->key() == Qt::Key_Up
203  || event->key() == Qt::Key_Down ) )
204  {
205  QPointF currentPos;
206 
207  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
208  {
209  QPointF delta = view()->deltaForKeyEvent( event );
210 
211  currentPos.setX( currentPos.x() + delta.x() );
212  currentPos.setY( currentPos.y() + delta.y() );
213 
214  layout()->undoStack()->beginCommand( mNodesItem, tr( "Move Item Node" ), QgsLayoutItem::UndoNodeMove );
215  mNodesItem->moveNode( mNodesItemIndex, currentPos );
216  layout()->undoStack()->endCommand();
217  layout()->update();
218  }
219  }
220  else
221  {
222  event->ignore();
223  }
224 }
225 
227 {
228  displayNodes( false );
229  deselectNodes();
231 }
232 
233 QList<QgsLayoutItem *> QgsLayoutViewToolEditNodes::ignoredSnapItems() const
234 {
235  QList< QgsLayoutItem * > items;
236  if ( mNodesItem )
237  items << mNodesItem;
238  return items;
239 }
240 
241 void QgsLayoutViewToolEditNodes::displayNodes( bool display )
242 {
243  QList<QgsLayoutNodesItem *> nodesShapes;
244  layout()->layoutItems( nodesShapes );
245 
246  for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
247  {
248  item->setDisplayNodes( display );
249  item->update();
250  }
251 }
252 
253 void QgsLayoutViewToolEditNodes::deselectNodes()
254 {
255  QList<QgsLayoutNodesItem *> nodesShapes;
256  layout()->layoutItems( nodesShapes );
257 
258  for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
259  {
260  item->deselectNode();
261  item->update();
262  }
263 }
264 
265 void QgsLayoutViewToolEditNodes::setSelectedNode( QgsLayoutNodesItem *shape, int index )
266 {
267  QList<QgsLayoutNodesItem *> nodesShapes;
268  layout()->layoutItems( nodesShapes );
269 
270  for ( QgsLayoutNodesItem *item : qgis::as_const( nodesShapes ) )
271  {
272  if ( item == shape )
273  {
274  item->setSelectedNode( index );
275  layout()->setSelectedItem( item );
276  item->update();
277  }
278  else
279  {
280  item->deselectNode();
281  item->update();
282  }
283  }
284 
285 }
286 
@ UndoNodeMove
Node move.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
An abstract layout item that provides generic methods for node based shapes such as polygon or polyli...
bool addNode(QPointF point, bool checkArea=true, double radius=10)
Add a node in current shape.
int nodeAtPosition(QPointF point, bool searchInRadius=true, double radius=10) const
Search for the nearest node in the shape within a maximal area.
void endCommand()
Saves final state of an object and pushes the active command to the undo history.
void cancelCommand()
Cancels the active command, discarding it without pushing to the undo history.
void beginCommand(QgsLayoutUndoObjectInterface *object, const QString &commandText, int id=0)
Begins a new undo command for the specified object.
A QgsLayoutViewMouseEvent is the result of a user interaction with the mouse on a QgsLayoutView.
QPointF snappedPoint() const
Returns the snapped event point location in layout coordinates.
QPointF layoutPoint() const
Returns the event point location in layout coordinates.
void layoutMoveEvent(QgsLayoutViewMouseEvent *event) override
Mouse move event for overriding.
void layoutDoubleClickEvent(QgsLayoutViewMouseEvent *event) override
Mouse double-click event for overriding.
void activate() override
Called when tool is set as the currently active layout tool.
QList< QgsLayoutItem * > ignoredSnapItems() const override
Returns a list of items which should be ignored while snapping events for this tool.
void layoutPressEvent(QgsLayoutViewMouseEvent *event) override
Mouse press event for overriding.
QgsLayoutViewToolEditNodes(QgsLayoutView *view)
Constructor for QgsLayoutViewToolEditNodes.
void keyPressEvent(QKeyEvent *event) override
Key press event for overriding.
void layoutReleaseEvent(QgsLayoutViewMouseEvent *event) override
Mouse release event for overriding.
void deactivate() override
Called when tool is deactivated.
void deleteSelectedNode()
Deletes the selected node from the item.
Abstract base class for all layout view tools.
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.
virtual void activate()
Called when tool is set as the currently active layout tool.
QgsLayout * layout() const
Returns the layout associated with the tool.
A graphical widget to display and interact with QgsLayouts.
Definition: qgslayoutview.h:50
QPointF deltaForKeyEvent(QKeyEvent *event)
Returns the delta (in layout coordinates) by which to move items for the given key event.
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
Definition: qgslayout.h:121
void setSelectedItem(QgsLayoutItem *item)
Clears any selected items and sets item as the current selection.
Definition: qgslayout.cpp:159
QgsLayoutUndoStack * undoStack()
Returns a pointer to the layout's undo stack, which manages undo/redo states for the layout and it's ...
Definition: qgslayout.cpp:686