QElectroTech  0.70
conductor.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 <QtDebug>
19 #include "conductor.h"
20 #include "conductorsegment.h"
22 #include "conductortextitem.h"
23 #include "element.h"
24 #include "diagram.h"
25 #include "diagramcommands.h"
26 #include "qetdiagrameditor.h"
27 #include "terminal.h"
32 
33 #define PR(x) qDebug() << #x " = " << x;
34 
36 QPen Conductor::conductor_pen = QPen();
37 QBrush Conductor::conductor_brush = QBrush();
38 
39 
41 {
42  friend class Conductor;
43 
44  static void loadSequential(const QDomElement &dom_element, const QString& seq, QStringList* list)
45  {
46  int i = 0;
47  while (!dom_element.attribute(seq + QString::number(i+1)).isEmpty())
48  {
49  list->append(dom_element.attribute(seq + QString::number(i+1)));
50  i++;
51  }
52  }
53 
54  static void loadSequential(const QDomElement &dom_element, Conductor *conductor)
55  {
57 
58  loadSequential(dom_element,"sequ_",&sn.unit);
59  loadSequential(dom_element,"sequf_",&sn.unit_folio);
60  loadSequential(dom_element,"seqt_",&sn.ten);
61  loadSequential(dom_element,"seqtf_",&sn.ten_folio);
62  loadSequential(dom_element,"seqh_",&sn.hundred);
63  loadSequential(dom_element,"seqhf_",&sn.hundred_folio);
64 
65  conductor->rSequenceNum() = sn;
66  }
67 };
68 
76  terminal1(p1),
77  terminal2(p2),
78  m_mouse_over(false),
79  m_text_item(nullptr),
80  segments(nullptr),
81  m_moving_segment(false),
82  modified_path(false),
83  has_to_save_profile(false),
85 {
86  //set Zvalue at 11 to be upper than the DiagramImageItem and element
87  setZValue(11);
88  m_previous_z_value = zValue();
89 
90  //Add this conductor to the list of conductor of each of the two terminal
91  bool ajout_p1 = terminal1 -> addConductor(this);
92  bool ajout_p2 = terminal2 -> addConductor(this);
93  //m_valid become false if the conductor can't be added to terminal (conductor already exist)
94  m_valid = (!ajout_p1 || !ajout_p2) ? false : true;
95 
96  //Default attribut for paint a conductor
98  {
99  conductor_pen.setJoinStyle(Qt::MiterJoin);
100  conductor_pen.setCapStyle(Qt::SquareCap);
101  conductor_pen.setColor(Qt::black);
102  conductor_pen.setStyle(Qt::SolidLine);
103  conductor_pen.setWidthF(1.0);
104  conductor_brush.setColor(Qt::white);
105  conductor_brush.setStyle(Qt::NoBrush);
107  }
108 
109  //By default, the 4 profils are nuls -> we must to use priv_calculeConductor
110  conductor_profiles.insert(Qt::TopLeftCorner, ConductorProfile());
111  conductor_profiles.insert(Qt::TopRightCorner, ConductorProfile());
112  conductor_profiles.insert(Qt::BottomLeftCorner, ConductorProfile());
113  conductor_profiles.insert(Qt::BottomRightCorner, ConductorProfile());
114 
115  //Generate the path of this conductor.
116  generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
117  setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsScenePositionChanges);
118  setAcceptHoverEvents(true);
119 
120  // Add the text field
123 
124  //Set the default conductor properties.
125  if (p1->diagram())
127  else if (p2->diagram())
129 }
130 
136 {
137  removeHandler();
138  terminal1->removeConductor(this);
139  terminal2->removeConductor(this);
140  deleteSegments();
141 }
142 
148 bool Conductor::isValid() const {
149  return m_valid;
150 }
151 
159 void Conductor::updatePath(const QRectF &rect) {
160  QPointF p1, p2;
161  p1 = terminal1 -> dockConductor();
162  p2 = terminal2 -> dockConductor();
163  if (segmentsCount() && !conductor_profiles[currentPathType()].isNull())
164  updateConductorPath(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
165  else
166  generateConductorPath(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
168  QGraphicsObject::update(rect);
169 }
170 
176 {
177  QPainterPath path;
178 
179  if (segments == nullptr)
180  setPath(path);
181 
182  //Start the path
183  path.moveTo(segments -> firstPoint());
184  //Each segments
185  ConductorSegment *segment = segments;
186  while(segment -> hasNextSegment()) {
187  path.lineTo(segment -> secondPoint());
188  segment = segment -> nextSegment();
189  }
190  //Finish the path
191  path.lineTo(segment -> secondPoint());
192 
193  setPath(path);
194 
195  //If conductor is selected and he's not being modified
196  //we update the position of the handlers
197  if (isSelected() && !m_moving_segment)
198  {
199  if(handlerPoints().size() == m_handler_vector.size())
201  else
202  {
203  removeHandler();
204  addHandler();
205  }
206  }
207 }
208 
216 void Conductor::updateConductorPath(const QPointF &p1, Qet::Orientation o1, const QPointF &p2, Qet::Orientation o2) {
217  Q_UNUSED(o1);
218  Q_UNUSED(o2);
219 
220  ConductorProfile &conductor_profile = conductor_profiles[currentPathType()];
221 
222  Q_ASSERT_X(conductor_profile.segmentsCount(QET::Both) > 1, "Conductor::priv_modifieConductor", "pas de points a modifier");
223  Q_ASSERT_X(!conductor_profile.isNull(), "Conductor::priv_modifieConductor", "pas de profil utilisable");
224 
225  // recupere les coordonnees fournies des bornes
226  QPointF new_p1 = mapFromScene(p1);
227  QPointF new_p2 = mapFromScene(p2);
228  QRectF new_rect = QRectF(new_p1, new_p2);
229 
230  // recupere la largeur et la hauteur du profil
231  qreal profile_width = conductor_profile.width();
232  qreal profile_height = conductor_profile.height();
233 
234  // calcule les differences verticales et horizontales a appliquer
235  qreal h_diff = (qAbs(new_rect.width()) - qAbs(profile_width) ) * getSign(profile_width);
236  qreal v_diff = (qAbs(new_rect.height()) - qAbs(profile_height)) * getSign(profile_height);
237 
238  // applique les differences aux segments
239  QHash<ConductorSegmentProfile *, qreal> segments_lengths;
240  segments_lengths.unite(shareOffsetBetweenSegments(h_diff, conductor_profile.horizontalSegments()));
241  segments_lengths.unite(shareOffsetBetweenSegments(v_diff, conductor_profile.verticalSegments()));
242 
243  // en deduit egalement les coefficients d'inversion (-1 pour une inversion, +1 pour conserver le meme sens)
244  int horiz_coeff = getCoeff(new_rect.width(), profile_width);
245  int verti_coeff = getCoeff(new_rect.height(), profile_height);
246 
247  // genere les nouveaux points
248  QList<QPointF> points;
249  points << new_p1;
250  int limit = conductor_profile.segments.count() - 1;
251  for (int i = 0 ; i < limit ; ++ i) {
252  // dernier point
253  QPointF previous_point = points.last();
254 
255  // profil de segment de conducteur en cours
256  ConductorSegmentProfile *csp = conductor_profile.segments.at(i);
257 
258  // coefficient et offset a utiliser pour ce point
259  qreal coeff = csp -> isHorizontal ? horiz_coeff : verti_coeff;
260  qreal offset_applied = segments_lengths[csp];
261 
262  // applique l'offset et le coeff au point
263  if (csp -> isHorizontal) {
264  points << QPointF (
265  previous_point.x() + (coeff * offset_applied),
266  previous_point.y()
267  );
268  } else {
269  points << QPointF (
270  previous_point.x(),
271  previous_point.y() + (coeff * offset_applied)
272  );
273  }
274  }
275  points << new_p2;
276  pointsToSegments(points);
277  segmentsToPath();
278 }
279 
285 QHash<ConductorSegmentProfile *, qreal> Conductor::shareOffsetBetweenSegments(
286  const qreal &offset,
287  const QList<ConductorSegmentProfile *> &segments_list,
288  const qreal &precision
289 ) const {
290  // construit le QHash qui sera retourne
291  QHash<ConductorSegmentProfile *, qreal> segments_hash;
292  foreach(ConductorSegmentProfile *csp, segments_list) {
293  segments_hash.insert(csp, csp -> length);
294  }
295 
296  // memorise le signe de la longueur de chaque segement
297  QHash<ConductorSegmentProfile *, int> segments_signs;
298  foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
299  segments_signs.insert(csp, getSign(csp -> length));
300  }
301 
302  //qDebug() << "repartition d'un offset de" << offset << "px sur" << segments_list.count() << "segments";
303 
304  // repartit l'offset sur les segments
305  qreal remaining_offset = offset;
306  while (remaining_offset > precision || remaining_offset < -precision) {
307  // recupere le nombre de segments differents ayant une longueur non nulle
308  uint segments_count = 0;
309  foreach(ConductorSegmentProfile *csp, segments_hash.keys()) if (segments_hash[csp]) ++ segments_count;
310  //qDebug() << " remaining_offset =" << remaining_offset;
311  qreal local_offset = remaining_offset / segments_count;
312  //qDebug() << " repartition d'un offset local de" << local_offset << "px sur" << segments_count << "segments";
313  remaining_offset = 0.0;
314  foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
315  // ignore les segments de longueur nulle
316  if (!segments_hash[csp]) continue;
317  // applique l'offset au segment
318  //qreal segment_old_length = segments_hash[csp];
319  segments_hash[csp] += local_offset;
320 
321  // (la longueur du segment change de signe) <=> (le segment n'a pu absorbe tout l'offset)
322  if (segments_signs[csp] != getSign(segments_hash[csp])) {
323 
324  // on remet le trop-plein dans la reserve d'offset
325  remaining_offset += qAbs(segments_hash[csp]) * getSign(local_offset);
326  //qDebug() << " trop-plein de" << qAbs(segments_hash[csp]) * getSign(local_offset) << "remaining_offset =" << remaining_offset;
327  segments_hash[csp] = 0.0;
328  } else {
329  //qDebug() << " offset local de" << local_offset << "accepte";
330  }
331  }
332  }
333 
334  return(segments_hash);
335 }
336 
344 void Conductor::generateConductorPath(const QPointF &p1, Qet::Orientation o1, const QPointF &p2, Qet::Orientation o2) {
345  QPointF sp1, sp2, depart, newp1, newp2, arrivee, depart0, arrivee0;
346  Qet::Orientation ori_depart, ori_arrivee;
347 
348  // s'assure qu'il n'y a ni points
349  QList<QPointF> points;
350 
351  // mappe les points par rapport a la scene
352  sp1 = mapFromScene(p1);
353  sp2 = mapFromScene(p2);
354 
355  // prolonge les bornes
356  newp1 = extendTerminal(sp1, o1);
357  newp2 = extendTerminal(sp2, o2);
358 
359  // distingue le depart de l'arrivee : le trajet se fait toujours de gauche a droite (apres prolongation)
360  if (newp1.x() <= newp2.x()) {
361  depart = newp1;
362  arrivee = newp2;
363  depart0 = sp1;
364  arrivee0 = sp2;
365  ori_depart = o1;
366  ori_arrivee = o2;
367  } else {
368  depart = newp2;
369  arrivee = newp1;
370  depart0 = sp2;
371  arrivee0 = sp1;
372  ori_depart = o2;
373  ori_arrivee = o1;
374  }
375 
376  // debut du trajet
377  points << depart0;
378 
379  // prolongement de la borne de depart
380  points << depart;
381 
382  // commence le vrai trajet
383  if (depart.y() < arrivee.y()) {
384  // trajet descendant
385  if ((ori_depart == Qet::North && (ori_arrivee == Qet::South || ori_arrivee == Qet::West)) || (ori_depart == Qet::East && ori_arrivee == Qet::West)) {
386  // cas "3"
387  int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
388  while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
389  points << QPointF(ligne_inter_x, depart.y());
390  points << QPointF(ligne_inter_x, arrivee.y());
391  } else if ((ori_depart == Qet::South && (ori_arrivee == Qet::North || ori_arrivee == Qet::East)) || (ori_depart == Qet::West && ori_arrivee == Qet::East)) {
392  // cas "4"
393  int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
394  while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
395  points << QPointF(depart.x(), ligne_inter_y);
396  points << QPointF(arrivee.x(), ligne_inter_y);
397  } else if ((ori_depart == Qet::North || ori_depart == Qet::East) && (ori_arrivee == Qet::North || ori_arrivee == Qet::East)) {
398  points << QPointF(arrivee.x(), depart.y()); // cas "2"
399  } else {
400  points << QPointF(depart.x(), arrivee.y()); // cas "1"
401  }
402  } else {
403  // trajet montant
404  if ((ori_depart == Qet::West && (ori_arrivee == Qet::East || ori_arrivee == Qet::South)) || (ori_depart == Qet::North && ori_arrivee == Qet::South)) {
405  // cas "3"
406  int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
407  while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
408  points << QPointF(depart.x(), ligne_inter_y);
409  points << QPointF(arrivee.x(), ligne_inter_y);
410  } else if ((ori_depart == Qet::East && (ori_arrivee == Qet::West || ori_arrivee == Qet::North)) || (ori_depart == Qet::South && ori_arrivee == Qet::North)) {
411  // cas "4"
412  int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
413  while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
414  points << QPointF(ligne_inter_x, depart.y());
415  points << QPointF(ligne_inter_x, arrivee.y());
416  } else if ((ori_depart == Qet::West || ori_depart == Qet::North) && (ori_arrivee == Qet::West || ori_arrivee == Qet::North)) {
417  points << QPointF(depart.x(), arrivee.y()); // cas "2"
418  } else {
419  points << QPointF(arrivee.x(), depart.y()); // cas "1"
420  }
421  }
422 
423  // fin du vrai trajet
424  points << arrivee;
425 
426  // prolongement de la borne d'arrivee
427  points << arrivee0;
428 
429  // inverse eventuellement l'ordre des points afin que le trajet soit exprime de la borne 1 vers la borne 2
430  if (newp1.x() > newp2.x()) {
431  QList<QPointF> points2;
432  for (int i = points.size() - 1 ; i >= 0 ; -- i) points2 << points.at(i);
433  points = points2;
434  }
435 
436  pointsToSegments(points);
437  segmentsToPath();
438 }
439 
447 QPointF Conductor::extendTerminal(const QPointF &terminal, Qet::Orientation terminal_orientation, qreal ext_size) {
448  QPointF extended_terminal;
449  switch(terminal_orientation) {
450  case Qet::North:
451  extended_terminal = QPointF(terminal.x(), terminal.y() - ext_size);
452  break;
453  case Qet::East:
454  extended_terminal = QPointF(terminal.x() + ext_size, terminal.y());
455  break;
456  case Qet::South:
457  extended_terminal = QPointF(terminal.x(), terminal.y() + ext_size);
458  break;
459  case Qet::West:
460  extended_terminal = QPointF(terminal.x() - ext_size, terminal.y());
461  break;
462  default: extended_terminal = terminal;
463  }
464  return(extended_terminal);
465 }
466 
473 void Conductor::paint(QPainter *qp, const QStyleOptionGraphicsItem *options, QWidget *qw)
474 {
475  Q_UNUSED(qw);
476  qp -> save();
477  qp -> setRenderHint(QPainter::Antialiasing, false);
478 
479  // Set the color of conductor
480  QColor final_conductor_color(m_properties.color);
481  if (must_highlight_ == Normal) {
482  final_conductor_color = QColor::fromRgb(69, 137, 255, 255);
483  } else if (must_highlight_ == Alert) {
484  final_conductor_color =QColor::fromRgb(255, 69, 0, 255);
485  } else if (isSelected()) {
486  final_conductor_color = Qt::red;
487  } else {
488  if (Diagram *parent_diagram = diagram()) {
489  if (!parent_diagram -> drawColoredConductors()) {
490  final_conductor_color = Qt::black;
491  }
492  }
493  }
494 
495  //Draw the conductor bigger when is hovered
497 
498  //Set the QPen and QBrush to the QPainter
499  qp -> setBrush(conductor_brush);
500  QPen final_conductor_pen = conductor_pen;
501 
502  //Set the conductor style
503  final_conductor_pen.setColor(final_conductor_color);
504  final_conductor_pen.setStyle(m_properties.style);
505  final_conductor_pen.setJoinStyle(Qt::SvgMiterJoin); // better rendering with dot
506 
507  //Use a cosmetique line, below a certain zoom
508  if (options && options -> levelOfDetail < 1.0) {
509  final_conductor_pen.setCosmetic(true);
510  }
511 
512  qp -> setPen(final_conductor_pen);
513 
514  //Draw the conductor
515  qp -> drawPath(path());
516  //Draw the second color
518  {
519  final_conductor_pen.setColor(m_properties.m_color_2);
520  final_conductor_pen.setStyle(Qt::CustomDashLine);
521  QVector<qreal> dash_pattern;
522  dash_pattern << m_properties.m_dash_size-2 << m_properties.m_dash_size;
523  final_conductor_pen.setDashPattern(dash_pattern);
524  qp->save();
525  qp->setPen(final_conductor_pen);
526  qp->drawPath(path());
527  qp->restore();
528  }
529 
531  qp -> setBrush(final_conductor_color);
533  qp,
534  middleSegment() -> isHorizontal() ? QET::Horizontal : QET::Vertical,
535  QRectF(middleSegment() -> middle() - QPointF(12.0, 12.0), QSizeF(24.0, 24.0))
536  );
537  if (isSelected()) qp -> setBrush(Qt::NoBrush);
538  }
539 
540  //Draw the junctions
541  QList<QPointF> junctions_list = junctions();
542  if (!junctions_list.isEmpty()) {
543  final_conductor_pen.setStyle(Qt::SolidLine);
544  QBrush junction_brush(final_conductor_color, Qt::SolidPattern);
545  qp -> setPen(final_conductor_pen);
546  qp -> setBrush(junction_brush);
547  qp -> setRenderHint(QPainter::Antialiasing, true);
548  foreach(QPointF point, junctions_list) {
549  qp -> drawEllipse(QRectF(point.x() - 1.5, point.y() - 1.5, 3.0, 3.0));
550  }
551  }
552 
553  qp -> restore();
554 }
555 
558  return(qobject_cast<Diagram *>(scene()));
559 }
560 
565  return(m_text_item);
566 }
567 
573 bool Conductor::valideXml(QDomElement &e){
574  // verifie le nom du tag
575  if (e.tagName() != "conductor") return(false);
576 
577  // verifie la presence des attributs minimaux
578  if (!e.hasAttribute("terminal1")) return(false);
579  if (!e.hasAttribute("terminal2")) return(false);
580 
581  bool conv_ok;
582  // parse l'abscisse
583  e.attribute("terminal1").toInt(&conv_ok);
584  if (!conv_ok) return(false);
585 
586  // parse l'ordonnee
587  e.attribute("terminal2").toInt(&conv_ok);
588  if (!conv_ok) return(false);
589  return(true);
590 }
591 
597 void Conductor::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
598  event->accept();
599  editProperty();
600 }
601 
607 void Conductor::mousePressEvent(QGraphicsSceneMouseEvent *event)
608 {
609  QGraphicsObject::mousePressEvent(event);
610 
611  if (event->modifiers() & Qt::ControlModifier)
612  setSelected(!isSelected());
613 }
614 
619 void Conductor::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
620 {
621  if (!(event -> modifiers() & Qt::ControlModifier))
622  QGraphicsObject::mouseReleaseEvent(event);
623 }
624 
630 void Conductor::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
631  Q_UNUSED(event);
632  m_mouse_over = true;
633  update();
634 }
635 
641 void Conductor::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
642  Q_UNUSED(event);
643  update();
644  m_mouse_over = false;
645 }
646 
653 QVariant Conductor::itemChange(GraphicsItemChange change, const QVariant &value)
654 {
655  if (change == QGraphicsItem::ItemSelectedChange)
656  {
657  if (value.toBool())
658  {
659  m_previous_z_value = zValue();
660  setZValue(qAbs(m_previous_z_value) + 10000);
661  addHandler();
662  }
663  else
664  {
665  setZValue(m_previous_z_value);
666  removeHandler();
667  }
668  }
669  else if (change == QGraphicsItem::ItemSceneHasChanged)
670  {
672 
673  if(!scene())
674  removeHandler();
675  else if (scene() && isSelected())
676  addHandler();
677  }
678  else if (change == QGraphicsItem::ItemVisibleHasChanged) {
680  }
681  else if (change == QGraphicsItem::ItemPositionHasChanged && isSelected()) {
683  }
684 
685  return(QGraphicsObject::itemChange(change, value));
686 }
687 
694 bool Conductor::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
695 {
696  //Watched must be an handler
697  if(watched->type() == QetGraphicsHandlerItem::Type)
698  {
699  QetGraphicsHandlerItem *qghi = qgraphicsitem_cast<QetGraphicsHandlerItem *>(watched);
700 
701  if(m_handler_vector.contains(qghi)) //Handler must be in m_vector_index, then we can start resize
702  {
703  m_vector_index = m_handler_vector.indexOf(qghi);
704  if (m_vector_index != -1)
705  {
706  if(event->type() == QEvent::GraphicsSceneMousePress) //Click
707  {
708  handlerMousePressEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
709  return true;
710  }
711  else if(event->type() == QEvent::GraphicsSceneMouseMove) //Move
712  {
713  handlerMouseMoveEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
714  return true;
715  }
716  else if (event->type() == QEvent::GraphicsSceneMouseRelease) //Release
717  {
718  handlerMouseReleaseEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
719  return true;
720  }
721  else if (event->type() == QEvent::GraphicsSceneMouseDoubleClick) //Double click
722  {
723  editProperty();
724  return true;
725  }
726  }
727  }
728  }
729 
730  return false;
731 }
732 
738 {
739  if (m_handler_vector.isEmpty())
740  return;
741 
742  if (m_handler_vector.size() == handlerPoints().size())
743  {
744  QVector <QPointF> points_vector = mapToScene(handlerPoints());
745  for (int i = 0 ; i < points_vector.size() ; ++i)
746  m_handler_vector.at(i)->setPos(points_vector.at(i));
747  }
748 }
749 
755 void Conductor::handlerMousePressEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
756 {
757  Q_UNUSED(event);
758 
759  //we get the segment corresponding to the handler
760  if (m_vector_index > -1)
761  {
762  qghi->setColor(Qt::cyan);
763  m_moving_segment = true;
766 
768  if(handler != qghi)
769  handler->hide();
770  }
771 }
772 
778 void Conductor::handlerMouseMoveEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
779 {
780  if (m_moving_segment)
781  {
782  //Snap the mouse pos to grid
783  QPointF pos_ = Diagram::snapToGrid(mapFromScene(event->scenePos()));
784 
785  //Position of the last point
786  QPointF p = m_moved_segment -> middle();
787 
788  //Calcul the movement
789  m_moved_segment -> moveX(pos_.x() - p.x());
790  m_moved_segment -> moveY(pos_.y() - p.y());
791 
792  //Apply the movement
793  modified_path = true;
794  has_to_save_profile = true;
795  segmentsToPath();
797  qghi->setPos(mapToScene(m_moved_segment->middle()));
798  }
799 }
800 
806 void Conductor::handlerMouseReleaseEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
807 {
808  Q_UNUSED(event);
809  Q_UNUSED(qghi);
810 
811  m_vector_index = -1;
812 
813  m_moving_segment = false;
815  {
816  saveProfile();
817  has_to_save_profile = false;
818  }
819  //When handler is released, the conductor can have more segment than befor the handler was moved
820  //then we remove all handles and new ones are added
821  removeHandler();
822  addHandler();
823 }
824 
830 {
831  if (m_handler_vector.isEmpty() && scene())
832  {
834 
836  {
837  handler->setColor(Qt::blue);
838  scene()->addItem(handler);
839  handler->installSceneEventFilter(this);
840  handler->setZValue(this->zValue()+1);
841  }
842  }
843 }
844 
850 {
851  if (!m_handler_vector.isEmpty())
852  {
853  qDeleteAll(m_handler_vector);
854  m_handler_vector.clear();
855  }
856 }
857 
863 {
864  QRectF br = shape().boundingRect();
865  return br.adjusted(-10, -10, 10, 10);
866 }
867 
873 QPainterPath Conductor::shape() const
874 {
875  QPainterPathStroker pps;
876  pps.setWidth(m_mouse_over? 5 : 1);
877  pps.setJoinStyle(conductor_pen.joinStyle());
878 
879  QPainterPath shape_(pps.createStroke(path()));
880 
881  return shape_;
882 }
883 
888 QPainterPath Conductor::nearShape() const
889 {
890  QPainterPathStroker pps;
891  pps.setWidth(1300);
892  pps.setJoinStyle(conductor_pen.joinStyle());
893  return pps.createStroke(path());
894 }
895 
901  QList<ConductorSegment *> segments_list = segmentsList();
902  if (type == QET::Both) return(segments_list.count());
903  uint nb_seg = 0;
904  foreach(ConductorSegment *conductor_segment, segments_list) {
905  if (conductor_segment -> type() == type) ++ nb_seg;
906  }
907  return(nb_seg);
908 }
909 
914 QList<QPointF> Conductor::segmentsToPoints() const {
915  // liste qui sera retournee
916  QList<QPointF> points_list;
917 
918  // on retourne la liste tout de suite s'il n'y a pas de segments
919  if (segments == nullptr) return(points_list);
920 
921  // recupere le premier point
922  points_list << segments -> firstPoint();
923 
924  // parcourt les segments pour recuperer les autres points
925  ConductorSegment *segment = segments;
926  while(segment -> hasNextSegment()) {
927  points_list << segment -> secondPoint();
928  segment = segment -> nextSegment();
929  }
930 
931  // recupere le dernier point
932  points_list << segment -> secondPoint();
933 
934  //retourne la liste
935  return(points_list);
936 }
937 
942 void Conductor::pointsToSegments(const QList<QPointF>& points_list) {
943  // supprime les segments actuels
944  deleteSegments();
945 
946  // cree les segments a partir de la liste de points
947  ConductorSegment *last_segment = nullptr;
948  for (int i = 0 ; i < points_list.size() - 1 ; ++ i) {
949  last_segment = new ConductorSegment(points_list.at(i), points_list.at(i + 1), last_segment);
950  if (!i) segments = last_segment;
951  }
952 }
953 
960 bool Conductor::fromXml(QDomElement &dom_element)
961 {
962  setPos(dom_element.attribute("x", nullptr).toDouble(),
963  dom_element.attribute("y", nullptr).toDouble());
964 
965  bool return_ = pathFromXml(dom_element);
966 
967  m_text_item -> fromXml(dom_element);
969  pr.fromXml(dom_element);
970 
971  //Load Sequential Values
972  if (dom_element.hasAttribute("sequ_1") || dom_element.hasAttribute("sequf_1") || dom_element.hasAttribute("seqt_1") || dom_element.hasAttribute("seqtf_1") || dom_element.hasAttribute("seqh_1") || dom_element.hasAttribute("sequf_1"))
974  else
975  m_autoNum_seq.fromXml(dom_element.firstChildElement("sequentialNumbers"));
976 
977  m_freeze_label = dom_element.attribute("freezeLabel") == "true"? true : false;
978 
979  setProperties(pr);
980 
981  return return_;
982 }
983 
991 QDomElement Conductor::toXml(QDomDocument &dom_document, QHash<Terminal *, int> &table_adr_id) const
992 {
993  QDomElement dom_element = dom_document.createElement("conductor");
994 
995  dom_element.setAttribute("x", QString::number(pos().x()));
996  dom_element.setAttribute("y", QString::number(pos().y()));
997  dom_element.setAttribute("terminal1", table_adr_id.value(terminal1));
998  dom_element.setAttribute("terminal2", table_adr_id.value(terminal2));
999  dom_element.setAttribute("freezeLabel", m_freeze_label? "true" : "false");
1000 
1001  // on n'exporte les segments du conducteur que si ceux-ci ont
1002  // ete modifies par l'utilisateur
1003  if (modified_path)
1004  {
1005  // parcours et export des segments
1006  QDomElement current_segment;
1007  foreach(ConductorSegment *segment, segmentsList())
1008  {
1009  current_segment = dom_document.createElement("segment");
1010  current_segment.setAttribute("orientation", segment -> isHorizontal() ? "horizontal" : "vertical");
1011  current_segment.setAttribute("length", QString("%1").arg(segment -> length()));
1012  dom_element.appendChild(current_segment);
1013  }
1014  }
1015 
1016  QDomElement dom_seq = m_autoNum_seq.toXml(dom_document);
1017  dom_element.appendChild(dom_seq);
1018 
1019  // Export the properties and text
1020  m_properties. toXml(dom_element);
1022  {
1023  dom_element.setAttribute("userx", QString::number(m_text_item->pos().x()));
1024  dom_element.setAttribute("usery", QString::number(m_text_item->pos().y()));
1025  }
1027  dom_element.setAttribute("rotation", QString::number(m_text_item->rotation()));
1028 
1029  return(dom_element);
1030 }
1031 
1038 bool Conductor::pathFromXml(const QDomElement &e) {
1039  // parcourt les elements XML "segment" et en extrait deux listes de longueurs
1040  // les segments non valides sont ignores
1041  QList<qreal> segments_x, segments_y;
1042  for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
1043  // on s'interesse aux elements XML "segment"
1044  QDomElement current_segment = node.toElement();
1045  if (current_segment.isNull() || current_segment.tagName() != "segment") continue;
1046 
1047  // le segment doit avoir une longueur
1048  if (!current_segment.hasAttribute("length")) continue;
1049 
1050  // cette longueur doit etre un reel
1051  bool ok;
1052  qreal segment_length = current_segment.attribute("length").toDouble(&ok);
1053  if (!ok) continue;
1054 
1055  if (current_segment.attribute("orientation") == "horizontal") {
1056  segments_x << segment_length;
1057  segments_y << 0.0;
1058  } else {
1059  segments_x << 0.0;
1060  segments_y << segment_length;
1061  }
1062  }
1063 
1064  //If there isn't segment we generate automatic path and return true
1065  if (!segments_x.size()) {
1066  generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
1067  return(true);
1068  }
1069 
1070  // les longueurs recueillies doivent etre coherentes avec les positions des bornes
1071  qreal width = 0.0, height = 0.0;
1072  foreach (qreal t, segments_x) width += t;
1073  foreach (qreal t, segments_y) height += t;
1074  QPointF t1 = terminal1 -> dockConductor();
1075  QPointF t2 = terminal2 -> dockConductor();
1076  qreal expected_width = t2.x() - t1.x();
1077  qreal expected_height = t2.y() - t1.y();
1078 
1079  // on considere que le trajet est incoherent a partir d'une unite de difference avec l'espacement entre les bornes
1080  if (
1081  qAbs(expected_width - width) > 1.0 ||
1082  qAbs(expected_height - height) > 1.0
1083  ) {
1084  qDebug() << "Conductor::fromXml : les segments du conducteur ne semblent pas coherents - utilisation d'un trajet automatique";
1085  return(false);
1086  }
1087 
1088  /* on recree les segments a partir des donnes XML */
1089  // cree la liste de points
1090  QList<QPointF> points_list;
1091  points_list << mapFromScene(t1);
1092  for (int i = 0 ; i < segments_x.size() ; ++ i) {
1093  points_list << QPointF(
1094  points_list.last().x() + segments_x.at(i),
1095  points_list.last().y() + segments_y.at(i)
1096  );
1097  }
1098 
1099  pointsToSegments(points_list);
1100 
1101  // initialise divers parametres lies a la modification des conducteurs
1102  modified_path = true;
1103  saveProfile(false);
1104 
1105  segmentsToPath();
1106  return(true);
1107 }
1108 
1116 QVector<QPointF> Conductor::handlerPoints() const
1117 {
1118  QList <ConductorSegment *> sl = segmentsList();
1119  if (sl.size() >= 3)
1120  {
1121  sl.removeFirst();
1122  sl.removeLast();
1123  }
1124 
1125  QVector <QPointF> middle_points;
1126 
1127  foreach(ConductorSegment *segment, sl)
1128  middle_points.append(segment->middle());
1129 
1130  return middle_points;
1131 }
1132 
1134 const QList<ConductorSegment *> Conductor::segmentsList() const {
1135  if (segments == nullptr) return(QList<ConductorSegment *>());
1136 
1137  QList<ConductorSegment *> segments_vector;
1138  ConductorSegment *segment = segments;
1139 
1140  while (segment -> hasNextSegment()) {
1141  segments_vector << segment;
1142  segment = segment -> nextSegment();
1143  }
1144  segments_vector << segment;
1145  return(segments_vector);
1146 }
1147 
1152 qreal Conductor::length() const{
1153  return path().length();
1154 }
1155 
1160  if (segments == nullptr) return(nullptr);
1161 
1162  qreal half_length = length() / 2.0;
1163 
1165  qreal l = 0;
1166 
1167  while (s -> hasNextSegment()) {
1168  l += qAbs(s -> length());
1169  if (l >= half_length) break;
1170  s = s -> nextSegment();
1171  }
1172  // s est le segment qui contient le point au milieu du conducteur
1173  return(s);
1174 }
1175 
1183 {
1184 
1185  ConductorSegment *segment = segments;
1186  bool all_segment_is_vertical = true;
1187  bool all_segment_is_horizontal = true;
1188 
1189  //Go to first segement
1190  while (!segment->isFirstSegment()) {
1191  segment = segment->previousSegment();
1192  }
1193 
1194 
1195  QPointF p1 = segment -> firstPoint(); //<First point of conductor
1196  ConductorSegment *biggest_segment = segment; //<biggest segment: contain the longest segment of conductor.
1197 
1198  if (segment -> firstPoint().x() != segment -> secondPoint().x())
1199  all_segment_is_vertical = false;
1200  if (segment -> firstPoint().y() != segment -> secondPoint().y())
1201  all_segment_is_horizontal = false;
1202 
1203  while (segment -> hasNextSegment())
1204  {
1205  segment = segment -> nextSegment();
1206 
1207  if (segment -> firstPoint().x() != segment -> secondPoint().x())
1208  all_segment_is_vertical = false;
1209  if (segment -> firstPoint().y() != segment -> secondPoint().y())
1210  all_segment_is_horizontal = false;
1211 
1212  //We must to compare length segment, but they can be negative
1213  //so we multiply by -1 to make it positive.
1214  int saved = biggest_segment -> length();
1215  if (saved < 0) saved *= -1;
1216  int curent = segment->length();
1217  if (curent < 0) curent *= -1;
1218 
1219  if (curent > saved) biggest_segment = segment;
1220  }
1221 
1222  QPointF p2 = segment -> secondPoint();//<Last point of conductor
1223 
1224  //If the conductor is horizontal or vertical
1225  //Return the point at the middle of conductor
1226  if (all_segment_is_vertical) { //<Vertical
1227  flag = Qt::Vertical;
1228  if (p1.y() > p2.y()) {
1229  p1.setY(p1.y() - (length()/2));
1230  } else {
1231  p1.setY(p1.y() + (length()/2));
1232  }
1233  } else if (all_segment_is_horizontal) { //<Horizontal
1234  flag = Qt::Horizontal;
1235  if (p1.x() > p2.x()) {
1236  p1.setX(p1.x() - (length()/2));
1237  } else {
1238  p1.setX(p1.x() + (length()/2));
1239  }
1240  } else { //Return the point at the middle of biggest segment.
1241  p1 = biggest_segment->middle();
1242  flag = (biggest_segment->isHorizontal())? Qt::Horizontal : Qt::Vertical;
1243  }
1244  return p1;
1245 }
1246 
1254 {
1256  return;
1257 
1258  if (diagram() -> defaultConductorProperties.m_one_text_per_folio == true &&
1259  relatedPotentialConductors(false).size() > 0)
1260  {
1261 
1262  Conductor *longuest_conductor = longuestConductorInPotential(this);
1263 
1264  //The longuest conductor isn't this conductor
1265  //we call calculateTextItemPosition of the longuest conductor
1266  if(longuest_conductor != this)
1267  {
1268  longuest_conductor -> calculateTextItemPosition();
1269  return;
1270  }
1271 
1272  //At this point this conductor is the longuest conductor we hide all text of conductor_list
1273  foreach (Conductor *c, relatedPotentialConductors(false)) {
1274  c -> textItem() -> setVisible(false);
1275  }
1276  //Make sure text item is visible
1277  m_text_item -> setVisible(true);
1278  }
1279 
1280  //position
1281  if (m_text_item -> wasMovedByUser())
1282  {
1283  //Text field was moved by user :
1284  //we check if text field is yet near the conductor
1285  QPointF text_item_pos = m_text_item -> pos();
1286  QPainterPath near_shape = nearShape();
1287  if (!near_shape.contains(text_item_pos)) {
1288  m_text_item -> setPos(movePointIntoPolygon(text_item_pos, near_shape));
1289  }
1290  }
1291  else
1292  {
1293  //Position and rotation of text is calculated.
1294  Qt::Orientations rotation;
1295  QPointF text_pos = posForText(rotation);
1296 
1297  if (!m_text_item -> wasRotateByUser())
1298  {
1299  rotation == Qt::Vertical ? m_text_item -> setRotation(m_properties.verti_rotate_text):
1300  m_text_item -> setRotation(m_properties.horiz_rotate_text);
1301  }
1302 
1303  //Adjust the position of text if his rotation
1304  //is 0° or 270°, to be exactly centered to the conductor
1305  if (m_text_item -> rotation() == 0)
1306  {
1307  text_pos.rx() -= m_text_item -> boundingRect().width()/2;
1308  if(m_properties.m_horizontal_alignment == Qt::AlignTop)
1309  text_pos.ry() -= m_text_item->boundingRect().height();
1310  }
1311  else if (m_text_item -> rotation() == 270)
1312  {
1313  text_pos.ry() += m_text_item -> boundingRect().width()/2;
1314  if(m_properties.m_vertical_alignment == Qt::AlignLeft)
1315  text_pos.rx() -= m_text_item->boundingRect().height();
1316  }
1317 
1318  m_text_item -> setPos(text_pos);
1319 
1320  //Ensure text item don't collide with this conductor
1321  while (m_text_item->collidesWithItem(this))
1322  {
1323  if(rotation == Qt::Vertical)
1324  {
1325  if(m_properties.m_vertical_alignment == Qt::AlignRight)
1326  m_text_item->setX(m_text_item->x()+1);
1327  else if (m_properties.m_vertical_alignment == Qt::AlignLeft)
1328  m_text_item->setX(m_text_item->x()-1);
1329  else
1330  return; //avoid infinite loop
1331  }
1332  else if (rotation == Qt::Horizontal)
1333  {
1334  if(m_properties.m_horizontal_alignment == Qt::AlignTop)
1335  m_text_item->setY(m_text_item->y()-1);
1336  else if (m_properties.m_horizontal_alignment == Qt::AlignBottom)
1337  m_text_item->setY(m_text_item->y()+1);
1338  else
1339  return; //avoid infinite loop
1340  }
1341  }
1342  }
1343 }
1344 
1349 void Conductor::saveProfile(bool undo) {
1350  Qt::Corner current_path_type = currentPathType();
1351  ConductorProfile old_profile(conductor_profiles[current_path_type]);
1352  conductor_profiles[current_path_type].fromConductor(this);
1353  Diagram *dia = diagram();
1354  if (undo && dia) {
1356  this,
1357  old_profile,
1358  conductor_profiles[current_path_type],
1359  current_path_type
1360  );
1361  undo_object -> setConductorTextItemMove(before_mov_text_pos_, m_text_item -> pos());
1362  dia -> undoStack().push(undo_object);
1363  }
1364 }
1365 
1371 int Conductor::getCoeff(const qreal &value1, const qreal &value2) {
1372  return(getSign(value1) * getSign(value2));
1373 }
1374 
1379 int Conductor::getSign(const qreal &value) {
1380  return(value < 0 ? -1 : 1);
1381 }
1382 
1388 void Conductor::setProfile(const ConductorProfile &cp, Qt::Corner path_type) {
1389  conductor_profiles[path_type] = cp;
1390  // si le type de trajet correspond a l'actuel
1391  if (currentPathType() == path_type) {
1392  if (conductor_profiles[path_type].isNull()) {
1393  generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
1394  modified_path = false;
1395  } else {
1396  updateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
1397  modified_path = true;
1398  }
1399  if (type() == ConductorProperties::Multi) {
1401  }
1402  }
1403 }
1404 
1406 ConductorProfile Conductor::profile(Qt::Corner path_type) const {
1407  return(conductor_profiles[path_type]);
1408 }
1409 
1416 {
1417  if (m_freeze_label)
1418  {
1420  }
1421  else
1422  {
1423  if (!m_properties.m_formula.isEmpty())
1424  {
1425  if (diagram())
1426  {
1428  m_properties.text = text;
1429  m_text_item->setPlainText(text);
1430  }
1431  else
1432  {
1435  }
1436  }
1437  else
1438  {
1440  }
1441  }
1442 }
1443 
1444 void Conductor::setPath(const QPainterPath &path)
1445 {
1446  if(path == m_path)
1447  return;
1448 
1449  prepareGeometryChange();
1450  m_path = path;
1451  update();
1452 }
1453 
1454 QPainterPath Conductor::path() const
1455 {
1456  return m_path;
1457 }
1458 
1467 void Conductor::setPropertyToPotential(const ConductorProperties &property, bool only_text)
1468 {
1469  setProperties(property);
1470  QSet <Conductor *> potential_list = relatedPotentialConductors();
1471 
1472  foreach(Conductor *other_conductor, potential_list)
1473  {
1474  if (only_text)
1475  {
1476  ConductorProperties other_properties = other_conductor->properties();
1477  other_properties.m_formula = m_properties.m_formula;
1478  other_properties.text = m_properties.text;
1479  other_properties.m_function = m_properties.m_function;
1481  other_conductor->setProperties(other_properties);
1482  }
1483  else
1484  {
1485  other_conductor->setProperties(property);
1486  }
1487  }
1488 }
1489 
1496 {
1497  if (m_properties == property) return;
1498 
1499  QString formula = m_properties.m_formula;
1500  m_properties = property;
1501 
1502  if (!m_properties.m_formula.isEmpty())
1503  {
1504  if (diagram())
1505  {
1507  m_properties.text = text;
1508  }
1509  else if (m_properties.text.isEmpty())
1510  {
1512  }
1513 
1515  }
1516 
1518  QFont font = m_text_item->font();
1519  font.setPointSize(m_properties.text_size);
1520  m_text_item->setFont(font);
1521 
1523  m_text_item->setVisible(false);
1524  else
1525  m_text_item->setVisible(m_properties.m_show_text);
1526 
1528  update();
1529 
1530  emit propertiesChange();
1531 }
1532 
1538 {
1539  return(m_properties);
1540 }
1541 
1546  return(must_highlight_);
1547 }
1548 
1553  must_highlight_ = hl;
1554  update();
1555 }
1556 
1563 {
1564  QVariant old_value, new_value;
1565  old_value.setValue(m_properties);
1566  ConductorProperties new_properties(m_properties);
1567  new_properties.m_formula = m_text_item->toPlainText();
1568  new_properties.text = m_text_item->toPlainText();
1569  new_value.setValue(new_properties);
1570 
1571 
1572  QUndoCommand *undo = new QUndoCommand(tr("Modifier les propriétés d'un conducteur", "undo caption"));
1573  new QPropertyUndoCommand(this, "properties", old_value, new_value, undo);
1574 
1575  if (!relatedPotentialConductors().isEmpty())
1576  {
1577  undo->setText(tr("Modifier les propriétés de plusieurs conducteurs", "undo caption"));
1578 
1579  foreach (Conductor *potential_conductor, relatedPotentialConductors())
1580  {
1581  old_value.setValue(potential_conductor->properties());
1582  ConductorProperties new_properties = potential_conductor->properties();
1583  new_properties.m_formula = m_text_item->toPlainText();
1584  new_properties.text = m_text_item->toPlainText();
1585  new_value.setValue(new_properties);
1586  new QPropertyUndoCommand (potential_conductor, "properties", old_value, new_value, undo);
1587  }
1588  }
1589 
1590  diagram()->undoStack().push(undo);
1591 }
1592 
1593 
1603 QSet<Conductor *> Conductor::relatedPotentialConductors(const bool all_diagram, QList <Terminal *> *t_list)
1604 {
1605  bool declar_t_list = false;
1606  if (t_list == nullptr)
1607  {
1608  declar_t_list = true;
1609  t_list = new QList <Terminal *>;
1610  }
1611 
1612  QSet <Conductor *> other_conductors;
1613  QList <Terminal *> this_terminal;
1614  this_terminal << terminal1 << terminal2;
1615 
1616  // Return all conductors of terminal 1 and 2
1617  for (Terminal *terminal : this_terminal)
1618  {
1619  if (!t_list->contains(terminal))
1620  {
1621  t_list->append(terminal);
1622  QList <Conductor *> other_conductors_list_t = terminal->conductors();
1623 
1624  //Get the other terminals of the parent element of @terminal, who share the same potential
1625  //This is use for element type "folio report" and "terminal element"
1626  for (Terminal *t : relatedPotentialTerminal(terminal, all_diagram))
1627  {
1628  if (!t_list->contains(t))
1629  {
1630  t_list -> append(t);
1631  other_conductors_list_t += t->conductors();
1632  }
1633  }
1634 
1635  other_conductors_list_t.removeAll(this);
1636  //Get the conductors at the same potential for each conductors of other_conductors_list_t
1637  for (Conductor *c : other_conductors_list_t) {
1638  other_conductors += c->relatedPotentialConductors(all_diagram, t_list);
1639  }
1640  other_conductors += other_conductors_list_t.toSet();
1641  }
1642  }
1643 
1644  other_conductors.remove(this);
1645 
1646  if (declar_t_list) delete t_list;
1647  return(other_conductors);
1648 }
1649 
1655  if (!diagram()) return nullptr;
1656  if (diagram() -> views().isEmpty()) return nullptr;
1657 
1658  QWidget *w = const_cast<QGraphicsView *>(diagram() -> views().at(0));
1659  while (w -> parentWidget() && !w -> isWindow()) {
1660  w = w -> parentWidget();
1661  }
1662  return(qobject_cast<QETDiagramEditor *>(w));
1663 }
1664 
1670 }
1671 
1673 {
1674  m_autoNum_seq = sn;
1675  refreshText();
1676 }
1677 
1684 void Conductor::setUpConnectionForFormula(QString old_formula, QString new_formula)
1685 {
1686  if (diagram())
1687  {
1688  //Because the variable %F is a reference to another text which can contain variables,
1689  //we must to replace %F by the real text, to check if the real text contain the variable %id
1690  if (old_formula.contains("%F"))
1691  old_formula.replace("%F", diagram()->border_and_titleblock.folio());
1692 
1693  if (old_formula.contains("%id"))
1695 
1696  //Label is frozen, so we don't update it.
1697  if (m_freeze_label == true)
1698  return;
1699 
1700  if (new_formula.contains("%F"))
1701  new_formula.replace("%F", diagram()->border_and_titleblock.folio());
1702 
1703  if (new_formula.contains("%id"))
1705  }
1706 }
1707 
1714 bool isContained(const QPointF &a, const QPointF &b, const QPointF &c) {
1715  return(
1716  isBetween(a.x(), b.x(), c.x()) &&
1717  isBetween(a.y(), b.y(), c.y())
1718  );
1719 }
1720 
1724 QList<QPointF> Conductor::junctions() const {
1725  QList<QPointF> junctions_list;
1726 
1727  // pour qu'il y ait des jonctions, il doit y avoir d'autres conducteurs et des bifurcations
1728  QList<Conductor *> other_conductors = relatedConductors(this);
1729  QList<ConductorBend> bends_list = bends();
1730  if (other_conductors.isEmpty() || bends_list.isEmpty()) {
1731  return(junctions_list);
1732  }
1733 
1734  QList<QPointF> points = segmentsToPoints();
1735  for (int i = 1 ; i < (points.size() -1) ; ++ i) {
1736  QPointF point = points.at(i);
1737 
1738  // determine si le point est une bifurcation ou non
1739  bool is_bend = false;
1740  Qt::Corner current_bend_type = Qt::TopLeftCorner;
1741  foreach(ConductorBend cb, bends_list)
1742  {
1743  if (cb.first == point)
1744  {
1745  is_bend = true;
1746  current_bend_type = cb.second;
1747  break;
1748  }
1749  }
1750  // si le point n'est pas une bifurcation, il ne peut etre une jonction (enfin pas au niveau de ce conducteur)
1751  if (!is_bend) continue;
1752 
1753  bool is_junction = false;
1754  QPointF scene_point = mapToScene(point);
1755  foreach(Conductor *c, other_conductors)
1756  {
1757  // exprime le point dans les coordonnees de l'autre conducteur
1758  QPointF conductor_point = c -> mapFromScene(scene_point);
1759  // recupere les segments de l'autre conducteur
1760  QList<ConductorSegment *> c_segments = c -> segmentsList();
1761  if (c_segments.isEmpty())
1762  continue;
1763  // parcoure les segments a la recherche d'un point commun
1764  for (int j = 0 ; j < c_segments.count() ; ++ j)
1765  {
1766  ConductorSegment *segment = c_segments[j];
1767  // un point commun a ete trouve sur ce segment
1768  if (isContained(conductor_point, segment -> firstPoint(), segment -> secondPoint()))
1769  {
1770  is_junction = true;
1771  // ce point commun ne doit pas etre une bifurcation identique a celle-ci
1772  QList<ConductorBend> other_conductor_bends = c -> bends();
1773  foreach(ConductorBend cb, other_conductor_bends)
1774  {
1775  if (cb.first == conductor_point && cb.second == current_bend_type)
1776  {
1777  is_junction = false;
1778  }
1779  }
1780  }
1781  if (is_junction) junctions_list << point;
1782  }
1783  }
1784  }
1785  return(junctions_list);
1786 }
1787 
1794 QList<ConductorBend> Conductor::bends() const {
1795  QList<ConductorBend> points;
1796  if (!segments) return(points);
1797 
1798  // recupere la liste des segments de taille non nulle
1799  QList<ConductorSegment *> visible_segments;
1800  ConductorSegment *segment = segments;
1801  while (segment -> hasNextSegment()) {
1802  if (!segment -> isPoint()) visible_segments << segment;
1803  segment = segment -> nextSegment();
1804  }
1805  if (!segment -> isPoint()) visible_segments << segment;
1806 
1807  ConductorSegment *next_segment;
1808  for (int i = 0 ; i < visible_segments.count() -1 ; ++ i) {
1809  segment = visible_segments[i];
1810  next_segment = visible_segments[i + 1];
1811  if (!segment -> isPoint() && !next_segment -> isPoint()) {
1812  // si les deux segments ne sont pas dans le meme sens, on a une bifurcation
1813  if (next_segment -> type() != segment -> type()) {
1814  Qt::Corner bend_type;
1815  qreal sl = segment -> length();
1816  qreal nsl = next_segment -> length();
1817 
1818  if (segment -> isHorizontal()) {
1819  if (sl < 0 && nsl < 0) {
1820  bend_type = Qt::BottomLeftCorner;
1821  } else if (sl < 0 && nsl > 0) {
1822  bend_type = Qt::TopLeftCorner;
1823  } else if (sl > 0 && nsl < 0) {
1824  bend_type = Qt::BottomRightCorner;
1825  } else {
1826  bend_type = Qt::TopRightCorner;
1827  }
1828  } else {
1829  if (sl < 0 && nsl < 0) {
1830  bend_type = Qt::TopRightCorner;
1831  } else if (sl < 0 && nsl > 0) {
1832  bend_type = Qt::TopLeftCorner;
1833  } else if (sl > 0 && nsl < 0) {
1834  bend_type = Qt::BottomRightCorner;
1835  } else {
1836  bend_type = Qt::BottomLeftCorner;
1837  }
1838  }
1839  points << qMakePair(segment -> secondPoint(), bend_type);
1840  }
1841  }
1842  }
1843  return(points);
1844 }
1845 
1851 Qt::Corner Conductor::movementType(const QPointF &start, const QPointF &end) {
1852  Qt::Corner result = Qt::BottomRightCorner;
1853  if (start.x() <= end.x()) {
1854  result = start.y() <= end.y() ? Qt::BottomRightCorner : Qt::TopRightCorner;
1855  } else {
1856  result = start.y() <= end.y() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
1857  }
1858  return(result);
1859 }
1860 
1862 Qt::Corner Conductor::currentPathType() const {
1863  return(movementType(terminal1 -> dockConductor(), terminal2 -> dockConductor()));
1864 }
1865 
1868  return(conductor_profiles);
1869 }
1870 
1876  conductor_profiles = cpg;
1877  if (conductor_profiles[currentPathType()].isNull()) {
1878  generateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
1879  modified_path = false;
1880  } else {
1881  updateConductorPath(terminal1 -> dockConductor(), terminal1 -> orientation(), terminal2 -> dockConductor(), terminal2 -> orientation());
1882  modified_path = true;
1883  }
1886  }
1887 }
1888 
1891  if (segments != nullptr) {
1892  while (segments -> hasNextSegment()) delete segments -> nextSegment();
1893  delete segments;
1894  segments = nullptr;
1895  }
1896 }
1897 
1904 QPointF Conductor::movePointIntoPolygon(const QPointF &point, const QPainterPath &polygon) {
1905  // decompose le polygone en lignes et points
1906  QList<QPolygonF> polygons = polygon.simplified().toSubpathPolygons();
1907  QList<QLineF> lines;
1908  QList<QPointF> points;
1909  foreach(QPolygonF polygon, polygons) {
1910  if (polygon.count() <= 1) continue;
1911 
1912  // on recense les lignes et les points
1913  for (int i = 1 ; i < polygon.count() ; ++ i) {
1914  lines << QLineF(polygon.at(i - 1), polygon.at(i));
1915  points << polygon.at(i -1);
1916  }
1917  }
1918 
1919  // on fait des projetes orthogonaux du point sur les differents segments du
1920  // polygone, en les triant par longueur croissante
1921  QMap<qreal, QPointF> intersections;
1922  foreach (QLineF line, lines) {
1923  QPointF intersection_point;
1924  if (QET::orthogonalProjection(point, line, &intersection_point)) {
1925  intersections.insert(QLineF(intersection_point, point).length(), intersection_point);
1926  }
1927  }
1928  if (intersections.count()) {
1929  // on determine la plus courte longueur pour un projete orthogonal
1930  QPointF the_point = intersections[intersections.keys().first()];
1931  return(the_point);
1932  } else {
1933  // determine le coin du polygone le plus proche du point exterieur
1934  qreal minimum_length = -1;
1935  int point_index = -1;
1936  for (int i = 0 ; i < points.count() ; ++ i) {
1937  qreal length = qAbs(QLineF(points.at(i), point).length());
1938  if (minimum_length < 0 || length < minimum_length) {
1939  minimum_length = length;
1940  point_index = i;
1941  }
1942  }
1943  // on connait desormais le coin le plus proche du texte
1944 
1945  // aucun projete orthogonal n'a donne quoi que ce soit, on met le texte sur un des coins du polygone
1946  return(points.at(point_index));
1947  }
1948 }
1949 
1956 Conductor * longuestConductorInPotential(Conductor *conductor, bool all_diagram) {
1957  Conductor *longuest_conductor = conductor;
1958  //Search the longuest conductor
1959  foreach (Conductor *c, conductor -> relatedPotentialConductors(all_diagram))
1960  if (c -> length() > longuest_conductor -> length())
1961  longuest_conductor = c;
1962 
1963  return longuest_conductor;
1964 }
1965 
1972 QList <Conductor *> relatedConductors(const Conductor *conductor) {
1973  QList<Conductor *> other_conductors_list = conductor -> terminal1 -> conductors();
1974  other_conductors_list << conductor -> terminal2->conductors();
1975  other_conductors_list.removeAll(const_cast<Conductor *> (conductor));
1976  return(other_conductors_list);
1977 }
1978 
1985 void Conductor::setFreezeLabel(bool freeze)
1986 {
1987  m_freeze_label = freeze;
1988 
1989  if (m_freeze_label != freeze)
1990  {
1991  m_freeze_label = freeze;
1992  QString f = m_properties.m_formula;
1994  }
1995 }
The QPropertyUndoCommand class This undo command manage QProperty of a QObject. This undo command can...
QDomElement toXml(QDomDocument &, QHash< Terminal *, int > &) const
Definition: conductor.cpp:991
static int yGrid
ordinate grid step size
Definition: diagram.h:78
void generateConductorPath(const QPointF &, Qet::Orientation, const QPointF &, Qet::Orientation)
Definition: conductor.cpp:344
Conductor(Terminal *, Terminal *)
Conductor::Conductor Default constructor.
Definition: conductor.cpp:75
Qt::Alignment m_vertical_alignment
QList< Conductor * > conductors() const
Definition: terminal.cpp:677
ConductorProperties properties
Conductor::properties.
Definition: conductor.h:48
QVariant itemChange(GraphicsItemChange, const QVariant &) override
Conductor::itemChange.
Definition: conductor.cpp:653
QPointF middle() const
void textEdited(const QString &old_str, const QString &new_str)
void refreshText()
Conductor::refreshText Refresh the text of this conductor. recalcule and set the text according to th...
Definition: conductor.cpp:1415
ConductorProfilesGroup conductor_profiles
Definition: conductor.h:176
static QString formulaToLabel(QString formula, sequentialNumbers &seqStruct, Diagram *diagram, const Element *elmt=nullptr)
AssignVariables::formulaToLabel Return the with variable assigned (ready to be displayed) ...
QIcon br
Definition: qeticons.cpp:185
bool isBetween(const T a, const T b, const T c)
Definition: conductor.h:214
virtual bool wasMovedByUser() const
ConductorSegment * previousSegment() const
void saveProfile(bool=true)
Definition: conductor.cpp:1349
QETDiagramEditor * diagramEditor() const
Conductor::diagramEditor.
Definition: conductor.cpp:1654
The QetGraphicsHandlerItem class This graphics item represents a point, destined to be used as an han...
Definition: qet.h:207
bool modified_path
Whether the conductor was manually modified by users.
Definition: conductor.h:171
bool m_mouse_over
Definition: conductor.h:157
qreal width() const
QPainterPath m_path
Definition: conductor.h:186
autonum::sequentialNumbers m_autoNum_seq
Definition: conductor.h:128
bool isHorizontal() const
QList< QPointF > segmentsToPoints() const
Definition: conductor.cpp:914
void setFreezeLabel(bool freeze)
Conductor::setFreezeLabel Freeze this conductor label if true Unfreeze this conductor label if false...
Definition: conductor.cpp:1985
QSet< Conductor * > relatedPotentialConductors(const bool all_diagram=true, QList< Terminal *> *t_list=nullptr)
Conductor::relatedPotentialConductors Return all conductors at the same potential of this conductor...
Definition: conductor.cpp:1603
ConductorProfilesGroup profiles() const
Definition: conductor.cpp:1867
static bool pen_and_brush_initialized
Definition: conductor.h:185
static void PropertiesDialog(Conductor *conductor, QWidget *parent=nullptr)
ConductorPropertiesDialog::PropertiesDialog Static method for open and apply properties.
SingleLineProperties singleLineProperties
bool fromXml(QDomElement &)
Conductor::fromXml Load the conductor and her information from xml element.
Definition: conductor.cpp:960
QList< ConductorBend > bends() const
Definition: conductor.cpp:1794
ConductorProfile profile(Qt::Corner) const
Definition: conductor.cpp:1406
bool isContained(const QPointF &a, const QPointF &b, const QPointF &c)
Definition: conductor.cpp:1714
bool m_moving_segment
Attributs related to mouse interaction.
Definition: conductor.h:165
Qt::Alignment m_horizontal_alignment
ConductorSegment * segments
Segments composing the conductor.
Definition: conductor.h:163
~Conductor() override
Conductor::~Conductor Destructor. The conductor is removed from is terminal.
Definition: conductor.cpp:135
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
Conductor::mouseDoubleClickEvent Manage the mouse double click.
Definition: conductor.cpp:597
int type() const override
Definition: conductor.h:75
QPointF posForText(Qt::Orientations &flag)
Conductor::posForText Calculate and return the better pos for text.
Definition: conductor.cpp:1182
QList< Terminal * > relatedPotentialTerminal(const Terminal *terminal, const bool all_diagram)
Conductor::relatedPotentialTerminal Return terminal at the same potential from the same parent elemen...
Definition: terminal.cpp:774
QList< ConductorSegmentProfile * > verticalSegments()
void handlerMouseMoveEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
Conductor::handlerMouseMoveEvent.
Definition: conductor.cpp:778
QIcon sl
Definition: qeticons.cpp:201
bool pathFromXml(const QDomElement &)
Conductor::pathFromXml Generate the path from xml file.
Definition: conductor.cpp:1038
bool orthogonalProjection(const QPointF &, const QLineF &, QPointF *=nullptr)
Definition: qet.cpp:165
QIcon Orientations
Definition: qeticons.cpp:126
QDomElement toXml(QDomDocument &document, const QString &tag_name=QString("sequentialNumbers")) const
sequentialNumbers::toXml export this sequential numbers into a QDomElement.
QRectF boundingRect() const override
Conductor::boundingRect.
Definition: conductor.cpp:862
void setPlainText(const QString &text)
QHash< ConductorSegmentProfile *, qreal > shareOffsetBetweenSegments(const qreal &offset, const QList< ConductorSegmentProfile *> &, const qreal &=0.01) const
Definition: conductor.cpp:285
void setColor(QColor color)
QetGraphicsHandlerItem::setColor.
void setPath(const QPainterPath &path)
Definition: conductor.cpp:1444
QList< QPointF > junctions() const
Definition: conductor.cpp:1724
void fromXml(QDomElement &)
ConductorProperties::fromXml Import conductor propertie, from the attribute of the xml element &#39;e&#39;...
void setProperties(const ConductorProperties &property)
Definition: conductor.cpp:1495
void setPropertyToPotential(const ConductorProperties &property, bool only_text=false)
Conductor::setPropertiesToPotential.
Definition: conductor.cpp:1467
Terminal * terminal2
Definition: conductor.h:68
virtual void setHighlighted(Highlight)
Definition: conductor.cpp:1552
static int xGrid
abscissa grid step size
Definition: diagram.h:76
static int getCoeff(const qreal &, const qreal &)
Definition: conductor.cpp:1371
ConductorSegmentType
Known kinds of conductor segments.
Definition: qet.h:86
QPointF before_mov_text_pos_
Definition: conductor.h:169
qreal length() const
void addHandler()
Conductor::addHandler Add handlers for this item.
Definition: conductor.cpp:829
int m_vector_index
Definition: conductor.h:156
QHash< Qt::Corner, ConductorProfile > ConductorProfilesGroup
Definition: conductor.h:37
QList< ConductorSegmentProfile * > segments
Segments composing the conductor.
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
Definition: conductor.cpp:473
bool isFirstSegment() const
qreal height() const
Diagram * diagram() const
Definition: conductor.cpp:557
Terminal * terminal1
Definition: conductor.h:67
ConductorSegment * middleSegment()
Definition: conductor.cpp:1159
autonum::sequentialNumbers & rSequenceNum()
Definition: conductor.h:123
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
Conductor::mouseReleaseEvent.
Definition: conductor.cpp:619
QPainterPath path() const
Definition: conductor.cpp:1454
ConductorTextItem * textItem() const
Definition: conductor.cpp:564
const QList< ConductorSegment * > segmentsList() const
Definition: conductor.cpp:1134
Vertical segment.
Definition: qet.h:88
ConductorSegment * m_moved_segment
Definition: conductor.h:168
void segmentsToPath()
Conductor::segmentsToPath Generate the QPainterPath from the list of points.
Definition: conductor.cpp:175
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override
Conductor::hoverLeaveEvent Manage the mouse leave event.
Definition: conductor.cpp:641
bool isValid() const
Conductor::isValid.
Definition: conductor.cpp:148
void setUpConnectionForFormula(QString old_formula, QString new_formula)
Conductor::setUpConnectionForFormula setup connection according to the variable of formula...
Definition: conductor.cpp:1684
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override
Conductor::hoverEnterEvent Manage the hover enter event.
Definition: conductor.cpp:630
static bool valideXml(QDomElement &)
Definition: conductor.cpp:573
void pointsToSegments(const QList< QPointF > &)
Definition: conductor.cpp:942
void removeHandler()
Conductor::removeHandler Remove the handlers of this item.
Definition: conductor.cpp:849
QIcon tr
Definition: qeticons.cpp:204
QVector< QPointF > handlerPoints() const
Conductor::handlerPoints.
Definition: conductor.cpp:1116
bool m_freeze_label
Definition: conductor.h:180
qreal m_previous_z_value
Definition: conductor.h:167
ConductorTextItem * m_text_item
Text input for non simple, non-singleline conductors.
Definition: conductor.h:161
uint segmentsCount(QET::ConductorSegmentType=QET::Both) const
Definition: conductor.cpp:900
Invalid segment.
Definition: qet.h:89
Definition: qet.h:205
ConductorProperties m_properties
Functional properties.
Definition: conductor.h:159
void setProfile(const ConductorProfile &, Qt::Corner)
Definition: conductor.cpp:1388
qreal length() const
Conductor::length.
Definition: conductor.cpp:1152
void editProperty()
Conductor::editProperty.
Definition: conductor.cpp:1668
QPointF pos
Definition: conductor.h:46
void handlerMousePressEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
Conductor::handlerMousePressEvent.
Definition: conductor.cpp:755
virtual bool wasRotateByUser() const
ConductorTextItem::wasRotateByUser.
QVector< QetGraphicsHandlerItem * > m_handler_vector
Definition: conductor.h:155
void projectDiagramsOrderChanged(QETProject *, int, int)
void handlerMouseReleaseEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
Conductor::handlerMouseReleaseEvent.
Definition: conductor.cpp:806
Highlight must_highlight_
Define whether and how the conductor should be highlighted.
Definition: conductor.h:178
void calculateTextItemPosition()
Conductor::calculateTextItemPosition Move the text at middle of conductor (if is vertical or horizont...
Definition: conductor.cpp:1253
static QPointF snapToGrid(const QPointF &p)
Diagram::snapToGrid Return a nearest snap point of p.
Definition: diagram.cpp:1653
static Qt::Corner movementType(const QPointF &, const QPointF &)
Definition: conductor.cpp:1851
static QBrush conductor_brush
Definition: conductor.h:184
static QPen conductor_pen
QPen et QBrush objects used to draw conductors.
Definition: conductor.h:183
QPainterPath shape() const override
Conductor::shape.
Definition: conductor.cpp:873
void setFont(const QFont &font)
QPair< QPointF, Qt::Corner > ConductorBend
Definition: conductor.h:34
QUndoStack & undoStack()
Definition: diagram.h:337
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
Conductor::mousePressEvent Manage the mouse press event.
Definition: conductor.cpp:607
void propertiesChange()
QList< ConductorSegmentProfile * > horizontalSegments()
virtual QPainterPath nearShape() const
Conductor::nearShape.
Definition: conductor.cpp:888
void removeConductor(Conductor *conductor)
Terminal::removeConductor Remove a conductor from this terminal.
Definition: terminal.cpp:215
bool m_valid
Definition: conductor.h:179
static void loadSequential(const QDomElement &dom_element, Conductor *conductor)
Definition: conductor.cpp:54
static QPointF movePointIntoPolygon(const QPointF &, const QPainterPath &)
Definition: conductor.cpp:1904
bool has_to_save_profile
Whether the current profile should be saved as soon as possible.
Definition: conductor.h:173
void fromXml(const QDomElement &element)
sequentialNumbers::fromXml Import sequential values from a QDomElement
static QVector< QetGraphicsHandlerItem * > handlerForPoint(const QVector< QPointF > &points, int size=10)
QetGraphicsHandlerItem::handlerForPoint.
static int getSign(const qreal &)
Definition: conductor.cpp:1379
QList< Conductor * > relatedConductors(const Conductor *conductor)
relatedConductors
Definition: conductor.cpp:1972
void updatePath(const QRectF &=QRectF())
Definition: conductor.cpp:159
Conductor * longuestConductorInPotential(Conductor *conductor, bool all_diagram)
longuestConductorInPotential
Definition: conductor.cpp:1956
ConductorProperties properties() const
void adjusteHandlerPos()
Conductor::adjusteHandlerPos Adjust the position of the handler item.
Definition: conductor.cpp:737
QETProject * project() const
Definition: diagram.cpp:1716
void setSequenceNum(const autonum::sequentialNumbers &sn)
Definition: conductor.cpp:1672
static QPointF extendTerminal(const QPointF &, Qet::Orientation, qreal=9.0)
Definition: conductor.cpp:447
Diagram * diagram() const
Definition: terminal.cpp:752
void displayedTextChanged()
Conductor::displayedTextChanged Update the properties (text) of this conductor and other conductors a...
Definition: conductor.cpp:1562
static void loadSequential(const QDomElement &dom_element, const QString &seq, QStringList *list)
Definition: conductor.cpp:44
uint segmentsCount(QET::ConductorSegmentType) const
Horizontal segment.
Definition: qet.h:87
ConductorProperties defaultConductorProperties
Default properties for new conductors.
Definition: diagram.h:72
Qt::Corner currentPathType() const
Definition: conductor.cpp:1862
void setProfiles(const ConductorProfilesGroup &)
Conductor::setProfiles.
Definition: conductor.cpp:1875
void deleteSegments()
Supprime les segments.
Definition: conductor.cpp:1890
void draw(QPainter *, QET::ConductorSegmentType, const QRectF &)
Orientation
Orientation (used for electrical elements and their terminals)
Definition: qet.h:204
virtual Highlight highlight() const
Definition: conductor.cpp:1545
void updateConductorPath(const QPointF &, Qet::Orientation, const QPointF &, Qet::Orientation)
Definition: conductor.cpp:216
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override
Conductor::sceneEventFilter.
Definition: conductor.cpp:694