All Classes Namespaces Functions Variables Enumerations Properties Pages
movetool.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 */
17 
18 #include "movetool.h"
19 
20 #include <cassert>
21 #include <QMessageBox>
22 #include <QSettings>
23 
24 #include "pointerevent.h"
25 #include "editor.h"
26 #include "toolmanager.h"
27 #include "viewmanager.h"
28 #include "strokemanager.h"
29 #include "selectionmanager.h"
30 #include "overlaymanager.h"
31 #include "scribblearea.h"
32 #include "layervector.h"
33 #include "layermanager.h"
34 #include "mathutils.h"
35 #include "vectorimage.h"
36 
37 
38 MoveTool::MoveTool(QObject* parent) : BaseTool(parent)
39 {
40 }
41 
42 ToolType MoveTool::type()
43 {
44  return MOVE;
45 }
46 
47 void MoveTool::loadSettings()
48 {
49  properties.width = -1;
50  properties.feather = -1;
51  properties.useFeather = false;
52  properties.stabilizerLevel = -1;
53  properties.useAA = -1;
54  mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
55  QSettings settings(PENCIL2D, PENCIL2D);
56  properties.showSelectionInfo = settings.value("ShowSelectionInfo").toBool();
57  mPropertyEnabled[SHOWSELECTIONINFO] = true;
58 
59  connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &MoveTool::updateSettings);
60 }
61 
62 QCursor MoveTool::cursor()
63 {
64  MoveMode mode = MoveMode::NONE;
65  if (mEditor->select()->somethingSelected())
66  {
67  mode = mEditor->select()->getMoveModeForSelectionAnchor(getCurrentPoint());
68  return mScribbleArea->currentTool()->selectMoveCursor(mode, type());
69  }
70  if (mEditor->overlays()->isPerspOverlaysActive())
71  {
72  mode = mEditor->overlays()->getMoveModeForOverlayAnchor(getCurrentPoint());
73  mPerspMode = mode;
74  return mScribbleArea->currentTool()->selectMoveCursor(mode, type());
75  }
76  return mScribbleArea->currentTool()->selectMoveCursor(mode, type());
77 }
78 
79 void MoveTool::updateSettings(const SETTING setting)
80 {
81  switch (setting)
82  {
83  case SETTING::ROTATION_INCREMENT:
84  {
85  mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
86  break;
87  }
88  default:
89  break;
90 
91  }
92 }
93 
94 void MoveTool::pointerPressEvent(PointerEvent* event)
95 {
96  mCurrentLayer = currentPaintableLayer();
97  if (mCurrentLayer == nullptr) return;
98 
99  if (mEditor->select()->somethingSelected())
100  {
101  mEditor->select()->updatePolygons();
102 
103  setAnchorToLastPoint();
104  beginInteraction(event->modifiers(), mCurrentLayer);
105  }
106  if (mEditor->overlays()->isPerspOverlaysActive())
107  {
108  QPointF point = mEditor->view()->mapScreenToCanvas(event->posF());
109  mEditor->overlays()->setMoveMode(mPerspMode);
110  mEditor->overlays()->updatePerspOverlay(point);
111  }
112 }
113 
114 void MoveTool::pointerMoveEvent(PointerEvent* event)
115 {
116  mCurrentLayer = currentPaintableLayer();
117  if (mCurrentLayer == nullptr) return;
118 
119  mEditor->select()->updatePolygons();
120 
121  if (mScribbleArea->isPointerInUse()) // the user is also pressing the mouse (dragging)
122  {
123  transformSelection(event->modifiers(), mCurrentLayer);
124  if (mEditor->overlays()->isPerspOverlaysActive())
125  {
126  QPointF mapped = mEditor->view()->mapScreenToCanvas(event->pos());
127  mEditor->overlays()->updatePerspOverlay(mapped);
128  }
129  }
130  else
131  {
132  // the user is moving the mouse without pressing it
133  // update cursor to reflect selection corner interaction
134  mScribbleArea->updateToolCursor();
135 
136  if (mCurrentLayer->type() == Layer::VECTOR)
137  {
138  storeClosestVectorCurve(mCurrentLayer);
139  }
140  if (mEditor->overlays()->isPerspOverlaysActive())
141  {
142 // QPointF mapped = mEditor->view()->mapScreenToCanvas(event->pos());
143 // mEditor->overlays()->updatePerspOverlay(mapped);
144  }
145  mEditor->getScribbleArea()->prepOverlays();
146  }
147  mScribbleArea->updateCurrentFrame();
148 }
149 
150 void MoveTool::pointerReleaseEvent(PointerEvent*)
151 {
152  if (mEditor->overlays()->isPerspOverlaysActive())
153  {
154  mEditor->overlays()->setMoveMode(MoveMode::NONE);
155  mPerspMode = MoveMode::NONE;
156  }
157 
158  auto selectMan = mEditor->select();
159  if (!selectMan->somethingSelected())
160  return;
161 
162  mRotatedAngle = selectMan->myRotation();
163  updateTransformation();
164 
165  Layer* layer = mEditor->layers()->currentLayer();
166  if (layer->type() == Layer::VECTOR) {
167  applyTransformation();
168  }
169 
170  selectMan->updatePolygons();
171 
172  mScribbleArea->updateToolCursor();
173  mScribbleArea->updateCurrentFrame();
174 }
175 
176 void MoveTool::updateTransformation()
177 {
178  auto selectMan = mEditor->select();
179  selectMan->updateTransformedSelection();
180 
181  // make sure transform is correct
182  selectMan->calculateSelectionTransformation();
183 
184  // paint the transformation
185  paintTransformedSelection();
186 }
187 
188 void MoveTool::transformSelection(Qt::KeyboardModifiers keyMod, Layer* layer)
189 {
190  auto selectMan = mEditor->select();
191  if (selectMan->somethingSelected())
192  {
193 
194  QPointF offset = offsetFromPressPos();
195 
196  // maintain aspect ratio
197  if (keyMod == Qt::ShiftModifier)
198  {
199  offset = selectMan->offsetFromAspectRatio(offset.x(), offset.y());
200  }
201 
202  int rotationIncrement = 0;
203  if (selectMan->getMoveMode() == MoveMode::ROTATION && keyMod & Qt::ShiftModifier)
204  {
205  rotationIncrement = mRotationIncrement;
206  }
207 
208  if(layer->type() == Layer::BITMAP)
209  {
210  offset = offset.toPoint();
211  }
212 
213  selectMan->adjustSelection(getCurrentPoint(), offset.x(), offset.y(), mRotatedAngle, rotationIncrement);
214 
215  selectMan->calculateSelectionTransformation();
216  paintTransformedSelection();
217 
218  }
219  else // there is nothing selected
220  {
221  selectMan->setMoveMode(MoveMode::NONE);
222  }
223 }
224 
225 void MoveTool::beginInteraction(Qt::KeyboardModifiers keyMod, Layer* layer)
226 {
227  auto selectMan = mEditor->select();
228  QRectF selectionRect = selectMan->myTransformedSelectionRect();
229  if (!selectionRect.isNull())
230  {
231  mEditor->backup(typeName());
232  }
233 
234  if (keyMod != Qt::ShiftModifier)
235  {
236  if (selectMan->isOutsideSelectionArea(getCurrentPoint()))
237  {
238  applyTransformation();
239  mEditor->deselectAll();
240  }
241  }
242 
243  if (selectMan->validateMoveMode(getLastPoint()) == MoveMode::MIDDLE)
244  {
245  if (keyMod == Qt::ControlModifier) // --- rotation
246  {
247  selectMan->setMoveMode(MoveMode::ROTATION);
248  }
249  }
250 
251  if (layer->type() == Layer::VECTOR)
252  {
253  createVectorSelection(keyMod, layer);
254  }
255 
256  if(selectMan->getMoveMode() == MoveMode::ROTATION) {
257  QPointF curPoint = getCurrentPoint();
258  QPointF anchorPoint = selectionRect.center();
259  mRotatedAngle = qRadiansToDegrees(MathUtils::getDifferenceAngle(anchorPoint, curPoint)) - selectMan->myRotation();
260  }
261 }
262 
269 {
270  assert(layer->type() == Layer::VECTOR);
271  LayerVector* vecLayer = static_cast<LayerVector*>(layer);
272  VectorImage* vectorImage = vecLayer->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
273  if (vectorImage == nullptr) { return; }
274 
275  if (!mEditor->select()->closestCurves().empty()) // the user clicks near a curve
276  {
277  setCurveSelected(vectorImage, keyMod);
278  }
279  else if (vectorImage->getLastAreaNumber(getLastPoint()) > -1)
280  {
281  setAreaSelected(vectorImage, keyMod);
282  }
283  mScribbleArea->update();
284 }
285 
286 void MoveTool::setCurveSelected(VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
287 {
288  auto selectMan = mEditor->select();
289  if (!vectorImage->isSelected(selectMan->closestCurves()))
290  {
291  if (keyMod != Qt::ShiftModifier)
292  {
293  applyTransformation();
294  }
295  vectorImage->setSelected(selectMan->closestCurves(), true);
296  selectMan->setSelection(vectorImage->getSelectionRect(), false);
297  }
298 }
299 
300 void MoveTool::setAreaSelected(VectorImage* vectorImage, Qt::KeyboardModifiers keyMod)
301 {
302  int areaNumber = vectorImage->getLastAreaNumber(getLastPoint());
303  if (!vectorImage->isAreaSelected(areaNumber))
304  {
305  if (keyMod != Qt::ShiftModifier)
306  {
307  applyTransformation();
308  }
309  vectorImage->setAreaSelected(areaNumber, true);
310  mEditor->select()->setSelection(vectorImage->getSelectionRect(), false);
311  }
312 }
313 
319 {
320  auto selectMan = mEditor->select();
321  auto layerVector = static_cast<LayerVector*>(layer);
322  VectorImage* pVecImg = layerVector->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
323  if (pVecImg == nullptr) { return; }
324  selectMan->setCurves(pVecImg->getCurvesCloseTo(getCurrentPoint(), selectMan->selectionTolerance()));
325 }
326 
327 void MoveTool::setAnchorToLastPoint()
328 {
329  anchorOriginPoint = getLastPoint();
330 }
331 
332 void MoveTool::cancelChanges()
333 {
334  mScribbleArea->cancelTransformedSelection();
335  mEditor->deselectAll();
336 }
337 
338 void MoveTool::applySelectionChanges()
339 {
340  mEditor->select()->setRotation(0);
341  mRotatedAngle = 0;
342 
343  mScribbleArea->applySelectionChanges();
344 }
345 
346 void MoveTool::applyTransformation()
347 {
348  mScribbleArea->applyTransformedSelection();
349 }
350 
351 void MoveTool::paintTransformedSelection()
352 {
353  mScribbleArea->paintTransformedSelection();
354 }
355 
356 bool MoveTool::leavingThisTool()
357 {
358  if (mCurrentLayer)
359  {
360  switch (mCurrentLayer->type())
361  {
362  case Layer::BITMAP: applySelectionChanges(); break;
363  case Layer::VECTOR: applyTransformation(); break;
364  default: break;
365  }
366  }
367  return true;
368 }
369 
370 bool MoveTool::switchingLayer()
371 {
372  auto selectMan = mEditor->select();
373  if (!selectMan->transformHasBeenModified())
374  {
375  mEditor->deselectAll();
376  return true;
377  }
378 
379  int returnValue = showTransformWarning();
380 
381  if (returnValue == QMessageBox::Yes)
382  {
383  if (mCurrentLayer->type() == Layer::BITMAP)
384  {
385  applySelectionChanges();
386  }
387  else if (mCurrentLayer->type() == Layer::VECTOR)
388  {
389  applyTransformation();
390  }
391 
392  mEditor->deselectAll();
393  return true;
394  }
395  else if (returnValue == QMessageBox::No)
396  {
397  cancelChanges();
398  return true;
399  }
400  else if (returnValue == QMessageBox::Cancel)
401  {
402  return false;
403  }
404  return true;
405 }
406 
407 void MoveTool::resetToDefault()
408 {
409  setShowSelectionInfo(false);
410 }
411 
412 void MoveTool::setShowSelectionInfo(const bool b)
413 {
414  properties.showSelectionInfo = b;
415 
416  QSettings settings(PENCIL2D, PENCIL2D);
417  settings.setValue("ShowSelectionInfo", b);
418 
419 }
420 
421 int MoveTool::showTransformWarning()
422 {
423  int returnValue = QMessageBox::warning(nullptr,
424  tr("Layer switch", "Windows title of layer switch pop-up."),
425  tr("You are about to switch away, do you want to apply the transformation?"),
428  return returnValue;
429 }
430 
431 Layer* MoveTool::currentPaintableLayer()
432 {
433  Layer* layer = mEditor->layers()->currentLayer();
434  if (layer == nullptr)
435  return nullptr;
436  if (!layer->isPaintable())
437  return nullptr;
438  return layer;
439 }
440 
441 QPointF MoveTool::offsetFromPressPos()
442 {
443  return getCurrentPoint() - getCurrentPressPoint();
444 }
typedef KeyboardModifiers
Qt::KeyboardModifiers modifiers() const
Returns the modifier created by keyboard while a device was in use.
int getLastAreaNumber(QPointF point)
VectorImage::getLastAreaNumber.
QList< int > getCurvesCloseTo(QPointF thisPoint, qreal maxDistance)
VectorImage::getCurvesCloseTo.
QPointF posF() const
Returns the QPointF of the device Returns pos() if used on mouse event.
QString tr(const char *sourceText, const char *disambiguation, int n)
void update()
qreal x() const const
qreal y() const const
bool empty() const const
bool isSelected(int curveNumber)
VectorImage::isSelected.
QPoint pos() const
Returns QPoint of the device.
Definition: layer.h:38
void createVectorSelection(Qt::KeyboardModifiers keyMod, Layer *layer)
MoveTool::createVectorSelection In vector the selection rectangle is based on the bounding box of the...
Definition: movetool.cpp:268
QPointF center() const const
void storeClosestVectorCurve(Layer *layer)
MoveTool::storeClosestVectorCurve stores the curves closest to the mouse position in mClosestCurves...
Definition: movetool.cpp:318
bool isNull() const const
bool isAreaSelected(int areaNumber)
VectorImage::isAreaSelected.
QPoint toPoint() const const
void setAreaSelected(int areaNumber, bool YesOrNo)
VectorImage::setAreaSelected.
void updateCurrentFrame()
Update current frame.
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setSelected(int curveNumber, bool YesOrNo)
VectorImage::setSelected.