QElectroTech  0.70
elementscene.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 "elementscene.h"
19 #include "qetelementeditor.h"
21 #include <cmath>
22 #include "partline.h"
23 #include "partrectangle.h"
24 #include "partellipse.h"
25 #include "partpolygon.h"
26 #include "partterminal.h"
27 #include "parttext.h"
28 #include "partarc.h"
29 #include "editorcommands.h"
30 #include "elementcontent.h"
32 #include "eseventinterface.h"
34 #include "partdynamictextfield.h"
36 #include "namelistdialog.h"
37 #include "namelistwidget.h"
38 
39 #include <algorithm>
40 #include <QKeyEvent>
41 
47 ElementScene::ElementScene(QETElementEditor *editor, QObject *parent) :
48  QGraphicsScene(parent),
49  m_elmt_type("simple"),
50  m_qgi_manager(this),
51  m_element_editor(editor)
52 {
53  setItemIndexMethod(QGraphicsScene::NoIndex);
54  //Set to no index, because they can be the source of the crash with conductor and shape ghost.
55  //https://forum.qt.io/topic/71316/qgraphicsscenefinditembsptreevisitor-visit-crashes-due-to-an-obsolete-paintevent-after-qgraphicsscene-removeitem
56  //https://stackoverflow.com/questions/38458830/crash-after-qgraphicssceneremoveitem-with-custom-item-class
57  //http://www.qtcentre.org/archive/index.php/t-33730.html
58  //http://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063
59 
61  setItemIndexMethod(NoIndex);
62  setGrid(1, 1);
63  initPasteArea();
64  m_undo_stack.setClean();
65  m_decorator_lock = new QMutex(QMutex::NonRecursive);
66  connect(&m_undo_stack, SIGNAL(indexChanged(int)), this, SLOT(managePrimitivesGroups()));
67  connect(this, SIGNAL(selectionChanged()), this, SLOT(managePrimitivesGroups()));
68 }
69 
74 {
75  //Disconnect to avoid crash, see bug report N° 122.
76  disconnect(&m_undo_stack, SIGNAL(indexChanged(int)), this, SLOT(managePrimitivesGroups()));
77  delete m_decorator_lock;
78 
80  delete m_event_interface;
81 
82  if (m_decorator)
83  delete m_decorator;
84 }
85 
90 void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
91  if (m_event_interface) {
93  if (m_event_interface->isFinish()) {
94  delete m_event_interface; m_event_interface = nullptr;
95  emit(partsAdded());
96  }
97  return;
98  }
99  }
100 
101  QPointF event_pos = e -> scenePos();
102  if (!(e -> modifiers() & Qt::ControlModifier))
103  event_pos = snapToGrid(event_pos);
104 
105  if (m_behavior == PasteArea) {
106  QRectF current_rect(m_paste_area -> rect());
107  current_rect.moveCenter(event_pos);
108  m_paste_area -> setRect(current_rect);
109  return;
110  }
111 
112  QGraphicsScene::mouseMoveEvent(e);
113 }
114 
119 void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
120  if (m_event_interface) {
122  if (m_event_interface->isFinish()) {
123  delete m_event_interface; m_event_interface = nullptr;
124  emit(partsAdded());
125  }
126  return;
127  }
128  }
129 
130  QGraphicsScene::mousePressEvent(e);
131 }
132 
137 void ElementScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
138  if (m_event_interface) {
140  if (m_event_interface->isFinish()) {
141  delete m_event_interface; m_event_interface = nullptr;
142  emit(partsAdded());
143  }
144  return;
145  }
146  }
147 
148  if (m_behavior == PasteArea) {
150  removeItem(m_paste_area);
152  m_behavior = Normal;
153  return;
154  }
155 
156  QGraphicsScene::mouseReleaseEvent(e);
157 }
158 
163 void ElementScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
164  if (m_event_interface) {
166  if (m_event_interface->isFinish()) {
167  delete m_event_interface; m_event_interface = nullptr;
168  emit(partsAdded());
169  }
170  return;
171  }
172  }
173 
174  QGraphicsScene::mouseDoubleClickEvent(event);
175 }
176 
182 void ElementScene::keyPressEvent(QKeyEvent *event)
183 {
184  if (m_event_interface)
185  {
186  if (m_event_interface -> keyPressEvent(event))
187  {
189  {
190  delete m_event_interface; m_event_interface = nullptr;
191  emit(partsAdded());
192  }
193  return;
194  }
195  }
196 
197  if(selectedItems().size() == 1)
198  {
199  if(selectedItems().first()->type() == PartText::Type)
200  {
201  PartText *t = static_cast<PartText *>(selectedItems().first());
202  if(t->textInteractionFlags() & Qt::TextEditorInteraction)
203  {
204  QGraphicsScene::keyPressEvent(event);
205  return;
206  }
207  }
208 
209  QGraphicsObject *qgo = selectedItems().first()->toGraphicsObject();
210  if(qgo)
211  {
212  QPointF original_pos = qgo->pos();
213  QPointF p = qgo->pos();
214  int k = event->key();
215  if(k == Qt::Key_Right)
216  p.rx() += 1;
217  else if (k == Qt::Key_Left)
218  p.rx() -= 1;
219  else if (k == Qt::Key_Up)
220  p.ry() -= 1;
221  else if (k == Qt::Key_Down)
222  p.ry() += 1;
223 
224  qgo->setPos(p);
225  QPropertyUndoCommand *undo = new QPropertyUndoCommand(qgo, "pos", QVariant(original_pos), QVariant(p));
226  undo->setText(tr("Déplacer une primitive"));
227  undo->enableAnimation();
228  undoStack().push(undo);
229  event->accept();
230  return;
231  }
232  }
233 
234  QGraphicsScene::keyPressEvent(event);
235 }
236 
242 void ElementScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
243 {
244  QGraphicsScene::contextMenuEvent(event);
245  if(event->isAccepted())
246  return;
247 
249  m_element_editor -> contextMenu(event->screenPos());
250 }
251 
257 void ElementScene::drawForeground(QPainter *p, const QRectF &rect) {
258  Q_UNUSED(rect);
259  p -> save();
260 
261  // desactive tout antialiasing, sauf pour le texte
262  p -> setRenderHint(QPainter::Antialiasing, false);
263  p -> setRenderHint(QPainter::TextAntialiasing, true);
264  p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
265 
266  QPen pen(Qt::red);
267  pen.setCosmetic(true);
268  p -> setPen(pen);
269  p -> setBrush(Qt::NoBrush);
270  p -> drawLine(-20, 0, 20, 0);
271  p -> drawLine(0, -20, 0, 20);
272  p -> restore();
273 }
274 
281 {
282  if (m_event_interface)
283  {
284  delete m_event_interface;
285  //We must to re-init because previous interface
286  //Reset his own init when deleted
287  event_interface->init();
288  }
289  m_event_interface = event_interface;
290 }
291 
297 {
299  {
300  delete m_event_interface;
301  m_event_interface = nullptr;
302  }
303 }
304 
311  m_behavior = b;
312 }
313 
315  return m_behavior;
316 }
317 
321 int ElementScene::xGrid() const {
322  return(m_x_grid);
323 }
324 
328 int ElementScene::yGrid() const {
329  return(m_y_grid);
330 }
331 
336 void ElementScene::setGrid(int x_g, int y_g) {
337  m_x_grid = x_g ? x_g : 1;
338  m_y_grid = y_g ? y_g : 1;
339 }
340 
348 const QDomDocument ElementScene::toXml(bool all_parts)
349 {
350  QRectF size= elementSceneGeometricRect();
351 
352  //if the element doesn't contains the origin point of the scene
353  //we move the element to the origin for solve this default before saving
354  if (!size.contains(0,0) && all_parts)
355  {
357  //recalcul the size after movement
359  }
360 
361  //define the size of the element by the upper multiple of 10
362  int upwidth = ((qRound(size.width())/10)*10)+10;
363  if ((qRound(size.width())%10) > 6) upwidth+=10;
364 
365  int upheight = ((qRound(size.height())/10)*10)+10;
366  if ((qRound(size.height())%10) > 6) upheight+=10;
367 
368  //the margin between the real size of the element and the rectangle that delimits
369  int xmargin = qRound(upwidth - size.width());
370  int ymargin = qRound(upheight - size.height());
371 
372  // document XML
373  QDomDocument xml_document;
374 
375  //Root of xml document
376  QDomElement root = xml_document.createElement("definition");
377  root.setAttribute("type", "element");
378  root.setAttribute("width", QString("%1").arg(upwidth));
379  root.setAttribute("height", QString("%1").arg(upheight));
380  root.setAttribute("hotspot_x", QString("%1").arg(-(qRound(size.x() - (xmargin/2)))));
381  root.setAttribute("hotspot_y", QString("%1").arg(-(qRound(size.y() - (ymargin/2)))));
382  root.setAttribute("orientation", "dyyy"); //we keep the orientation for compatibility with previous version of qet
383  root.setAttribute("version", QET::version);
384  root.setAttribute("link_type", m_elmt_type);
385 
386  //Uuid used to compare two elements
387  QDomElement uuid = xml_document.createElement("uuid");
388  uuid.setAttribute("uuid", QUuid::createUuid().toString());
389  root.appendChild(uuid);
390 
391  //names of element
392  root.appendChild(m_names_list.toXml(xml_document));
393 
394  if (m_elmt_type == "slave" || m_elmt_type == "master")
395  {
396  QDomElement kindInfo = xml_document.createElement("kindInformations");
397  m_elmt_kindInfo.toXml(kindInfo, "kindInformation");
398  root.appendChild(kindInfo);
399  }
400 
401  if(m_elmt_type == "simple" || m_elmt_type == "master" || m_elmt_type == "terminal")
402  {
403  QDomElement element_info = xml_document.createElement("elementInformations");
404  m_elmt_information.toXml(element_info, "elementInformation");
405  root.appendChild(element_info);
406  }
407 
408  //complementary information about the element
409  QDomElement informations_element = xml_document.createElement("informations");
410  root.appendChild(informations_element);
411  informations_element.appendChild(xml_document.createTextNode(informations()));
412 
413  QDomElement description = xml_document.createElement("description");
414 
415  //the graphic description of the element
416  foreach(QGraphicsItem *qgi, zItems())
417  {
418  //If the export concerns only the selection, the not selected part is ignored
419  if (!all_parts && !qgi -> isSelected()) continue;
420  if (CustomElementPart *ce = dynamic_cast<CustomElementPart *>(qgi))
421  {
422  if (ce -> isUseless()) continue;
423  description.appendChild(ce -> toXml(xml_document));
424  }
425  }
426  root.appendChild(description);
427 
428  xml_document.appendChild(root);
429  return(xml_document);
430 }
431 
436 QRectF ElementScene::boundingRectFromXml(const QDomDocument &xml_document) {
437  // charge les parties depuis le document XML
438  ElementContent loaded_content = loadContent(xml_document);
439  if (loaded_content.isEmpty()) return(QRectF());
440 
441  // calcule le boundingRect
442  QRectF bounding_rect = elementContentBoundingRect(loaded_content);
443 
444  // detruit les parties chargees
445  qDeleteAll(loaded_content);
446 
447  return(bounding_rect);
448 }
449 
463 void ElementScene::fromXml(const QDomDocument &xml_document, const QPointF &position, bool consider_informations, ElementContent *content_ptr)
464 {
465  bool state = true;
466 
467  //Consider the informations of the element
468  if (consider_informations) {
469  state = applyInformations(xml_document);
470  }
471 
472  if (state)
473  {
474  ElementContent loaded_content = loadContent(xml_document);
475  if (position != QPointF())
476  addContentAtPos(loaded_content, position);
477  else
478  addContent(loaded_content);
479 
480  if (content_ptr)
481  *content_ptr = loaded_content;
482  }
483 }
484 
491  QRectF esgr;
492  foreach (QGraphicsItem *qgi, items()) {
493  if (qgi->type() == ElementPrimitiveDecorator::Type) continue;
494  if (qgi->type() == QGraphicsRectItem::Type) continue;
495  if (qgi->type() == PartDynamicTextField::Type) continue;
496  if (CustomElementPart *cep = dynamic_cast <CustomElementPart*> (qgi)) {
497  esgr |= cep -> sceneGeometricRect();
498  }
499  }
500  return (esgr);
501 }
502 
508  foreach(QGraphicsItem *qgi,items()) {
509  if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
510  return(true);
511  }
512  }
513  return(false);
514 }
515 
519 QUndoStack &ElementScene::undoStack() {
520  return(m_undo_stack);
521 }
522 
527  return(m_qgi_manager);
528 }
529 
534  QString clipboard_text = QApplication::clipboard() -> text().trimmed();
535  bool may_be_element = clipboard_text.startsWith("<definition") && clipboard_text.endsWith("</definition>");
536  return(may_be_element);
537 }
538 
544 bool ElementScene::wasCopiedFromThisElement(const QString &clipboard_content) {
545  return(clipboard_content == m_last_copied);
546 }
547 
553  copy();
554  QList<QGraphicsItem *> cut_content = selectedItems();
555  clearSelection();
556  undoStack().push(new CutPartsCommand(this, cut_content));
557 }
558 
564  // accede au presse-papier
565  QClipboard *clipboard = QApplication::clipboard();
566 
567  // genere la description XML de la selection
568  QString clipboard_content = toXml(false).toString(4);
569 
570  // met la description XML dans le presse-papier
571  if (clipboard -> supportsSelection()) {
572  clipboard -> setText(clipboard_content, QClipboard::Selection);
573  }
574  clipboard -> setText(clipboard_content);
575 
576  // retient le dernier contenu copie
577  m_last_copied = clipboard_content;
578 }
579 
581  return m_element_editor;
582 }
583 
585 {
586  if(m_elmt_information != dc)
587  {
588  m_elmt_information = dc;
589  emit elementInfoChanged();
590  }
591 }
592 
599 {
600  blockSignals(true);
601 
602  //Befor clear selection, we must to remove the handlers items in @content,
603  //because if in @content there are a selected item, but also its handlers items,
604  //When item is deselected, the item delete its handlers items,
605  //then handlers in content doesn't exist anymore and cause segfault
606  QList<QGraphicsItem*> items_list;
607  for (QGraphicsItem *qgi : content)
608  {
609  if(qgi->type() != QetGraphicsHandlerItem::Type)
610  items_list << qgi;
611  }
612  clearSelection();
613 
614  foreach(QGraphicsItem *qgi, items_list)
615  qgi -> setSelected(true);
616 
617  blockSignals(false);
618  emit(selectionChanged());
619 }
620 
626  slot_select(items());
627 }
628 
635 }
636 
641  blockSignals(true);
642  foreach(QGraphicsItem *qgi, items()) qgi -> setSelected(!qgi -> isSelected());
643  blockSignals(false);
644  emit(selectionChanged());
645 }
646 
651  // verifie qu'il y a qqc de selectionne
652  QList<QGraphicsItem *> selected_items = selectedItems();
653  if (selected_items.isEmpty()) return;
654 
655  // efface tout ce qui est selectionne
656  m_undo_stack.push(new DeletePartsCommand(this, selected_items));
657 
658  // removing items does not trigger QGraphicsScene::selectionChanged()
659  emit(partsRemoved());
660  emit(selectionChanged());
661 }
662 
669  bool is_read_only = m_element_editor && m_element_editor -> isReadOnly();
670 
671  // cree un dialogue
672  QDialog dialog_author(m_element_editor);
673  dialog_author.setModal(true);
674 #ifdef Q_OS_MAC
675  dialog_author.setWindowFlags(Qt::Sheet);
676 #endif
677  dialog_author.setMinimumSize(400, 260);
678  dialog_author.setWindowTitle(tr("Éditer les informations sur l'auteur", "window title"));
679  QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_author);
680 
681  // ajoute un champ explicatif au dialogue
682  QLabel *information_label = new QLabel(tr("Vous pouvez utiliser ce champ libre pour mentionner les auteurs de l'élément, sa licence, ou tout autre renseignement que vous jugerez utile."));
683  information_label -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
684  information_label -> setWordWrap(true);
685  dialog_layout -> addWidget(information_label);
686 
687  // ajoute un QTextEdit au dialogue
688  QTextEdit *text_field = new QTextEdit();
689  text_field -> setAcceptRichText(false);
690  text_field -> setPlainText(informations());
691  text_field -> setReadOnly(is_read_only);
692  dialog_layout -> addWidget(text_field);
693 
694  // ajoute deux boutons au dialogue
695  QDialogButtonBox *dialog_buttons = new QDialogButtonBox(is_read_only ? QDialogButtonBox::Ok : QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
696  dialog_layout -> addWidget(dialog_buttons);
697  connect(dialog_buttons, SIGNAL(accepted()), &dialog_author, SLOT(accept()));
698  connect(dialog_buttons, SIGNAL(rejected()), &dialog_author, SLOT(reject()));
699 
700  // lance le dialogue
701  if (dialog_author.exec() == QDialog::Accepted && !is_read_only) {
702  QString new_infos = text_field -> toPlainText().remove(QChar(13)); // CR-less text
703  if (new_infos != informations()) {
704  undoStack().push(new ChangeInformationsCommand(this, informations(), new_infos));
705  }
706  }
707 }
708 
714 {
715  QString type = m_elmt_type;
716  DiagramContext kind_info = m_elmt_kindInfo;
718 
719  ElementPropertiesEditorWidget epew(type, kind_info, elmt_info);
720  epew.exec();
721 
722  if (type != m_elmt_type ||
723  kind_info != m_elmt_kindInfo ||
724  elmt_info != m_elmt_information)
725  undoStack().push(new ChangePropertiesCommand(this, type, kind_info, elmt_info));
726 }
727 
733 {
734  bool is_read_only = m_element_editor && m_element_editor -> isReadOnly();
735 
737 
738  dialog_.setModal(true);
739  dialog_.setMinimumSize(400, 330);
740  dialog_.setWindowTitle(tr("Éditer les noms", "window title"));
741 
742  dialog_.setInformationText(tr("Vous pouvez spécifier le nom de l'élément dans plusieurs langues."));
743 
744  NameListWidget *nlw_ = dialog_.namelistWidget();
745  nlw_->setNames(m_names_list);
746  nlw_->setReadOnly(is_read_only);
747 
748  if (dialog_.exec() == QDialog::Accepted && !is_read_only && !nlw_->isEmpty())
749  {
750  NamesList new_names = nlw_->names();
751  if (new_names != m_names_list) {
752  undoStack().push(new ChangeNamesCommand(this, m_names_list, new_names));
753  }
754  }
755 }
756 
760 QList<CustomElementPart *> ElementScene::primitives() const {
761  QList<CustomElementPart *> primitives_list;
762  foreach (QGraphicsItem *item, items()) {
763  if (CustomElementPart *primitive = dynamic_cast<CustomElementPart *>(item)) {
764  primitives_list << primitive;
765  }
766  }
767  return(primitives_list);
768 }
769 
774 QList<QGraphicsItem *> ElementScene::zItems(ItemOptions options) const {
775  // handle dummy request, i.e. when neither Selected nor NonSelected are set
776  if (!(options & ElementScene::Selected) && !(options & ElementScene::NonSelected)) {
777  return(QList<QGraphicsItem *>());
778  }
779 
780  // retrieve all items
781  QList<QGraphicsItem *> all_items_list(items());
782  QMutableListIterator<QGraphicsItem *> i(all_items_list);
783 
784  // remove unrequired items
786  bool keep_selected = options & ElementScene::Selected;
787  while (i.hasNext()) {
788  if (i.next() -> isSelected() != keep_selected) {
789  i.remove();
790  }
791  }
792  }
793 
794  QList<QGraphicsItem *> terminals;
795  QList<QGraphicsItem *> helpers;
796  for (i.toFront(); i.hasNext(); ) {
797  i.next();
798  QGraphicsItem *qgi = i.value();
799  if (
800  qgi -> type() == ElementPrimitiveDecorator::Type ||
801  qgi -> type() == QGraphicsRectItem::Type ||
802  qgi->type() == QetGraphicsHandlerItem::Type
803  ) {
804  i.remove();
805  helpers << qgi;
806  }
807  else if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
808  i.remove();
809  terminals << qgi;
810  }
811  }
812 
813  // ordonne les parties par leur zValue
814  if (options & SortByZValue) {
815  std::sort (all_items_list.begin(), all_items_list.end(), ElementScene::zValueLessThan);
816  }
817 
818  // rajoute eventuellement les bornes
819  if (options & ElementScene::IncludeTerminals) {
820  all_items_list += terminals;
821  }
822  if (options & ElementScene::IncludeHelperItems) {
823  all_items_list += helpers;
824  }
825  return(all_items_list);
826 }
827 
832  ElementContent content;
833  foreach(QGraphicsItem *qgi, zItems()) {
834  if (qgi -> isSelected()) content << qgi;
835  }
836  return(content);
837 }
838 
843 void ElementScene::getPasteArea(const QRectF &to_paste) {
844  // on le dessine sur la scene
845  m_paste_area -> setRect(to_paste);
846  addItem(m_paste_area);
847 
848  // on passe la scene en mode "recherche de zone pour copier/coller"
850 }
851 
857 {
858  clearSelection();
859  undoStack().clear();
860 
861  //We don't add handlers, because it's the role of the primitive or decorator to remove it.
862  QList<QGraphicsItem*> items_list;
863  for (QGraphicsItem *qgi : items())
864  {
865  if(qgi->type() != QetGraphicsHandlerItem::Type)
866  items_list << qgi;
867  }
868 
869  for (QGraphicsItem *qgi : items_list)
870  {
871  removeItem(qgi);
872  qgiManager().release(qgi);
873  }
874 
875  delete m_decorator;
876  m_decorator = nullptr;
877 }
878 
885  QRectF bounding_rect;
886  foreach(QGraphicsItem *qgi, content) {
887  // skip non-primitives QGraphicsItems (paste area, selection decorator)
888  if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
889  if (qgi -> type() == QGraphicsRectItem::Type) continue;
890  bounding_rect |= qgi -> sceneBoundingRect();
891  }
892  return(bounding_rect);
893 }
894 
902 bool ElementScene::applyInformations(const QDomDocument &xml_document)
903 {
904  // Root must be an element definition
905  QDomElement root = xml_document.documentElement();
906 
907  if (root.tagName() != "definition" || root.attribute("type") != "element")
908  return(false);
909 
910  //Extract info about element type
911  m_elmt_type = root.attribute("link_type", "simple");
912  m_elmt_kindInfo.fromXml(root.firstChildElement("kindInformations"), "kindInformation");
913  //Extract info of element
914  m_elmt_information.fromXml(root.firstChildElement("elementInformations"), "elementInformation");
915 
916  //Extract names of xml definition
917  m_names_list.fromXml(root);
918 
919  //extract additional informations
920  setInformations(QString());
921  for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
922  QDomElement elmt = node.toElement();
923  if (elmt.isNull()) continue;
924  if (elmt.tagName() == "informations") {
925  setInformations(elmt.text());
926  break;
927  }
928  }
929 
930  return(true);
931 }
932 
951 ElementContent ElementScene::loadContent(const QDomDocument &xml_document)
952 {
953  ElementContent loaded_parts;
954 
955  //The root is supposed to be an element definition
956  QDomElement root = xml_document.documentElement();
957 
958  if (root.tagName() != "definition" || root.attribute("type") != "element")
959  return(loaded_parts);
960 
961  //Load the graphic description of the element
962  for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling())
963  {
964  QDomElement elmts = node.toElement();
965  if (elmts.isNull())
966  continue;
967 
968  if (elmts.tagName() == "description")
969  {
970  int z = 1;
971  for (QDomNode n = node.firstChild() ; !n.isNull() ; n = n.nextSibling())
972  {
973  QDomElement qde = n.toElement();
974  if (qde.isNull())
975  continue;
976  CustomElementPart *cep = nullptr;
977  PartDynamicTextField *pdtf = nullptr;
978 
979  if (qde.tagName() == "line") cep = new PartLine (m_element_editor);
980  else if (qde.tagName() == "rect") cep = new PartRectangle(m_element_editor);
981  else if (qde.tagName() == "ellipse") cep = new PartEllipse (m_element_editor);
982  else if (qde.tagName() == "circle") cep = new PartEllipse (m_element_editor);
983  else if (qde.tagName() == "polygon") cep = new PartPolygon (m_element_editor);
984  else if (qde.tagName() == "terminal") cep = new PartTerminal (m_element_editor);
985  else if (qde.tagName() == "text") cep = new PartText (m_element_editor);
986  else if (qde.tagName() == "arc") cep = new PartArc (m_element_editor);
987  else if (qde.tagName() == "dynamic_text") cep = new PartDynamicTextField (m_element_editor);
988  //For the input (aka the old text field) we try to convert it to the new partDynamicTextField
989  else if (qde.tagName() == "input") cep = pdtf = new PartDynamicTextField(m_element_editor);
990  else continue;
991 
992  if (QGraphicsItem *qgi = dynamic_cast<QGraphicsItem *>(cep))
993  {
994  if (!qgi->zValue())
995  qgi->setZValue(z++);
996 
997  loaded_parts<<qgi;
998 
999  if(pdtf)
1000  pdtf->fromTextFieldXml(qde);
1001  else
1002  cep->fromXml(qde);
1003  }
1004  else
1005  delete cep;
1006  }
1007  }
1008  }
1009 
1010  return(loaded_parts);
1011 }
1012 
1019  foreach(QGraphicsItem *part, content) {
1020  addPrimitive(part);
1021  }
1022  return(content);
1023 }
1024 
1031 ElementContent ElementScene::addContentAtPos(const ElementContent &content, const QPointF &pos) {
1032  // calcule le boundingRect du contenu a ajouter
1033  QRectF bounding_rect = elementContentBoundingRect(content);
1034 
1035  // en deduit le decalage a appliquer aux parties pour les poser au point demander
1036  QPointF offset = pos - bounding_rect.topLeft();
1037 
1038  // ajoute les parties avec le decalage adequat
1039  foreach(QGraphicsItem *part, content) {
1040  part -> setPos(part -> pos() + offset);
1041  addPrimitive(part);
1042  }
1043  return(content);
1044 }
1045 
1050 void ElementScene::addPrimitive(QGraphicsItem *primitive) {
1051  if (!primitive) return;
1052  addItem(primitive);
1053 }
1054 
1059  m_paste_area = new QGraphicsRectItem();
1060  m_paste_area -> setZValue(1000000);
1061 
1062  QPen paste_area_pen;
1063  paste_area_pen.setStyle(Qt::DashDotLine);
1064  paste_area_pen.setColor(QColor(30, 56, 86, 255));
1065 
1066  QBrush paste_area_brush;
1067  paste_area_brush.setStyle(Qt::SolidPattern);
1068  paste_area_brush.setColor(QColor(90, 167, 255, 64));
1069 
1070  m_paste_area -> setPen(paste_area_pen);
1071  m_paste_area -> setBrush(paste_area_brush);
1072 }
1073 
1080 QPointF ElementScene::snapToGrid(QPointF point) {
1081  point.rx() = qRound(point.x() / m_x_grid) * m_x_grid;
1082  point.ry() = qRound(point.y() / m_y_grid) * m_y_grid;
1083  return point;
1084 }
1085 
1089 bool ElementScene::zValueLessThan(QGraphicsItem *item1, QGraphicsItem *item2) {
1090  return(item1-> zValue() < item2 -> zValue());
1091 }
1092 
1099  QRectF size= elementSceneGeometricRect();
1100  int center_x = qRound(size.center().x());
1101  int center_y = qRound(size.center().y());
1102 
1103  //define the movement of translation
1104  int move_x = center_x - (center_x %10);
1105  if (center_x < 0) move_x -= 10;
1106  int move_y = center_y - (center_y %10);
1107  if (center_y < 0) move_y -= 10;
1108 
1109  //move each primitive by @move
1110  foreach (QGraphicsItem *qgi, items()) {
1111  if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
1112  if (qgi -> type() == QGraphicsRectItem::Type) continue;
1113  //deselect item for disable decorator
1114  qgi -> setSelected(false);
1115  qgi -> moveBy(-(move_x), -(move_y));
1116  }
1117  emit (needZoomFit());
1118 }
1119 
1126 {
1127  //this function is not supposed to be reentrant
1128  if (!m_decorator_lock->tryLock())
1129  return;
1130 
1131  if (!m_decorator)
1132  {
1134  connect(m_decorator, SIGNAL(actionFinished(ElementEditionCommand*)), this, SLOT(stackAction(ElementEditionCommand *)));
1135  addItem(m_decorator);
1136  m_decorator -> hide();
1137  }
1138 
1139  // should we hide the decorator?
1140  QList<QGraphicsItem *> selected_items = zItems(ElementScene::Selected | ElementScene::IncludeTerminals);
1141  if (selected_items.size() <= 1)
1142  {
1143  m_decorator -> hide();
1144  }
1145  else
1146  {
1147  for(QGraphicsItem *qgi : selected_items)
1148  {
1149  //We recall set selected, then every primitive will remove there handler because there are several item selected
1150  qgi->setSelected(true);
1151  }
1152  m_decorator -> setZValue(1000000);
1153  m_decorator -> setPos(0, 0);
1154  m_decorator -> setItems(selected_items);
1155  }
1156  m_decorator_lock -> unlock();
1157 }
1158 
1163  if (command -> elementScene()) {
1164  if (command -> elementScene() != this) return;
1165  } else {
1166  command -> setElementScene(this);
1167  }
1168 
1169  if (!command -> elementView()) {
1170  foreach (QGraphicsView *view, views()) {
1171  if (ElementView *element_view = dynamic_cast<ElementView *>(view)) {
1172  command -> setElementView(element_view);
1173  break;
1174  }
1175  }
1176  }
1177 
1178  undoStack().push(command);
1179 }
void setEventInterface(ESEventInterface *event_interface)
ElementScene::setEventInterface Set a new event interface.
The QPropertyUndoCommand class This undo command manage QProperty of a QObject. This undo command can...
virtual void fromXml(const QDomDocument &, const QPointF &=QPointF(), bool=true, ElementContent *=nullptr)
void mousePressEvent(QGraphicsSceneMouseEvent *) override
ElementScene::mousePressEvent.
void setInformationText(const QString &text)
void drawForeground(QPainter *, const QRectF &) override
virtual void fromXml(const QDomElement &)=0
DiagramContext m_elmt_kindInfo
element type
Definition: elementscene.h:69
void slot_deselectAll()
ElementScene::slot_deselectAll deselect all item.
void elementInfoChanged()
NamesList m_names_list
Definition: elementscene.h:66
void setNames(const NamesList &name_list)
NameListWidget::setNames Set the current names of this dialog from .
void setBehavior(ElementScene::Behavior)
ElementScene::setBehavior Modifie the current behavior of this scene.
void fromXml(const QDomElement &, const QHash< QString, QString > &=QHash< QString, QString >())
Definition: nameslist.cpp:120
void slot_editAuthorInformations()
QMutex * m_decorator_lock
Definition: elementscene.h:144
void partsRemoved()
Signal emitted after one or several parts were removed.
virtual QRectF boundingRectFromXml(const QDomDocument &)
void slot_selectAll()
ElementScene::slot_selectAll Select all items.
QGIManager m_qgi_manager
element kind info
Definition: elementscene.h:71
QIcon PartRectangle
Definition: qeticons.cpp:132
void init()
ESEventInterface::init Init this event interface.
void slot_editProperties()
ElementScene::slot_editProperties Open dialog to edit the element properties.
QString informations() const
Definition: elementscene.h:193
NamesList names() const
NameListWidget::names.
bool wasCopiedFromThisElement(const QString &)
Behavior m_behavior
Definition: elementscene.h:75
NameListWidget * namelistWidget() const
NameListDialog::namelistWidget.
void release(QGraphicsItem *)
Definition: qgimanager.cpp:60
void stackAction(ElementEditionCommand *)
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
ElementScene::mouseReleaseEvent.
static bool zValueLessThan(QGraphicsItem *, QGraphicsItem *)
bool isEmpty() const
NameListWidget::isEmpty.
The PartDynamicTextField class This class represents an editable dynamic text field which may be used...
void fromTextFieldXml(const QDomElement &dom_element)
PartDynamicTextField::fromTextFieldXml Setup this text from the xml definition of a text field (The x...
ElementContent loadContent(const QDomDocument &)
ElementScene::loadContent.
virtual const QDomDocument toXml(bool=true)
ElementScene::toXml Export this element as a xml file.
QDomElement toXml(QDomDocument &, const QHash< QString, QString > &=QHash< QString, QString >()) const
Definition: nameslist.cpp:143
virtual void setGrid(int, int)
void enableAnimation(bool animate=true)
QPropertyUndoCommand::enableAnimation True to enable animation.
void addPrimitive(QGraphicsItem *)
The ElementPropertiesEditorWidget class This class provide a dialog for edit various property of elem...
void slot_select(const ElementContent &)
ElementScene::slot_select Select the item in content, every others items in the scene are deselected...
QGIManager & qgiManager()
QRectF elementContentBoundingRect(const ElementContent &) const
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
ElementScene::mouseDoubleClickEvent.
~ElementScene() override
ElementScene::~ElementScene.
ESEventInterface * m_event_interface
Definition: elementscene.h:74
void pasteAreaDefined(const QRectF &)
Signal emitted when users have defined the copy/paste area.
QETElementEditor * editor() const
const QString version
QElectroTech version (as string, used to mark projects and elements XML documents) ...
Definition: qet.h:30
void managePrimitivesGroups()
ElementScene::managePrimitivesGroups Ensure the decorator is adequately shown, hidden or updated so i...
The NameListDialog class Provide a dialog for let user define localized string;.
QUndoStack & undoStack()
void needZoomFit()
Signal emitted when need zoomFit.
The ElementEditionCommand class ElementEditionCommand is the base class for all commands classes invo...
void fromXml(const QDomElement &, const QString &="property")
QString m_last_copied
Definition: elementscene.h:81
void partsAdded()
Signal emitted after one or several parts were added.
ElementScene(QETElementEditor *, QObject *=nullptr)
virtual void reset()
ElementScene::reset Remove all QGraphicsItems in the scene and clear the undo stack.
static bool clipboardMayContainElement()
QIcon tr
Definition: qeticons.cpp:204
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
ElementScene::contextMenuEvent Display the context menu event, only if behavior are Normal...
void keyPressEvent(QKeyEvent *event) override
ElementScene::keyPressEvent manage key press event.
virtual int yGrid() const
friend class ChangePropertiesCommand
Definition: elementscene.h:40
bool containsTerminals() const
void clearEventInterface()
ElementScene::clearEventInterface Clear the current event interface.
bool applyInformations(const QDomDocument &)
The PartPolygon class This class represents a polygon primitive which may be used to compose the draw...
Definition: partpolygon.h:33
virtual ElementContent selectedContent() const
virtual QList< CustomElementPart * > primitives() const
QString m_elmt_type
Extra informations.
Definition: elementscene.h:68
void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
ElementScene::mouseMoveEvent.
void toXml(QDomElement &, const QString &="property") const
void slot_delete()
DiagramContext m_elmt_information
Definition: elementscene.h:69
virtual QList< QGraphicsItem * > zItems(ItemOptions options=ItemOptions(SortByZValue|IncludeTerminals|SelectedOrNot)) const
QRectF m_defined_paste_area
Definition: elementscene.h:79
void centerElementToOrigine()
ElementScene::centerElementToOrigine try to center better is possible the element to the scene (the c...
virtual void getPasteArea(const QRectF &)
virtual bool isFinish() const
QETElementEditor * m_element_editor
Definition: elementscene.h:76
ElementContent addContentAtPos(const ElementContent &, const QPointF &)
The PartArc class This class represents an elliptical arc primitive which may be used to compose the ...
Definition: partarc.h:31
QIcon Cancel
Definition: qeticons.cpp:34
virtual int xGrid() const
void setReadOnly(bool ro)
NameListWidget::setReadOnly Set this dialog to read only or not.
QList< QGraphicsItem * > ElementContent
The PartEllipse class This class represents an ellipse primitive which may be used to compose the dra...
Definition: partellipse.h:30
void initPasteArea()
void setInformations(const QString &)
Definition: elementscene.h:200
QPointF snapToGrid(QPointF point)
ElementPrimitiveDecorator * m_decorator
Decorator item displayed when at least one item is selected.
Definition: elementscene.h:84
QRectF elementSceneGeometricRect() const
void slot_editNames()
ElementScene::slot_editNames Launch a dialog for edit the names of the edited element.
ElementScene::Behavior behavior() const
void setElementInfo(const DiagramContext &dc)
QGraphicsRectItem * m_paste_area
Definition: elementscene.h:78
ElementContent addContent(const ElementContent &)
QUndoStack m_undo_stack
Definition: elementscene.h:72
The NameListWidget class Provide a widget for let user define localized string;.
void slot_invertSelection()