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 <math.h>
19 #include "qetgraphicsitem/conductor.h"
20 #include "qetgraphicsitem/conductortextitem.h"
21 #include "factory/elementfactory.h"
22 #include "diagram.h"
23 #include "diagramcommands.h"
24 #include "diagramcontent.h"
25 #include "diagramposition.h"
26 #include "exportdialog.h"
27 #include "qetgraphicsitem/independenttextitem.h"
28 #include "qetgraphicsitem/diagramimageitem.h"
29 #include "qetgraphicsitem/qetshapeitem.h"
30 #include "terminal.h"
31 #include "diagrameventinterface.h"
32 #include "qetapp.h"
33 #include "elementcollectionhandler.h"
34 #include "element.h"
35 #include "diagramview.h"
36 #include "dynamicelementtextitem.h"
37 #include "elementtextitemgroup.h"
38 #include "undocommand/addelementtextcommand.h"
39 #include "QPropertyUndoCommand/qpropertyundocommand.h"
40 
41 int Diagram::xGrid  = 10;
42 int Diagram::yGrid  = 10;
43 int Diagram::xKeyGrid = 10;
44 int Diagram::yKeyGrid = 10;
45 int Diagram::xKeyGridFine = 1;
46 int Diagram::yKeyGridFine = 1;
47 const qreal Diagram::margin = 5.0;
48 
49 // static variable to keep track of present background color of the diagram.
50 QColor		Diagram::background_color = Qt::white;
51 
52 /**
53  * @brief Diagram::Diagram
54  * Constructor
55  * @param project : The project of this diagram and also parent QObject
56  */
Diagram(QETProject * project)57 Diagram::Diagram(QETProject *project) :
58 	QGraphicsScene           (project),
59 	m_project                 (nullptr),
60 	diagram_qet_version_     (-1),
61 	draw_grid_               (true),
62 	use_border_              (true),
63 	draw_terminals_          (true),
64 	draw_colored_conductors_ (true),
65 	m_event_interface (nullptr),
66 	m_freeze_new_elements   (false),
67 	m_freeze_new_conductors_ (false)
68 {
69 	setItemIndexMethod(QGraphicsScene::NoIndex);
70 		//Set to no index, because they can be the source of the crash with conductor and shape ghost.
71 		//https://forum.qt.io/topic/71316/qgraphicsscenefinditembsptreevisitor-visit-crashes-due-to-an-obsolete-paintevent-after-qgraphicsscene-removeitem
72 		//https://stackoverflow.com/questions/38458830/crash-after-qgraphicssceneremoveitem-with-custom-item-class
73 		//http://www.qtcentre.org/archive/index.php/t-33730.html
74 		//http://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063
75 
76 	setProject(project);
77 	qgi_manager_ = new QGIManager(this);
78 	setBackgroundBrush(Qt::white);
79 	conductor_setter_ = new QGraphicsLineItem(nullptr);
80 	conductor_setter_ -> setZValue(1000000);
81 
82 	QPen pen(Qt::NoBrush, 1.5, Qt::DashLine);
83 	pen.setColor(Qt::black);
84 	conductor_setter_ -> setPen(pen);
85 
86 	connect(&border_and_titleblock, SIGNAL(needTitleBlockTemplate(const QString &)), this, SLOT(setTitleBlockTemplate(const QString &)));
87 	connect(&border_and_titleblock, SIGNAL(diagramTitleChanged(const QString &)),    this, SLOT(titleChanged(const QString &)));
88 	connect(&border_and_titleblock, SIGNAL(titleBlockFolioChanged(const QString &)),    this, SLOT(titleChanged(const QString &)));
89 	connect(&border_and_titleblock, SIGNAL(borderChanged(QRectF,QRectF)), this, SLOT(adjustSceneRect()));
90 	connect(&border_and_titleblock, SIGNAL(titleBlockFolioChanged(const QString &)), this, SLOT(updateLabels()));
91 	connect(this, SIGNAL (diagramActivated()), this, SLOT(loadElmtFolioSeq()));
92 	connect(this, SIGNAL (diagramActivated()), this, SLOT(loadCndFolioSeq()));
93 	adjustSceneRect();
94 }
95 
96 /**
97  * @brief Diagram::~Diagram
98  * Destructor
99  */
~Diagram()100 Diagram::~Diagram()
101 {
102         //First clear every selection to close an hypothetical editor
103     clearSelection();
104         // clear undo stack to prevent errors, because contains pointers to this diagram and is elements.
105 	undoStack().clear();
106         //delete of QGIManager, every elements he knows are removed
107 	delete qgi_manager_;
108         // remove of conductor setter
109 	delete conductor_setter_;
110 
111 	if (m_event_interface)
112         delete m_event_interface;
113 
114         // list removable items
115 	QList<QGraphicsItem *> deletable_items;
116 	for(QGraphicsItem *qgi : items())
117     {
118 		if (qgi -> parentItem()) continue;
119 		if (qgraphicsitem_cast<Conductor *>(qgi)) continue;
120 		deletable_items << qgi;
121 	}
122 
123 	qDeleteAll (deletable_items);
124 }
125 
126 /**
127 	Dessine l'arriere-plan du schema, cad la grille.
128 	@param p Le QPainter a utiliser pour dessiner
129 	@param r Le rectangle de la zone a dessiner
130 */
drawBackground(QPainter * p,const QRectF & r)131 void Diagram::drawBackground(QPainter *p, const QRectF &r) {
132 	p -> save();
133 
134 	// desactive tout antialiasing, sauf pour le texte
135 	p -> setRenderHint(QPainter::Antialiasing, false);
136 	p -> setRenderHint(QPainter::TextAntialiasing, true);
137 	p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
138 
139 	// dessine un fond blanc
140 	p -> setPen(Qt::NoPen);
141 	//set brush color to present background color.
142 	p -> setBrush(Diagram::background_color);
143 	p -> drawRect(r);
144 
145 	if (draw_grid_) {
146 			//Draw the point of the grid
147 			// if background color is black, then grid spots shall be white, else they shall be black in color.
148 		QPen pen;
149 		Diagram::background_color == Qt::black? pen.setColor(Qt::white) : pen.setColor(Qt::black);
150 		pen.setCosmetic(true);
151 		p->setPen(pen);
152 
153 		p -> setBrush(Qt::NoBrush);
154 
155 			//If user allow zoom out beyond of folio, we draw grid outside of border.
156 		QSettings settings;
157 		int xGrid = settings.value("diagrameditor/Xgrid", Diagram::xGrid).toInt();
158 		int yGrid = settings.value("diagrameditor/Ygrid", Diagram::yGrid).toInt();
159 		QRectF rect = settings.value("diagrameditor/zoom-out-beyond-of-folio", false).toBool() ?
160 						  r :
161 						  border_and_titleblock.insideBorderRect().intersected(r);
162 
163 		qreal limite_x = rect.x() + rect.width();
164 		qreal limite_y = rect.y() + rect.height();
165 
166 		int g_x = (int)ceil(rect.x());
167 		while (g_x % xGrid) ++ g_x;
168 		int g_y = (int)ceil(rect.y());
169 		while (g_y % yGrid) ++ g_y;
170 
171 		QPolygon points;
172 		for (int gx = g_x ; gx < limite_x ; gx += xGrid) {
173 			for (int gy = g_y ; gy < limite_y ; gy += yGrid) {
174 				points << QPoint(gx, gy);
175 			}
176 		}
177 		p -> drawPoints(points);
178 	}
179 
180 	if (use_border_) border_and_titleblock.draw(p);
181 	p -> restore();
182 }
183 
184 /**
185  * @brief Diagram::mouseDoubleClickEvent
186  * This event is managed by diagram event interface if any.
187  * @param event :
188  */
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)189 void Diagram::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
190 {
191 	event->setAccepted(false);
192 
193 	if (m_event_interface) {
194 		m_event_interface->mouseDoubleClickEvent(event);
195 		if (event->isAccepted()) {
196 			return;
197 		}
198 	}
199 
200 	QGraphicsScene::mouseDoubleClickEvent(event);
201 }
202 
203 /**
204  * @brief Diagram::mousePressEvent
205  * This event is managed by diagram event interface if any.
206  * @param event
207  */
mousePressEvent(QGraphicsSceneMouseEvent * event)208 void Diagram::mousePressEvent(QGraphicsSceneMouseEvent *event)
209 {
210 	event->setAccepted(false);
211 
212 	if (m_event_interface) {
213 		m_event_interface->mousePressEvent(event);
214 		if (event->isAccepted()) {
215 			return;
216 		}
217 	}
218 
219 	QGraphicsScene::mousePressEvent(event);
220 }
221 
222 /**
223  * @brief Diagram::mouseMoveEvent
224  * This event is managed by diagram event interface if any.
225  * @param event
226  */
mouseMoveEvent(QGraphicsSceneMouseEvent * event)227 void Diagram::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
228 {
229 	event->setAccepted(false);
230 
231 	if (m_event_interface) {
232 		m_event_interface->mouseMoveEvent(event);
233 		if (event->isAccepted()) {
234 			return;
235 		}
236 	}
237 
238 	QGraphicsScene::mouseMoveEvent(event);
239 }
240 
241 /**
242  * @brief Diagram::mouseReleaseEvent
243  * This event is managed by diagram event interface if any.
244  * @param event
245  */
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)246 void Diagram::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
247 {
248 	event->setAccepted(false);
249 
250 	if (m_event_interface) {
251 		m_event_interface->mouseReleaseEvent(event);
252 		if (event->isAccepted()) {
253 			return;
254 		}
255 	}
256 
257 	QGraphicsScene::mouseReleaseEvent(event);
258 }
259 
260 /**
261  * @brief Diagram::wheelEvent
262  * This event is managed by diagram event interface if any.
263  * @param event
264  */
wheelEvent(QGraphicsSceneWheelEvent * event)265 void Diagram::wheelEvent(QGraphicsSceneWheelEvent *event)
266 {
267 	event->setAccepted(false);
268 
269 	if (m_event_interface) {
270 		m_event_interface->wheelEvent(event);
271 		if (event->isAccepted()) {
272 			return;
273 		}
274 	}
275 
276 	QGraphicsScene::wheelEvent(event);
277 }
278 
279 /**
280  * @brief Diagram::keyPressEvent
281  * This event is managed by diagram event interface if any.
282  * Else move selected elements
283  * @param e
284  */
keyPressEvent(QKeyEvent * event)285 void Diagram::keyPressEvent(QKeyEvent *event)
286 {
287 	QSettings settings;
288 	int xKeyGrid = settings.value("diagrameditor/key_Xgrid", Diagram::xKeyGrid).toInt();
289 	int yKeyGrid = settings.value("diagrameditor/key_Ygrid", Diagram::yKeyGrid).toInt();
290 	int xKeyGridFine = settings.value("diagrameditor/key_fine_Xgrid", Diagram::xKeyGridFine).toInt();
291 	int yKeyGridFine = settings.value("diagrameditor/key_fine_Ygrid", Diagram::yKeyGridFine).toInt();
292 	event->setAccepted(false);
293 
294 	if (m_event_interface) {
295 		m_event_interface->keyPressEvent(event);
296 		if (event->isAccepted()) {
297 			return;
298 		}
299 	}
300 
301 	if (!isReadOnly())
302 	{
303 		QPointF movement;
304 		qreal top_position = 0;
305 		qreal left_position = 0;
306 		DiagramContent dc(this);
307 		if (!dc.items(DiagramContent::All).isEmpty())
308 		{
309 				//Move item with the keyboard arrow
310 			if(event->modifiers() == Qt::NoModifier)
311 			{
312 				switch(event->key())
313 				{
314 					case Qt::Key_Left:
315 						for (Element *item : dc.m_elements)
316 						{
317 							left_position = item->sceneBoundingRect().x();
318 							if(left_position <= 5)
319 								return;
320 						}
321 						movement = QPointF(-xKeyGrid, 0.0);
322 						break;
323 					case Qt::Key_Right:
324 						movement = QPointF(+xKeyGrid, 0.0);
325 						break;
326 					case Qt::Key_Up:
327 						for(Element *item : dc.m_elements)
328 						{
329 							top_position = item->sceneBoundingRect().y();
330 							if(top_position <= 5)
331 								return;
332 						}
333 						movement = QPointF(0.0, -yKeyGrid);
334 						break;
335 					case Qt::Key_Down:
336 						movement = QPointF(0.0, +yKeyGrid);
337 						break;
338 				}
339 
340 				if (!movement.isNull() && !focusItem())
341 				{
342 					m_elements_mover.beginMovement(this);
343 					m_elements_mover.continueMovement(movement);
344 					event->accept();
345 					return;
346 				}
347 			}
348             else if(event->modifiers() == Qt::AltModifier)
349 
350                         {
351                             switch(event->key())
352                             {
353                                 case Qt::Key_Left:
354                                     for (Element *item : dc.m_elements)
355                                     {
356                                         left_position = item->sceneBoundingRect().x();
357                                         if(left_position <= 5)
358                                             return;
359                                     }
360                                     movement = QPointF(-xKeyGridFine, 0.0);
361                                     break;
362                                 case Qt::Key_Right:
363                                     movement = QPointF(+xKeyGridFine, 0.0);
364                                     break;
365                                 case Qt::Key_Up:
366                                     for(Element *item : dc.m_elements)
367                                     {
368                                         top_position = item->sceneBoundingRect().y();
369                                         if(top_position <= 5)
370                                             return;
371                                     }
372                                     movement = QPointF(0.0, -yKeyGridFine);
373                                     break;
374                                 case Qt::Key_Down:
375                                     movement = QPointF(0.0, +yKeyGridFine);
376                                     break;
377                             }
378 
379                             if (!movement.isNull() && !focusItem())
380                             {
381                                 m_elements_mover.beginMovement(this);
382                                 m_elements_mover.continueMovement(movement);
383                                 event->accept();
384                                 return;
385                             }
386                         }
387             else if(event->modifiers() == Qt::ControlModifier)
388 			{
389 				//Adjust the alignment of a texts group
390 				if(selectedItems().size() == 1 && selectedItems().first()->type() == QGraphicsItemGroup::Type)
391 				{
392 					if(ElementTextItemGroup *etig = dynamic_cast<ElementTextItemGroup *>(selectedItems().first()))
393 					{
394 						if(event->key() == Qt::Key_Left &&  etig->alignment() != Qt::AlignLeft)
395 							undoStack().push(new AlignmentTextsGroupCommand(etig, Qt::AlignLeft));
396 
397 						else if (event->key() == Qt::Key_Up && etig->alignment() != Qt::AlignVCenter)
398 							undoStack().push(new AlignmentTextsGroupCommand(etig, Qt::AlignVCenter));
399 
400 						else if (event->key() == Qt::Key_Right && etig->alignment() != Qt::AlignRight)
401 							undoStack().push(new AlignmentTextsGroupCommand(etig, Qt::AlignRight));
402 					}
403 				}
404 			}
405 		}
406 
407 		event->ignore();
408 		QGraphicsScene::keyPressEvent(event);
409 	}
410 }
411 
412 /**
413  * @brief Diagram::keyReleaseEvent
414  * This event is managed by diagram event interface if any.
415  * Else move selected element
416  * @param e
417  */
keyReleaseEvent(QKeyEvent * e)418 void Diagram::keyReleaseEvent(QKeyEvent *e)
419 {
420 	e->setAccepted(false);
421 
422 	if (m_event_interface) {
423 		m_event_interface->keyReleaseEvent(e);
424 		if (e->isAccepted()) {
425 			return;
426 		}
427 	}
428 
429 	bool transmit_event = true;
430 	if (!isReadOnly()) {
431 		// detecte le relachement d'une touche de direction ( = deplacement d'elements)
432 		if (
433 			(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right  ||
434 			 e -> key() == Qt::Key_Up   || e -> key() == Qt::Key_Down)  &&
435 			!e -> isAutoRepeat()
436 		) {
437 			m_elements_mover.endMovement();
438 			e -> accept();
439 			transmit_event = false;
440 		}
441 	}
442 	if (transmit_event) {
443 		QGraphicsScene::keyReleaseEvent(e);
444 	}
445 }
446 
447 /**
448  * @brief Diagram::setEventInterface
449  * Set event_interface has current interface.
450  * Diagram become the ownership of event_interface
451  * If there is a previous interface, they will be delete before
452  * and call init() to the new interface.
453  * @param event_interface
454  */
setEventInterface(DiagramEventInterface * event_interface)455 void Diagram::setEventInterface(DiagramEventInterface *event_interface)
456 {
457 	if (m_event_interface)
458 	{
459 		delete m_event_interface;
460 		event_interface -> init();
461 	}
462 	m_event_interface = event_interface;
463 
464 	connect(m_event_interface, &DiagramEventInterface::finish, [this]()
465 	{
466 		delete this->m_event_interface;
467 		this->m_event_interface = nullptr;
468 	});
469 }
470 
471 /**
472  * @brief Diagram::clearEventInterface
473  * Clear the current event interface.
474  */
clearEventInterface()475 void Diagram::clearEventInterface()
476 {
477 	if(m_event_interface)
478 	{
479 		delete m_event_interface;
480 		m_event_interface = nullptr;
481 	}
482 }
483 
484 /**
485  * @brief Diagram::conductorsAutonumName
486  * @return the name of autonum to use.
487  */
conductorsAutonumName() const488 QString Diagram::conductorsAutonumName() const {
489 	return m_conductors_autonum_name;
490 }
491 
492 /**
493  * @brief Diagram::setConductorsAutonumName
494  * @param name, name of autonum to use.
495  */
setConductorsAutonumName(const QString & name)496 void Diagram::setConductorsAutonumName(const QString &name) {
497 	m_conductors_autonum_name= name;
498 }
499 
500 /**
501 	Exporte le schema vers une image
502 	@return Une QImage representant le schema
503 */
toPaintDevice(QPaintDevice & pix,int width,int height,Qt::AspectRatioMode aspectRatioMode)504 bool Diagram::toPaintDevice(QPaintDevice &pix, int width, int height, Qt::AspectRatioMode aspectRatioMode) {
505 	// determine la zone source =  contenu du schema + marges
506 	QRectF source_area;
507 	if (!use_border_) {
508 		source_area = itemsBoundingRect();
509 		source_area.translate(-margin, -margin);
510 		source_area.setWidth (source_area.width () + 2.0 * margin);
511 		source_area.setHeight(source_area.height() + 2.0 * margin);
512 	} else {
513 		source_area = QRectF(
514 			0.0,
515 			0.0,
516 			border_and_titleblock.borderAndTitleBlockRect().width()  + 2.0 * margin,
517 			border_and_titleblock.borderAndTitleBlockRect().height() + 2.0 * margin
518 		);
519 	}
520 
521 	// si les dimensions ne sont pas precisees, l'image est exportee a l'echelle 1:1
522 	QSize image_size = (width == -1 && height == -1) ? source_area.size().toSize() : QSize(width, height);
523 
524 	// prepare le rendu
525 	QPainter p;
526 	if (!p.begin(&pix)) return(false);
527 
528 	// rendu antialiase
529 	p.setRenderHint(QPainter::Antialiasing, true);
530 	p.setRenderHint(QPainter::TextAntialiasing, true);
531 	p.setRenderHint(QPainter::SmoothPixmapTransform, true);
532 
533 	// deselectionne tous les elements
534 	QList<QGraphicsItem *> selected_elmts = selectedItems();
535 	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(false);
536 
537 	// effectue le rendu lui-meme
538 	render(&p, QRect(QPoint(0, 0), image_size), source_area, aspectRatioMode);
539 	p.end();
540 
541 	// restaure les elements selectionnes
542 	foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(true);
543 
544 	return(true);
545 }
546 
547 /**
548 	Permet de connaitre les dimensions qu'aura l'image generee par la methode toImage()
549 	@return La taille de l'image generee par toImage()
550 */
imageSize() const551 QSize Diagram::imageSize() const {
552 	// determine la zone source =  contenu du schema + marges
553 	qreal image_width, image_height;
554 	if (!use_border_) {
555 		QRectF items_rect = itemsBoundingRect();
556 		image_width  = items_rect.width();
557 		image_height = items_rect.height();
558 	} else {
559 		image_width  = border_and_titleblock.borderAndTitleBlockRect().width();
560 		image_height = border_and_titleblock.borderAndTitleBlockRect().height();
561 	}
562 
563 	image_width  += 2.0 * margin;
564 	image_height += 2.0 * margin;
565 
566 	// renvoie la taille de la zone source
567 	return(QSizeF(image_width, image_height).toSize());
568 }
569 
570 /**
571 	@return true si le schema est considere comme vide, false sinon.
572 	Un schema vide ne contient ni element, ni conducteur, ni champ de texte
573 */
isEmpty() const574 bool Diagram::isEmpty() const {
575 	return(!items().count());
576 }
577 
578 /**
579  * @brief Diagram::potential
580  * @return all potential in the diagram
581  *each potential are in the QList and each conductors of one potential are in the QSet
582  */
potentials()583 QList < QSet <Conductor *> > Diagram::potentials() {
584 	QList < QSet <Conductor *> > potential_List;
585 	if (content().conductors().size() == 0) return (potential_List); //return an empty potential
586 	QList <Conductor *> conductors_list = content().conductors();
587 
588 	do {
589 		QSet <Conductor *> one_potential = conductors_list.first() -> relatedPotentialConductors();
590 		one_potential << conductors_list.takeFirst();
591 		foreach (Conductor *c, one_potential) conductors_list.removeOne(c);
592 		potential_List << one_potential;
593 	} while (!conductors_list.empty());
594 
595 	return (potential_List);
596 }
597 
598 /**
599 	Exporte tout ou partie du schema
600 	@param whole_content Booleen (a vrai par defaut) indiquant si le XML genere doit
601 	representer l'integralite du schema ou seulement le contenu selectionne
602 	@return Un Document XML (QDomDocument)
603 */
toXml(bool whole_content)604 QDomDocument Diagram::toXml(bool whole_content) {
605 	// document
606 	QDomDocument document;
607 
608 	// racine de l'arbre XML
609 	QDomElement racine = document.createElement("diagram");
610 
611 	// add the application version number
612 	racine.setAttribute("version", QET::version);
613 
614 	// proprietes du schema
615 	if (whole_content) {
616 		border_and_titleblock.titleBlockToXml(racine);
617 		border_and_titleblock.borderToXml(racine);
618 
619 		// Default conductor properties
620 		QDomElement default_conductor = document.createElement("defaultconductor");
621 		defaultConductorProperties.toXml(default_conductor);
622 		racine.appendChild(default_conductor);
623 
624 		// Conductor autonum
625 		if (!m_conductors_autonum_name.isEmpty()) {
626 			racine.setAttribute("conductorAutonum", m_conductors_autonum_name);
627 		}
628 
629 		//Default New Element
630 		racine.setAttribute("freezeNewElement", m_freeze_new_elements ? "true" : "false");
631 
632 		//Default New Conductor
633 		racine.setAttribute("freezeNewConductor", m_freeze_new_conductors_ ? "true" : "false");
634 
635 		//Element Folio Sequential Variables
636 		if (!m_elmt_unitfolio_max.isEmpty() || !m_elmt_tenfolio_max.isEmpty() || !m_elmt_hundredfolio_max.isEmpty()) {
637 			QDomElement elmtfoliosequential = document.createElement("elementautonumfoliosequentials");
638 			if (!m_elmt_unitfolio_max.isEmpty()) {
639 				QDomElement elmtfolioseq = document.createElement("elementunitfolioseq");
640 				folioSequentialsToXml(&m_elmt_unitfolio_max, &elmtfolioseq, "sequf_", "unitfolioseq", &document);
641 				elmtfoliosequential.appendChild(elmtfolioseq);
642 			}
643 			if (!m_elmt_tenfolio_max.isEmpty()) {
644 				QDomElement elmtfolioseq = document.createElement("elementtenfolioseq");
645 				folioSequentialsToXml(&m_elmt_tenfolio_max, &elmtfolioseq, "seqtf_", "tenfolioseq", &document);
646 				elmtfoliosequential.appendChild(elmtfolioseq);
647 			}
648 			if (!m_elmt_hundredfolio_max.isEmpty()) {
649 				QDomElement elmtfolioseq = document.createElement("elementhundredfolioseq");
650 				folioSequentialsToXml(&m_elmt_hundredfolio_max, &elmtfolioseq, "seqhf_", "hundredfolioseq", &document);
651 				elmtfoliosequential.appendChild(elmtfolioseq);
652 			}
653 			racine.appendChild(elmtfoliosequential);
654 		}
655 		//Conductor Folio Sequential Variables
656 		if (!m_cnd_unitfolio_max.isEmpty() || !m_cnd_tenfolio_max.isEmpty() || !m_cnd_hundredfolio_max.isEmpty()) {
657 			QDomElement cndfoliosequential = document.createElement("conductorautonumfoliosequentials");
658 			QHash<QString, QStringList>::iterator i;
659 			if (!m_cnd_unitfolio_max.isEmpty()) {
660 				QDomElement cndfolioseq = document.createElement("conductorunitfolioseq");
661 				folioSequentialsToXml(&m_cnd_unitfolio_max, &cndfolioseq, "sequf_", "unitfolioseq", &document);
662 				cndfoliosequential.appendChild(cndfolioseq);
663 			}
664 			if (!m_cnd_tenfolio_max.isEmpty()) {
665 				QDomElement cndfolioseq = document.createElement("conductortenfolioseq");
666 				folioSequentialsToXml(&m_cnd_tenfolio_max, &cndfolioseq, "seqtf_", "tenfolioseq", &document);
667 				cndfoliosequential.appendChild(cndfolioseq);
668 			}
669 			if (!m_cnd_hundredfolio_max.isEmpty()) {
670 				QDomElement cndfolioseq = document.createElement("conductorhundredfolioseq");
671 				folioSequentialsToXml(&m_cnd_hundredfolio_max, &cndfolioseq, "seqhf_", "hundredfolioseq", &document);
672 				cndfoliosequential.appendChild(cndfolioseq);
673 			}
674 			racine.appendChild(cndfoliosequential);
675 		}
676 	}
677 	else {
678 			//this method with whole_content to false,
679 			//is often use to copy and paste the current selection
680 			//so we add the id of the project where copy occur.
681 		racine.setAttribute("projectId", QETApp::projectId(m_project));
682 	}
683 	document.appendChild(racine);
684 
685 	// si le schema ne contient pas d'element (et donc pas de conducteurs), on retourne de suite le document XML
686 	if (items().isEmpty()) return(document);
687 
688 	// creation de trois listes : une qui contient les elements, une qui contient les conducteurs, une qui contient les champs de texte
689 	QList<Element *> list_elements;
690 	QList<Conductor *> list_conductors;
691 	QList<DiagramTextItem *> list_texts;
692 	QList<DiagramImageItem *> list_images;
693 	QList<QetShapeItem *> list_shapes;
694 
695 	QList<QGraphicsItem *> list_items = items();
696 	;
697 	// Determine les elements a "XMLiser"
698 	foreach(QGraphicsItem *qgi, list_items) {
699 		if (Element *elmt = qgraphicsitem_cast<Element *>(qgi)) {
700 			if (whole_content) list_elements << elmt;
701 			else if (elmt -> isSelected()) list_elements << elmt;
702 		} else if (Conductor *f = qgraphicsitem_cast<Conductor *>(qgi)) {
703 			if (whole_content) list_conductors << f;
704 			// lorsqu'on n'exporte pas tout le diagram, il faut retirer les conducteurs non selectionnes
705 			// et pour l'instant, les conducteurs non selectionnes sont les conducteurs dont un des elements n'est pas selectionne
706 			else if (f -> terminal1 -> parentItem() -> isSelected() && f -> terminal2 -> parentItem() -> isSelected()) {
707 				list_conductors << f;
708 			}
709 		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(qgi)) {
710 			if (whole_content) list_texts << iti;
711 			else if (iti -> isSelected()) list_texts << iti;
712 		} else if (DiagramImageItem *dii = qgraphicsitem_cast<DiagramImageItem *>(qgi)) {
713 			if (whole_content) list_images << dii;
714 			else if (dii -> isSelected()) list_images << dii;
715 		} else if (QetShapeItem *dsi = qgraphicsitem_cast<QetShapeItem *>(qgi)) {
716 			if (whole_content) list_shapes << dsi;
717 			else if (dsi -> isSelected()) list_shapes << dsi;
718 		}
719 	}
720 
721 	// table de correspondance entre les adresses des bornes et leurs ids
722 	QHash<Terminal *, int> table_adr_id;
723 
724 	// enregistrement des elements
725 	if (!list_elements.isEmpty()) {
726 		QDomElement elements = document.createElement("elements");
727 		foreach(Element *elmt, list_elements) {
728 			elements.appendChild(elmt -> toXml(document, table_adr_id));
729 		}
730 		racine.appendChild(elements);
731 	}
732 
733 	// enregistrement des conducteurs
734 	if (!list_conductors.isEmpty()) {
735 		QDomElement conductors = document.createElement("conductors");
736 		foreach(Conductor *cond, list_conductors) {
737 			conductors.appendChild(cond -> toXml(document, table_adr_id));
738 		}
739 		racine.appendChild(conductors);
740 	}
741 
742 	// enregistrement des champs de texte
743 	if (!list_texts.isEmpty()) {
744 		QDomElement inputs = document.createElement("inputs");
745 		foreach(DiagramTextItem *dti, list_texts) {
746 			inputs.appendChild(dti -> toXml(document));
747 		}
748 		racine.appendChild(inputs);
749 	}
750 
751 	// save of images
752 	if (!list_images.isEmpty()) {
753 		QDomElement images = document.createElement("images");
754 		foreach (DiagramImageItem *dii, list_images) {
755 			images.appendChild(dii -> toXml(document));
756 		}
757 		racine.appendChild(images);
758 	}
759 
760 	// save of basic shapes
761 	if (!list_shapes.isEmpty()) {
762 		QDomElement shapes = document.createElement("shapes");
763 		foreach (QetShapeItem *dii, list_shapes) {
764 			shapes.appendChild(dii -> toXml(document));
765 		}
766 		racine.appendChild(shapes);
767 	}
768 	// on retourne le document XML ainsi genere
769 	return(document);
770 }
771 
772 /**
773 + * @brief Diagram::folioSequentialsToXml
774 + * Add folio sequential to QDomElement
775 + * @param domElement to add attributes
776 + * @param hash to retrieve content with content
777 + * @param sequential type
778 + */
folioSequentialsToXml(QHash<QString,QStringList> * hash,QDomElement * domElement,const QString & seq_type,const QString & type,QDomDocument * doc)779 void Diagram::folioSequentialsToXml(QHash<QString, QStringList> *hash, QDomElement *domElement, const QString& seq_type, const QString& type, QDomDocument *doc) {
780 	QHash<QString, QStringList>::iterator i;
781 	for (i = hash->begin(); i != hash->end(); i++) {
782 		QDomElement folioseq = doc->createElement(type);
783 		folioseq.setAttribute("title", i.key());
784 		for (int j = 0; j < i.value().size(); j++) {
785 			folioseq.setAttribute(seq_type + QString::number(j+1), i.value().at(j));
786 		}
787 		domElement->appendChild(folioseq);
788 	}
789 }
790 
791 /**
792 	Importe le schema decrit dans un document XML. Si une position est
793 	precisee, les elements importes sont positionnes de maniere a ce que le
794 	coin superieur gauche du plus petit rectangle pouvant les entourant tous
795 	(le bounding rect) soit a cette position.
796 	@param document Le document XML a analyser
797 	@param position La position du schema importe
798 	@param consider_informations Si vrai, les informations complementaires
799 	(auteur, titre, ...) seront prises en compte
800 	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
801 	il sera rempli avec le contenu ajoute au schema par le fromXml
802 	@return true si l'import a reussi, false sinon
803 */
fromXml(QDomDocument & document,QPointF position,bool consider_informations,DiagramContent * content_ptr)804 bool Diagram::fromXml(QDomDocument &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
805 	QDomElement root = document.documentElement();
806 	return(fromXml(root, position, consider_informations, content_ptr));
807 }
808 
809 /**
810 	Importe le schema decrit dans un element XML. Cette methode delegue son travail a Diagram::fromXml
811 	Si l'import reussit, cette methode initialise egalement le document XML
812 	interne permettant de bien gerer l'enregistrement de ce schema dans le
813 	projet auquel il appartient.
814 	@see Diagram::fromXml
815 	@param document Le document XML a analyser
816 	@param position La position du schema importe
817 	@param consider_informations Si vrai, les informations complementaires
818 	(auteur, titre, ...) seront prises en compte
819 	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
820 	il sera rempli avec le contenu ajoute au schema par le fromXml
821 	@return true si l'import a reussi, false sinon
822 
823 */
initFromXml(QDomElement & document,QPointF position,bool consider_informations,DiagramContent * content_ptr)824 bool Diagram::initFromXml(QDomElement &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
825 	// import le contenu et les proprietes du schema depuis l'element XML fourni en parametre
826 	bool from_xml = fromXml(document, position, consider_informations, content_ptr);
827 
828 	return(from_xml);
829 }
830 
831 /**
832 	Importe le schema decrit dans un element XML. Si une position est
833 	precisee, les elements importes sont positionnes de maniere a ce que le
834 	coin superieur gauche du plus petit rectangle pouvant les entourant tous
835 	(le bounding rect) soit a cette position.
836 	@param document Le document XML a analyser
837 	@param position La position du schema importe
838 	@param consider_informations Si vrai, les informations complementaires
839 	(auteur, titre, ...) seront prises en compte
840 	@param content_ptr si ce pointeur vers un DiagramContent est different de 0,
841 	il sera rempli avec le contenu ajoute au schema par le fromXml
842 	@return true si l'import a reussi, false sinon
843 */
fromXml(QDomElement & document,QPointF position,bool consider_informations,DiagramContent * content_ptr)844 bool Diagram::fromXml(QDomElement &document, QPointF position, bool consider_informations, DiagramContent *content_ptr) {
845 	const QDomElement& root = document;
846 	// The first element must be a diagram
847 	if (root.tagName() != "diagram") return(false);
848 
849 	// Read attributes of this diagram
850 	if (consider_informations) {
851 		// Version of diagram
852 		bool conv_ok;
853 		qreal version_value = root.attribute("version").toDouble(&conv_ok);
854 		if (conv_ok) {
855 			diagram_qet_version_ = version_value;
856 		}
857 
858 		// Load border and titleblock
859 		border_and_titleblock.titleBlockFromXml(root);
860 		border_and_titleblock.borderFromXml(root);
861 
862 		// Find the element "defaultconductor".
863 		// If found, load default conductor properties.
864 		QDomElement default_conductor_elmt = root.firstChildElement("defaultconductor");
865 		if (!default_conductor_elmt.isNull()) {
866 			defaultConductorProperties.fromXml(default_conductor_elmt);
867 		}
868 
869 		// Load the autonum
870 		m_conductors_autonum_name = root.attribute("conductorAutonum");
871 
872 		// Load Freeze New Element
873 		m_freeze_new_elements = root.attribute("freezeNewElement").toInt();
874 
875 		// Load Freeze New Conductor
876 		m_freeze_new_conductors_ = root.attribute("freezeNewConductor").toInt();
877 
878 		//Load Element Folio Sequential
879 		folioSequentialsFromXml(root, &m_elmt_unitfolio_max, "elementunitfolioseq","sequf_","unitfolioseq", "elementautonumfoliosequentials");
880 		folioSequentialsFromXml(root, &m_elmt_tenfolio_max, "elementtenfolioseq","seqtf_", "tenfolioseq", "elementautonumfoliosequentials");
881 		folioSequentialsFromXml(root, &m_elmt_hundredfolio_max, "elementhundredfolioseq","seqhf_", "hundredfolioseq", "elementautonumfoliosequentials");
882 
883 		//Load Conductor Folio Sequential
884 		folioSequentialsFromXml(root, &m_cnd_unitfolio_max, "conductorunitfolioseq","sequf_","unitfolioseq", "conductorautonumfoliosequentials");
885 		folioSequentialsFromXml(root, &m_cnd_tenfolio_max, "conductortenfolioseq","seqtf_","tenfolioseq", "conductorautonumfoliosequentials");
886 		folioSequentialsFromXml(root, &m_cnd_hundredfolio_max, "conductorhundredfolioseq","seqhf_","hundredfolioseq", "conductorautonumfoliosequentials");
887 	}
888 
889 	// if child haven't got a child, loading is finish (diagram is empty)
890 	if (root.firstChild().isNull()) {
891 		return(true);
892 	}
893 
894 	// Backward compatibility: prior to version 0.3, we need to compensate, at
895 	// diagram-opening time, the rotation of the element for each of its
896 	// textfields having the "FollowParentRotation" option disabled.
897 	// After 0.3, elements textfields get userx, usery and userrotation attributes
898 	// that explicitly specify their position and orientation.
899 	qreal project_qet_version = declaredQElectroTechVersion(true);
900 	bool handle_inputs_rotation = (
901 		project_qet_version != -1 && project_qet_version < 0.3 &&
902 		m_project -> state() == QETProject::ProjectParsingRunning
903 	);
904 
905 		//If paste from another project
906 	if (root.hasAttribute("projectId")) {
907 		QETProject *other_project = QETApp::project(root.attribute("projectId", "-1").toInt());
908 
909 			//We try to paste from another project, then befor paste elements,
910 			//we must to import the definition of the pasted elements (owned by other project)
911 			//in the embedded collection of this project
912 		if (other_project && other_project != m_project) {
913 			ElementCollectionHandler ech;
914 			foreach (QDomElement element_xml, QET::findInDomElement(root, "elements", "element")) {
915 				if (!Element::valideXml(element_xml)) continue;
916 
917 				QString type_id = element_xml.attribute("type");
918 
919 				if (type_id.startsWith("embed://")) {
920 					ElementsLocation location(type_id, other_project);
921 					ech.importFromProject(m_project, location);
922 				}
923 			}
924 		}
925 	}
926 		//Load all elements from the XML
927 	QList<Element *> added_elements;
928 	QHash<int, Terminal *> table_adr_id;
929 	foreach (QDomElement element_xml, QET::findInDomElement(root, "elements", "element"))
930 	{
931 		if (!Element::valideXml(element_xml)) continue;
932 
933 		// cree un element dont le type correspond a l'id type
934 		QString type_id = element_xml.attribute("type");
935 		ElementsLocation element_location;
936 		if (type_id.startsWith("embed://")) {
937 			element_location = ElementsLocation(type_id, m_project);
938 		}
939 		else {
940 			element_location = ElementsLocation(type_id);
941 		}
942 
943 		int state = 0;
944 		Element *nvel_elmt = ElementFactory::Instance() -> createElement(element_location, nullptr, &state);
945 		if (state)
946 		{
947 			QString debug_message = QString("Diagram::fromXml() : Le chargement de la description de l'element %1 a echoue avec le code d'erreur %2").arg(element_location.path()).arg(state);
948 			qDebug() << qPrintable(debug_message);
949 			delete nvel_elmt;
950 			continue;
951 		}
952 
953 		addItem(nvel_elmt);
954 			//Loading fail, remove item from the diagram
955 		if (!nvel_elmt->fromXml(element_xml, table_adr_id, handle_inputs_rotation))
956 		{
957 			removeItem(nvel_elmt);
958 			delete nvel_elmt;
959 			qDebug() << "Diagram::fromXml() : Le chargement des parametres d'un element a echoue";
960 		} else {
961 			added_elements << nvel_elmt;
962 		}
963 	}
964 
965 	// Load text
966 	QList<IndependentTextItem *> added_texts;
967 	foreach (QDomElement text_xml, QET::findInDomElement(root, "inputs", "input")) {
968 		IndependentTextItem *iti = new IndependentTextItem();
969 		iti -> fromXml(text_xml);
970 		addItem(iti);
971 		added_texts << iti;
972 	}
973 
974 	// Load image
975 	QList<DiagramImageItem *> added_images;
976 	foreach (QDomElement image_xml, QET::findInDomElement(root, "images", "image")) {
977 		DiagramImageItem *dii = new DiagramImageItem ();
978 		dii -> fromXml(image_xml);
979 		addItem(dii);
980 		added_images << dii;
981 	}
982 
983 	// Load shape
984 	QList<QetShapeItem *> added_shapes;
985 	foreach (QDomElement shape_xml, QET::findInDomElement(root, "shapes", "shape")) {
986 		QetShapeItem *dii = new QetShapeItem (QPointF(0,0));
987 		dii -> fromXml(shape_xml);
988 		addItem(dii);
989 		added_shapes << dii;
990 	}
991 
992 		// Load conductor
993 	QList<Conductor *> added_conductors;
994 	foreach (QDomElement f, QET::findInDomElement(root, "conductors", "conductor"))
995 	{
996 		if (!Conductor::valideXml(f)) continue;
997 
998 			//Check if terminal that conductor must be linked is know
999 		int id_p1 = f.attribute("terminal1").toInt();
1000 		int id_p2 = f.attribute("terminal2").toInt();
1001 		if (table_adr_id.contains(id_p1) && table_adr_id.contains(id_p2))
1002 		{
1003 			Terminal *p1 = table_adr_id.value(id_p1);
1004 			Terminal *p2 = table_adr_id.value(id_p2);
1005 			if (p1 != p2)
1006 			{
1007 				Conductor *c = new Conductor(p1, p2);
1008 				if (c->isValid())
1009 				{
1010 					addItem(c);
1011 					c -> fromXml(f);
1012 					added_conductors << c;
1013 				}
1014 				else
1015 					delete c;
1016 			}
1017 		}
1018 		else qDebug() << "Diagram::fromXml() : Le chargement du conducteur" << id_p1 << id_p2 << "a echoue";
1019 	}
1020 
1021 	//Translate items if a new position was given in parameter
1022 	if (position != QPointF()) {
1023 
1024 		QList<QGraphicsItem *> added_items;
1025 		foreach (Element          *added_element, added_elements  ) added_items << added_element;
1026 		foreach (Conductor        *added_cond,    added_conductors) added_items << added_cond;
1027 		foreach (QetShapeItem     *added_shape,   added_shapes    ) added_items << added_shape;
1028 		foreach (DiagramTextItem  *added_text,    added_texts     ) added_items << added_text;
1029 		foreach (DiagramImageItem *added_image,   added_images    ) added_items << added_image;
1030 
1031 		//Get the top left corner of the rectangle that contain all added items
1032 		QRectF items_rect;
1033 		foreach (QGraphicsItem *item, added_items) {
1034 			items_rect = items_rect.united(item -> mapToScene(item -> boundingRect()).boundingRect());
1035 		}
1036 
1037 		QPointF point_ = items_rect.topLeft();
1038 		QPointF pos_ = Diagram::snapToGrid(QPointF (position.x() - point_.x(),
1039 													position.y() - point_.y()));
1040 
1041 		//Translate all added items
1042 		foreach (QGraphicsItem *qgi, added_items)
1043 			qgi -> setPos( qgi -> pos() += pos_);
1044 	}
1045 
1046 	// remplissage des listes facultatives
1047 	if (content_ptr) {
1048 		content_ptr -> m_elements         = added_elements;
1049 		content_ptr -> m_conductors_to_move = added_conductors;
1050 		content_ptr -> m_text_fields       = added_texts.toSet();
1051 		content_ptr -> m_images			= added_images.toSet();
1052 		content_ptr -> m_shapes			= added_shapes.toSet();
1053 	}
1054 	adjustSceneRect();
1055 	return(true);
1056 }
1057 
1058 /**
1059  * @brief Diagram::folioSequentialsFromXml
1060  * Load folio sequential from QDomElement
1061  * @param root containing all folio sequentials
1062  * @param hash to be loaded with content
1063  * @param folioSeq type
1064  * @param seq type
1065  * @param type of sequential
1066  */
folioSequentialsFromXml(const QDomElement & root,QHash<QString,QStringList> * hash,const QString & folioSeq,const QString & seq,const QString & type,const QString & autonumFolioSeqType)1067 void Diagram::folioSequentialsFromXml(const QDomElement &root, QHash<QString, QStringList>* hash, const QString& folioSeq, const QString& seq, const QString& type, const QString& autonumFolioSeqType) {
1068 	foreach (QDomElement folioSeqAutoNum, QET::findInDomElement(root, autonumFolioSeqType, folioSeq)) {
1069 		for(QDomElement folioseq = folioSeqAutoNum.firstChildElement(type); !folioseq.isNull(); folioseq = folioseq.nextSiblingElement(type)) {
1070 			QString title = folioseq.attribute("title");
1071 			QStringList list;
1072 			int i = 1;
1073 			while (folioseq.hasAttribute(seq + QString::number(i))) {
1074 				list << folioseq.attribute(seq + QString::number(i));
1075 				i++;
1076 			}
1077 			hash->insert(title,list);
1078 		}
1079 	}
1080 }
1081 
1082 /**
1083  * @brief Diagram::refreshContents
1084  * refresh all content of diagram.
1085  * - refresh conductor text.
1086  * - linking the elements waiting to be linked
1087  * - Refresh the connection of the dynamic element text item (use for text with source of text label)
1088  */
refreshContents()1089 void Diagram::refreshContents()
1090 {
1091 	for (Element *elmt : elements())
1092 	{
1093 		elmt->initLink(project());
1094 		for (DynamicElementTextItem *deti : elmt->dynamicTextItems())
1095 			deti->refreshLabelConnection();
1096 	}
1097 
1098 	for (Conductor *conductor : conductors())
1099 		conductor->refreshText();
1100 }
1101 
1102 /**
1103  * @brief Diagram::addItem
1104  * Réimplemented from QGraphicsScene::addItem(QGraphicsItem *item)
1105  * Do some specific operation if item need it (for exemple an element)
1106  * @param item
1107  */
addItem(QGraphicsItem * item)1108 void Diagram::addItem(QGraphicsItem *item)
1109 {
1110 	if (!item || isReadOnly() || item->scene() == this) return;
1111 	QGraphicsScene::addItem(item);
1112 
1113 	switch (item->type())
1114 	{
1115 		case Conductor::Type:
1116 		{
1117 			Conductor *conductor = static_cast<Conductor *>(item);
1118 			conductor->terminal1->addConductor(conductor);
1119 			conductor->terminal2->addConductor(conductor);
1120 			conductor->calculateTextItemPosition();
1121 			break;
1122 		}
1123 		default: {break;}
1124 	}
1125 }
1126 
1127 /**
1128  * @brief Diagram::removeItem
1129  * Réimplemented from QGraphicsScene::removeItem(QGraphicsItem *item)
1130  * Do some specific operation if item need it (for exemple an element)
1131  * @param item
1132  */
removeItem(QGraphicsItem * item)1133 void Diagram::removeItem(QGraphicsItem *item)
1134 {
1135 	if (!item || isReadOnly()) return;
1136 
1137 	switch (item->type())
1138 	{
1139 		case Element::Type:
1140 		{
1141 			Element *elmt = static_cast<Element*>(item);
1142 			elmt->unlinkAllElements();
1143 			break;
1144 		}
1145 		case Conductor::Type:
1146 		{
1147 			Conductor *conductor = static_cast<Conductor *>(item);
1148 			conductor->terminal1->removeConductor(conductor);
1149 			conductor->terminal2->removeConductor(conductor);
1150 			break;
1151 		}
1152 		default: {break;}
1153 	}
1154 
1155 	QGraphicsScene::removeItem(item);
1156 }
1157 
titleChanged(const QString & title)1158 void Diagram::titleChanged(const QString &title) {
1159 	emit(diagramTitleChanged(this, title));
1160 }
1161 
1162 /**
1163 	This slot may be used to inform the diagram object that the given title
1164 	block template has changed. The diagram will thus flush its title
1165 	block-dedicated rendering cache.
1166 	@param template_name Name of the title block template that has changed
1167 */
titleBlockTemplateChanged(const QString & template_name)1168 void Diagram::titleBlockTemplateChanged(const QString &template_name) {
1169 	if (border_and_titleblock.titleBlockTemplateName() != template_name) return;
1170 
1171 	border_and_titleblock.titleBlockTemplateChanged(template_name);
1172 	update();
1173 }
1174 
1175 /**
1176 	This slot has to be be used to inform this class that the given title block
1177 	template is about to be removed and is no longer accessible. This class
1178 	will either use the provided  optional TitleBlockTemplate or the default
1179 	title block provided by QETApp::defaultTitleBlockTemplate()
1180 	@param template_name Name of the title block template that has changed
1181 	@param new_template (Optional) Name of the title block template to use instead
1182 */
titleBlockTemplateRemoved(const QString & template_name,const QString & new_template)1183 void Diagram::titleBlockTemplateRemoved(const QString &template_name, const QString &new_template)
1184 {
1185 	if (border_and_titleblock.titleBlockTemplateName() != template_name) return;
1186 	const TitleBlockTemplate *final_template = m_project->embeddedTitleBlockTemplatesCollection()->getTemplate(new_template);
1187 	border_and_titleblock.titleBlockTemplateRemoved(template_name, final_template);
1188 	update();
1189 }
1190 
1191 /**
1192 	Set the template to use to render the title block of this diagram.
1193 	@param template_name Name of the title block template.
1194 */
setTitleBlockTemplate(const QString & template_name)1195 void Diagram::setTitleBlockTemplate(const QString &template_name)
1196 {
1197 	if (!m_project) return;
1198 
1199 	QString current_name = border_and_titleblock.titleBlockTemplateName();
1200 	const TitleBlockTemplate *titleblock_template = m_project->embeddedTitleBlockTemplatesCollection()->getTemplate(template_name);
1201 	border_and_titleblock.titleBlockTemplateRemoved(current_name, titleblock_template);
1202 
1203 	if (template_name != current_name)
1204 		emit(usedTitleBlockTemplateChanged(template_name));
1205 }
1206 
1207 /**
1208 	Selectionne tous les objets du schema
1209 */
selectAll()1210 void Diagram::selectAll() {
1211 	if (items().isEmpty()) return;
1212 
1213 	blockSignals(true);
1214 	foreach(QGraphicsItem *qgi, items()) qgi -> setSelected(true);
1215 	blockSignals(false);
1216 	emit(selectionChanged());
1217 }
1218 
1219 /**
1220 	Deslectionne tous les objets selectionnes
1221 */
deselectAll()1222 void Diagram::deselectAll() {
1223 	if (items().isEmpty()) return;
1224 
1225 	clearSelection();
1226 }
1227 
1228 /**
1229 	Inverse l'etat de selection de tous les objets du schema
1230 */
invertSelection()1231 void Diagram::invertSelection() {
1232 	if (items().isEmpty()) return;
1233 
1234 	blockSignals(true);
1235 	foreach (QGraphicsItem *item, items()) item -> setSelected(!item -> isSelected());
1236 	blockSignals(false);
1237 	emit(selectionChanged());
1238 }
1239 
1240 /**
1241  * @brief Diagram::updateLabels
1242  * Update elements and conductors that reference folio field
1243  * in their labels.
1244  */
updateLabels()1245 void Diagram::updateLabels()
1246 {
1247 	for (Conductor *cnd : content().conductors())
1248 	{
1249 		cnd->refreshText();
1250 	}
1251 }
1252 
1253 /**
1254  * @brief Diagram::insertFolioSeqHash
1255  * This class inserts a stringlist containing all
1256  * sequential variables related to an autonum in a QHash
1257  * @param Hash to be accessed
1258  * @param autonum title
1259  * @param sequential to be treated
1260  * @param type to be treated
1261  * @param Numerotation Context to be manipulated
1262  */
insertFolioSeqHash(QHash<QString,QStringList> * hash,const QString & title,const QString & type,NumerotationContext * nc)1263 void Diagram::insertFolioSeqHash(QHash<QString, QStringList> *hash, const QString& title, const QString& type, NumerotationContext *nc) {
1264 	QStringList max;
1265 	for (int i = 0; i < nc->size(); i++) {
1266 		if (nc->itemAt(i).at(0) == type) {
1267 			nc->replaceValue(i, QString::number(nc->itemAt(i).at(3).toInt()));
1268 			max.append(QString::number(nc->itemAt(i).at(3).toInt() - nc->itemAt(i).at(2).toInt()));
1269 		}
1270 	}
1271 	hash->insert(title,max);
1272 }
1273 
1274 /**
1275  * @brief Diagram::loadFolioSeqHash
1276  * This class loads all folio sequential variables
1277  * related to the current autonum
1278  * @param Hash to be accessed
1279  * @param autonum title
1280  * @param sequential to be treated
1281  * @param type to be treated
1282  * @param Numerotation Context to be manipulated
1283  */
loadFolioSeqHash(QHash<QString,QStringList> * hash,const QString & title,const QString & type,NumerotationContext * nc)1284 void Diagram::loadFolioSeqHash(QHash<QString, QStringList> *hash, const QString& title, const QString& type, NumerotationContext *nc) {
1285 		int j = 0;
1286 		for (int i = 0; i < nc->size(); i++) {
1287 			if (nc->itemAt(i).at(0) == type) {
1288 				QString new_value;
1289 				new_value = QString::number(hash->value(title).at(j).toInt() + nc->itemAt(i).at(2).toInt());
1290 				nc->replaceValue(i,new_value);
1291 				j++;
1292 			}
1293 		}
1294 }
1295 
1296 /**
1297  * @brief Diagram::changeZValue
1298  * Change the Z value of the current selected item, according to @option
1299  */
changeZValue(QET::DepthOption option)1300 void Diagram::changeZValue(QET::DepthOption option)
1301 {
1302 	DiagramContent dc(this);
1303 	QUndoCommand *undo = new QUndoCommand(tr("Modifier la profondeur"));
1304 	QList<QGraphicsItem *> l = dc.items(DiagramContent::SelectedOnly | \
1305 											 DiagramContent::Elements | \
1306 											 DiagramContent::Shapes | \
1307 											 DiagramContent::Images);
1308 	QList<QGraphicsObject *> list;
1309 	for(QGraphicsItem *item : l)
1310 		list << item->toGraphicsObject();
1311 
1312 	qreal maxz=0,
1313 		  minz=0;
1314 	for(QGraphicsItem *item : this->items())
1315 	{
1316 		qreal z = item->zValue();
1317 		if(z >= Terminal::Z-2)
1318 			continue;
1319 		maxz = std::max(maxz,z);
1320 		minz = std::min(minz,z);
1321 	}
1322 
1323 	if(option == QET::Raise)
1324 	{
1325 		for(QGraphicsObject *qgo : list)
1326 			if(qgo->zValue() < (Terminal::Z-2))	//Ensure item is always below terminal
1327 				new QPropertyUndoCommand(qgo, "z", qgo->zValue(), qgo->zValue()+1, undo);
1328 	}
1329 	else if(option == QET::Lower)
1330 	{
1331 		for(QGraphicsObject *qgo : list)
1332 			if(qgo->zValue() < (Terminal::Z-2))	//Ensure item is always below terminal
1333 				new QPropertyUndoCommand(qgo, "z", qgo->zValue(), qgo->zValue()-1, undo);
1334 	}
1335 	else if (option == QET::BringForward)
1336 	{
1337 		for(QGraphicsObject *qgo : list)
1338 				new QPropertyUndoCommand(qgo, "z", qgo->zValue(), maxz+1, undo);
1339 	}
1340 	else if(option == QET::SendBackward)
1341 	{
1342 		for(QGraphicsObject *qgo : list)
1343 				new QPropertyUndoCommand(qgo, "z", qgo->zValue(), minz-1, undo);
1344 	}
1345 
1346 	if(undo->childCount())
1347 		this->undoStack().push(undo);
1348 	else
1349 		delete undo;
1350 }
1351 
1352 /**
1353  * @brief Diagram::loadElmtFolioSeq
1354  * This class loads all folio sequential variables related
1355  * to the current autonum
1356  */
loadElmtFolioSeq()1357 void Diagram::loadElmtFolioSeq() {
1358 	QString title = project()->elementCurrentAutoNum();
1359 	NumerotationContext nc = project()->elementAutoNum(title);
1360 
1361 	//Unit Folio
1362 	if (m_elmt_unitfolio_max.isEmpty() || !m_elmt_unitfolio_max.contains(title)) {
1363 		//Insert Initial Value
1364 		if (project()->elementAutoNumCurrentFormula().contains("%sequf_")) {
1365 			insertFolioSeqHash(&m_elmt_unitfolio_max,title,"unitfolio",&nc);
1366 			project()->addElementAutoNum(title,nc);
1367 		}
1368 	}
1369 	else if (m_elmt_unitfolio_max.contains(title)) {
1370 		//Load Folio Current Value
1371 		if (project()->elementAutoNumCurrentFormula().contains("%sequf_")) {
1372 			loadFolioSeqHash(&m_elmt_unitfolio_max,title,"unitfolio",&nc);
1373 			project()->addElementAutoNum(title,nc);
1374 		}
1375 	}
1376 
1377 	//Ten Folio
1378 	if (m_elmt_tenfolio_max.isEmpty() || !m_elmt_tenfolio_max.contains(title)) {
1379 		//Insert Initial Value
1380 		if (project()->elementAutoNumCurrentFormula().contains("%seqtf_")) {
1381 			insertFolioSeqHash(&m_elmt_tenfolio_max,title,"tenfolio",&nc);
1382 			project()->addElementAutoNum(title,nc);
1383 		}
1384 	}
1385 	else if (m_elmt_tenfolio_max.contains(title)) {
1386 		//Load Folio Current Value
1387 		if (project()->elementAutoNumCurrentFormula().contains("%seqtf_")) {
1388 			loadFolioSeqHash(&m_elmt_tenfolio_max,title,"tenfolio",&nc);
1389 			project()->addElementAutoNum(title,nc);
1390 		}
1391 	}
1392 
1393 	//Hundred Folio
1394 	if (m_elmt_hundredfolio_max.isEmpty() || !m_elmt_hundredfolio_max.contains(title)) {
1395 		//Insert Initial Value
1396 		if (project()->elementAutoNumCurrentFormula().contains("%seqhf_")) {
1397 			insertFolioSeqHash(&m_elmt_hundredfolio_max,title,"hundredfolio",&nc);
1398 			project()->addElementAutoNum(title,nc);
1399 		}
1400 	}
1401 	else if (m_elmt_hundredfolio_max.contains(title)) {
1402 		//Load Folio Current Value
1403 		if (project()->elementAutoNumCurrentFormula().contains("%seqhf_")) {
1404 			loadFolioSeqHash(&m_elmt_hundredfolio_max,title,"hundredfolio",&nc);
1405 			project()->addElementAutoNum(title,nc);
1406 		}
1407 	}
1408 }
1409 
1410 /**
1411  * @brief Diagram::loadCndFolioSeq
1412  * This class loads all conductor folio sequential variables related
1413  * to the current autonum
1414  */
loadCndFolioSeq()1415 void Diagram::loadCndFolioSeq() {
1416 	//Conductor
1417 	QString title = project()->conductorCurrentAutoNum();
1418 	NumerotationContext nc = project()->conductorAutoNum(title);
1419 	QString formula = autonum::numerotationContextToFormula(nc);
1420 
1421 	//Unit Folio
1422 	if (m_cnd_unitfolio_max.isEmpty() || !m_cnd_unitfolio_max.contains(title)) {
1423 		//Insert Initial Value
1424 		if (formula.contains("%sequf_")) {
1425 			insertFolioSeqHash(&m_cnd_unitfolio_max,title,"unitfolio",&nc);
1426 			project()->addConductorAutoNum(title,nc);
1427 		}
1428 	}
1429 	else if (m_cnd_unitfolio_max.contains(title)) {
1430 		//Load Folio Current Value
1431 		if (formula.contains("%sequf_")) {
1432 			loadFolioSeqHash(&m_cnd_unitfolio_max,title,"unitfolio",&nc);
1433 			project()->addConductorAutoNum(title,nc);
1434 		}
1435 	}
1436 
1437 	//Ten Folio
1438 	if (m_cnd_tenfolio_max.isEmpty() || !m_cnd_tenfolio_max.contains(title)) {
1439 		//Insert Initial Value
1440 		if (formula.contains("%seqtf_")) {
1441 			insertFolioSeqHash(&m_cnd_tenfolio_max,title,"tenfolio",&nc);
1442 			project()->addConductorAutoNum(title,nc);
1443 		}
1444 	}
1445 	else if (m_cnd_tenfolio_max.contains(title)) {
1446 		//Load Folio Current Value
1447 		if (formula.contains("%seqtf_")) {
1448 			loadFolioSeqHash(&m_cnd_tenfolio_max,title,"tenfolio",&nc);
1449 			project()->addConductorAutoNum(title,nc);
1450 		}
1451 	}
1452 
1453 	//Hundred Folio
1454 	if (m_cnd_hundredfolio_max.isEmpty() || !m_cnd_hundredfolio_max.contains(title)) {
1455 		//Insert Initial Value
1456 		if (formula.contains("%seqhf_")) {
1457 			insertFolioSeqHash(&m_cnd_hundredfolio_max,title,"hundredfolio",&nc);
1458 			project()->addConductorAutoNum(title,nc);
1459 		}
1460 	}
1461 	else if (m_cnd_hundredfolio_max.contains(title)) {
1462 		//Load Folio Current Value
1463 		if (formula.contains("%seqhf_")) {
1464 			loadFolioSeqHash(&m_cnd_hundredfolio_max,title,"hundredfolio",&nc);
1465 			project()->addConductorAutoNum(title,nc);
1466 		}
1467 	}
1468 }
1469 
1470 /**
1471 	@return le titre du cartouche
1472 */
title() const1473 QString Diagram::title() const {
1474 	return(border_and_titleblock.title());
1475 }
1476 
elements() const1477 QList <Element *> Diagram::elements() const {
1478 	QList<Element *> element_list;
1479 	foreach (QGraphicsItem *qgi, items()) {
1480 		if (Element *elmt = qgraphicsitem_cast<Element *>(qgi))
1481 			element_list <<elmt;
1482 	}
1483 	return (element_list);
1484 }
1485 
1486 /**
1487  * @brief Diagram::conductors
1488  * Return the list containing all conductors
1489  */
conductors() const1490 QList <Conductor *> Diagram::conductors() const {
1491 	QList<Conductor *> cnd_list;
1492 	foreach (QGraphicsItem *qgi, items()) {
1493 		if (Conductor *cnd = qgraphicsitem_cast<Conductor *>(qgi))
1494 			cnd_list <<cnd;
1495 	}
1496 	return (cnd_list);
1497 }
1498 
elementsMover()1499 ElementsMover &Diagram::elementsMover() {
1500 	return m_elements_mover;
1501 }
1502 
elementTextsMover()1503 ElementTextsMover &Diagram::elementTextsMover() {
1504 	return m_element_texts_mover;
1505 }
1506 
1507 /**
1508 	Permet de savoir si un element est utilise sur un schema
1509 	@param location Emplacement d'un element
1510 	@return true si l'element location est utilise sur ce schema, false sinon
1511 */
usesElement(const ElementsLocation & location)1512 bool Diagram::usesElement(const ElementsLocation &location)
1513 {
1514 	for(Element *element : elements()) {
1515 		if (element -> location() == location) {
1516 			return(true);
1517 		}
1518 	}
1519 	return(false);
1520 }
1521 
1522 /**
1523 	@param a title block template name
1524 	@return true if the provided template is used by this diagram, false
1525 	otherwise.
1526 */
usesTitleBlockTemplate(const QString & name)1527 bool Diagram::usesTitleBlockTemplate(const QString &name) {
1528 	return(name == border_and_titleblock.titleBlockTemplateName());
1529 }
1530 
1531 /**
1532  * @brief Diagram::freezeElements
1533  * Freeze every existent element label.
1534  */
freezeElements(bool freeze)1535 void Diagram::freezeElements(bool freeze) {
1536 	foreach (Element *elmt, elements()) {
1537 		elmt->freezeLabel(freeze);
1538 	}
1539 }
1540 
1541 /**
1542  * @brief Diagram::unfreezeElements
1543  * Unfreeze every existent element label.
1544  */
unfreezeElements()1545 void Diagram::unfreezeElements() {
1546 	foreach (Element *elmt, elements()) {
1547 		elmt->freezeLabel(false);
1548 	}
1549 }
1550 
1551 /**
1552  * @brief Diagram::freezeNewElements
1553  * Set new element label to be frozen.
1554  */
setFreezeNewElements(bool b)1555 void Diagram::setFreezeNewElements(bool b) {
1556 	m_freeze_new_elements = b;
1557 }
1558 
1559 /**
1560  * @brief Diagram::freezeNewElements
1561  * @return current freeze new element status .
1562  */
freezeNewElements()1563 bool Diagram::freezeNewElements() {
1564 	return m_freeze_new_elements;
1565 }
1566 
1567 /**
1568  * @brief Diagram::freezeConductors
1569  * Freeze every existent conductor label.
1570  */
freezeConductors(bool freeze)1571 void Diagram::freezeConductors(bool freeze) {
1572 	foreach (Conductor *cnd, conductors()) {
1573 		cnd->setFreezeLabel(freeze);
1574 	}
1575 }
1576 
1577 /**
1578  * @brief Diagram::setfreezeNewConductors
1579  * Set new conductor label to be frozen.
1580  */
setFreezeNewConductors(bool b)1581 void Diagram::setFreezeNewConductors(bool b) {
1582 	m_freeze_new_conductors_ = b;
1583 }
1584 
1585 /**
1586  * @brief Diagram::freezeNewConductors
1587  * @return current freeze new conductor status .
1588  */
freezeNewConductors()1589 bool Diagram::freezeNewConductors() {
1590 	return m_freeze_new_conductors_;
1591 }
1592 
1593 /**
1594  * @brief Diagram::adjustSceneRect
1595  * Recalcul and adjust the size of the scene
1596  */
adjustSceneRect()1597 void Diagram::adjustSceneRect()
1598 {
1599 	QRectF old_rect = sceneRect();
1600 	setSceneRect(border_and_titleblock.borderAndTitleBlockRect().united(itemsBoundingRect()));
1601 	update(old_rect.united(sceneRect()));
1602 }
1603 
1604 /**
1605 	Cette methode permet d'appliquer de nouvelles options de rendu tout en
1606 	accedant aux proprietes de rendu en cours.
1607 	@param new_properties Nouvelles options de rendu a appliquer
1608 	@return les options de rendu avant l'application de new_properties
1609 */
applyProperties(const ExportProperties & new_properties)1610 ExportProperties Diagram::applyProperties(const ExportProperties &new_properties) {
1611 	// exporte les options de rendu en cours
1612 	ExportProperties old_properties;
1613 	old_properties.draw_grid               = displayGrid();
1614 	old_properties.draw_border             = border_and_titleblock.borderIsDisplayed();
1615 	old_properties.draw_titleblock         = border_and_titleblock.titleBlockIsDisplayed();
1616 	old_properties.draw_terminals          = drawTerminals();
1617 	old_properties.draw_colored_conductors = drawColoredConductors();
1618 	old_properties.exported_area           = useBorder() ? QET::BorderArea : QET::ElementsArea;
1619 
1620 	// applique les nouvelles options de rendu
1621 	setUseBorder                  (new_properties.exported_area == QET::BorderArea);
1622 	setDrawTerminals              (new_properties.draw_terminals);
1623 	setDrawColoredConductors      (new_properties.draw_colored_conductors);
1624 	setDisplayGrid                (new_properties.draw_grid);
1625 	border_and_titleblock.displayBorder(new_properties.draw_border);
1626 	border_and_titleblock.displayTitleBlock (new_properties.draw_titleblock);
1627 
1628 	// retourne les anciennes options de rendu
1629 	return(old_properties);
1630 }
1631 
1632 /**
1633 	@param pos Position cartesienne (ex : 10.3, 45.2) a transformer en position
1634 	dans la grille (ex : B2)
1635 	@return la position dans la grille correspondant a pos
1636 */
convertPosition(const QPointF & pos)1637 DiagramPosition Diagram::convertPosition(const QPointF &pos) {
1638 	// delegue le calcul au BorderTitleBlock
1639 	DiagramPosition diagram_position = border_and_titleblock.convertPosition(pos);
1640 
1641 	// embarque la position cartesienne
1642 	diagram_position.setPosition(pos);
1643 
1644 	return(diagram_position);
1645 }
1646 
1647 /**
1648  * @brief Diagram::snapToGrid
1649  * Return a nearest snap point of p
1650  * @param p point to find the nearest snaped point
1651  * @return
1652  */
snapToGrid(const QPointF & p)1653 QPointF Diagram::snapToGrid(const QPointF &p)
1654 {
1655 		//Return a point rounded to the nearest pixel
1656 	if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
1657 	{
1658 		int p_x = qRound(p.x());
1659 		int p_y = qRound(p.y());
1660 		return (QPointF(p_x, p_y));
1661 	}
1662 
1663 		//Return a point snapped to the grid
1664 	int p_x = qRound(p.x() / Diagram::xGrid) * Diagram::xGrid;
1665 	int p_y = qRound(p.y() / Diagram::yGrid) * Diagram::yGrid;
1666 	return (QPointF(p_x, p_y));
1667 }
1668 
1669 
1670 
1671 /**
1672 	Definit s'il faut afficher ou non les bornes
1673 	@param dt true pour afficher les bornes, false sinon
1674 */
setDrawTerminals(bool dt)1675 void Diagram::setDrawTerminals(bool dt) {
1676 	foreach(QGraphicsItem *qgi, items()) {
1677 		if (Terminal *t = qgraphicsitem_cast<Terminal *>(qgi)) {
1678 			t -> setVisible(dt);
1679 		}
1680 	}
1681 }
1682 
1683 /**
1684 	Definit s'il faut respecter ou non les couleurs des conducteurs.
1685 	Si non, les conducteurs sont tous dessines en noir.
1686 	@param dcc true pour respecter les couleurs, false sinon
1687 */
setDrawColoredConductors(bool dcc)1688 void Diagram::setDrawColoredConductors(bool dcc) {
1689 	draw_colored_conductors_ = dcc;
1690 }
1691 
1692 /**
1693 	@return la liste des conducteurs selectionnes sur le schema
1694 */
selectedConductors() const1695 QSet<Conductor *> Diagram::selectedConductors() const {
1696 	QSet<Conductor *> conductors_set;
1697 	foreach(QGraphicsItem *qgi, selectedItems()) {
1698 		if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
1699 			conductors_set << c;
1700 		}
1701 	}
1702 	return(conductors_set);
1703 }
1704 
1705 /// @return true si le presse-papier semble contenir un schema
clipboardMayContainDiagram()1706 bool Diagram::clipboardMayContainDiagram() {
1707 	QString clipboard_text = QApplication::clipboard() -> text().trimmed();
1708 	bool may_be_diagram = clipboard_text.startsWith("<diagram") && clipboard_text.endsWith("</diagram>");
1709 	return(may_be_diagram);
1710 }
1711 
1712 /**
1713 	@return le projet auquel ce schema appartient ou 0 s'il s'agit d'un schema
1714 	independant.
1715 */
project() const1716 QETProject *Diagram::project() const {
1717 	return(m_project);
1718 }
1719 
1720 /**
1721  * @brief Diagram::setProject
1722  * Set parent project of this diagram, project also become the parent QObject of this diagram
1723  * @param project new project
1724  */
setProject(QETProject * project)1725 void Diagram::setProject(QETProject *project)
1726 {
1727 	if (m_project == project)
1728 		return;
1729 
1730 	m_project = project;
1731 	setParent (project);
1732 }
1733 
1734 /**
1735 	@return the folio number of this diagram within its parent project, or -1
1736 	if it is has no parent project
1737 */
folioIndex() const1738 int Diagram::folioIndex() const {
1739 	if (!m_project) return(-1);
1740 	return(m_project -> folioIndex(this));
1741 }
1742 
1743 /**
1744 	@param fallback_to_project When a diagram does not have a declared version,
1745 	this method will use the one declared by its parent project only if
1746 	fallback_to_project is true.
1747 	@return the declared QElectroTech version of this diagram
1748 */
declaredQElectroTechVersion(bool fallback_to_project) const1749 qreal Diagram::declaredQElectroTechVersion(bool fallback_to_project) const {
1750 	if (diagram_qet_version_ != -1) {
1751 		return diagram_qet_version_;
1752 	}
1753 	if (fallback_to_project && m_project) {
1754 		return(m_project -> declaredQElectroTechVersion());
1755 	}
1756 	return(-1);
1757 }
1758 
1759 /**
1760  * @brief Diagram::isReadOnly
1761  * @return  true if this diagram is read only.
1762  * This method is same has call Diagram::project() -> isReadOnly()
1763  */
isReadOnly() const1764 bool Diagram::isReadOnly() const
1765 {
1766 	return m_project -> isReadOnly();
1767 }
1768 
1769 /**
1770 	@return Le contenu du schema. Les conducteurs sont tous places dans
1771 	conductorsToMove.
1772 */
content() const1773 DiagramContent Diagram::content() const {
1774 	DiagramContent dc;
1775 	foreach(QGraphicsItem *qgi, items()) {
1776 		if (Element *e = qgraphicsitem_cast<Element *>(qgi)) {
1777 			dc.m_elements << e;
1778 		} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(qgi)) {
1779 			dc.m_text_fields << iti;
1780 		} else if (Conductor *c = qgraphicsitem_cast<Conductor *>(qgi)) {
1781 			dc.m_conductors_to_move << c;
1782 		}
1783 	}
1784 	return(dc);
1785 }
1786 
1787 /**
1788  * @brief Diagram::canRotateSelection
1789  * @return True if a least one of selected items can be rotated
1790  */
canRotateSelection() const1791 bool Diagram::canRotateSelection() const
1792 {
1793 	for (QGraphicsItem *qgi : selectedItems())
1794 	{
1795 		if (qgi->type() == IndependentTextItem::Type ||
1796 			qgi->type() == ConductorTextItem::Type ||
1797 			qgi->type() == DiagramImageItem::Type ||
1798 			qgi->type() == Element::Type ||
1799 			qgi->type() == DynamicElementTextItem::Type)
1800 			return true;
1801 
1802 		if(qgi->type() == QGraphicsItemGroup::Type)
1803 			if(dynamic_cast<ElementTextItemGroup *>(qgi))
1804 				return true;
1805 	}
1806 
1807 	return false;
1808 }
1809