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