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 "crossrefitem.h"
19 #include "element.h"
20 #include "qetapp.h"
21 #include "diagramposition.h"
22 #include "diagram.h"
23 #include "qgraphicsitemutility.h"
24 #include "assignvariables.h"
25 #include "dynamicelementtextitem.h"
26 #include "elementtextitemgroup.h"
27 
28 //define the height of the header.
29 static int header = 5;
30 //define the minimal height of the cross (without header)
31 static int cross_min_heigth = 33;
32 
33 /**
34  * @brief CrossRefItem::CrossRefItem
35  * @param elmt : element to display the cross ref
36  */
CrossRefItem(Element * elmt)37 CrossRefItem::CrossRefItem(Element *elmt) :
38 	QGraphicsObject(elmt),
39 	m_element(elmt)
40 {init();}
41 
42 /**
43  * @brief CrossRefItem::CrossRefItem
44  * @param elmt : element to display the cross ref
45  * @param text : If the Xref must be displayed under a text, the text.
46  */
CrossRefItem(Element * elmt,DynamicElementTextItem * text)47 CrossRefItem::CrossRefItem(Element *elmt, DynamicElementTextItem *text) :
48 	QGraphicsObject(text),
49 	m_element (elmt),
50 	m_text(text)
51 {init();}
52 
53 /**
54  * @brief CrossRefItem::CrossRefItem
55  * @param elmt : element to display the cross ref
56  * @param group : If the Xref must be displayed under a group, the group.
57  */
CrossRefItem(Element * elmt,ElementTextItemGroup * group)58 CrossRefItem::CrossRefItem(Element *elmt, ElementTextItemGroup *group) :
59 	QGraphicsObject(group),
60 	m_element(elmt),
61 	m_group(group)
62 {init();}
63 
64 /**
65  * @brief CrossRefItem::~CrossRefItem
66  * Default destructor
67  */
~CrossRefItem()68 CrossRefItem::~CrossRefItem() {}
69 
70 /**
71  * @brief CrossRefItem::init
72  * init this Xref
73  */
init()74 void CrossRefItem::init()
75 {
76 	if(!m_element->diagram())
77 	{
78 		qDebug() << "CrossRefItem constructor" << "element is not in a diagram";
79 		return;
80 	}
81 
82 	QETProject *project = m_element->diagram()->project();
83 	connect(project, &QETProject::XRefPropertiesChanged, this, &CrossRefItem::updateProperties);
84 
85 	m_properties = m_element->diagram()->project()->defaultXRefProperties(m_element->kindInformations()["type"].toString());
86 	setAcceptHoverEvents(true);
87 
88 	setUpConnection();
89 	linkedChanged();
90 	updateLabel();
91 }
92 
93 /**
94  * @brief CrossRefItem::setUpConnection
95  * Set up several connection to keep up to date the Xref
96  */
setUpConnection()97 void CrossRefItem::setUpConnection()
98 {
99 	for(const QMetaObject::Connection& c : m_update_connection)
100 		disconnect(c);
101 
102 	m_update_connection.clear();
103 	QETProject *project = m_element->diagram()->project();
104 	bool set=false;
105 
106 	if(m_properties.snapTo() == XRefProperties::Label && (m_text || m_group)) //Snap to label and parent is a text or a group
107 		set=true;
108 	else if(m_properties.snapTo() == XRefProperties::Bottom && !m_text && !m_group) //Snap to bottom of element and parent is the element itself
109 	{
110 		m_update_connection << connect(m_element, SIGNAL(yChanged()),        this, SLOT(autoPos()));
111 		m_update_connection << connect(m_element, SIGNAL(rotationChanged()), this, SLOT(autoPos()));
112 		set=true;
113 	}
114 
115 	if(set)
116 	{
117 		m_update_connection << connect(project, &QETProject::projectDiagramsOrderChanged, this, &CrossRefItem::updateLabel);
118 		m_update_connection << connect(project, &QETProject::diagramRemoved,              this, &CrossRefItem::updateLabel);
119 		m_update_connection << connect(m_element, &Element::linkedElementChanged, this, &CrossRefItem::linkedChanged);
120 		linkedChanged();
121 		updateLabel();
122 	}
123 }
124 
125 /**
126  * @brief CrossRefItem::boundingRect
127  * @return the bounding rect of this item
128  */
boundingRect() const129 QRectF CrossRefItem::boundingRect() const {
130 	return m_bounding_rect;
131 }
132 
133 /**
134  * @brief CrossRefItem::shape
135  * @return the shape of this item
136  */
shape() const137 QPainterPath CrossRefItem::shape() const{
138 	return m_shape_path;
139 }
140 
141 /**
142  * @brief CrossRefItem::elementPositionText
143  * @param elmt
144  * @return the string corresponding to the position of @elmt in the diagram.
145  * if @add_prefix is true, prefix (for power and delay contact) is added to the poistion text.
146  */
elementPositionText(const Element * elmt,const bool & add_prefix) const147 QString CrossRefItem::elementPositionText(const Element *elmt, const bool &add_prefix) const
148 {
149 	XRefProperties xrp = m_element->diagram()->project()->defaultXRefProperties(m_element->kindInformations()["type"].toString());
150 	QString formula = xrp.masterLabel();
151 	autonum::sequentialNumbers seq;
152 	QString txt = autonum::AssignVariables::formulaToLabel(formula, seq, elmt->diagram(), elmt);
153 
154 	if (add_prefix)
155 	{
156 		if      (elmt->kindInformations()["type"].toString() == "power")        txt.prepend(m_properties.prefix("power"));
157 		else if (elmt->kindInformations()["type"].toString().contains("delay")) txt.prepend(m_properties.prefix("delay"));
158 		else if (elmt->kindInformations()["state"].toString() == "SW")          txt.prepend(m_properties.prefix("switch"));
159 	}
160 	return txt;
161 }
162 
163 /**
164  * @brief CrossRefItem::updateProperties
165  * update the curent properties
166  */
updateProperties()167 void CrossRefItem::updateProperties()
168 {
169 	XRefProperties xrp = m_element->diagram()->project()->defaultXRefProperties(m_element->kindInformations()["type"].toString());
170 
171 	if (m_properties != xrp)
172 	{
173 		m_properties = xrp;
174 		hide();
175 		if(m_properties.snapTo() == XRefProperties::Label && (m_text || m_group)) //Snap to label and parent is text or group
176 			show();
177 		else if((m_properties.snapTo() == XRefProperties::Bottom && !m_text && !m_group)) //Snap to bottom of element is the parent
178 			show();
179 
180 		setUpConnection();
181 		updateLabel();
182 	}
183 }
184 
185 /**
186  * @brief CrossRefItem::updateLabel
187  * Update the content of the item
188  */
updateLabel()189 void CrossRefItem::updateLabel()
190 {
191 		//init the shape and bounding rect
192 	m_shape_path    = QPainterPath();
193 	prepareGeometryChange();
194 	m_bounding_rect = QRectF();
195 
196 		//init the painter
197 	QPainter qp;
198 	qp.begin(&m_drawing);
199 	QPen pen_;
200 	pen_.setWidthF(0.5);
201 	qp.setPen(pen_);
202 	qp.setFont(QETApp::diagramTextsFont(5));
203 
204 		//Draw cross or contact, only if master element is linked.
205 	if (! m_element->linkedElements().isEmpty())
206 	{
207 		XRefProperties::DisplayHas dh = m_properties.displayHas();
208 
209 		if (dh == XRefProperties::Cross)
210 			drawAsCross(qp);
211 		else if (dh == XRefProperties::Contacts)
212 			drawAsContacts(qp);
213 	}
214 
215 //	AddExtraInfo(qp, "comment");
216 //	AddExtraInfo(qp, "location");
217 	qp.end();
218 
219 	autoPos();
220 	update();
221 }
222 
223 /**
224  * @brief CrossRefItem::autoPos
225  * Calculate and set position automaticaly.
226  */
autoPos()227 void CrossRefItem::autoPos() {
228 		//We calcul the position according to the @snapTo of the xrefproperties
229 	if (m_properties.snapTo() == XRefProperties::Bottom)
230 		centerToBottomDiagram(this, m_element, m_properties.offset() <= 40 ? 5 : m_properties.offset());
231 	else
232 		centerToParentBottom(this);
233 }
234 
sceneEvent(QEvent * event)235 bool CrossRefItem::sceneEvent(QEvent *event)
236 {
237 		//By default when a QGraphicsItem is a child of a QGraphicsItemGroup
238 		//all events are forwarded to group.
239 		//We override it, when this Xref is in a group
240 	if(m_group)
241 	{
242 		switch (event->type())
243 		{
244 			case QEvent::GraphicsSceneHoverEnter:
245 				hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
246 				break;
247 			case QEvent::GraphicsSceneHoverMove:
248 				hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
249 				break;
250 			case QEvent::GraphicsSceneHoverLeave:
251 				hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event));
252 				break;
253 			case QEvent::GraphicsSceneMouseDoubleClick:
254 				mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
255 				break;
256 			default:break;
257 		}
258 		return true;
259 	}
260 
261 	return QGraphicsObject::sceneEvent(event);
262 }
263 
264 /**
265  * @brief CrossRefItem::paint
266  * Paint this item
267  * @param painter
268  * @param option
269  * @param widget
270  */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)271 void CrossRefItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
272 	Q_UNUSED(option);
273 	Q_UNUSED(widget);
274 	m_drawing.play(painter);
275 }
276 
277 /**
278  * @brief CrossRefItem::mouseDoubleClickEvent
279  * @param event
280  */
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)281 void CrossRefItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
282 {
283 	event->accept();
284 	if (m_hovered_contact && m_hovered_contact->scene())
285 	{
286 			//Show and select the linked slave element
287 		if (scene() != m_hovered_contact->scene())
288 		{
289 			m_hovered_contact->diagram()->showMe();
290 		}
291 		m_hovered_contact->setSelected(true);
292 
293 			//Zoom to the linked slave element
294 		foreach(QGraphicsView *view, m_hovered_contact->diagram()->views())
295 		{
296 			QRectF fit = m_hovered_contact->sceneBoundingRect();
297 			fit.adjust(-200, -200, 200, 200);
298 			view->fitInView(fit, Qt::KeepAspectRatioByExpanding);
299 		}
300 	}
301 }
302 
hoverEnterEvent(QGraphicsSceneHoverEvent * event)303 void CrossRefItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
304 {
305 	m_hovered_contact = nullptr;
306 		QGraphicsObject::hoverEnterEvent(event);
307 }
308 
hoverMoveEvent(QGraphicsSceneHoverEvent * event)309 void CrossRefItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
310 {
311 	QPointF pos = event->pos();
312 
313 	if (m_hovered_contact)
314 	{
315 		foreach(QRectF rect, m_hovered_contacts_map.values(m_hovered_contact))
316 		{
317 				//Mouse hover the same rect than previous hover event
318 			if (rect.contains(pos))
319 			{
320 				QGraphicsObject::hoverMoveEvent(event);
321 				return;
322 			}
323 		}
324 
325 			//At this point, mouse don't hover previous rect
326 		m_hovered_contact = nullptr;
327 
328 		foreach (Element *elmt, m_hovered_contacts_map.keys())
329 		{
330 			foreach(QRectF rect, m_hovered_contacts_map.values(elmt))
331 			{
332 					//Mouse hover a contact
333 				if (rect.contains(pos))
334 				{
335 					m_hovered_contact = elmt;
336 				}
337 			}
338 		}
339 
340 		updateLabel();
341 		QGraphicsObject::hoverMoveEvent(event);
342 		return;
343 	}
344 	else
345 	{
346 		foreach (Element *elmt, m_hovered_contacts_map.keys())
347 		{
348 			foreach(QRectF rect, m_hovered_contacts_map.values(elmt))
349 			{
350 					//Mouse hover a contact
351 				if (rect.contains(pos))
352 				{
353 					m_hovered_contact = elmt;
354 					updateLabel();
355 					QGraphicsObject::hoverMoveEvent(event);
356 					return;
357 				}
358 			}
359 		}
360 	}
361 }
362 
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)363 void CrossRefItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
364 {
365 	m_hovered_contact = nullptr;
366 	updateLabel();
367 	QGraphicsObject::hoverLeaveEvent(event);
368 }
369 
linkedChanged()370 void CrossRefItem::linkedChanged()
371 {
372 	for(const QMetaObject::Connection& c : m_slave_connection)
373 		disconnect(c);
374 
375 	m_slave_connection.clear();
376 
377 	if(!isVisible())
378 		return;
379 
380 	for(Element *elmt : m_element->linkedElements())
381 	{
382 		m_slave_connection << connect(elmt, &Element::xChanged, this, &CrossRefItem::updateLabel);
383 		m_slave_connection << connect(elmt, &Element::yChanged, this, &CrossRefItem::updateLabel);
384 	}
385 
386 	updateLabel();
387 }
388 
389 /**
390  * @brief CrossRefItem::buildHeaderContact
391  * Draw the QPicture of m_hdr_no_ctc and m_hdr_nc_ctc
392  */
buildHeaderContact()393 void CrossRefItem::buildHeaderContact() {
394 	if (!m_hdr_no_ctc.isNull() && !m_hdr_nc_ctc.isNull()) return;
395 
396 	//init the painter
397 	QPainter qp;
398 	QPen pen_;
399 	pen_.setWidthF(0.2);
400 
401 	//draw the NO contact
402 	if (m_hdr_no_ctc.isNull()) {
403 		qp.begin(&m_hdr_no_ctc);
404 		qp.setPen(pen_);
405 		qp.drawLine(0, 3, 5, 3);
406 		QPointF p1[3] = {
407 			QPointF(5, 0),
408 			QPointF(10, 3),
409 			QPointF(15, 3),
410 		};
411 		qp.drawPolyline(p1,3);
412 		qp.end();
413 	}
414 
415 	//draw the NC contact
416 	if (m_hdr_nc_ctc.isNull()) {
417 		qp.begin(&m_hdr_nc_ctc);
418 		qp.setPen(pen_);
419 		QPointF p2[3] = {
420 			QPointF(0, 3),
421 			QPointF(5, 3),
422 			QPointF(5, 0)
423 		};
424 		qp.drawPolyline(p2,3);
425 		QPointF p3[3] = {
426 			QPointF(4, 0),
427 			QPointF(10, 3),
428 			QPointF(15, 3),
429 		};
430 		qp.drawPolyline(p3,3);
431 		qp.end();
432 	}
433 }
434 
435 /**
436  * @brief CrossRefItem::setUpCrossBoundingRect
437  * Get the numbers of slaves elements linked to this parent element,
438  * for calculate the size of the cross bounding rect.
439  * The cross ref item is drawing according to the size of the cross bounding rect.
440  */
setUpCrossBoundingRect(QPainter & painter)441 void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
442 {
443 		//No need to calcul if nothing is linked
444 	if (m_element->isFree()) return;
445 
446 	QStringList no_str, nc_str;
447 
448 	foreach (Element *elmt, NOElements())
449 		no_str.append(elementPositionText(elmt, true));
450 	foreach(Element *elmt, NCElements())
451 		nc_str.append(elementPositionText(elmt, true));
452 
453 
454 		//There is no string to display, we return now
455 	if (no_str.isEmpty() && nc_str.isEmpty()) return;
456 
457 		//this is the default size of cross ref item
458 	QRectF default_bounding(0, 0, 40, header + cross_min_heigth);
459 
460 		//Bounding rect of the NO text
461 	QRectF no_bounding;
462 	foreach(QString str, no_str)
463 	{
464 		QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
465 		no_bounding = no_bounding.united(bounding);
466 	}
467 		//Adjust according to the NO
468 	if (no_bounding.height() > default_bounding.height() - header)
469 		default_bounding.setHeight(no_bounding.height() + header); //adjust the height
470 	if (no_bounding.width() > default_bounding.width()/2)
471 		default_bounding.setWidth(no_bounding.width()*2);			//adjust the width
472 
473 		//Bounding rect of the NC text
474 	QRectF nc_bounding;
475 	foreach(QString str, nc_str)
476 	{
477 		QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
478 		nc_bounding = nc_bounding.united(bounding);
479 	}
480 		//Adjust according to the NC
481 	if (nc_bounding.height() > default_bounding.height() - header)
482 		default_bounding.setHeight(nc_bounding.height() + header); //adjust the heigth
483 	if (nc_bounding.width() > default_bounding.width()/2)
484 		default_bounding.setWidth(nc_bounding.width()*2);			//adjust the width
485 
486 		//Minor adjustement for better visual
487 	default_bounding.adjust(0, 0, 4, 0);
488 	m_shape_path.addRect(default_bounding);
489 	prepareGeometryChange();
490 	m_bounding_rect = default_bounding;
491 }
492 
493 /**
494  * @brief CrossRefItem::drawAsCross
495  * Draw this crossref with a cross
496  * @param painter, painter to use
497  */
drawAsCross(QPainter & painter)498 void CrossRefItem::drawAsCross(QPainter &painter)
499 {
500 		//calcul the size of the cross
501 	setUpCrossBoundingRect(painter);
502 	m_hovered_contacts_map.clear();
503 
504 		//Bounding rect is empty that mean there's no contact to draw
505 	if (boundingRect().isEmpty()) return;
506 
507 		//draw the cross
508 	QRectF br = boundingRect();
509 	painter.drawLine(br.width()/2, 0, br.width()/2, br.height());	//vertical line
510 	painter.drawLine(0, header, br.width(), header);	//horizontal line
511 
512 		//Add the symbolic contacts
513 	buildHeaderContact();
514 	QPointF p((m_bounding_rect.width()/4) - (m_hdr_no_ctc.width()/2), 0);
515 	painter.drawPicture (p, m_hdr_no_ctc);
516 	p.setX((m_bounding_rect.width() * 3/4) - (m_hdr_nc_ctc.width()/2));
517 	painter.drawPicture (p, m_hdr_nc_ctc);
518 
519 		//and fill it
520 	fillCrossRef(painter);
521 }
522 
523 /**
524  * @brief CrossRefItem::drawAsContacts
525  * Draw this crossref with symbolic contacts
526  * @param painter painter to use
527  */
drawAsContacts(QPainter & painter)528 void CrossRefItem::drawAsContacts(QPainter &painter)
529 {
530 	if (m_element -> isFree())
531 		return;
532 
533 	m_drawed_contacts = 0;
534 	m_hovered_contacts_map.clear();
535 	QRectF bounding_rect;
536 
537 	//Draw each linked contact
538 	foreach (Element *elmt,  m_element->linkedElements())
539 	{
540 		DiagramContext info = elmt->kindInformations();
541 
542 		for (int i=0; i<info["number"].toInt(); i++)
543 		{
544 			int option = 0;
545 
546 			QString state = info["state"].toString();
547 				 if (state == "NO") option = NO;
548 			else if (state == "NC") option = NC;
549 			else if (state == "SW") option = SW;
550 
551 			QString type = info["type"].toString();
552 				 if (type == "power")    option += Power;
553 			else if (type == "delayOn")  option += DelayOn;
554 			else if (type == "delayOff") option += DelayOff;
555 			else if (type == "delayOnOff") option += DelayOnOff;
556 
557 			QRectF br = drawContact(painter, option, elmt);
558 			bounding_rect = bounding_rect.united(br);
559 		}
560 	}
561 
562 	bounding_rect.adjust(-4, -4, 4, 4);
563 	prepareGeometryChange();
564 	m_bounding_rect = bounding_rect;
565 	m_shape_path.addRect(bounding_rect);
566 }
567 
568 /**
569  * @brief CrossRefItem::drawContact
570  * Draw one contact, the type of contact to draw is define in @flags.
571  * @param painter, painter to use
572  * @param flags, define how to draw the contact (see enul CONTACTS)
573  * @param elmt, the element to display text (the position of the contact)
574  * @return The bounding rect of the draw (contact + text)
575  */
drawContact(QPainter & painter,int flags,Element * elmt)576 QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
577 {
578 	QString str = elementPositionText(elmt);
579 	int offset = m_drawed_contacts*10;
580 	QRectF bounding_rect;
581 
582 	QPen pen = painter.pen();
583 	m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
584 	painter.setPen(pen);
585 
586 	//Draw NO or NC contact
587 	if (flags &NOC)
588 	{
589 		bounding_rect = QRectF(0, offset, 24, 10);
590 
591 		//draw the basic line
592 		painter.drawLine(0, offset+6, 8, offset+6);
593 		painter.drawLine(16, offset+6, 24, offset+6);
594 
595 		///take exemple of this code for display the terminal text
596 		/*QFont font = QETApp::diagramTextsFont(4);
597 		font.setBold(true);
598 		painter.setFont(font);
599 		QRectF bt(0, offset, 24, 10);
600 		int txt = 10 + m_drawed_contacts;
601 		painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, QString::number(txt));
602 		painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, QString::number(txt));
603 		painter.setFont(QETApp::diagramTextsFont(5));*/
604 
605 		//draw open contact
606 		if (flags &NO) {
607 			painter.drawLine(8, offset+9, 16, offset+6);
608 		}
609 		//draw close contact
610 		if (flags &NC) {
611 			QPointF p1[3] = {
612 				QPointF(8, offset+6),
613 				QPointF(9, offset+6),
614 				QPointF(9, offset+2.5)
615 			};
616 			painter.drawPolyline(p1,3);
617 			painter.drawLine(8, offset+3, 16, offset+6);
618 		}
619 
620 		//draw half circle for power contact
621 		if (flags &Power) {
622 			QRectF arc(4, offset+4, 4, 4);
623 			if (flags &NO)
624 				painter.drawArc(arc, 180*16, 180*16);
625 			else
626 				painter.drawArc(arc, 0, 180*16);
627 		}
628 
629 			// draw half circle for delay contact
630 		if(flags &Delay) {
631 				// for delay on contact
632 			if (flags &DelayOn) {
633 				if (flags &NO) {
634                     painter.drawLine(12, offset+8, 12, offset+11);
635                     QRectF r(9.5, offset+9, 5, 3);
636                     painter.drawArc(r, 180*16, 180*16);
637 				}
638                 if (flags &NC) {
639                     painter.drawLine(QPointF(12.5, offset+5), QPointF(12.5, offset+8));
640                     QRectF r(10, offset+6, 5, 3);
641                     painter.drawArc(r, 180*16, 180*16);
642 				}
643 			}
644 				// for delay off contact
645 			else if ( flags &DelayOff){
646 				if (flags &NO) {
647                     painter.drawLine(12, offset+8, 12, offset+9.5);
648                     QRectF r(9.5, offset+9.5, 5, 3);
649                     painter.drawArc(r, 0, 180*16);
650                 }
651 				if (flags &NC) {
652                     painter.drawLine(QPointF(12.5, offset+5), QPointF(12.5, offset+7));
653                     QRectF r(10, offset+7.5, 5, 3);
654                     painter.drawArc(r, 0, 180*16);
655 				}
656 
657 			}
658 			else {
659 					// for delay on contact
660 				if (flags &NO) {
661 					painter.drawLine(12, offset+8, 12, offset+11);
662 					QRectF r(9.5, offset+11.7, 5, 3);
663 					painter.drawArc(r, 0, 180*16);
664 					QRectF rl(9.5, offset+9, 5, 3);
665 					painter.drawArc(rl, 180*16, 180*16);
666 				}
667 				if (flags &NC) {
668 					painter.drawLine(QPointF(12.5, offset+5), QPointF(12.5, offset+8));
669 					QRectF r(9.5, offset+10.7, 5, 3);
670 					painter.drawArc(r, 0, 180*16);
671 					QRectF rl(9.5, offset+8, 5, 3);
672 					painter.drawArc(rl, 180*16, 180*16);
673 				}
674 			}
675 		}
676 
677 		QRectF text_rect = painter.boundingRect(QRectF(30, offset, 5, 10), Qt::AlignLeft | Qt::AlignVCenter, str);
678 		painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
679 		bounding_rect = bounding_rect.united(text_rect);
680 
681 		if (m_hovered_contacts_map.contains(elmt))
682 		{
683 			m_hovered_contacts_map.insertMulti(elmt, bounding_rect);
684 		}
685 		else
686 		{
687 			m_hovered_contacts_map.insert(elmt, bounding_rect);
688 		}
689 
690 		++m_drawed_contacts;
691 	}
692 
693 		//Draw a switch contact
694 	else if (flags &SW)
695 	{
696 		bounding_rect = QRectF(0, offset, 24, 20);
697 
698 			//draw the NO side
699 		painter.drawLine(0, offset+6, 8, offset+6);
700 			//Draw the NC side
701 		QPointF p1[3] = {
702 			QPointF(0, offset+16),
703 			QPointF(8, offset+16),
704 			QPointF(8, offset+12)
705 		};
706 		painter.drawPolyline(p1, 3);
707 
708 			//Draw the common side
709 		QPointF p2[3] = {
710 			QPointF(7, offset+14),
711 			QPointF(16, offset+11),
712 			QPointF(24, offset+11),
713 		};
714 		painter.drawPolyline(p2, 3);
715 
716 			//Draw the half ellipse off delay
717 		if (flags &Delay)
718 		{
719 			painter.drawLine(12, offset+13, 12, offset+16);
720 			if (flags &DelayOn) {
721 				QRectF r(9.5, offset+14, 5, 3);
722 				painter.drawArc(r, 180*16, 180*16);
723 			}
724 			else if (flags &DelayOff) {
725 				QRectF r(9.5, offset+16.5, 5, 3);
726 				painter.drawArc(r, 0, 180*16);
727 			}
728 			else if (flags &DelayOnOff) {
729 				QRectF r(9.5, offset+14, 5, 3);
730 				painter.drawArc(r, 180*16, 180*16);
731 				QRectF rr(9.5, offset+17, 5, 3);
732 				painter.drawArc(rr, 0, 180*16);
733 			}
734 		}
735 
736 			//Draw position text
737 		QRectF text_rect = painter.boundingRect(QRectF(30, offset+5, 5, 10), Qt::AlignLeft | Qt::AlignVCenter, str);
738 		painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
739 		bounding_rect = bounding_rect.united(text_rect);
740 
741 		if (m_hovered_contacts_map.contains(elmt)) {
742 			m_hovered_contacts_map.insertMulti(elmt, bounding_rect);
743 		}
744 		else {
745 			m_hovered_contacts_map.insert(elmt, bounding_rect);
746 		}
747 
748 			//a switch contact take place of two normal contact
749 		m_drawed_contacts += 2;
750 	}
751 
752 	return bounding_rect;
753 }
754 
755 /**
756  * @brief CrossRefItem::fillCrossRef
757  * Fill the content of the cross ref
758  * @param painter painter to use.
759  */
fillCrossRef(QPainter & painter)760 void CrossRefItem::fillCrossRef(QPainter &painter)
761 {
762 	if (m_element->isFree()) return;
763 
764 	qreal middle_cross = m_bounding_rect.width()/2;
765 
766 		//Fill NO
767 	QPointF no_top_left(0, header);
768 	foreach(Element *elmt, NOElements())
769 	{
770 		QPen pen = painter.pen();
771 		m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
772 		painter.setPen(pen);
773 
774 		QString str = elementPositionText(elmt, true);
775 		QRectF bounding = painter.boundingRect(QRectF(no_top_left, QSize(middle_cross, 1)), Qt::AlignLeft, str);
776 		painter.drawText(bounding, Qt::AlignLeft, str);
777 
778 		if (m_hovered_contacts_map.contains(elmt))
779 		{
780 			m_hovered_contacts_map.insertMulti(elmt, bounding);
781 		}
782 		else
783 		{
784 			m_hovered_contacts_map.insert(elmt, bounding);
785 		}
786 
787 		no_top_left.ry() += bounding.height();
788 	}
789 
790 		//Fill NC
791 	QPointF nc_top_left(middle_cross, header);
792 	foreach(Element *elmt, NCElements())
793 	{
794 		QPen pen = painter.pen();
795 		m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
796 		painter.setPen(pen);
797 
798 		QString str = elementPositionText(elmt, true);
799 		QRectF bounding = painter.boundingRect(QRectF(nc_top_left, QSize(middle_cross, 1)), Qt::AlignRight, str);
800 		painter.drawText(bounding, Qt::AlignRight, str);
801 
802 		if (m_hovered_contacts_map.contains(elmt))
803 		{
804 			m_hovered_contacts_map.insertMulti(elmt, bounding);
805 		}
806 		else
807 		{
808 			m_hovered_contacts_map.insert(elmt, bounding);
809 		}
810 
811 		nc_top_left.ry() += bounding.height();
812 	}
813 }
814 
815 /**
816  * @brief CrossRefItem::AddExtraInfo
817  * Add the comment info of the parent item if needed.
818  * @param painter painter to use for draw the text
819  * @param type type of Info do be draw e.g. comment, location.
820  */
AddExtraInfo(QPainter & painter,const QString & type)821 void CrossRefItem::AddExtraInfo(QPainter &painter, const QString& type)
822 {
823 	QString text = autonum::AssignVariables::formulaToLabel(m_element -> elementInformations()[type].toString(), m_element->rSequenceStruct(), m_element->diagram(), m_element);
824 	bool must_show  = m_element -> elementInformations().keyMustShow(type);
825 
826 	if (!text.isEmpty() && must_show)
827 	{
828 		painter.save();
829 		painter.setFont(QETApp::diagramTextsFont(6));
830 
831 		QRectF r, text_bounding;
832 		qreal center = boundingRect().center().x();
833 		qreal width = boundingRect().width() > 70 ? boundingRect().width()/2 : 35;
834 
835 		r = QRectF(QPointF(center - width, boundingRect().bottom()),
836 				   QPointF(center + width, boundingRect().bottom() + 1));
837 
838 		text_bounding = painter.boundingRect(r, Qt::TextWordWrap | Qt::AlignHCenter, text);
839 		painter.drawText(text_bounding, Qt::TextWordWrap | Qt::AlignHCenter, text);
840 
841 		text_bounding.adjust(-1,0,1,0); //adjust only for better visual
842 
843 		m_shape_path.addRect(text_bounding);
844 		prepareGeometryChange();
845 		m_bounding_rect = m_bounding_rect.united(text_bounding);
846 		if (type == "comment") painter.drawRoundedRect(text_bounding, 2, 2);
847 		painter.restore();
848 	}
849 }
850 
851 /**
852  * @brief CrossRefItem::NOElements
853  * @return The linked elements of @m_element wich are open or switch contact.
854  * If linked element is a power contact, xref propertie is set to don't show power contact
855  * and this cross item must be drawed as cross, the element is not append in the list.
856  */
NOElements() const857 QList<Element *> CrossRefItem::NOElements() const
858 {
859 	QList<Element *> no_list;
860 
861 	foreach (Element *elmt, m_element->linkedElements())
862 	{
863 			//We continue if element is a power contact and xref propertie
864 			//is set to don't show power contact
865 		if (m_properties.displayHas() == XRefProperties::Cross &&
866 			!m_properties.showPowerContact() &&
867 			elmt -> kindInformations()["type"].toString() == "power")
868 			continue;
869 
870 		QString state = elmt->kindInformations()["state"].toString();
871 
872 		if (state == "NO" || state == "SW")
873 		{
874 			no_list.append(elmt);
875 		}
876 	}
877 
878 	return no_list;
879 }
880 
881 /**
882  * @brief CrossRefItem::NCElements
883  * @return The linked elements of @m_element wich are close or switch contact
884  * If linked element is a power contact, xref propertie is set to don't show power contact
885  * and this cross item must be drawed as cross, the element is not append in the list.
886  */
NCElements() const887 QList<Element *> CrossRefItem::NCElements() const
888 {
889 	QList<Element *> nc_list;
890 
891 	foreach (Element *elmt, m_element->linkedElements())
892 	{
893 			//We continue if element is a power contact and xref propertie
894 			//is set to don't show power contact
895 		if (m_properties.displayHas() == XRefProperties::Cross &&
896 			!m_properties.showPowerContact() &&
897 			elmt -> kindInformations()["type"].toString() == "power")
898 			continue;
899 
900 		QString state = elmt->kindInformations()["state"].toString();
901 
902 		if (state == "NC" || state == "SW")
903 		{
904 			nc_list.append(elmt);
905 		}
906 	}
907 
908 	return nc_list;
909 }
910