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 "customelementgraphicpart.h"
19 #include "elementscene.h"
20 #include "QPropertyUndoCommand/qpropertyundocommand.h"
21 
22 /**
23  * @brief CustomElementGraphicPart::CustomElementGraphicPart
24  * Default constructor.
25  * By default, item is selectable, send geometry change (Qt > 4.6),
26  * accept mouse left button and accept hover event
27  * @param editor QETElement editor that belong this.
28  */
CustomElementGraphicPart(QETElementEditor * editor,QGraphicsItem * parent)29 CustomElementGraphicPart::CustomElementGraphicPart(QETElementEditor *editor, QGraphicsItem *parent) :
30 	QGraphicsObject (parent),
31 	CustomElementPart(editor),
32 	m_hovered (false),
33 	_linestyle(NormalStyle),
34 	_lineweight(NormalWeight),
35 	_filling(NoneFilling),
36 	_color(BlackColor),
37 	_antialiased(false)
38 {
39 	setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
40 	setAcceptHoverEvents(true);
41 }
42 
43 /**
44  * @brief CustomElementGraphicPart::~CustomElementGraphicPart
45  * Destructor
46  */
~CustomElementGraphicPart()47 CustomElementGraphicPart::~CustomElementGraphicPart() {}
48 
49 /**
50  * @brief CustomElementGraphicPart::drawCross
51  * Draw a cross at pos center
52  * @param center : center of cross
53  * @param painter : painter to use for draw cross,
54  * the painter state is restored at end of this method.
55  */
drawCross(const QPointF & center,QPainter * painter)56 void CustomElementGraphicPart::drawCross(const QPointF &center, QPainter *painter)
57 {
58 	painter -> save();
59 	painter -> setRenderHint(QPainter::Antialiasing, false);
60 	painter -> setPen((painter -> brush().color() == QColor(Qt::black) && painter -> brush().isOpaque()) ? Qt::yellow : Qt::blue);
61 	painter -> drawLine(QLineF(center.x() - 2.0, center.y(), center.x() + 2.0, center.y()));
62 	painter -> drawLine(QLineF(center.x(), center.y() - 2.0, center.x(), center.y() + 2.0));
63 	painter -> restore();
64 }
65 
66 /**
67  * @brief CustomElementGraphicPart::setLineStyle
68  * Set line style to ls
69  * @param ls
70  */
setLineStyle(const LineStyle ls)71 void CustomElementGraphicPart::setLineStyle(const LineStyle ls)
72 {
73 	if (_linestyle == ls) return;
74 	_linestyle = ls;
75 	update();
76 }
77 
78 /**
79  * @brief CustomElementGraphicPart::setLineWeight
80  * Set line weight to lw
81  * @param lw
82  */
setLineWeight(const LineWeight lw)83 void CustomElementGraphicPart::setLineWeight(const LineWeight lw)
84 {
85 	if (_lineweight == lw) return;
86 	_lineweight = lw;
87 	update();
88 }
89 
90 /**
91  * @brief CustomElementGraphicPart::penWeight
92  * @return the weight of pen
93  */
penWeight() const94 qreal CustomElementGraphicPart::penWeight() const
95 {
96 	if      (_lineweight == NoneWeight || _lineweight == ThinWeight) return 0;
97 	else if (_lineweight == NormalWeight) return 1;
98 	else if (_lineweight == UltraWeight)  return 2;
99 	else if (_lineweight == BigWeight)    return 5;
100 	return 1;
101 }
102 
103 /**
104  * @brief CustomElementGraphicPart::setFilling
105  * Set filling to f
106  * @param f
107  */
setFilling(const Filling f)108 void CustomElementGraphicPart::setFilling(const Filling f)
109 {
110 	if (_filling == f) return;
111 	_filling = f;
112 	update();
113 }
114 
115 /**
116  * @brief CustomElementGraphicPart::setColor
117  * Set color to c
118  * @param c
119  */
setColor(const Color c)120 void CustomElementGraphicPart::setColor(const Color c)
121 {
122 	if (_color == c) return;
123 	_color = c;
124 	update();
125 }
126 
127 /**
128  * @brief CustomElementGraphicPart::setAntialiased
129  * Set antialias to b
130  * @param b
131  */
setAntialiased(const bool b)132 void CustomElementGraphicPart::setAntialiased(const bool b)
133 {
134 	if (_antialiased == b) return;
135 	_antialiased = b;
136 	update();
137 }
138 
139 /**
140  * @brief CustomElementGraphicPart::stylesToXml
141  * Write the curent style to xml element.
142  * The style are stored like this:
143  * name-of-style:value;name-of-style:value
144  * Each style separate by ; and name-style/value are separate by :
145  * @param qde : QDOmElement used to write the style.
146  */
stylesToXml(QDomElement & qde) const147 void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const
148 {
149 	QString css_like_styles;
150 
151 	css_like_styles += "line-style:";
152 	if      (_linestyle == DashedStyle)     css_like_styles += "dashed";
153 	else if (_linestyle == DottedStyle)     css_like_styles += "dotted";
154 	else if (_linestyle == DashdottedStyle) css_like_styles += "dashdotted";
155 	else if (_linestyle == NormalStyle)     css_like_styles += "normal";
156 
157 	css_like_styles += ";line-weight:";
158 	if      (_lineweight == NoneWeight)   css_like_styles += "none";
159 	else if (_lineweight == ThinWeight)   css_like_styles += "thin";
160 	else if (_lineweight == NormalWeight) css_like_styles += "normal";
161 	else if (_lineweight == UltraWeight)  css_like_styles += "hight";
162 	else if (_lineweight == BigWeight)    css_like_styles += "eleve";
163 
164 
165 	css_like_styles += ";filling:";
166 	if      (_filling == NoneFilling)  css_like_styles += "none";
167 	else if (_filling == BlackFilling) css_like_styles += "black";
168 	else if (_filling == WhiteFilling) css_like_styles += "white";
169 	else if (_filling == GreenFilling) css_like_styles += "green";
170 	else if (_filling == BlueFilling)  css_like_styles += "blue";
171 	else if (_filling == RedFilling)   css_like_styles += "red";
172 	else if (_filling == GrayFilling)  css_like_styles += "gray";
173 	else if (_filling == BrunFilling)  css_like_styles += "brun";
174 	else if (_filling == YellowFilling)  css_like_styles += "yellow";
175 	else if (_filling == CyanFilling)  css_like_styles += "cyan";
176 	else if (_filling == MagentaFilling)  css_like_styles += "magenta";
177 	else if (_filling == LightgrayFilling)  css_like_styles += "lightgray";
178 	else if (_filling == OrangeFilling)  css_like_styles += "orange";
179 	else if (_filling == PurpleFilling)  css_like_styles += "purple";
180 	else if (_filling == HorFilling) css_like_styles += "hor";
181 	else if (_filling == VerFilling) css_like_styles += "ver";
182 	else if (_filling == BdiagFilling) css_like_styles += "bdiag";
183 	else if (_filling == FdiagFilling) css_like_styles += "fdiag";
184 
185 
186 	css_like_styles += ";color:";
187 	if      (_color == WhiteColor) css_like_styles += "white";
188 	else if (_color == BlackColor) css_like_styles += "black";
189 	else if (_color == GreenColor) css_like_styles += "green";
190 	else if (_color == RedColor)   css_like_styles += "red";
191 	else if (_color == BlueColor)  css_like_styles += "blue";
192 	else if (_color == GrayColor)  css_like_styles += "gray";
193 	else if (_color == BrunColor)  css_like_styles += "brun";
194 	else if (_color == YellowColor)  css_like_styles += "yellow";
195 	else if (_color == CyanColor)  css_like_styles += "cyan";
196 	else if (_color == MagentaColor)  css_like_styles += "magenta";
197 	else if (_color == LightgrayColor)  css_like_styles += "lightgray";
198 	else if (_color == OrangeColor)  css_like_styles += "orange";
199 	else if (_color == PurpleColor)  css_like_styles += "purple";
200 	else if (_color == NoneColor)  css_like_styles += "none";
201 
202 
203 	qde.setAttribute("style", css_like_styles);
204 	qde.setAttribute("antialias", _antialiased ? "true" : "false");
205 }
206 
207 
208 /**
209  * @brief CustomElementGraphicPart::stylesFromXml
210  * Read the style used by this, from a xml element.
211  * @param qde : QDomElement used to read the style
212  */
stylesFromXml(const QDomElement & qde)213 void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde)
214 {
215 	resetStyles();
216 
217 		//Get the list of pair style/value
218 	QStringList styles = qde.attribute("style").split(";", QString::SkipEmptyParts);
219 
220 		//Check each pair of style
221 	QRegExp rx("^\\s*([a-z-]+)\\s*:\\s*([a-z-]+)\\s*$");
222 	foreach (QString style, styles)
223 	{
224 		if (!rx.exactMatch(style)) continue;
225 		QString style_name = rx.cap(1);
226 		QString style_value = rx.cap(2);
227 		if (style_name == "line-style")
228 		{
229 			if      (style_value == "dashed")     _linestyle = DashedStyle;
230 			else if (style_value == "dotted")     _linestyle = DottedStyle;
231 			else if (style_value == "dashdotted") _linestyle = DashdottedStyle;
232 			else if (style_value == "normal")     _linestyle = NormalStyle;
233 		}
234 		else if (style_name == "line-weight")
235 		{
236 			if      (style_value == "none")   _lineweight = NoneWeight;
237 			else if (style_value == "thin")   _lineweight = ThinWeight;
238 			else if (style_value == "normal") _lineweight = NormalWeight;
239 			else if (style_value == "hight")  _lineweight = UltraWeight;
240 			else if (style_value == "eleve")  _lineweight = BigWeight;
241 		}
242 		else if (style_name == "filling")
243 		{
244 			if      (style_value == "white") _filling = WhiteFilling;
245 			else if (style_value == "black") _filling = BlackFilling;
246 			else if (style_value == "red")   _filling = RedFilling;
247 			else if (style_value == "green") _filling = GreenFilling;
248 			else if (style_value == "blue")  _filling = BlueFilling;
249 			else if (style_value == "gray")  _filling = GrayFilling;
250 			else if (style_value == "brun")  _filling = BrunFilling;
251 			else if (style_value == "yellow")  _filling = YellowFilling;
252 			else if (style_value == "cyan")  _filling = CyanFilling;
253 			else if (style_value == "magenta")  _filling = MagentaFilling;
254 			else if (style_value == "lightgray")  _filling = LightgrayFilling;
255 			else if (style_value == "orange")  _filling = OrangeFilling;
256 			else if (style_value == "purple")  _filling = PurpleFilling;
257 			else if (style_value == "hor")  _filling = HorFilling;
258 			else if (style_value == "ver")  _filling = VerFilling;
259 			else if (style_value == "bdiag")  _filling = BdiagFilling;
260 			else if (style_value == "fdiag")  _filling = FdiagFilling;
261 			else if (style_value == "none")  _filling = NoneFilling;
262 		}
263 		else if (style_name == "color")
264 		{
265 			if      (style_value == "black") _color = BlackColor;
266 			else if (style_value == "white") _color = WhiteColor;
267 			else if (style_value == "green") _color = GreenColor;
268 			else if (style_value == "red")   _color = RedColor;
269 			else if (style_value == "blue")  _color = BlueColor;
270 			else if (style_value == "gray")  _color = GrayColor;
271 			else if (style_value == "brun")  _color = BrunColor;
272 			else if (style_value == "yellow")  _color = YellowColor;
273 			else if (style_value == "cyan")  _color = CyanColor;
274 			else if (style_value == "magenta")  _color = MagentaColor;
275 			else if (style_value == "lightgray")  _color = LightgrayColor;
276 			else if (style_value == "orange")  _color = OrangeColor;
277 			else if (style_value == "purple")  _color = PurpleColor;
278 			else if (style_value == "none")  _color = NoneColor;
279 		}
280 	}
281 		//Get antialiasing
282 	_antialiased = qde.attribute("antialias") == "true";
283 }
284 
285 
286 /**
287  * @brief CustomElementGraphicPart::resetStyles
288  * Reset the curent style to default,
289  * same style of default constructor
290  */
resetStyles()291 void CustomElementGraphicPart::resetStyles()
292 {
293 	_linestyle = NormalStyle;
294 	_lineweight = NormalWeight;
295 	_filling = NoneFilling;
296 	_color = BlackColor;
297 	_antialiased = false;
298 }
299 
300 /**
301  * @brief CustomElementGraphicPart::applyStylesToQPainter
302  * Apply the current style to the QPainter
303  * @param painter
304  */
applyStylesToQPainter(QPainter & painter) const305 void CustomElementGraphicPart::applyStylesToQPainter(QPainter &painter) const
306 {
307 		//Get the pen and brush
308 	QPen pen = painter.pen();
309 	QBrush brush = painter.brush();
310 
311 		//Apply pen style
312 	if      (_linestyle == DashedStyle)     pen.setStyle(Qt::DashLine);
313 	else if (_linestyle == DashdottedStyle) pen.setStyle(Qt::DashDotLine);
314 	else if (_linestyle == DottedStyle)     pen.setStyle(Qt::DotLine);
315 	else if (_linestyle == NormalStyle)     pen.setStyle(Qt::SolidLine);
316 
317 		//Apply pen width
318 	if      (_lineweight == NoneWeight)   pen.setColor(QColor(0, 0, 0, 0));
319 	else if (_lineweight == ThinWeight)   pen.setWidth(0);
320 	else if (_lineweight == NormalWeight) pen.setWidthF(1.0);
321 	else if (_lineweight == UltraWeight)  pen.setWidthF(2.0);
322 	else if (_lineweight == BigWeight)    pen.setWidthF(5.0);
323 
324 		//Apply brush color
325 	if (_filling == NoneFilling) brush.setStyle(Qt::NoBrush);
326 	else if (_filling == HorFilling) brush.setStyle(Qt::HorPattern);
327 	else if (_filling == VerFilling) brush.setStyle(Qt::VerPattern);
328 	else if (_filling == BdiagFilling) brush.setStyle(Qt::BDiagPattern);
329 	else if (_filling == FdiagFilling) brush.setStyle(Qt::FDiagPattern);
330 	else
331 	{
332 		brush.setStyle(Qt::SolidPattern);
333 		if (_filling == BlackFilling)      brush.setColor(Qt::black);
334 		else if (_filling == WhiteFilling) brush.setColor(Qt::white);
335 		else if (_filling == GreenFilling) brush.setColor(Qt::green);
336 		else if (_filling == RedFilling)   brush.setColor(Qt::red);
337 		else if (_filling == BlueFilling)  brush.setColor(Qt::blue);
338 		else if (_filling == GrayFilling)  brush.setColor(Qt::gray);
339 		else if (_filling == BrunFilling)  brush.setColor(QColor(97, 44, 0));
340 		else if (_filling == YellowFilling)  brush.setColor(Qt::yellow);
341 		else if (_filling == CyanFilling)  brush.setColor(Qt::cyan);
342 		else if (_filling == MagentaFilling)  brush.setColor(Qt::magenta);
343 		else if (_filling == LightgrayFilling)  brush.setColor(Qt::lightGray);
344 		else if (_filling == OrangeFilling)  brush.setColor(QColor(255, 128, 0));
345 		else if (_filling == PurpleFilling)  brush.setColor(QColor(136, 28, 168));
346 	}
347 
348 		//Apply pen color
349 	if      (_color == WhiteColor) pen.setColor(QColor(255, 255, 255, pen.color().alpha()));
350 	else if (_color == BlackColor) pen.setColor(QColor(  0,   0,   0, pen.color().alpha()));
351 	else if (_color == GreenColor) pen.setColor(QColor(Qt::green));
352 	else if (_color == RedColor)   pen.setColor(QColor(Qt::red));
353 	else if (_color == BlueColor)  pen.setColor(QColor(Qt::blue));
354 	else if (_color == GrayColor)  pen.setColor(QColor(Qt::gray));
355 	else if (_color == BrunColor)  pen.setColor(QColor(97, 44, 0));
356 	else if (_color == YellowColor)  pen.setColor(QColor(Qt::yellow));
357 	else if (_color == CyanColor)  pen.setColor(Qt::cyan);
358 	else if (_color == MagentaColor)  pen.setColor(Qt::magenta);
359 	else if (_color == LightgrayColor)  pen.setColor(Qt::lightGray);
360 	else if (_color == OrangeColor)  pen.setColor(QColor(255, 128, 0));
361 	else if (_color == PurpleColor)  pen.setColor(QColor(136, 28, 168));
362 	else if (_color == NoneColor)  pen.setBrush(Qt::transparent);
363 
364 		//Apply antialiasing
365 	painter.setRenderHint(QPainter::Antialiasing,          _antialiased);
366 	painter.setRenderHint(QPainter::TextAntialiasing,      _antialiased);
367 	painter.setRenderHint(QPainter::SmoothPixmapTransform, _antialiased);
368 
369 	painter.setPen(pen);
370 	painter.setBrush(brush);
371 }
372 
373 /**
374  * @brief CustomElementGraphicPart::drawShadowShape
375  * Draw a transparent blue shadow arround the shape of this item.
376  * The QPainterPathStroker used to draw shadows have a width of SHADOWS_HEIGHT
377  * Be carefull if penWeight of this item is to 0 the outline of strock is bigger of 0.5
378  * @param painter : painter to use for draw this shadows
379  */
drawShadowShape(QPainter * painter)380 void CustomElementGraphicPart::drawShadowShape(QPainter *painter)
381 {
382 		//@FIXME if pen weight is 0, the strock outline is SHADOWS_HEIGHT/2 + 0.5
383 		//may be because shape have no line weight
384 	QPainterPathStroker strock;
385 	strock.setWidth(SHADOWS_HEIGHT);
386 	strock.setJoinStyle(Qt::RoundJoin);
387 
388 	painter->save();
389 	QColor color(Qt::darkBlue);
390 	color.setAlpha(50);
391 	painter -> setBrush (QBrush (color));
392 	painter -> setPen   (Qt::NoPen);
393 	painter -> drawPath (strock.createStroke(shadowShape()));
394 	painter -> restore  ();
395 }
396 
397 /**
398  * @brief CustomElementGraphicPart::itemChange
399  * Reimplemented from QGraphicsObject.
400  * If the item position change call updateCurrentPartEditor()
401  * the change is always send to QGraphicsObject
402  * @param change
403  * @param value
404  * @return the returned value of QGraphicsObject::itemChange
405  */
itemChange(GraphicsItemChange change,const QVariant & value)406 QVariant CustomElementGraphicPart::itemChange(GraphicsItemChange change, const QVariant &value)
407 {
408 	if (scene())
409 		if (change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged)
410 			updateCurrentPartEditor();
411 
412 	return(QGraphicsObject::itemChange(change, value));
413 }
414 
415 /**
416  * @brief CustomElementGraphicPart::hoverEnterEvent
417  * Reimplemented from QGraphicsObject.
418  * Set m_hovered to true
419  * @param event
420  */
hoverEnterEvent(QGraphicsSceneHoverEvent * event)421 void CustomElementGraphicPart::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
422 {
423 	m_hovered = true;
424 	QGraphicsObject::hoverEnterEvent(event);
425 }
426 
427 /**
428  * @brief CustomElementGraphicPart::hoverLeaveEvent
429  * Reimplemented from QGraphicsObject.
430  * Set m_hovered to false
431  * @param event
432  */
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)433 void CustomElementGraphicPart::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
434 {
435 	m_hovered = false;
436 	QGraphicsObject::hoverLeaveEvent(event);
437 }
438 
mousePressEvent(QGraphicsSceneMouseEvent * event)439 void CustomElementGraphicPart::mousePressEvent(QGraphicsSceneMouseEvent *event)
440 {
441 	if(event->button() == Qt::LeftButton)
442 		m_origin_pos = this->pos();
443 
444 	QGraphicsObject::mousePressEvent(event);
445 }
446 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)447 void CustomElementGraphicPart::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
448 {
449 	if((event->buttons() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable))
450 	{
451 		QPointF pos = event->scenePos() + (m_origin_pos - event->buttonDownScenePos(Qt::LeftButton));
452 		event->modifiers() == Qt::ControlModifier ? setPos(pos) : setPos(elementScene()->snapToGrid(pos));
453 	}
454 	else
455 		QGraphicsObject::mouseMoveEvent(event);
456 }
457 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)458 void CustomElementGraphicPart::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
459 {
460 	if((event->button() & Qt::LeftButton) && (flags() & QGraphicsItem::ItemIsMovable) && m_origin_pos != pos())
461 	{
462 		QPropertyUndoCommand *undo = new QPropertyUndoCommand(this, "pos", QVariant(m_origin_pos), QVariant(pos()));
463 		undo->setText(tr("Déplacer une primitive"));
464 		undo->enableAnimation();
465 		elementScene()->undoStack().push(undo);
466 	}
467 
468 	QGraphicsObject::mouseReleaseEvent(event);
469 }
470