QElectroTech  0.70
qet.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 "qet.h"
19 #include "qeticons.h"
20 
21 #include <limits>
22 #include <QGraphicsSceneContextMenuEvent>
23 #include <QAction>
24 #include <QFileInfo>
25 #include <QSaveFile>
26 
36  QChar c = s[0];
37  if (c == 'e') return(Qet::East);
38  else if (c == 's') return(Qet::South);
39  else if (c == 'w') return (Qet::West);
40  else return(Qet::North);
41 }
42 
48  QString ret;
49  switch(o) {
50  case Qet::North: ret = "n"; break;
51  case Qet::East : ret = "e"; break;
52  case Qet::South: ret = "s"; break;
53  case Qet::West : ret = "w"; break;
54  }
55  return(ret);
56 }
57 
65  if ((a == Qet::North || a == Qet::South) && (b == Qet::North || b == Qet::South)) return(true);
66  else if ((a == Qet::East || a == Qet::West) && (b == Qet::East || b == Qet::West)) return(true);
67  else return(false);
68 }
69 
77 {
78  bool result = false;
79 
80  switch (a)
81  {
82  case Qet::North:
83  if (b == Qet::South) result = true;
84  break;
85  case Qet::East:
86  if (b == Qet::West) result = true;
87  break;
88  case Qet::South:
89  if (b == Qet::North) result = true;
90  break;
91  case Qet::West:
92  if (b == Qet::East) result = true;
93  break;
94  default:
95  break;
96  }
97 
98  return result;
99 }
100 
107  return(a == Qet::East || a == Qet::West);
108 }
109 
116  return(a == Qet::North || a == Qet::South);
117 }
118 
127  if (o < 0 || o > 2) return(Qet::North);
128  return((Qet::Orientation)(o + 1));
129 }
130 
139  if (o < 0 || o > 3) return(Qet::North);
140  if (o == Qet::North) return(Qet::West);
141  return((Qet::Orientation)(o - 1));
142 }
143 
149 bool QET::lineContainsPoint(const QLineF &line, const QPointF &point) {
150  if (point == line.p1()) return(true);
151  QLineF point_line(line.p1(), point);
152  if (point_line.unitVector() != line.unitVector()) return(false);
153  return(point_line.length() <= line.length());
154 }
155 
165 bool QET::orthogonalProjection(const QPointF &point, const QLineF &line, QPointF *intersection) {
166  // recupere le vecteur normal de `line'
167  QLineF line_normal_vector(line.normalVector());
168  QPointF normal_vector(line_normal_vector.dx(), line_normal_vector.dy());
169 
170  // cree une droite perpendiculaire a `line' passant par `point'
171  QLineF perpendicular_line(point, point + normal_vector);
172 
173  // determine le point d'intersection des deux droites = le projete orthogonal
174  QPointF intersection_point;
175  QLineF::IntersectType it = line.intersect(perpendicular_line, &intersection_point);
176 
177  // ne devrait pas arriver (mais bon...)
178  if (it == QLineF::NoIntersection) return(false);
179 
180  // fournit le point d'intersection a l'appelant si necessaire
181  if (intersection) {
182  *intersection = intersection_point;
183  }
184 
185  // determine si le point d'intersection appartient au segment de droite
186  if (QET::lineContainsPoint(line, intersection_point)) {
187  return(true);
188  }
189  return(false);
190 }
191 
200 bool QET::attributeIsAnInteger(const QDomElement &e, const QString& nom_attribut, int *entier) {
201  // verifie la presence de l'attribut
202  if (!e.hasAttribute(nom_attribut)) return(false);
203  // verifie la validite de l'attribut
204  bool ok;
205  int tmp = e.attribute(nom_attribut).toInt(&ok);
206  if (!ok) return(false);
207  if (entier != nullptr) *entier = tmp;
208  return(true);
209 }
210 
219 bool QET::attributeIsAReal(const QDomElement &e, const QString& nom_attribut, qreal *reel) {
220  // verifie la presence de l'attribut
221  if (!e.hasAttribute(nom_attribut)) return(false);
222  // verifie la validite de l'attribut
223  bool ok;
224  qreal tmp = e.attribute(nom_attribut).toDouble(&ok);
225  if (!ok) return(false);
226  if (reel != nullptr) *reel = tmp;
227  return(true);
228 }
229 
240 QString QET::ElementsAndConductorsSentence(int elements_count, int conductors_count, int texts_count, int images_count, int shapes_count, int element_text_count) {
241  QString text;
242  if (elements_count) {
243  text += QObject::tr(
244  "%n élément(s)",
245  "part of a sentence listing the content of a diagram",
246  elements_count
247  );
248  }
249 
250  if (conductors_count) {
251  if (!text.isEmpty()) text += ", ";
252  text += QObject::tr(
253  "%n conducteur(s)",
254  "part of a sentence listing the content of a diagram",
255  conductors_count
256  );
257  }
258 
259  if (texts_count) {
260  if (!text.isEmpty()) text += ", ";
261  text += QObject::tr(
262  "%n champ(s) de texte",
263  "part of a sentence listing the content of a diagram",
264  texts_count
265  );
266  }
267 
268  if (images_count) {
269  if (!text.isEmpty()) text += ", ";
270  text += QObject::tr(
271  "%n image(s)",
272  "part of a sentence listing the content of a diagram",
273  images_count
274  );
275  }
276 
277  if (shapes_count) {
278  if (!text.isEmpty()) text += ", ";
279  text += QObject::tr(
280  "%n forme(s)",
281  "part of a sentence listing the content of a diagram",
282  shapes_count
283  );
284  }
285 
286  if (element_text_count) {
287  if (!text.isEmpty()) text += ", ";
288  text += QObject::tr(
289  "%n texte(s) d'élément",
290  "part of a sentence listing the content of a diagram",
291  element_text_count);
292  }
293 
294  return(text);
295 }
296 
300 QList<QDomElement> QET::findInDomElement(const QDomElement &e, const QString &tag_name) {
301  QList<QDomElement> return_list;
302  for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
303  if (!node.isElement()) continue;
304  QDomElement element = node.toElement();
305  if (element.isNull() || element.tagName() != tag_name) continue;
306  return_list << element;
307  }
308  return(return_list);
309 }
310 
319 QList<QDomElement> QET::findInDomElement(const QDomElement &e, const QString &parent, const QString &children) {
320  QList<QDomElement> return_list;
321 
322  // parcours des elements parents
323  for (QDomNode enfant = e.firstChild() ; !enfant.isNull() ; enfant = enfant.nextSibling()) {
324  // on s'interesse a l'element XML "parent"
325  QDomElement parents = enfant.toElement();
326  if (parents.isNull() || parents.tagName() != parent) continue;
327  // parcours des enfants de l'element XML "parent"
328  for (QDomNode node_children = parents.firstChild() ; !node_children.isNull() ; node_children = node_children.nextSibling()) {
329  // on s'interesse a l'element XML "children"
330  QDomElement n_children = node_children.toElement();
331  if (!n_children.isNull() && n_children.tagName() == children) return_list.append(n_children);
332  }
333  }
334  return(return_list);
335 }
336 
338 QString QET::license() {
339  // Recuperation du texte de la GNU/GPL dans un fichier integre a l'application
340  QFile *file_license = new QFile(":/LICENSE");
341  QString txt_license;
342  // verifie que le fichier existe
343  if (!file_license -> exists()) {
344  txt_license = QString(QObject::tr("Le fichier texte contenant la licence GNU/GPL est introuvable - bon bah de toute façon, vous la connaissez par coeur non ?"));
345  } else {
346  // ouvre le fichier en mode texte et en lecture seule
347  if (!file_license -> open(QIODevice::ReadOnly | QIODevice::Text)) {
348  txt_license = QString(QObject::tr("Le fichier texte contenant la licence GNU/GPL existe mais n'a pas pu être ouvert - bon bah de toute façon, vous la connaissez par coeur non ?"));
349  } else {
350  // charge le contenu du fichier dans une QString
351  QTextStream in(file_license);
352  txt_license = QString("");
353  while (!in.atEnd()) txt_license += in.readLine()+"\n";
354  // ferme le fichier
355  file_license -> close();
356  }
357  }
358  return(txt_license);
359 };
360 
361 
366 QList<QChar> QET::forbiddenCharacters() {
367  return(QList<QChar>() << '\\' << '/' << ':' << '*' << '?' << '"' << '<' << '>' << '|');
368 }
369 
379 QString QET::stringToFileName(const QString &name) {
380  QString file_name(name.toLower());
381 
382  // remplace les caracteres interdits par des tirets
383  foreach(QChar c, QET::forbiddenCharacters()) {
384  file_name.replace(c, '-');
385  }
386 
387  // remplace les espaces par des underscores
388  file_name.replace(' ', '_');
389 
390  return(file_name);
391 }
392 
398 QString QET::escapeSpaces(const QString &string) {
399  return(QString(string).replace('\\', "\\\\").replace(' ', "\\ "));
400 }
401 
407 QString QET::unescapeSpaces(const QString &string) {
408  return(QString(string).replace("\\\\", "\\").replace("\\ ", " "));
409 }
410 
417 QString QET::joinWithSpaces(const QStringList &string_list) {
418  QString returned_string;
419 
420  for (int i = 0 ; i < string_list.count() ; ++ i) {
421  returned_string += QET::escapeSpaces(string_list.at(i));
422  if (i != string_list.count() - 1) returned_string += " ";
423  }
424 
425  return(returned_string);
426 }
427 
434 QStringList QET::splitWithSpaces(const QString &string) {
435  // les chaines sont separees par des espaces non echappes = avec un nombre nul ou pair de backslashes devant
436  QStringList escaped_strings = string.split(QRegExp("[^\\]?(?:\\\\)* "), QString::SkipEmptyParts);
437 
438  QStringList returned_list;
439  foreach(QString escaped_string, escaped_strings) {
440  returned_list << QET::unescapeSpaces(escaped_string);
441  }
442  return(returned_list);
443 }
444 
449 QString Qet::endTypeToString(const Qet::EndType &end_type) {
450  switch(end_type) {
451  case Qet::Simple: return("simple");
452  case Qet::Triangle: return("triangle");
453  case Qet::Circle: return("circle");
454  case Qet::Diamond: return("diamond");
455  case Qet::None:
456  default:
457  return("none");
458  }
459 }
460 
466 Qet::EndType Qet::endTypeFromString(const QString &string) {
467  if (string == "simple") return(Qet::Simple);
468  else if (string == "triangle") return(Qet::Triangle);
469  else if (string == "circle") return(Qet::Circle);
470  else if (string == "diamond") return(Qet::Diamond);
471  else return(Qet::None);
472 }
473 
478 QString QET::diagramAreaToString(const QET::DiagramArea &diagram_area) {
479  if (diagram_area == ElementsArea) return("elements");
480  else return("border");
481 }
482 
489  if (!string.compare("border", Qt::CaseInsensitive)) return(QET::BorderArea);
490  else return(QET::ElementsArea);
491 }
492 
497 qreal QET::round(qreal x, qreal epsilon) {
498  return(int(x * epsilon) / epsilon);
499 }
500 
505 qreal QET::correctAngle(const qreal &angle) {
506  // ramene l'angle demande entre -360.0 et +360.0 degres
507  qreal corrected_angle = angle;
508  while (corrected_angle <= -360.0) corrected_angle += 360.0;
509  while (corrected_angle >= 360.0) corrected_angle -= 360.0;
510  return(corrected_angle);
511 }
512 
519 bool QET::compareCanonicalFilePaths(const QString &first, const QString &second) {
520  QString first_canonical_path = QFileInfo(first).canonicalFilePath();
521  if (first_canonical_path.isEmpty()) return(false);
522 
523  QString second_canonical_path = QFileInfo(second).canonicalFilePath();
524  if (second_canonical_path.isEmpty()) return(false);
525 
526 #ifdef Q_OS_WIN
527  // sous Windows, on ramene les chemins en minuscules
528  first_canonical_path = first_canonical_path.toLower();
529  second_canonical_path = second_canonical_path.toLower();
530 #endif
531 
532  return(first_canonical_path == second_canonical_path);
533 }
534 
544 bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *error_message)
545 {
546  QSaveFile file(filepath);
547 
548  // Note: we do not set QIODevice::Text to avoid generating CRLF end of lines
549  bool file_opening = file.open(QIODevice::WriteOnly);
550  if (!file_opening)
551  {
552  if (error_message)
553  {
554  *error_message = QString(QObject::tr("Impossible d'ouvrir le fichier %1 en écriture, erreur %2 rencontrée.",
555  "error message when attempting to write an XML file")).arg(filepath).arg(file.error());
556  }
557  return(false);
558  }
559 
560  QTextStream out(&file);
561  out.setCodec("UTF-8");
562  out.setGenerateByteOrderMark(false);
563  out << xml_doc.toString(4);
564  if (!file.commit())
565  {
566  if (error_message) {
567  *error_message = QString(QObject::tr("Une erreur est survenue lors de l'écriture du fichier %1, erreur %2 rencontrée.",
568  "error message when attempting to write an XML file")).arg(filepath).arg(file.error());
569  }
570 
571  return false;
572  }
573 
574  return(true);
575 }
576 
584 bool QET::eachStrIsEqual(const QStringList &qsl) {
585  if (qsl.size() == 1) return true;
586  foreach (const QString t, qsl) {
587  if (qsl.at(0) != t) return false;
588  }
589  return true;
590 }
591 
598 {
599  switch (c)
600  {
601  case Common :
602  return "common";
603  case Custom :
604  return "custom";
605  case Embedded :
606  return "embedded";
607  default:
608  return "common";
609  }
610 }
611 
619 {
620  if (str == "common")
621  return QetCollection::Common;
622  else if (str == "custom")
623  return QetCollection::Custom;
624  else if (str == "embedded")
626  else
627  return QetCollection::Common;
628 }
629 
636 QActionGroup *QET::depthActionGroup(QObject *parent)
637 {
638  QActionGroup *action_group = new QActionGroup(parent);
639 
640  QAction *edit_forward = new QAction(QET::Icons::BringForward, QObject::tr("Amener au premier plan"), action_group);
641  QAction *edit_raise = new QAction(QET::Icons::Raise, QObject::tr("Rapprocher"), action_group);
642  QAction *edit_lower = new QAction(QET::Icons::Lower, QObject::tr("Éloigner"), action_group);
643  QAction *edit_backward = new QAction(QET::Icons::SendBackward, QObject::tr("Envoyer au fond"), action_group);
644 
645  edit_forward ->setStatusTip(QObject::tr("Ramène la ou les sélections au premier plan"));
646  edit_raise ->setStatusTip(QObject::tr("Rapproche la ou les sélections"));
647  edit_lower ->setStatusTip(QObject::tr("Éloigne la ou les sélections"));
648  edit_backward->setStatusTip(QObject::tr("Envoie en arrière plan la ou les sélections"));
649 
650  edit_raise ->setShortcut(QKeySequence(QObject::tr("Ctrl+Shift+Up")));
651  edit_lower ->setShortcut(QKeySequence(QObject::tr("Ctrl+Shift+Down")));
652  edit_backward->setShortcut(QKeySequence(QObject::tr("Ctrl+Shift+End")));
653  edit_forward ->setShortcut(QKeySequence(QObject::tr("Ctrl+Shift+Home")));
654 
655  edit_forward ->setData(QET::BringForward);
656  edit_raise ->setData(QET::Raise);
657  edit_lower ->setData(QET::Lower);
658  edit_backward->setData(QET::SendBackward);
659 
660  return action_group;
661 }
662 
663 bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message)
664 {
665  bool opened_here = file->isOpen() ? false : true;
666 
667  if (!file->isOpen())
668  {
669  bool open_ = file->open(QIODevice::WriteOnly);
670  if (!open_)
671  {
672  if (error_message)
673  {
674  QFileInfo info_(*file);
675  *error_message = QString(
676  QObject::tr("Impossible d'ouvrir le fichier %1 en écriture, erreur %2 rencontrée.",
677  "error message when attempting to write an XML file")
678  ).arg(info_.absoluteFilePath()).arg(file->error());
679  }
680  return false;
681  }
682  }
683 
684  QTextStream out(file);
685  out.seek(0);
686  out.setCodec("UTF-8");
687  out.setGenerateByteOrderMark(false);
688  out << xml_doc.toString(4);
689  if (opened_here) {
690  file->close();
691  }
692 
693  return(true);
694 }
EndType
This enum lists the various available endings for line primitives when drawing an electrical element...
Definition: qet.h:191
bool attributeIsAnInteger(const QDomElement &, const QString &, int *=nullptr)
Definition: qet.cpp:200
QET::DiagramArea diagramAreaFromString(const QString &)
Definition: qet.cpp:488
Send item one layer below their current one; zValues are decremented.
Definition: qet.h:46
QStringList splitWithSpaces(const QString &)
Definition: qet.cpp:434
From an embedded collection (a project for exemple)
Definition: qet.h:153
Raise item one layer above their current one; zValues are incremented.
Definition: qet.h:45
QIcon SendBackward
Definition: qeticons.cpp:158
From user collection.
Definition: qet.h:152
static Qet::Orientation nextOrientation(Qet::Orientation)
Definition: qet.cpp:126
Definition: qet.h:207
Circle.
Definition: qet.h:195
static bool isHorizontal(Qet::Orientation)
Qet::isHorizontal.
Definition: qet.cpp:106
Regular line.
Definition: qet.h:192
DiagramArea
Definition: qet.h:137
Diamond.
Definition: qet.h:196
QString license()
Definition: qet.cpp:338
QString joinWithSpaces(const QStringList &)
Definition: qet.cpp:417
bool compareCanonicalFilePaths(const QString &, const QString &)
Definition: qet.cpp:519
bool orthogonalProjection(const QPointF &, const QLineF &, QPointF *=nullptr)
Definition: qet.cpp:165
QetCollection
Enum used to specify the origin of a collection of thing (title block, element etc...)
Definition: qet.h:150
QString diagramAreaToString(const QET::DiagramArea &)
Definition: qet.cpp:478
Bring item to the foreground so they have the highest zValue.
Definition: qet.h:44
QString qetCollectionToString(const QetCollection &c)
QET::qetCollectionToString.
Definition: qet.cpp:597
static Qet::Orientation orientationFromString(const QString &)
Definition: qet.cpp:35
static QString endTypeToString(const Qet::EndType &)
Definition: qet.cpp:449
QetCollection qetCollectionFromString(const QString &str)
QET::qetCollectionFromString.
Definition: qet.cpp:618
static bool isOpposed(Qet::Orientation a, Qet::Orientation b)
Qet::isOpposed.
Definition: qet.cpp:76
qreal correctAngle(const qreal &)
Definition: qet.cpp:505
qreal round(qreal, qreal)
Definition: qet.cpp:497
QString ElementsAndConductorsSentence(int, int, int=0, int=0, int=0, int=0)
Definition: qet.cpp:240
Export the content of the diagram only.
Definition: qet.h:139
bool writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message=nullptr)
Definition: qet.cpp:663
QList< QDomElement > findInDomElement(const QDomElement &, const QString &)
Definition: qet.cpp:300
QIcon tr
Definition: qeticons.cpp:204
bool writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *error_message=nullptr)
Definition: qet.cpp:544
static bool surLeMemeAxe(Qet::Orientation, Qet::Orientation)
Definition: qet.cpp:64
static Qet::EndType endTypeFromString(const QString &)
Definition: qet.cpp:466
static QString orientationToString(Qet::Orientation)
Definition: qet.cpp:47
QIcon Lower
Definition: qeticons.cpp:117
Triangle.
Definition: qet.h:194
Definition: qet.h:205
QString stringToFileName(const QString &)
Definition: qet.cpp:379
bool attributeIsAReal(const QDomElement &, const QString &, qreal *=nullptr)
Definition: qet.cpp:219
QString escapeSpaces(const QString &)
Definition: qet.cpp:398
Base-less triangle.
Definition: qet.h:193
static bool isVertical(Qet::Orientation)
Qet::isVertical.
Definition: qet.cpp:115
bool lineContainsPoint(const QLineF &, const QPointF &)
Definition: qet.cpp:149
QString unescapeSpaces(const QString &)
Definition: qet.cpp:407
Send item to the background so they have the lowest zValue.
Definition: qet.h:47
From common collection.
Definition: qet.h:151
Export the diagram along with its border and title block.
Definition: qet.h:138
QActionGroup * depthActionGroup(QObject *parent=nullptr)
QET::depthActionGroup.
Definition: qet.cpp:636
QIcon it
Definition: qeticons.cpp:196
QIcon Raise
Definition: qeticons.cpp:154
static Qet::Orientation previousOrientation(Qet::Orientation)
Definition: qet.cpp:138
QList< QChar > forbiddenCharacters()
Definition: qet.cpp:366
QIcon BringForward
Definition: qeticons.cpp:33
Orientation
Orientation (used for electrical elements and their terminals)
Definition: qet.h:204
bool eachStrIsEqual(const QStringList &qsl)
QET::eachStrIsEqual.
Definition: qet.cpp:584