QElectroTech  0.70
diagramview.cpp
Go to the documentation of this file.
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"
21 #include "diagramcommands.h"
22 #include "diagramposition.h"
27 #include "templatelocation.h"
28 #include "qetproject.h"
29 #include "projectview.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"
39 #include "dveventinterface.h"
40 #include "diagrameventaddelement.h"
42 #include "qetshapeitem.h"
44 #include "dynamicelementtextitem.h"
45 #include "multipastedialog.h"
47 #include "conductorcreator.h"
48 
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);
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()));
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 
118 }
119 
124  m_diagram -> selectAll();
125 }
126 
131  m_diagram -> deselectAll();
132 }
133 
138  m_diagram -> invertSelection();
139 }
140 
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 
161 void DiagramView::dragMoveEvent(QDragMoveEvent *e) {
162  if (e -> mimeData() -> hasFormat("text/plain")) e -> acceptProposedAction();
163  else e-> ignore();
164 }
165 
170 void DiagramView::dropEvent(QDropEvent *e) {
171 
172  if (e -> mimeData() -> hasFormat("application/x-qet-element-uri")) {
174  } else if (e -> mimeData() -> hasFormat("application/x-qet-titleblock-uri")) {
176  } else if (e -> mimeData() -> hasText()) {
177  handleTextDrop(e);
178  }
179 }
180 
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 
207  // fetch the title block template location from the drop event
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  {
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 
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 
271  setDragMode(ScrollHandDrag);
272  applyReadOnly();
273  setInteractive(false);
274  emit(modeChanged());
275 }
276 
281  setDragMode(RubberBandDrag);
282  setInteractive(true);
283  applyReadOnly();
284  emit(modeChanged());
285 }
286 
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  }
310  adjustSceneRect();
311 }
312 
319  adjustSceneRect();
320  fitInView(m_diagram->sceneRect(), Qt::KeepAspectRatio);
322 }
323 
328  fitInView(m_diagram -> itemsBoundingRect(), Qt::KeepAspectRatio);
330 }
331 
336  resetMatrix();
338 }
339 
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 
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 
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 
391  paste(mapToScene(m_paste_here_pos));
392 }
393 
398 void DiagramView::mousePressEvent(QMouseEvent *e)
399 {
400  e->ignore();
401 
402  if (m_fresh_focus_in)
403  {
405  m_fresh_focus_in = false;
406  }
407 
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 
455 void DiagramView::mouseMoveEvent(QMouseEvent *e)
456 {
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()));
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 
516 void DiagramView::mouseReleaseEvent(QMouseEvent *e)
517 {
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  {
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();
560  e->accept();
561  }
562  else
563  QGraphicsView::mouseReleaseEvent(e);
564 }
565 
571 {
572  QSettings settings;
573  return(settings.value("diagramview/gestures", false).toBool());
574 }
575 
580 void DiagramView::wheelEvent(QWheelEvent *event)
581 {
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 
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 
636 void DiagramView::focusInEvent(QFocusEvent *e) {
637  if (e -> reason() == Qt::MouseFocusReason) {
638  m_fresh_focus_in = true;
639  }
640 }
641 
648 void DiagramView::keyPressEvent(QKeyEvent *e)
649 {
651  return;
652 
653  ProjectView *current_project = this->diagramEditor()->currentProjectView();
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 
726  QGraphicsView::keyPressEvent(e);
727 }
728 
733 void DiagramView::keyReleaseEvent(QKeyEvent *e) {
735 
737  QGraphicsView::keyReleaseEvent(e);
738 }
739 
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)){
799  }
800  h -> setValue(h -> value() + h_increment);
801  v -> setValue(v -> value() + v_increment);
802  }
803  }
804 }
805 
806 
813 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 
830 }
831 
837 {
838  QRectF scene_rect = m_diagram->sceneRect();
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 
857  emit(titleChanged(this, title()));
858 }
859 
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 
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 
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 
908  if (!m_diagram) return;
909 
910  bool is_writable = !m_diagram -> isReadOnly();
911  setInteractive(is_writable);
912  setAcceptDrops(is_writable);
913 }
914 
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 
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 
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 
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 &&
1027  ) {
1028  e -> accept();
1029  return(true);
1030  }
1031  return(QGraphicsView::event(e));
1032 }
1033 
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 
1064  if (isCtrlShifting(e) && !selectedItemHasFocus()) {
1065  if (dragMode() != QGraphicsView::ScrollHandDrag) {
1067  return(true);
1068  }
1069  }
1070  return(false);
1071 }
1072 
1079  if (!selectedItemHasFocus() && !isCtrlShifting(e)) {
1080  setSelectionMode();
1081  return(true);
1082  }
1083  return(false);
1084 }
1085 
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 
1109  return(
1110  m_diagram -> hasFocus() &&
1111  m_diagram -> focusItem() &&
1112  m_diagram -> focusItem() -> isSelected()
1113  );
1114 }
1115 
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 
1143 {
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 
1154 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 
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();
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 
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 
1245 {
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();
1256  return;
1257  }
1258  QGraphicsView::mouseDoubleClickEvent(e);
1259 }
void editConductorColor(Conductor *)
void changeFirstTab()
change current diagramview to first tab
The QPropertyUndoCommand class This undo command manage QProperty of a QObject. This undo command can...
void dragEnterEvent(QDragEnterEvent *) override
void focusInEvent(QFocusEvent *) override
QAction * m_paste_here
Definition: diagramview.h:53
void handleElementDrop(QDropEvent *)
QRectF columnsRect() const
BorderTitleBlock::columnsRect.
void setEventInterface(DVEventInterface *event_interface)
DiagramView::setEventInterface Set an event interface to diagram view. If diagram view already have a...
void setDisplayGrid(bool)
Definition: diagram.h:283
void changeTabDown()
change current diagramview to next folio
void zoomFit()
QString title() const
void zoom(const qreal zoom_factor)
DiagramView::zoom Zomm the view. A zoom_factor > 1 zoom in. A zoom_factor < 1 zoom out...
QList< DiagramTextItem * > selectedTexts() const
DiagramContent::selectedTexts.
QETDiagramEditor * diagramEditor() const
virtual bool keyPressEvent(QKeyEvent *event)
DVEventInterface::keyPressEvent By default, press escape key abort the curent action. isFinish return true, and emit finish.
void adjustSceneRect()
Diagram::adjustSceneRect Recalcul and adjust the size of the scene.
Definition: diagram.cpp:1597
void scrollOnMovement(QKeyEvent *)
virtual bool switchToVisualisationModeIfNeeded(QInputEvent *e)
static const qreal margin
margin around the diagram
Definition: diagram.h:88
void mousePressEvent(QMouseEvent *) override
Diagram * diagram()
Definition: diagramview.h:67
void findElementRequired(const ElementsLocation &)
Signal emitted when users wish to locate an element from the diagram within elements collection...
void modeChanged()
Signal emitted after the selection mode changed.
QIcon QETLogo
Definition: qeticons.cpp:151
static void create(Diagram *d, const QPolygonF &polygon)
ConductorCreator::create Create an electrical potential between the terminals of the diagram d...
TitleBlockProperties exportTitleBlock()
void applyReadOnly()
void selectionChanged()
Signal emitted after the selection changed.
QRectF rowsRect() const
BorderTitleBlock::rowsRect.
void contextMenuEvent(QContextMenuEvent *) override
DiagramView::contextMenuEvent.
virtual bool KeyReleaseEvent(QKeyEvent *event)
The AddItemCommand class This command add an item in a diagram The item to add is template...
void changeTabUp()
change current diagramview to previous tab
void handleTextDrop(QDropEvent *)
DiagramView::handleTextDrop handle the drop of text.
DiagramView(Diagram *diagram, QWidget *=nullptr)
Definition: diagramview.cpp:54
void zoomContent()
QPointF m_drag_last_pos
Definition: diagramview.h:56
void paintEvent(QPaintEvent *event) override
DiagramView::paintEvent Reimplemented from QGraphicsView.
The ChangeTitleBlockCommand class This command changes the title block properties for a particular di...
QPoint m_paste_here_pos
Definition: diagramview.h:55
void zoomReset()
QList< QGraphicsItem * > items(int=All) const
DiagramContent::items.
void editSelection()
DiagramView::editSelection Edit the selected item if he can be edited and if only one item is selecte...
virtual bool mouseReleaseEvent(QMouseEvent *event)
void finish()
finish emited when the interface finish is work
void selectInvert()
void adjustSceneRect()
DiagramView::adjustSceneRect Calcul and set the area of the scene visualized by this view...
QHash< Qt::Corner, ConductorProfile > ConductorProfilesGroup
Definition: conductor.h:37
void keyPressEvent(QKeyEvent *) override
DiagramView::keyPressEvent Handles "key press" events. Reimplemented here to switch to visualisation ...
void adjustGridToZoom()
void mouseDoubleClickEvent(QMouseEvent *) override
DiagramView::mouseDoubleClickEvent.
void keyReleaseEvent(QKeyEvent *) override
The DVEventInterface class This class is the main interface for manage event of a Diagram View...
void setSelectionMode()
void titleChanged(DiagramView *, const QString &)
Signal emitted after the diagram title changed.
QAction * m_multi_paste
Definition: diagramview.h:54
QRectF titleBlockRect() const
BorderTitleBlock::titleBlockRect.
bool isEmpty() const
Definition: diagram.cpp:574
void editSelectedConductorColor()
DiagramView::editSelectedConductorColor Edit the color of the selected conductor; does nothing if mul...
virtual bool mouseMoveEvent(QMouseEvent *event)
bool gestureEvent(QGestureEvent *event)
DiagramView::gestureEvent Use the pinch of the trackpad for zoom.
void updateWindowTitle()
void changeLastTab()
change current diagramview to last tab
ProjectView * currentProjectView() const
QIcon tr
Definition: qeticons.cpp:204
void wheelEvent(QWheelEvent *) override
QIcon EditPaste
Definition: qeticons.cpp:67
~DiagramView() override
static void diagramPropertiesDialog(Diagram *diagram, QWidget *parent=nullptr)
DiagramPropertiesDialog::diagramPropertiesDialog Static method to get a DiagramPropertiesDialog.
void handleTitleBlockDrop(QDropEvent *)
DiagramView::handleTitleBlockDrop Handle the dropEvent that contain data of a titleblock.
bool m_first_activation
Definition: diagramview.h:58
void setEventInterface(DiagramEventInterface *event_interface)
Diagram::setEventInterface Set event_interface has current interface. Diagram become the ownership of...
Definition: diagram.cpp:455
void fromString(const QString &)
void mouseMoveEvent(QMouseEvent *) override
DiagramView::mouseMoveEvent Manage the event move mouse.
void dropEvent(QDropEvent *) override
bool event(QEvent *) override
DiagramView::event Manage the event on this diagram view. -At first activation (QEvent::WindowActivat...
void showDiagram(Diagram *)
Signal emmitted when diagram must be show.
QList< QAction * > m_separators
Definition: diagramview.h:59
QString integrateTitleBlockTemplate(const TitleBlockTemplateLocation &, MoveTitleBlockTemplatesHandler *handler)
QList< QAction * > contextMenuActions() const
DiagramView::contextMenuActions.
virtual bool isCtrlShifting(QInputEvent *)
virtual bool wheelEvent(QWheelEvent *event)
bool mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &) const
void pasteHere()
virtual bool switchToSelectionModeIfNeeded(QInputEvent *e)
bool gestures() const
DiagramView::gestures.
bool fromXml(QDomDocument &, QPointF=QPointF(), bool=true, DiagramContent *=nullptr)
Definition: diagram.cpp:804
void selectAll()
void freeRubberBandChanged(QPolygonF polygon)
QRectF viewedSceneRect() const
The DiagramEventAddElement class This diagram event add a new element, for each left click button at ...
void setVisualisationMode()
QETProject * parentProject() const
QUndoStack & undoStack()
Definition: diagram.h:337
virtual bool mousePressEvent(QMouseEvent *event)
bool hasTextEditing()
DiagramContent::hasTextEditing.
void resetConductors()
static bool clipboardMayContainDiagram()
Definition: diagram.cpp:1706
void editElementRequired(const ElementsLocation &)
Signal emitted when users wish to edit an element from the diagram.
DVEventInterface * m_event_interface
Definition: diagramview.h:52
void loadCndFolioSeq()
Diagram::loadCndFolioSeq This class loads all conductor folio sequential variables related to the cur...
Definition: diagram.cpp:1415
bool m_free_rubberbanding
Definition: diagramview.h:61
void loadElmtFolioSeq()
Diagram::loadElmtFolioSeq This class loads all folio sequential variables related to the current auto...
Definition: diagram.cpp:1357
bool m_fresh_focus_in
Definition: diagramview.h:57
BorderTitleBlock border_and_titleblock
Diagram dimensions and title block.
Definition: diagram.h:74
QETProject * project() const
Definition: diagram.cpp:1716
void mouseReleaseEvent(QMouseEvent *) override
DiagramView::mouseReleaseEvent Manage event release click mouse.
QList< Conductor * > conductors(int=AnyConductor) const
DiagramContent::conductors.
void dragMoveEvent(QDragMoveEvent *) override
void editDiagramProperties()
DiagramView::editDiagramProperties Edit the properties of the viewed digram.
virtual bool selectedItemHasFocus()
int count(int=All) const
DiagramContent::count.
QString template_name
Name of the template used to render the title block - an empty string means "the default template pro...
QPolygonF m_free_rubberband
Definition: diagramview.h:60
Diagram * m_diagram
Definition: diagramview.h:51
void paste(const QPointF &=QPointF(), QClipboard::Mode=QClipboard::Clipboard)
DiagramView::paste Import the element stored in the clipboard to the diagram.
void selectNothing()