1 /*
2 Copyright 2006-2019 The QElectroTech Team
3 This file is part of QElectroTech.
4
5 QElectroTech is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (at your option) any later version.
9
10 QElectroTech is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "partarc.h"
19 #include "QPropertyUndoCommand/qpropertyundocommand.h"
20 #include "elementscene.h"
21 #include "QetGraphicsItemModeler/qetgraphicshandleritem.h"
22 #include "QetGraphicsItemModeler/qetgraphicshandlerutility.h"
23
24
25 /**
26 * @brief PartArc::PartArc
27 * Constructor
28 * @param editor : QETElementEditor of this part
29 * @param parent : parent item
30 */
PartArc(QETElementEditor * editor,QGraphicsItem * parent)31 PartArc::PartArc(QETElementEditor *editor, QGraphicsItem *parent) :
32 AbstractPartEllipse(editor, parent)
33 {
34 m_start_angle = 0;
35 m_span_angle = -1440;
36 }
37
38 /**
39 * @brief PartArc::~PartArc
40 * Destructor
41 */
~PartArc()42 PartArc::~PartArc()
43 {
44 if(m_undo_command) delete m_undo_command;
45 removeHandler();
46 }
47
48 /**
49 * @brief PartArc::paint
50 * Draw this arc
51 * @param painter
52 * @param options
53 * @param widget
54 */
paint(QPainter * painter,const QStyleOptionGraphicsItem * options,QWidget * widget)55 void PartArc::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget)
56 {
57 Q_UNUSED(widget);
58
59 applyStylesToQPainter(*painter);
60
61 //Always remove the brush
62 painter -> setBrush(Qt::NoBrush);
63 QPen t = painter -> pen();
64 t.setCosmetic(options && options -> levelOfDetail < 1.0);
65 painter -> setPen(t);
66
67 if (isSelected())
68 {
69 painter->save();
70 QPen pen(Qt::DotLine);
71 pen.setWidth(1);
72 pen.setCosmetic(true);
73 painter->setPen(pen);
74 //Draw the ellipse in black
75 painter -> drawEllipse(rect());
76 painter->restore();
77
78 //Draw the arc in red
79 t.setColor(Qt::red);
80 painter -> setPen(t);
81 }
82
83 painter -> drawArc(m_rect, m_start_angle, m_span_angle);
84
85 if (m_hovered)
86 drawShadowShape(painter);
87
88 if (isSelected())
89 drawCross(m_rect.center(), painter);
90 }
91
92 /**
93 * @brief PartArc::toXml
94 * Export this arc in xml
95 * @param xml_document : Xml document to use for create the xml element.
96 * @return : an xml element that describe this arc
97 */
toXml(QDomDocument & xml_document) const98 const QDomElement PartArc::toXml(QDomDocument &xml_document) const {
99 QDomElement xml_element = xml_document.createElement("arc");
100 QPointF top_left(sceneTopLeft());
101 xml_element.setAttribute("x", QString("%1").arg(top_left.x()));
102 xml_element.setAttribute("y", QString("%1").arg(top_left.y()));
103 xml_element.setAttribute("width", QString("%1").arg(rect().width()));
104 xml_element.setAttribute("height", QString("%1").arg(rect().height()));
105 //to maintain compatibility with the previous version, we write the angle in degrees.
106 xml_element.setAttribute("start", QString("%1").arg(m_start_angle / 16));
107 xml_element.setAttribute("angle", QString("%1").arg(m_span_angle / 16));
108 stylesToXml(xml_element);
109 return(xml_element);
110 }
111
112 /**
113 * @brief PartArc::fromXml
114 * Import the properties of this arc from a xml element.
115 * @param qde : Xml document to use.
116 */
fromXml(const QDomElement & qde)117 void PartArc::fromXml(const QDomElement &qde) {
118 stylesFromXml(qde);
119 m_rect = QRectF(mapFromScene(qde.attribute("x", "0").toDouble(),
120 qde.attribute("y", "0").toDouble()),
121 QSizeF(qde.attribute("width", "0").toDouble(),
122 qde.attribute("height", "0").toDouble()) );
123
124 m_start_angle = qde.attribute("start", "0").toDouble() * 16;
125 m_span_angle = qde.attribute("angle", "-1440").toDouble() * 16;
126 }
127
128 /**
129 * @brief PartArc::shape
130 * @return the shape of this item
131 */
shape() const132 QPainterPath PartArc::shape() const
133 {
134 QPainterPath shape;
135 shape.arcMoveTo(m_rect, m_start_angle/16);
136 shape.arcTo(m_rect, m_start_angle /16, m_span_angle /16);
137
138 QPainterPathStroker pps;
139 pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
140 shape = pps.createStroke(shape);
141
142 return shape;
143 }
144
shadowShape() const145 QPainterPath PartArc::shadowShape() const
146 {
147 QPainterPath shape;
148 shape.arcMoveTo(m_rect, m_start_angle/16);
149 shape.arcTo(m_rect, m_start_angle /16, m_span_angle /16);
150
151 QPainterPathStroker pps;
152 pps.setWidth(penWeight());
153
154 return (pps.createStroke(shape));
155 }
156
157 /**
158 * @brief PartArc::mouseReleaseEvent
159 * Handle mouse release event
160 * @param event
161 */
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)162 void PartArc::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
163 {
164 if (event->button() == Qt::LeftButton && event->buttonDownPos(Qt::LeftButton) == event->pos())
165 switchResizeMode();
166
167 CustomElementGraphicPart::mouseReleaseEvent(event);
168 }
169
170 /**
171 * @brief PartArc::itemChange
172 * @param change
173 * @param value
174 * @return
175 */
itemChange(QGraphicsItem::GraphicsItemChange change,const QVariant & value)176 QVariant PartArc::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
177 {
178 if (change == ItemSelectedHasChanged && scene())
179 {
180 if (value.toBool() == true)
181 {
182 //When item is selected, he must to be up to date whene the selection in the scene change, for display or not the handler,
183 //according to the number of selected items.
184 connect(scene(), &QGraphicsScene::selectionChanged, this, &PartArc::sceneSelectionChanged);
185
186 if (scene()->selectedItems().size() == 1)
187 addHandler();
188 }
189 else
190 {
191 disconnect(scene(), &QGraphicsScene::selectionChanged, this, &PartArc::sceneSelectionChanged);
192 removeHandler();
193 }
194 }
195 else if (change == ItemPositionHasChanged)
196 {
197 adjusteHandlerPos();
198 }
199 else if (change == ItemSceneChange)
200 {
201 if(scene())
202 disconnect(scene(), &QGraphicsScene::selectionChanged, this, &PartArc::sceneSelectionChanged);
203
204 setSelected(false); //This is item removed from scene, then we deselect this, and so, the handlers is also removed.
205 }
206
207 return QGraphicsItem::itemChange(change, value);
208 }
209
210 /**
211 * @brief PartArc::sceneEventFilter
212 * @param watched
213 * @param event
214 * @return
215 */
sceneEventFilter(QGraphicsItem * watched,QEvent * event)216 bool PartArc::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
217 {
218 //Watched must be an handler
219 if(watched->type() == QetGraphicsHandlerItem::Type)
220 {
221 QetGraphicsHandlerItem *qghi = qgraphicsitem_cast<QetGraphicsHandlerItem *>(watched);
222
223 if(m_handler_vector.contains(qghi)) //Handler must be in m_vector_index, then we can start resize
224 {
225 m_vector_index = m_handler_vector.indexOf(qghi);
226 if (m_vector_index != -1)
227 {
228 if(event->type() == QEvent::GraphicsSceneMousePress) //Click
229 {
230 handlerMousePressEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
231 return true;
232 }
233 else if(event->type() == QEvent::GraphicsSceneMouseMove) //Move
234 {
235 handlerMouseMoveEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
236 return true;
237 }
238 else if (event->type() == QEvent::GraphicsSceneMouseRelease) //Release
239 {
240 handlerMouseReleaseEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
241 return true;
242 }
243 }
244 }
245 }
246
247 return false;
248 }
249
250 /**
251 * @brief PartArc::switchResizeMode
252 */
switchResizeMode()253 void PartArc::switchResizeMode()
254 {
255 if (m_resize_mode == 1)
256 {
257 m_resize_mode = 2;
258 for (QetGraphicsHandlerItem *qghi : m_handler_vector)
259 qghi->setColor(Qt::darkGreen);
260 }
261 else if (m_resize_mode == 2)
262 {
263 m_resize_mode = 3;
264
265 //From rect mode to angle mode, then numbers of handlers change
266 removeHandler();
267 addHandler();
268
269 for (QetGraphicsHandlerItem *qghi : m_handler_vector)
270 qghi->setColor(Qt::magenta);
271 }
272 else
273 {
274 m_resize_mode = 1;
275
276 //From angle mode to rect mode, then numbers of handlers change
277 removeHandler();
278 addHandler();
279
280 for (QetGraphicsHandlerItem *qghi : m_handler_vector)
281 qghi->setColor(Qt::blue);
282 }
283 }
284
285 /**
286 * @brief PartArc::adjusteHandlerPos
287 */
adjusteHandlerPos()288 void PartArc::adjusteHandlerPos()
289 {
290 if (m_handler_vector.isEmpty())
291 return;
292
293 QVector <QPointF> points_vector;
294
295 if(m_resize_mode == 3)
296 points_vector = QetGraphicsHandlerUtility::pointsForArc(m_rect, m_start_angle/16, m_span_angle/16);
297 else
298 points_vector = QetGraphicsHandlerUtility::pointsForRect(m_rect);
299
300
301 if (m_handler_vector.size() == points_vector.size())
302 {
303 points_vector = mapToScene(points_vector);
304 for (int i = 0 ; i < points_vector.size() ; ++i)
305 m_handler_vector.at(i)->setPos(points_vector.at(i));
306 }
307 }
308
309 /**
310 * @brief PartArc::handlerMousePressEvent
311 * @param qghi
312 * @param event
313 */
handlerMousePressEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)314 void PartArc::handlerMousePressEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
315 {
316 Q_UNUSED(qghi);
317 Q_UNUSED(event);
318
319 if (m_resize_mode == 3) //Resize angle
320 {
321 if (m_vector_index == 0)
322 {
323 m_span_point = QetGraphicsHandlerUtility::pointsForArc(m_rect, m_start_angle /16, m_span_angle /16).at(1);
324
325 m_undo_command = new QPropertyUndoCommand(this, "startAngle", QVariant(m_start_angle));
326 m_undo_command->setText(tr("Modifier un arc"));
327 m_undo_command->enableAnimation();
328
329 m_undo_command2 = new QPropertyUndoCommand(this, "spanAngle", QVariant(m_span_angle), m_undo_command);
330 m_undo_command2->setText(tr("Modifier un arc"));
331 m_undo_command2->enableAnimation();
332 }
333 else if (m_vector_index == 1)
334 {
335 m_undo_command = new QPropertyUndoCommand(this, "spanAngle", QVariant(m_span_angle));
336 m_undo_command->setText(tr("Modifier un arc"));
337 m_undo_command->enableAnimation();
338 }
339 }
340 else //resize rect
341 {
342 m_undo_command = new QPropertyUndoCommand(this, "rect", QVariant(m_rect));
343 m_undo_command->setText(tr("Modifier un arc"));
344 m_undo_command->enableAnimation();
345 }
346 }
347
348 /**
349 * @brief PartArc::handlerMouseMoveEvent
350 * @param qghi
351 * @param event
352 */
handlerMouseMoveEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)353 void PartArc::handlerMouseMoveEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
354 {
355 Q_UNUSED(qghi);
356
357 QPointF new_pos = event->scenePos();
358 if (event->modifiers() != Qt::ControlModifier)
359 new_pos = elementScene()->snapToGrid(event->scenePos());
360 new_pos = mapFromScene(new_pos);
361
362 if (m_resize_mode == 1)
363 setRect(QetGraphicsHandlerUtility::rectForPosAtIndex(m_rect, new_pos, m_vector_index));
364 else if (m_resize_mode == 2)
365 setRect(QetGraphicsHandlerUtility::mirrorRectForPosAtIndex(m_rect, new_pos, m_vector_index));
366 else
367 {
368 QLineF line(m_rect.center(), mapFromScene(event->scenePos()));
369 prepareGeometryChange();
370
371 if (m_vector_index == 0) {
372 setStartAngle(line.angle()*16);
373 setSpanAngle(line.angleTo(QLineF(m_rect.center(), m_span_point))*16);
374 }
375 else if (m_vector_index == 1) {
376 QLineF line2(m_rect.center(), QetGraphicsHandlerUtility::pointsForArc(m_rect, m_start_angle/16, m_span_angle/16).at(0));
377 setSpanAngle (line2.angleTo(line)*16);
378 }
379 }
380 }
381
382 /**
383 * @brief PartArc::handlerMouseReleaseEvent
384 * @param qghi
385 * @param event
386 */
handlerMouseReleaseEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)387 void PartArc::handlerMouseReleaseEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
388 {
389 Q_UNUSED(qghi);
390 Q_UNUSED(event);
391
392 if (m_resize_mode == 3)
393 {
394 if (m_vector_index == 0)
395 {
396 m_undo_command->setNewValue(QVariant(m_start_angle));
397 m_undo_command2->setNewValue(QVariant(m_span_angle));
398 elementScene()->undoStack().push(m_undo_command);
399 m_undo_command = nullptr;
400 m_undo_command2 = nullptr;
401 m_vector_index = -1;
402 }
403 else if (m_vector_index == 1)
404 {
405 m_undo_command->setNewValue(QVariant(m_span_angle));
406 elementScene()->undoStack().push(m_undo_command);
407 m_undo_command = nullptr;
408 m_vector_index = -1;
409 }
410 }
411 else
412 {
413 if (!m_rect.isValid())
414 m_rect = m_rect.normalized();
415
416 m_undo_command->setNewValue(QVariant(m_rect));
417 elementScene()->undoStack().push(m_undo_command);
418 m_undo_command = nullptr;
419 m_vector_index = -1;
420 }
421 }
422
423 /**
424 * @brief PartArc::sceneSelectionChanged
425 * When the scene selection change, if there are several primitive selected, we remove the handler of this item
426 */
sceneSelectionChanged()427 void PartArc::sceneSelectionChanged()
428 {
429 if (this->isSelected() && scene()->selectedItems().size() == 1)
430 addHandler();
431 else
432 removeHandler();
433 }
434
435 /**
436 * @brief PartArc::addHandler
437 * Add handlers for this item
438 */
addHandler()439 void PartArc::addHandler()
440 {
441 if (m_handler_vector.isEmpty() && scene())
442 {
443 if(m_resize_mode == 3)
444 {
445 m_handler_vector = QetGraphicsHandlerItem::handlerForPoint(mapToScene(QetGraphicsHandlerUtility::pointsForArc(m_rect, m_start_angle/16, m_span_angle/16)));
446 }
447 else
448 m_handler_vector = QetGraphicsHandlerItem::handlerForPoint(mapToScene(QetGraphicsHandlerUtility::pointsForRect(m_rect)));
449
450 for(QetGraphicsHandlerItem *handler : m_handler_vector)
451 {
452 QColor color = Qt::blue;
453 if (m_resize_mode == 2)
454 color = Qt::darkGreen;
455 else if (m_resize_mode == 3)
456 color = Qt::magenta;
457
458 handler->setColor(color);
459 scene()->addItem(handler);
460 handler->installSceneEventFilter(this);
461 handler->setZValue(this->zValue()+1);
462 }
463 }
464 }
465
466 /**
467 * @brief PartArc::removeHandler
468 * Remove the handlers of this item
469 */
removeHandler()470 void PartArc::removeHandler()
471 {
472 if (!m_handler_vector.isEmpty())
473 {
474 qDeleteAll(m_handler_vector);
475 m_handler_vector.clear();
476 }
477 }
478