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 "diagramview.h"
19 #include "diagram.h"
20 #include "qetgraphicsitem/conductor.h"
21 #include "diagramcommands.h"
22 #include "diagramposition.h"
23 #include "conductorpropertieswidget.h"
24 #include "qetgraphicsitem/conductortextitem.h"
25 #include "qetgraphicsitem/independenttextitem.h"
26 #include "qetgraphicsitem/diagramimageitem.h"
27 #include "templatelocation.h"
28 #include "qetproject.h"
29 #include "projectview.h"
30 #include "integrationmovetemplateshandler.h"
31 #include "qetdiagrameditor.h"
32 #include "qeticons.h"
33 #include "qetmessagebox.h"
34 #include <QGraphicsObject>
35 #include <QGraphicsPixmapItem>
36 #include <QGraphicsSceneMouseEvent>
37 #include "factory/elementfactory.h"
38 #include "diagrampropertiesdialog.h"
39 #include "dveventinterface.h"
40 #include "diagrameventaddelement.h"
41 #include "QPropertyUndoCommand/qpropertyundocommand.h"
42 #include "qetshapeitem.h"
43 #include "undocommand/deleteqgraphicsitemcommand.h"
44 #include "dynamicelementtextitem.h"
45 #include "multipastedialog.h"
46 #include "changetitleblockcommand.h"
47 #include "conductorcreator.h"
48 
49 /**
50 	Constructeur
51 	@param diagram Schema a afficher ; si diagram vaut 0, un nouveau Diagram est utilise
52 	@param parent Le QWidget parent de cette vue de schema
53 */
DiagramView(Diagram * diagram,QWidget * parent)54 DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
55 	QGraphicsView (parent),
56 	m_diagram (diagram)
57 {
58 	grabGesture(Qt::PinchGesture);
59 	setAttribute(Qt::WA_DeleteOnClose, true);
60 	setInteractive(true);
61 
62 	QString whatsthis = tr(
63 		"Ceci est la zone dans laquelle vous concevez vos schémas en y ajoutant"
64 		" des éléments et en posant des conducteurs entre leurs bornes. Il est"
65 		" également possible d'ajouter des textes indépendants.",
66 		"\"What's this?\" tip"
67 	);
68 	setWhatsThis(whatsthis);
69 
70 	// active l'antialiasing
71 	setRenderHint(QPainter::Antialiasing, true);
72 	setRenderHint(QPainter::TextAntialiasing, true);
73 	setRenderHint(QPainter::SmoothPixmapTransform, true);
74 
75 	setScene(m_diagram);
76 	m_diagram -> undoStack().setClean();
77 	setWindowIcon(QET::Icons::QETLogo);
78 	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
79 	setResizeAnchor(QGraphicsView::AnchorUnderMouse);
80 	setAlignment(Qt::AlignLeft | Qt::AlignTop);
81 	setSelectionMode();
82 	adjustSceneRect();
83 	updateWindowTitle();
84 	m_diagram->loadElmtFolioSeq();
85 	m_diagram->loadCndFolioSeq();
86 
87 	m_paste_here = new QAction(QET::Icons::EditPaste, tr("Coller ici", "context menu action"), this);
88 	connect(m_paste_here, SIGNAL(triggered()), this, SLOT(pasteHere()));
89 
90 	m_multi_paste = new QAction(QET::Icons::EditPaste, tr("Collage multiple"), this);
91 	connect(m_multi_paste, &QAction::triggered, [this]() {
92 		MultiPasteDialog d(this->m_diagram, this);
93 		d.exec();
94 	});
95 
96 		//setup three separators, to be use in context menu
97 	for(int i=0 ; i<3 ; ++i)
98 	{
99 		m_separators << new QAction(this);
100 		m_separators.last()->setSeparator(true);
101 	}
102 
103 	connect(m_diagram, SIGNAL(showDiagram(Diagram*)), this, SIGNAL(showDiagram(Diagram*)));
104 	connect(m_diagram, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
105 	connect(m_diagram, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(adjustSceneRect()));
106 	connect(&(m_diagram -> border_and_titleblock), SIGNAL(diagramTitleChanged(const QString &)), this, SLOT(updateWindowTitle()));
107 	connect(diagram, SIGNAL(editElementRequired(ElementsLocation)), this, SIGNAL(editElementRequired(ElementsLocation)));
108 	connect(diagram, SIGNAL(findElementRequired(ElementsLocation)), this, SIGNAL(findElementRequired(ElementsLocation)));
109 
110 	QShortcut *edit_conductor_color_shortcut = new QShortcut(QKeySequence(Qt::Key_F2), this);
111 	connect(edit_conductor_color_shortcut, SIGNAL(activated()), this, SLOT(editSelectedConductorColor()));
112 }
113 
114 /**
115 	Destructeur
116 */
~DiagramView()117 DiagramView::~DiagramView() {
118 }
119 
120 /**
121 	Selectionne tous les objets du schema
122 */
selectAll()123 void DiagramView::selectAll() {
124 	m_diagram -> selectAll();
125 }
126 
127 /**
128 	Deslectionne tous les objets selectionnes
129 */
selectNothing()130 void DiagramView::selectNothing() {
131 	m_diagram -> deselectAll();
132 }
133 
134 /**
135 	Inverse l'etat de selection de tous les objets du schema
136 */
selectInvert()137 void DiagramView::selectInvert() {
138 	m_diagram -> invertSelection();
139 }
140 
141 /**
142 	Accepte ou refuse le drag'n drop en fonction du type de donnees entrant
143 	@param e le QDragEnterEvent correspondant au drag'n drop tente
144 */
dragEnterEvent(QDragEnterEvent * e)145 void DiagramView::dragEnterEvent(QDragEnterEvent *e) {
146 	if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
147 		e -> acceptProposedAction();
148 	} else if (e -> mimeData() -> hasFormat("application/x-qet-titleblock-uri")) {
149 		e -> acceptProposedAction();
150 	} else if (e -> mimeData() -> hasText()) {
151 		e -> acceptProposedAction();
152 	} else {
153 		e -> ignore();
154 	}
155 }
156 
157 /**
158 	Accepte ou refuse le drag'n drop en fonction du type de donnees entrant
159 	@param e le QDragMoveEvent correspondant au drag'n drop tente
160 */
dragMoveEvent(QDragMoveEvent * e)161 void DiagramView::dragMoveEvent(QDragMoveEvent *e) {
162 	if (e -> mimeData() -> hasFormat("text/plain")) e -> acceptProposedAction();
163 	else e-> ignore();
164 }
165 
166 /**
167 	Handle the drops accepted on diagram (elements and title block templates).
168 	@param e the QDropEvent describing the current drag'n drop
169 */
dropEvent(QDropEvent * e)170 void DiagramView::dropEvent(QDropEvent *e) {
171 
172 	if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
173 		handleElementDrop(e);
174 	} else if (e -> mimeData() -> hasFormat("application/x-qet-titleblock-uri")) {
175 		handleTitleBlockDrop(e);
176 	} else if (e -> mimeData() -> hasText()) {
177 		handleTextDrop(e);
178 	}
179 }
180 
181 /**
182 	Handle the drop of an element.
183 	@param e the QDropEvent describing the current drag'n drop
184 */
handleElementDrop(QDropEvent * event)185 void DiagramView::handleElementDrop(QDropEvent *event)
186 {
187 		//Build an element from the text of the mime data
188 	ElementsLocation location(event->mimeData()->text());
189 
190 	if ( !(location.isElement() && location.exist()) )
191 	{
192 		qDebug() << "DiagramView::handleElementDrop, location can't be use : " << location;
193 		return;
194 	}
195 
196 	diagram()->setEventInterface(new DiagramEventAddElement(location, diagram(), mapToScene(event->pos())));
197 		//Set focus to the view to get event
198 	this->setFocus();
199 }
200 
201 /**
202  * @brief DiagramView::handleTitleBlockDrop
203  * Handle the dropEvent that contain data of a titleblock
204  * @param e
205  */
handleTitleBlockDrop(QDropEvent * e)206 void DiagramView::handleTitleBlockDrop(QDropEvent *e) {
207 		// fetch the title block template location from the drop event
208 	TitleBlockTemplateLocation tbt_loc;
209 	tbt_loc.fromString(e->mimeData()->text());
210 
211 
212 	if (tbt_loc.isValid())
213 	{
214 			// fetch the current title block properties
215 		TitleBlockProperties titleblock_properties_before = m_diagram->border_and_titleblock.exportTitleBlock();
216 
217 			// check the provided template is not already applied
218 		QETProject *tbt_parent_project = tbt_loc.parentProject();
219 		if (tbt_parent_project && tbt_parent_project == m_diagram -> project())
220 		{
221 				// same parent project and same name = same title block template
222 			if (tbt_loc.name() == titleblock_properties_before.template_name)
223 				return;
224 		}
225 
226 			// integrate the provided template into the project if needed
227 		QString integrated_template_name = tbt_loc.name();
228 		if (mustIntegrateTitleBlockTemplate(tbt_loc))
229 		{
230 			IntegrationMoveTitleBlockTemplatesHandler *handler = new IntegrationMoveTitleBlockTemplatesHandler(this);
231 			//QString error_message;
232 			integrated_template_name = m_diagram->project()->integrateTitleBlockTemplate(tbt_loc, handler);
233 
234 			if (integrated_template_name.isEmpty())
235 				return;
236 		}
237 
238 			// apply the provided title block template
239 		if (titleblock_properties_before.template_name == integrated_template_name)
240 			return;
241 
242 		TitleBlockProperties titleblock_properties_after = titleblock_properties_before;
243 		titleblock_properties_after.template_name = integrated_template_name;
244 		m_diagram->undoStack().push(new ChangeTitleBlockCommand(m_diagram, titleblock_properties_before, titleblock_properties_after));
245 
246 		adjustSceneRect();
247 	}
248 }
249 
250 /**
251  * @brief DiagramView::handleTextDrop
252  *handle the drop of text
253  * @param e the QDropEvent describing the current drag'n drop
254  */
handleTextDrop(QDropEvent * e)255 void DiagramView::handleTextDrop(QDropEvent *e) {
256 	if (m_diagram -> isReadOnly() || (e -> mimeData() -> hasText() == false) ) return;
257 
258 	IndependentTextItem *iti = new IndependentTextItem (e -> mimeData() -> text());
259 
260 	if (e -> mimeData() -> hasHtml()) {
261 		iti -> setHtml (e -> mimeData() -> text());
262 	}
263 
264 	m_diagram -> undoStack().push(new AddItemCommand<IndependentTextItem *>(iti, m_diagram, mapToScene(e->pos())));
265 }
266 
267 /**
268 	Set the Diagram in visualisation mode
269 */
setVisualisationMode()270 void DiagramView::setVisualisationMode() {
271 	setDragMode(ScrollHandDrag);
272 	applyReadOnly();
273 	setInteractive(false);
274 	emit(modeChanged());
275 }
276 
277 /**
278 	Set the Diagram in Selection mode
279 */
setSelectionMode()280 void DiagramView::setSelectionMode() {
281 	setDragMode(RubberBandDrag);
282 	setInteractive(true);
283 	applyReadOnly();
284 	emit(modeChanged());
285 }
286 
287 /**
288  * @brief DiagramView::zoom
289  * Zomm the view.
290  * A zoom_factor > 1 zoom in.
291  * A zoom_factor < 1 zoom out
292  * @param zoom_factor
293  */
zoom(const qreal zoom_factor)294 void DiagramView::zoom(const qreal zoom_factor)
295 {
296 	if (zoom_factor >= 1){
297 		scale(zoom_factor, zoom_factor);
298 	}
299 	else
300 	{
301 		QSettings settings;
302 		if (settings.value("diagrameditor/zoom-out-beyond-of-folio", false).toBool() ||
303 			(horizontalScrollBar()->maximum() || verticalScrollBar()->maximum()) )
304 			if (zoom_factor >= 0){
305 				scale(zoom_factor, zoom_factor);
306 			}
307 	}
308 	m_diagram->adjustSceneRect();
309 	adjustGridToZoom();
310 	adjustSceneRect();
311 }
312 
313 /**
314 	Agrandit ou rectrecit le schema de facon a ce que tous les elements du
315 	schema soient visibles a l'ecran. S'il n'y a aucun element sur le schema,
316 	le zoom est reinitialise
317 */
zoomFit()318 void DiagramView::zoomFit() {
319 	adjustSceneRect();
320 	fitInView(m_diagram->sceneRect(), Qt::KeepAspectRatio);
321 	adjustGridToZoom();
322 }
323 
324 /**
325 	Adjust zoom to fit all elements in the view, regardless of diagram borders.
326 */
zoomContent()327 void DiagramView::zoomContent() {
328 	fitInView(m_diagram -> itemsBoundingRect(), Qt::KeepAspectRatio);
329 	adjustGridToZoom();
330 }
331 
332 /**
333 	Reinitialise le zoom
334 */
zoomReset()335 void DiagramView::zoomReset() {
336 	resetMatrix();
337 	adjustGridToZoom();
338 }
339 
340 /**
341 	Copie les elements selectionnes du schema dans le presse-papier puis les supprime
342 */
cut()343 void DiagramView::cut() {
344 	copy();
345 	DiagramContent cut_content(m_diagram);
346 	m_diagram -> clearSelection();
347 	m_diagram -> undoStack().push(new CutDiagramCommand(m_diagram, cut_content));
348 }
349 
350 /**
351 	Copie les elements selectionnes du schema dans le presse-papier
352 */
copy()353 void DiagramView::copy() {
354 	QClipboard *presse_papier = QApplication::clipboard();
355 	QString contenu_presse_papier = m_diagram -> toXml(false).toString(4);
356 	if (presse_papier -> supportsSelection()) presse_papier -> setText(contenu_presse_papier, QClipboard::Selection);
357 	presse_papier -> setText(contenu_presse_papier);
358 }
359 
360 /**
361  * @brief DiagramView::paste
362  * Import the element stored in the clipboard to the diagram.
363  * @param pos : top left corner of the bounding rect of imported elements
364  * @param clipboard_mode
365  */
paste(const QPointF & pos,QClipboard::Mode clipboard_mode)366 void DiagramView::paste(const QPointF &pos, QClipboard::Mode clipboard_mode) {
367 	if (!isInteractive() || m_diagram -> isReadOnly()) return;
368 
369 	QString texte_presse_papier = QApplication::clipboard() -> text(clipboard_mode);
370 	if ((texte_presse_papier).isEmpty()) return;
371 
372 	QDomDocument document_xml;
373 	if (!document_xml.setContent(texte_presse_papier)) return;
374 
375 	DiagramContent content_pasted;
376 	m_diagram->fromXml(document_xml, pos, false, &content_pasted);
377 
378 		//If something was really added to diagram, we create an undo object.
379 	if (content_pasted.count())
380 	{
381 		m_diagram -> clearSelection();
382 		m_diagram -> undoStack().push(new PasteDiagramCommand(m_diagram, content_pasted));
383 		adjustSceneRect();
384 	}
385 }
386 
387 /**
388 	Colle le contenu du presse-papier sur le schema a la position de la souris
389 */
pasteHere()390 void DiagramView::pasteHere() {
391 	paste(mapToScene(m_paste_here_pos));
392 }
393 
394 /**
395 	Manage the events press click :
396 	 *  click to add an independent text field
397 */
mousePressEvent(QMouseEvent * e)398 void DiagramView::mousePressEvent(QMouseEvent *e)
399 {
400 	e->ignore();
401 
402 	if (m_fresh_focus_in)
403 	{
404 		switchToVisualisationModeIfNeeded(e);
405 		m_fresh_focus_in = false;
406 	}
407 
408 	if (m_event_interface && m_event_interface->mousePressEvent(e)) return;
409 
410 		//Start drag view when hold the middle button
411 	if (e->button() == Qt::MidButton)
412 	{
413 		m_drag_last_pos = e->pos();
414 		viewport()->setCursor(Qt::ClosedHandCursor);
415 		e->accept();
416 		return;
417 	}
418 
419 		//There is a good luck that user want to do a free selection
420 		//In this case we temporally disable the dragmode because if the QGraphicsScene don't accept the event,
421 		//and the drag mode is set to rubberbanddrag, the QGraphicsView start rubber band drag, and accept the event.
422 	if (e->button() == Qt::LeftButton &&
423 		e->modifiers() == Qt::CTRL)
424 	{
425 		QGraphicsView::DragMode dm = dragMode();
426 		setDragMode(QGraphicsView::NoDrag);
427 		QGraphicsView::mousePressEvent(e);
428 		setDragMode(dm);
429 	} else {
430 		QGraphicsView::mousePressEvent(e);
431 	}
432 
433 	if (e->isAccepted()) {
434 		return;
435 	}
436 
437 	if (e->button() == Qt::LeftButton &&
438 			 e->modifiers() == Qt::CTRL)
439 	{
440 		m_free_rubberbanding = true;
441 		m_free_rubberband = QPolygon();
442 		e->accept();
443 		return;
444 	}
445 
446 	if (!e->isAccepted()) {
447 		QGraphicsView::mousePressEvent(e);
448 	}
449 }
450 
451 /**
452  * @brief DiagramView::mouseMoveEvent
453  * Manage the event move mouse
454  */
mouseMoveEvent(QMouseEvent * e)455 void DiagramView::mouseMoveEvent(QMouseEvent *e)
456 {
457 	if (m_event_interface && m_event_interface->mouseMoveEvent(e)) return;
458 
459 		//Drag the view
460 	if (e->buttons() == Qt::MidButton)
461 	{
462 		QScrollBar *h = horizontalScrollBar();
463 		QScrollBar *v = verticalScrollBar();
464 		QPointF pos = m_drag_last_pos - e -> pos();
465 		m_drag_last_pos = e -> pos();
466 		h -> setValue(h -> value() + pos.x());
467 		v -> setValue(v -> value() + pos.y());
468 		adjustSceneRect();
469 	}
470 	else if (m_free_rubberbanding)
471 	{
472 			//Update old free rubberband
473 		if (viewportUpdateMode() != QGraphicsView::NoViewportUpdate && !m_free_rubberband.isEmpty())
474 		{
475 			if (viewportUpdateMode() != QGraphicsView::FullViewportUpdate) {
476 				viewport()->update(m_free_rubberband.boundingRect().toRect().adjusted(-10,-10,10,10));
477 			}
478 			else {
479 				update();
480 			}
481 		}
482 
483 			//Stop polygon rubberbanding if user has let go of all buttons (even
484 			//if we didn't get the release events)
485 		if (!e->buttons()) {
486 			m_free_rubberbanding = false;
487 			m_free_rubberband = QPolygon();
488 			return;
489 		}
490 		m_free_rubberband.append(mapToScene(e->pos()));
491 		emit freeRubberBandChanged(m_free_rubberband);
492 
493 		if (viewportUpdateMode() != QGraphicsView::NoViewportUpdate)
494 		{
495 			if (viewportUpdateMode() != QGraphicsView::FullViewportUpdate) {
496 				viewport()->update(mapFromScene(m_free_rubberband.boundingRect().adjusted(-10,-10,10,10)));
497 			}
498 			else {
499 				update();
500 			}
501 		}
502 
503 			//Set the new selection area
504 		QPainterPath selection_area;
505 		selection_area.addPolygon(m_free_rubberband);
506 		m_diagram->setSelectionArea(selection_area);
507 	}
508 
509 	else QGraphicsView::mouseMoveEvent(e);
510 }
511 
512 /**
513  * @brief DiagramView::mouseReleaseEvent
514  * Manage event release click mouse
515  */
mouseReleaseEvent(QMouseEvent * e)516 void DiagramView::mouseReleaseEvent(QMouseEvent *e)
517 {
518 	if (m_event_interface && m_event_interface->mouseReleaseEvent(e)) return;
519 
520 		//Stop drag view
521 	if (e -> button() == Qt::MidButton)
522 	{
523 		viewport()->setCursor(Qt::ArrowCursor);
524 	}
525 	else if (m_free_rubberbanding && !e->buttons())
526 	{
527 		if (viewportUpdateMode() != QGraphicsView::NoViewportUpdate)
528 		{
529 			if (viewportUpdateMode() != QGraphicsView::FullViewportUpdate)
530 			{
531 				QRectF r(mapFromScene(m_free_rubberband).boundingRect());
532 				r.adjust(-10, -10, 10, 10);
533 				viewport()->update(r.toRect());
534 			}
535 			else
536 			{
537 				update();
538 			}
539 		}
540 
541 		if (m_free_rubberband.count() > 3)
542 		{
543 				//Popup a menu with an action to create conductors between
544 				//all selected terminals.
545 			QAction *act = new QAction(tr("Connecter les bornes sélectionnées"), this);
546 			QPolygonF polygon_ = m_free_rubberband;
547 			connect(act, &QAction::triggered, [this, polygon_]()
548 			{
549 				ConductorCreator::create(m_diagram, polygon_);
550 				diagram()->clearSelection();
551 			});
552 			QMenu *menu = new QMenu(this);
553 			menu->addAction(act);
554 			menu->popup(e->globalPos());
555 		}
556 
557 		m_free_rubberbanding = false;
558 		m_free_rubberband = QPolygon();
559 		emit freeRubberBandChanged(m_free_rubberband);
560 		e->accept();
561 	}
562 	else
563 		QGraphicsView::mouseReleaseEvent(e);
564 }
565 
566 /**
567  * @brief DiagramView::gestures
568  * @return
569  */
gestures() const570 bool DiagramView::gestures() const
571 {
572 	QSettings settings;
573 	return(settings.value("diagramview/gestures", false).toBool());
574 }
575 
576 /**
577 	Manage wheel event of mouse
578 	@param e QWheelEvent
579 */
wheelEvent(QWheelEvent * event)580 void DiagramView::wheelEvent(QWheelEvent *event)
581 {
582 	if (m_event_interface && m_event_interface->wheelEvent(event)) return;
583 
584 		//Zoom and scrolling
585 	QPoint angle = event->angleDelta();
586 
587 	if (gestures()) //When gesture mode is enable, we suppose the wheel event are made from a trackpad.
588 	{
589 		if (event->modifiers() == Qt::ControlModifier) //zoom
590 		{
591 			qreal value = angle.y();
592 			zoom(1 + value/1000);
593 		}
594 		else //scroll
595 		{
596 			horizontalScrollBar()->setValue(horizontalScrollBar()->value() - angle.x());
597 			verticalScrollBar()->setValue(verticalScrollBar()->value() - angle.y());
598 		}
599 	}
600 	else if (event->modifiers() == Qt::NoModifier) //Else we suppose the wheel event are made from a mouse.
601 	{
602 		qreal value = angle.y();
603 		zoom(1 + value/1000);
604 	}
605 	else
606 		QGraphicsView::wheelEvent(event);
607 }
608 
609 /**
610  * @brief DiagramView::gestureEvent
611  * Use the pinch of the trackpad for zoom
612  * @param event
613  * @return
614  */
gestureEvent(QGestureEvent * event)615 bool DiagramView::gestureEvent(QGestureEvent *event)
616 {
617 	if (QGesture *gesture = event->gesture(Qt::PinchGesture))
618 	{
619 		QPinchGesture *pinch = static_cast<QPinchGesture *>(gesture);
620 		if (pinch->changeFlags() & QPinchGesture::ScaleFactorChanged)
621 		{
622 			qreal value = gesture->property("scaleFactor").toReal();
623 			value > 1 ? zoom(1.02) : zoom(0.98);
624 		}
625 	}
626 	return true;
627 }
628 
629 
630 
631 /**
632 	Handles "Focus in" events. Reimplemented here to store the fact the focus
633 	was freshly acquired again using the mouse. This information is later used
634 	in DiagramView::mousePressEvent().
635 */
focusInEvent(QFocusEvent * e)636 void DiagramView::focusInEvent(QFocusEvent *e) {
637 	if (e -> reason() == Qt::MouseFocusReason) {
638 		m_fresh_focus_in = true;
639 	}
640 }
641 
642 /**
643  * @brief DiagramView::keyPressEvent
644  * 	Handles "key press" events. Reimplemented here to switch to visualisation
645  *	mode if needed.
646  * @param e
647  */
keyPressEvent(QKeyEvent * e)648 void DiagramView::keyPressEvent(QKeyEvent *e)
649 {
650 	if (m_event_interface && m_event_interface->keyPressEvent(e))
651 		return;
652 
653 	ProjectView *current_project = this->diagramEditor()->currentProjectView();
654 	DiagramContent dc(m_diagram);
655 	switch(e -> key())
656 	{
657 		case Qt::Key_PageUp:
658 			current_project->changeTabUp();
659 			return;
660 		case Qt::Key_PageDown:
661 			current_project->changeTabDown();
662 			return;
663 		case Qt::Key_Home:
664 			if (dc.selectedTexts().isEmpty()) {
665 				if (
666 					qgraphicsitem_cast<IndependentTextItem *>(m_diagram->focusItem()) ||
667 					qgraphicsitem_cast<ConductorTextItem *>(m_diagram->focusItem()) ||
668 					qgraphicsitem_cast<DiagramTextItem *>(m_diagram->focusItem())
669 					)
670 					break;
671 				current_project->changeFirstTab();
672 				return;
673 			}
674 			else break;
675 		case Qt::Key_End:
676 			if (dc.selectedTexts().isEmpty()) {
677 				if (
678 					qgraphicsitem_cast<IndependentTextItem *>(m_diagram->focusItem()) ||
679 					qgraphicsitem_cast<ConductorTextItem *>(m_diagram->focusItem()) ||
680 					qgraphicsitem_cast<DiagramTextItem *>(m_diagram->focusItem())
681 					)
682 					break;
683 				current_project->changeLastTab();
684 				return;
685 			}
686 			else break;
687 		case Qt::Key_ZoomOut:
688 			zoom(0.85);
689 			return;
690 		case Qt::Key_ZoomIn:
691 			zoom(1.15);
692 			return;
693 		case Qt::Key_Minus: {
694 			if (e->modifiers() & Qt::ControlModifier)
695 				zoom(0.85);
696 		}
697 			break;
698 		case Qt::Key_Plus: {
699 			if (e->modifiers() & Qt::ControlModifier)
700 				zoom(1.15);
701 		}
702 			break;
703 		case Qt::Key_Up: {
704 			if(!dc.items(DiagramContent::All).isEmpty() && !dc.hasTextEditing())
705 				scrollOnMovement(e);
706 		}
707 			break;
708 		case Qt::Key_Down: {
709 			if(!dc.items(DiagramContent::All).isEmpty() && !dc.hasTextEditing())
710 				scrollOnMovement(e);
711 		}
712 			break;
713 		case Qt::Key_Left: {
714 			if(!dc.items(DiagramContent::All).isEmpty() && !dc.hasTextEditing())
715 				scrollOnMovement(e);
716 		}
717 			break;
718 		case Qt::Key_Right: {
719 			if(!dc.items(DiagramContent::All).isEmpty() && !dc.hasTextEditing())
720 				scrollOnMovement(e);
721 		}
722 			break;
723 	}
724 
725 	switchToVisualisationModeIfNeeded(e);
726 	QGraphicsView::keyPressEvent(e);
727 }
728 
729 /**
730 	Handles "key release" events. Reimplemented here to switch to selection
731 	mode if needed.
732 */
keyReleaseEvent(QKeyEvent * e)733 void DiagramView::keyReleaseEvent(QKeyEvent *e) {
734 	if (m_event_interface && m_event_interface->KeyReleaseEvent(e)) return;
735 
736 	switchToSelectionModeIfNeeded(e);
737 	QGraphicsView::keyReleaseEvent(e);
738 }
739 
740 /**
741 	Handles element movement when editor is zoomed in and scrolls vertical
742 	and horizontal bar. If element is moved to the right side of the editor
743 	or below the editor SceneRect is expanded
744 */
scrollOnMovement(QKeyEvent * e)745 void DiagramView::scrollOnMovement(QKeyEvent *e)
746 {
747 			const QList<QGraphicsItem *> selected_elmts = DiagramContent(m_diagram).items(DiagramContent::All);
748 			QRectF viewed_scene = viewedSceneRect();
749 			for (QGraphicsItem *qgi : selected_elmts)
750 			{
751 				if (qgraphicsitem_cast<Conductor *>(qgi))
752 					continue;
753 				if(qgi->parentItem() && qgi->parentItem()->isSelected())
754 					continue;
755 
756 				qreal x = qgi->pos().x();
757 				qreal y = qgi->pos().y();
758 				qreal bottom = viewed_scene.bottom();
759 				qreal top = viewed_scene.top();
760 				qreal left = viewed_scene.left();
761 				qreal right = viewed_scene.right();
762 				qreal elmt_top = y + qgi->boundingRect().top();
763 				qreal elmt_bottom = y + qgi->boundingRect().bottom();
764 				qreal elmt_right = x + qgi->boundingRect().right();
765 				qreal elmt_left = x + qgi->boundingRect().left();
766 
767 				bool elmt_right_of_left_margin = elmt_left>=left;
768 				bool elmt_left_of_right_margin = elmt_right<=right;
769 				bool elmt_below_top_margin     = elmt_top>=top;
770 				bool elmt_above_bottom_margin  = elmt_bottom<=bottom;
771 
772 				if (!(elmt_right_of_left_margin && elmt_left_of_right_margin) ||
773 					!(elmt_below_top_margin    && elmt_above_bottom_margin )  )
774 				{
775 						QScrollBar *h = horizontalScrollBar();
776 						QScrollBar *v = verticalScrollBar();
777 						int h_increment=0;
778 						int v_increment=0;
779 						if (e->key()==Qt::Key_Up && elmt_above_bottom_margin) {
780 							v_increment = 2*qgi->boundingRect().top();
781 							if (v_increment == 0) v_increment = -2*qgi->boundingRect().height();
782 						}
783 						else if(e->key()==Qt::Key_Down && elmt_below_top_margin) {
784 							v_increment = 2*qgi->boundingRect().bottom();
785 							if (v_increment == 0) v_increment = -2*qgi->boundingRect().height();
786 						}
787 						else if (e->key()==Qt::Key_Left && elmt_left_of_right_margin) {
788 							h_increment = 2*qgi->boundingRect().left();
789 							if (h_increment == 0) h_increment = -2*qgi->boundingRect().width();
790 						}
791 						else if (e->key()==Qt::Key_Right && elmt_right_of_left_margin) {
792 							h_increment = 2*qgi->boundingRect().right();
793 							if (h_increment == 0) h_increment = -2*qgi->boundingRect().width();
794 						}
795 						if (((elmt_right  >= m_diagram->sceneRect().right() -  qgi->boundingRect().right())  ||
796 							(elmt_bottom >= m_diagram->sceneRect().bottom() - qgi->boundingRect().bottom())) &&
797 							(e->key()==Qt::Key_Right || e->key()==Qt::Key_Down)){
798 							m_diagram->adjustSceneRect();
799 						}
800 						h -> setValue(h -> value() + h_increment);
801 						v -> setValue(v -> value() + v_increment);
802 				}
803 			}
804 }
805 
806 
807 /**
808 	@return le titre de cette vue ; cela correspond au titre du schema
809 	visualise precede de la mention "Schema". Si le titre du schema est vide,
810 	la mention "Schema sans titre" est utilisee
811 	@see Diagram::title()
812 */
title() const813 QString DiagramView::title() const {
814 	QString view_title;
815 	QString diagram_title(m_diagram -> title());
816 	if (diagram_title.isEmpty()) {
817 		view_title = tr("Sans titre", "what to display for untitled diagrams");
818 	} else {
819 		view_title = diagram_title;
820 	}
821 	return(view_title);
822 }
823 
824 /**
825  * @brief DiagramView::editDiagramProperties
826  * Edit the properties of the viewed digram
827  */
editDiagramProperties()828 void DiagramView::editDiagramProperties() {
829 	DiagramPropertiesDialog::diagramPropertiesDialog(m_diagram, diagramEditor());
830 }
831 
832 /**
833  * @brief DiagramView::adjustSceneRect
834  * Calcul and set the area of the scene visualized by this view
835  */
adjustSceneRect()836 void DiagramView::adjustSceneRect()
837 {
838 	QRectF scene_rect = m_diagram->sceneRect();
839 	scene_rect.adjust(-Diagram::margin, -Diagram::margin, Diagram::margin, Diagram::margin);
840 
841 	QSettings settings;
842 	if (settings.value("diagrameditor/zoom-out-beyond-of-folio", false).toBool())
843 	{
844 			//When zoom out beyong of folio is active,
845 			//we always adjust the scene rect to be 1/3 bigger than the wiewport
846 		QRectF vpbr = mapToScene(viewport()->rect()).boundingRect();
847 		vpbr.adjust(0, 0, vpbr.width()/3, vpbr.height()/3);
848 		scene_rect = scene_rect.united(vpbr);
849 	}
850 	setSceneRect(scene_rect);
851 }
852 
853 /**
854 	Met a jour le titre du widget
855 */
updateWindowTitle()856 void DiagramView::updateWindowTitle() {
857 	emit(titleChanged(this, title()));
858 }
859 
860 /**
861 	Enables or disables the drawing grid according to the amount of pixels display
862 */
adjustGridToZoom()863 void DiagramView::adjustGridToZoom() {
864 	QRectF viewed_scene = viewedSceneRect();
865 	if (diagramEditor()->drawGrid())
866 		m_diagram->setDisplayGrid(viewed_scene.width() < 2000 || viewed_scene.height() < 2000);
867 	else
868 		m_diagram->setDisplayGrid(false);
869 }
870 
871 /**
872 	@return le rectangle du schema (classe Diagram) visualise par ce DiagramView
873 */
viewedSceneRect() const874 QRectF DiagramView::viewedSceneRect() const {
875 	// recupere la taille du widget viewport
876 	QSize viewport_size = viewport() -> size();
877 
878 	// recupere la transformation viewport -> scene
879 	QTransform view_to_scene   = viewportTransform().inverted();
880 
881 	// mappe le coin superieur gauche et le coin inferieur droit de la viewport sur la scene
882 	QPointF scene_left_top     = view_to_scene.map(QPointF(0.0, 0.0));
883 	QPointF scene_right_bottom = view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
884 
885 	// en deduit le rectangle visualise par la scene
886 	return(QRectF(scene_left_top, scene_right_bottom));
887 }
888 
889 /**
890 	@param tbt_loc A title block template location
891 	@return true if the title block template needs to be integrated in the
892 	parent project before being applied to the current diagram, or false if it
893 	can be directly applied
894 */
mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation & tbt_loc) const895 bool DiagramView::mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &tbt_loc) const {
896 	// unlike elements, the integration of title block templates is mandatory, so we simply check whether the parent project of the template is also the parent project of the diagram
897 	QETProject *tbt_parent_project = tbt_loc.parentProject();
898 	if (!tbt_parent_project) return(true);
899 
900 	return(tbt_parent_project != m_diagram -> project());
901 }
902 
903 /**
904 	Fait en sorte que le schema ne soit editable que s'il n'est pas en lecture
905 	seule
906 */
applyReadOnly()907 void DiagramView::applyReadOnly() {
908 	if (!m_diagram) return;
909 
910 	bool is_writable = !m_diagram -> isReadOnly();
911 	setInteractive(is_writable);
912 	setAcceptDrops(is_writable);
913 }
914 
915 /**
916  * @brief DiagramView::editSelectedConductorColor
917  * Edit the color of the selected conductor; does nothing if multiple conductors are selected
918  */
editSelectedConductorColor()919 void DiagramView::editSelectedConductorColor()
920 {
921 		//retrieve selected content
922 	DiagramContent selection(m_diagram);
923 
924 		// we'll focus on the selected conductor (we do not handle multiple conductors edition)
925 	QList<Conductor *> selected_conductors = selection.conductors(DiagramContent::AnyConductor | DiagramContent::SelectedOnly);
926 	if (selected_conductors.count() == 1) {
927 		editConductorColor(selected_conductors.at(0));
928 	}
929 }
930 
931 /**
932 	Edit the color of the given conductor
933 	@param edited_conductor Conductor we want to change the color
934 */
editConductorColor(Conductor * edited_conductor)935 void DiagramView::editConductorColor(Conductor *edited_conductor)
936 {
937 	if (m_diagram -> isReadOnly() || !edited_conductor) return;
938 
939 		// store the initial properties of the provided conductor
940 	ConductorProperties initial_properties = edited_conductor -> properties();
941 
942 		// prepare a color dialog showing the initial conductor color
943 	QColorDialog *color_dialog = new QColorDialog(this);
944 	color_dialog -> setWindowTitle(tr("Choisir la nouvelle couleur de ce conducteur"));
945 #ifdef Q_OS_MAC
946 	color_dialog -> setWindowFlags(Qt::Sheet);
947 #endif
948 	color_dialog -> setCurrentColor(initial_properties.color);
949 
950 		// asks the user what color he wishes to apply
951 	if (color_dialog -> exec() == QDialog::Accepted)
952 	{
953 		QColor new_color = color_dialog -> selectedColor();
954 		if (new_color != initial_properties.color)
955 		{
956 				// the user chose a different color
957 			QVariant old_value, new_value;
958 			old_value.setValue(initial_properties);
959 			initial_properties.color = new_color;
960 			new_value.setValue(initial_properties);
961 
962 			QPropertyUndoCommand *undo = new QPropertyUndoCommand(edited_conductor, "properties", old_value, new_value);
963 			undo->setText(tr("Modifier les propriétés d'un conducteur", "undo caption"));
964 			diagram() -> undoStack().push(undo);
965 		}
966 	}
967 }
968 
969 /**
970 	Reinitialise le profil des conducteurs selectionnes
971 */
resetConductors()972 void DiagramView::resetConductors() {
973 	if (m_diagram -> isReadOnly()) return;
974 	// recupere les conducteurs selectionnes
975 	QSet<Conductor *> selected_conductors = m_diagram -> selectedConductors();
976 
977 	// repere les conducteurs modifies (= profil non nul)
978 	QHash<Conductor *, ConductorProfilesGroup> conductors_and_profiles;
979 	foreach(Conductor *conductor, selected_conductors) {
980 		ConductorProfilesGroup profile = conductor -> profiles();
981 		if (
982 			!profile[Qt::TopLeftCorner].isNull() ||\
983 			!profile[Qt::TopRightCorner].isNull() ||\
984 			!profile[Qt::BottomLeftCorner].isNull() ||\
985 			!profile[Qt::BottomRightCorner].isNull()
986 		) {
987 			conductors_and_profiles.insert(conductor, profile);
988 		}
989 	}
990 
991 	if (conductors_and_profiles.isEmpty()) return;
992 	m_diagram -> undoStack().push(new ResetConductorCommand(conductors_and_profiles));
993 }
994 
995 /**
996 	Gere les evenements de la DiagramView
997 	@param e Evenement
998 */
999 /**
1000  * @brief DiagramView::event
1001  * Manage the event on this diagram view.
1002  * -At first activation (QEvent::WindowActivate or QEvent::Show) we zoomFit.
1003  * -Convert event interpreted to mouse event to gesture event if needed.
1004  * -send Shortcut to view (by default send to QMenu /QAction)
1005  * @param e the event.
1006  * @return
1007  */
event(QEvent * e)1008 bool DiagramView::event(QEvent *e) {
1009 	if (Q_UNLIKELY(m_first_activation)) {
1010 		if (e -> type() == QEvent::Show) {
1011 			zoomFit();
1012 			m_first_activation = false;
1013 		}
1014 	}
1015 	// By default touch events are converted to mouse events. So
1016 	// after this event we will get a mouse event also but we want
1017 	// to handle touch events as gestures only. So we need this safeguard
1018 	// to block mouse events that are actually generated from touch.
1019 	if (e->type() == QEvent::Gesture)
1020 		return gestureEvent(static_cast<QGestureEvent *>(e));
1021 
1022 		// fait en sorte que les raccourcis clavier arrivent prioritairement sur la
1023 		// vue plutot que de remonter vers les QMenu / QAction
1024 	if (
1025 		e -> type() == QEvent::ShortcutOverride &&
1026 		selectedItemHasFocus()
1027 	) {
1028 		e -> accept();
1029 		return(true);
1030 	}
1031 	return(QGraphicsView::event(e));
1032 }
1033 
1034 /**
1035  * @brief DiagramView::paintEvent
1036  * Reimplemented from QGraphicsView
1037  * @param event
1038  */
paintEvent(QPaintEvent * event)1039 void DiagramView::paintEvent(QPaintEvent *event)
1040 {
1041 	QGraphicsView::paintEvent(event);
1042 
1043 	if (m_free_rubberbanding && m_free_rubberband.count() >= 3)
1044 	{
1045 		QPainter painter(viewport());
1046 		painter.setRenderHint(QPainter::Antialiasing);
1047 		QPen pen(Qt::darkGreen);
1048 		pen.setWidth(1);
1049 		painter.setPen(pen);
1050 		QColor color(Qt::darkGreen);
1051 		color.setAlpha(50);
1052 		QBrush brush(color);
1053 		painter.setBrush(brush);
1054 		painter.drawPolygon(mapFromScene(m_free_rubberband));
1055 	}
1056 }
1057 
1058 /**
1059 	Switch to visualisation mode if the user is pressing Ctrl and Shift.
1060 	@return true if the view was switched to visualisation mode, false
1061 	otherwise.
1062 */
switchToVisualisationModeIfNeeded(QInputEvent * e)1063 bool DiagramView::switchToVisualisationModeIfNeeded(QInputEvent *e) {
1064 	if (isCtrlShifting(e) && !selectedItemHasFocus()) {
1065 		if (dragMode() != QGraphicsView::ScrollHandDrag) {
1066 			setVisualisationMode();
1067 			return(true);
1068 		}
1069 	}
1070 	return(false);
1071 }
1072 
1073 /**
1074 	Switch back to selection mode if the user is not pressing Ctrl and Shift.
1075 	@return true if the view was switched to selection mode, false
1076 	otherwise.
1077 */
switchToSelectionModeIfNeeded(QInputEvent * e)1078 bool DiagramView::switchToSelectionModeIfNeeded(QInputEvent *e) {
1079 	if (!selectedItemHasFocus() && !isCtrlShifting(e)) {
1080 		setSelectionMode();
1081 		return(true);
1082 	}
1083 	return(false);
1084 }
1085 
1086 /**
1087 	@return true if the user is pressing Ctrl and Shift simultaneously.
1088 */
isCtrlShifting(QInputEvent * e)1089 bool DiagramView::isCtrlShifting(QInputEvent *e) {
1090 	bool result = false;
1091 	// note: QInputEvent::modifiers and QKeyEvent::modifiers() do not return the
1092 	// same values, hence the casts
1093 	if (e -> type() == QEvent::KeyPress || e -> type() == QEvent::KeyRelease) {
1094 		if (QKeyEvent *ke = static_cast<QKeyEvent *>(e)) {
1095 			result = (ke -> modifiers() == (Qt::ControlModifier | Qt::ShiftModifier));
1096 		}
1097 	} else if (e -> type() >= QEvent::MouseButtonPress && e -> type() <= QEvent::MouseMove) {
1098 		if (QMouseEvent *me = static_cast<QMouseEvent *>(e)) {
1099 			result = (me -> modifiers() == (Qt::ControlModifier | Qt::ShiftModifier));
1100 		}
1101 	}
1102 	return(result);
1103 }
1104 
1105 /**
1106 	@return true if there is a selected item and that item has the focus.
1107 */
selectedItemHasFocus()1108 bool DiagramView::selectedItemHasFocus() {
1109 	return(
1110 		m_diagram -> hasFocus() &&
1111 		m_diagram -> focusItem() &&
1112 		m_diagram -> focusItem() -> isSelected()
1113 	);
1114 }
1115 
1116 /**
1117  * @brief DiagramView::editSelection
1118  * Edit the selected item if he can be edited and if only  one item is selected
1119  */
editSelection()1120 void DiagramView::editSelection() {
1121 	if (m_diagram -> isReadOnly() || m_diagram -> selectedItems().size() != 1 ) return;
1122 
1123 	QGraphicsItem *item = m_diagram->selectedItems().first();
1124 
1125 		//We use dynamic_cast instead of qgraphicsitem_cast for QetGraphicsItem
1126 		//because they haven't got they own type().
1127 		//Use qgraphicsitem_cast will have weird behavior for this class.
1128 	if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(item))
1129 		iti -> edit();
1130 	else if (QetGraphicsItem *qgi = dynamic_cast<QetGraphicsItem *> (item))
1131 		qgi -> editProperty();
1132 	else if (Conductor *c = qgraphicsitem_cast<Conductor *>(item))
1133 		c -> editProperty();
1134 }
1135 
1136 /**
1137  * @brief DiagramView::setEventInterface
1138  * Set an event interface to diagram view.
1139  * If diagram view already have an event interface, he delete it before.
1140  * Diagram view take ownership of event interface and delete it when event interface is finish
1141  */
setEventInterface(DVEventInterface * event_interface)1142 void DiagramView::setEventInterface(DVEventInterface *event_interface)
1143 {
1144 	if (m_event_interface) delete m_event_interface;
1145 	m_event_interface = event_interface;
1146 	connect(m_event_interface, &DVEventInterface::finish, this, [=](){delete this->m_event_interface; this->m_event_interface = nullptr;}, Qt::QueuedConnection);
1147 }
1148 
1149 /**
1150  * @brief DiagramView::contextMenuActions
1151  * @return a list of actions currently available for a context menu.
1152  *
1153  */
contextMenuActions() const1154 QList<QAction *> DiagramView::contextMenuActions() const
1155 {
1156 	QList<QAction *> list;
1157 	if (QETDiagramEditor *qde = diagramEditor())
1158 	{
1159 		if (m_diagram->selectedItems().isEmpty())
1160 		{
1161 			list << m_paste_here;
1162 			list << m_separators.at(0);
1163 			list << qde->m_edit_diagram_properties;
1164 			list << qde->m_row_column_actions_group.actions();
1165 		}
1166 		else
1167 		{
1168 			list << qde->m_cut;
1169 			list << qde->m_copy;
1170 			list << m_multi_paste;
1171 			list << m_separators.at(0);
1172 			list << qde->m_conductor_reset;
1173 			list << m_separators.at(1);
1174 			list << qde->m_selection_actions_group.actions();
1175 			list << m_separators.at(2);
1176 			list << qde->m_depth_action_group->actions();
1177 		}
1178 
1179 			//Remove from the context menu the actions which are disabled.
1180 		const QList<QAction *> actions = list;
1181 		for(QAction *action : actions)
1182 		{
1183 			if (!action->isEnabled()) {
1184 				list.removeAll(action);
1185 			}
1186 		}
1187 	}
1188 
1189 	return list;
1190 }
1191 
1192 /**
1193  * @brief DiagramView::contextMenuEvent
1194  * @param e
1195  */
contextMenuEvent(QContextMenuEvent * e)1196 void DiagramView::contextMenuEvent(QContextMenuEvent *e)
1197 {
1198 	QGraphicsView::contextMenuEvent(e);
1199 	if(e->isAccepted())
1200 		return;
1201 
1202 	if (QGraphicsItem *qgi = m_diagram->itemAt(mapToScene(e->pos()), transform()))
1203 	{
1204 		if (!qgi -> isSelected()) {
1205 			m_diagram->clearSelection();
1206 		}
1207 
1208 		qgi->setSelected(true);
1209 	}
1210 
1211 	if (m_diagram->selectedItems().isEmpty())
1212 	{
1213 		m_paste_here_pos = e->pos();
1214 		m_paste_here->setEnabled(Diagram::clipboardMayContainDiagram());
1215 	}
1216 
1217 	QList <QAction *> list = contextMenuActions();
1218 	if(!list.isEmpty())
1219 	{
1220 		QMenu *context_menu = new QMenu(this);
1221 		context_menu->addActions(list);
1222 		context_menu->popup(e->globalPos());
1223 		e->accept();
1224 	}
1225 }
1226 
1227 /**
1228 	@return l'editeur de schemas parent ou 0
1229 */
diagramEditor() const1230 QETDiagramEditor *DiagramView::diagramEditor() const {
1231 	// remonte la hierarchie des widgets
1232 	QWidget *w = const_cast<DiagramView *>(this);
1233 	while (w -> parentWidget() && !w -> isWindow()) {
1234 		w = w -> parentWidget();
1235 	}
1236 	// la fenetre est supposee etre un QETDiagramEditor
1237 	return(qobject_cast<QETDiagramEditor *>(w));
1238 }
1239 
1240 /**
1241  * @brief DiagramView::mouseDoubleClickEvent
1242  * @param e
1243  */
mouseDoubleClickEvent(QMouseEvent * e)1244 void DiagramView::mouseDoubleClickEvent(QMouseEvent *e)
1245 {
1246 	if (m_event_interface && m_event_interface -> mouseDoubleClickEvent(e)) return;
1247 
1248 	BorderTitleBlock &bi = m_diagram -> border_and_titleblock;
1249 
1250 	//Get the click pos on the diagram
1251 	QPointF click_pos = viewportTransform().inverted().map(e -> pos());
1252 
1253 	if (bi.titleBlockRect().contains(click_pos) || bi.columnsRect().contains(click_pos) || bi.rowsRect().contains(click_pos)) {
1254 		e->accept();
1255 		editDiagramProperties();
1256 		return;
1257 	}
1258 	QGraphicsView::mouseDoubleClickEvent(e);
1259 }
1260