mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-14 17:44:13 +02:00
e89ccf2a40
A .qetz now carries index.sqlite: the project database as QElectroTech keeps it -- every table but the drawing items, which exist for built folios only -- with each folio's own data (Diagram::headerXml()) and a stamp of the schema it was written with (projectDataBase::indexFile(), QETProject::writeContainerFile(): Save, --resave, --set-titleblock, a script's and live mode's save). Written when every folio, symbol and wire has a uuid of its own; a reader that does not know the entry skips it. With QET_LAZY_FOLIOS=1, a .qetz with an index opens from it: the project document is joined without its folios (QetContainer::join() with folio_files), the database and the stores of symbol information and wire properties are filled from the index (fillFromIndex()), each folio is made from its own data and keeps the name of its part, read when the folio is built (Diagram::deferredXml(), QETProject::readFolioPart(), QetContainer::readFolio() -- pictures and engineering data back from project.sqlite, QetContainerDb::restoreFolio()). A terminal strip finds its terminals' folios in the database. Any other .qetz, or one whose index cannot be used, is read whole as before. The index is untrusted: refused unless written with this database's schema, every table read a plain table (not a view), nothing in its schema run (trusted_schema off); refused, the project is read whole. Checked: an index of garbage, a wrong stamp, a table made a view -- each refused and the project saved as a plain save. The 136-folio f2099 as .qetz opens in 1.8 s (3.65 s read whole, 29 s building every folio); what is left is mostly its 7 MB of symbol definitions (0.8 s). The file grows from 2.6 to 5.2 MB, the index overlapping project.sqlite until D3. tst_lazyfolios: a .qetz opened from its index builds only the terminal strip's folios, numbering read as when built, every folio shown in turn draws and links as when built, the save as when built; fails when a folio part's engineering data is not put back. Corpus, 342 projects: 340 .qetz carry an index and open from it (2 have symbols sharing a uuid); saved back as .qet = eager = master on all 342. Probe on industrial and f2099 .qetz: every folio as eager but the 3 render-noise folios of f2099. In a real window the .qetz opens and pages building only shown folios and partners. ctest 96/96 with and without QET_LAZY_FOLIOS=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
535 lines
16 KiB
C++
535 lines
16 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef DIAGRAM_H
|
|
#define DIAGRAM_H
|
|
#include "autoNum/numerotationcontext.h"
|
|
#include "bordertitleblock.h"
|
|
#include "conductorproperties.h"
|
|
#include "elementsmover.h"
|
|
#include "elementtextsmover.h"
|
|
#include "exportproperties.h"
|
|
#include "properties/xrefproperties.h"
|
|
#include "qgimanager.h"
|
|
|
|
#include <QHash>
|
|
#include <QPointer>
|
|
#include <QUuid>
|
|
#include <QtWidgets>
|
|
#include <QtXml>
|
|
|
|
class Conductor;
|
|
class CablePart;
|
|
class CustomElement;
|
|
class DiagramContent;
|
|
class DiagramPosition;
|
|
class DiagramTextItem;
|
|
class Element;
|
|
class ElementsLocation;
|
|
class DiagramImageItem;
|
|
class DiagramEventInterface;
|
|
class DiagramFolioList;
|
|
class QETProject;
|
|
struct GuideProperties;
|
|
|
|
/**
|
|
@brief The Diagram class
|
|
This class represents an electric diagram.
|
|
It manages its various child elements,
|
|
conductors and texts and handles their graphic rendering.
|
|
*/
|
|
class Diagram : public QGraphicsScene
|
|
{
|
|
friend DiagramFolioList;
|
|
friend QETProject;
|
|
|
|
Q_OBJECT
|
|
|
|
// constructors, destructor
|
|
private:
|
|
Diagram(QETProject *project);
|
|
~Diagram() override;
|
|
Diagram(const Diagram &diagram);
|
|
|
|
// ATTRIBUTES
|
|
public:
|
|
struct Guide {
|
|
enum Orientation { Horizontal, Vertical };
|
|
Orientation orientation;
|
|
qreal position;
|
|
QColor color;
|
|
};
|
|
|
|
/**
|
|
@brief The BorderOptions enum
|
|
Represents available options when rendering a particular diagram:
|
|
EmptyBorder: display border only
|
|
TitleBlock: display title block
|
|
Columns: display columns
|
|
*/
|
|
enum BorderOptions { EmptyBorder, TitleBlock, Columns };
|
|
/// Represents available option of Numerotation type.
|
|
enum NumerotationType { Conductors };
|
|
/// Default properties for new conductors
|
|
ConductorProperties defaultConductorProperties;
|
|
/// Diagram dimensions and title block
|
|
BorderTitleBlock border_and_titleblock;
|
|
/// abscissa grid step size
|
|
static int xGrid;
|
|
/// ordinate grid step size
|
|
static int yGrid;
|
|
/// Key grid x step size
|
|
static int xKeyGrid;
|
|
/// Key grid y step size
|
|
static int yKeyGrid;
|
|
/// Key grid fine x step size
|
|
static int xKeyGridFine;
|
|
/// Key grid fine y step size
|
|
static int yKeyGridFine;
|
|
/// margin around the diagram
|
|
static const qreal margin;
|
|
/// background color of diagram
|
|
static QColor background_color;
|
|
/// Hash containing max values for folio sequential autonums in this diagram
|
|
QHash <QString, QStringList> m_elmt_unitfolio_max;
|
|
QHash <QString, QStringList> m_elmt_tenfolio_max;
|
|
QHash <QString, QStringList> m_elmt_hundredfolio_max;
|
|
/// Hash containing max values for folio sequential autonums in this diagram
|
|
QHash <QString, QStringList> m_cnd_unitfolio_max;
|
|
QHash <QString, QStringList> m_cnd_tenfolio_max;
|
|
QHash <QString, QStringList> m_cnd_hundredfolio_max;
|
|
|
|
private:
|
|
QGraphicsLineItem *conductor_setter_;
|
|
ElementsMover m_elements_mover;
|
|
ElementTextsMover m_element_texts_mover;
|
|
QGIManager *qgi_manager_;
|
|
QETProject *m_project;
|
|
QPointer<QGraphicsObject> m_keyboard_item;
|
|
|
|
QDomDocument xml_document_;
|
|
|
|
qreal diagram_qet_version_;
|
|
|
|
bool draw_grid_;
|
|
bool m_inverted_lightness = false;
|
|
bool use_border_;
|
|
bool draw_guides_;
|
|
QList<Diagram::Guide> m_guides_list;
|
|
bool draw_terminals_;
|
|
bool draw_terminal_names_;
|
|
bool draw_colored_conductors_;
|
|
|
|
QString m_conductors_autonum_name;
|
|
DiagramEventInterface *m_event_interface;
|
|
|
|
bool m_freeze_new_elements;
|
|
bool m_freeze_new_conductors_;
|
|
QUuid m_uuid = QUuid::createUuid();
|
|
/// The XML of this folio's items, while they are not built yet
|
|
QDomElement m_deferred;
|
|
QString m_deferred_part;
|
|
QString m_opened_header;
|
|
QByteArray m_kept_text;
|
|
bool m_loading_deferred = false;
|
|
|
|
//Selection before the current click, see completeGroupSelection()
|
|
QList<QPointer<QGraphicsObject>> m_previous_selection;
|
|
void rememberSelection();
|
|
//Member of a wholly selected group under the current click, which
|
|
//the click picks out on its own if it ends without a drag
|
|
QPointer<QGraphicsObject> m_member_to_pick;
|
|
|
|
bool uuidUsedByOtherDiagram(const QUuid &uuid) const;
|
|
QUuid derivedUuid(const QDomElement &root, const QString &reason) const;
|
|
|
|
//Wires of the loaded file whose ends could not be found
|
|
QStringList m_wires_not_reconnected;
|
|
|
|
// METHODS
|
|
protected:
|
|
void drawBackground(QPainter *, const QRectF &) override;
|
|
|
|
void mouseDoubleClickEvent (
|
|
QGraphicsSceneMouseEvent *event) override;
|
|
void mousePressEvent (QGraphicsSceneMouseEvent *event) override;
|
|
void mouseMoveEvent (QGraphicsSceneMouseEvent *event) override;
|
|
void mouseReleaseEvent (
|
|
QGraphicsSceneMouseEvent *event) override;
|
|
void wheelEvent (QGraphicsSceneWheelEvent *event) override;
|
|
void keyPressEvent (QKeyEvent *event) override;
|
|
void keyReleaseEvent (QKeyEvent *) override;
|
|
bool event(QEvent *event) override;
|
|
|
|
private:
|
|
void selectNextItem(bool forward);
|
|
|
|
public:
|
|
void correctTextPos(Element* elmt);
|
|
void restoreText(Element* elmt);
|
|
QUuid uuid();
|
|
QStringList wiresNotReconnected() const;
|
|
void setEventInterface (DiagramEventInterface *event_interface);
|
|
void clearEventInterface();
|
|
|
|
//methods related to autonum
|
|
QString conductorsAutonumName() const;
|
|
void setConductorsAutonumName(const QString &name);
|
|
|
|
static bool clipboardMayContainDiagram();
|
|
|
|
// methods related to parent project
|
|
QETProject *project() const;
|
|
int folioIndex() const;
|
|
void showMe() {emit showDiagram(this);}
|
|
bool isReadOnly() const;
|
|
|
|
// methods related to conductor creation
|
|
void setConductor(bool);
|
|
void setConductorStart (QPointF);
|
|
void setConductorStop(QPointF);
|
|
QList < QSet <Conductor *> > potentials();
|
|
|
|
// methods related to XML import/export
|
|
QDomDocument toXml(bool wholeContent = true, bool is_copy_command = false);
|
|
void writeHeader(QDomDocument &document, QDomElement &dom_root);
|
|
bool initFromXml(QDomElement &,
|
|
QPointF = QPointF(),
|
|
bool = true,
|
|
DiagramContent * = nullptr);
|
|
/// false while this folio's items wait in its XML to be
|
|
/// built (QET_LAZY_FOLIOS)
|
|
bool isLoaded() const {return m_deferred.isNull() && m_deferred_part.isEmpty();}
|
|
QDomElement deferredXml();
|
|
/// The folio's items are in the part @p file of the .qetz the
|
|
/// project was opened from, read when they are first needed
|
|
/// (stage D2)
|
|
void setDeferredPart(const QString &file) {m_deferred = QDomElement(); m_deferred_part = file;}
|
|
QString headerXml();
|
|
/// The folio's text in the file it was read from, while it is
|
|
/// not built (QETProject::toXml())
|
|
QByteArray keptText() const {return m_kept_text;}
|
|
void setKeptText(const QByteArray &text) {m_kept_text = text;}
|
|
/// Remember the folio's own data as it is now, for
|
|
/// headerChanged() (QETProject::toXml())
|
|
void rememberHeader() {m_opened_header = headerXml();}
|
|
bool headerChanged() {return headerXml() != m_opened_header;}
|
|
void ensureLoaded();
|
|
bool buildDeferred();
|
|
//The last argument is only for a copy fragment: when it is
|
|
//given, the cable lines that fragment carries are built as
|
|
//well and handed back through it -- they are not part of a
|
|
//DiagramContent, so the caller has to know about them to
|
|
//put them on the undo stack (see CableCopy)
|
|
bool fromXml(QDomDocument &,
|
|
QPointF = QPointF(),
|
|
bool = true,
|
|
DiagramContent * = nullptr,
|
|
QList<CablePart *> * = nullptr);
|
|
bool fromXml(QDomElement &,
|
|
QPointF = QPointF(),
|
|
bool = true,
|
|
DiagramContent * = nullptr,
|
|
QList<CablePart *> * = nullptr);
|
|
void folioSequentialsToXml(QHash<QString,
|
|
QStringList>*,
|
|
QDomElement *,
|
|
const QString&,
|
|
const QString&,
|
|
QDomDocument *);
|
|
void folioSequentialsFromXml(const QDomElement&,
|
|
QHash<QString,
|
|
QStringList>*,
|
|
const QString&,
|
|
const QString&,
|
|
const QString&,
|
|
const QString&);
|
|
|
|
void refreshContents();
|
|
|
|
// methods related to graphics items addition/removal on the diagram
|
|
virtual void addItem (QGraphicsItem *item);
|
|
virtual void removeItem (QGraphicsItem *item);
|
|
bool eventInterfaceIsRunning() const;
|
|
bool cableToolIsRunning() const;
|
|
|
|
// methods related to graphics options
|
|
ExportProperties applyProperties(const ExportProperties &);
|
|
void setDisplayGrid(bool);
|
|
bool displayGrid();
|
|
void setInvertedLightness(bool);
|
|
void setDisplayGuides(bool);
|
|
bool displayGuides();
|
|
void updateProjectGuides(const QList<GuideProperties> &guides);
|
|
void setUseBorder(bool);
|
|
bool useBorder();
|
|
void setBorderOptions(BorderOptions);
|
|
BorderOptions borderOptions();
|
|
DiagramPosition convertPosition(const QPointF &);
|
|
static QPointF snapToGrid(const QPointF &p);
|
|
static QPointF snapToTextGrid(const QPointF &p);
|
|
|
|
bool drawTerminals() const;
|
|
void setDrawTerminals(bool);
|
|
bool drawTerminalNames() const;
|
|
void setDrawTerminalNames(bool);
|
|
bool drawColoredConductors() const;
|
|
void setDrawColoredConductors(bool);
|
|
|
|
QString title() const;
|
|
bool toPaintDevice(QPaintDevice &, int = -1, int = -1,
|
|
Qt::AspectRatioMode = Qt::KeepAspectRatio);
|
|
QSize imageSize() const;
|
|
QRectF visibleItemsBoundingRect() const;
|
|
|
|
bool isEmpty() const;
|
|
|
|
QList<Element *> elements() const;
|
|
QList<Conductor *> conductors() const;
|
|
QSet<Conductor *> selectedConductors() const;
|
|
DiagramContent content() const;
|
|
bool canRotateSelection() const;
|
|
ElementsMover &elementsMover();
|
|
ElementTextsMover &elementTextsMover();
|
|
bool usesElement(const ElementsLocation &);
|
|
bool usesTitleBlockTemplate(const QString &);
|
|
|
|
QUndoStack &undoStack();
|
|
QGIManager &qgiManager();
|
|
/// The item that gets keys before the view's own shortcuts, while
|
|
/// it is in a state Escape must end (a picture whose handle is
|
|
/// dragged). nullptr the rest of the time.
|
|
void setKeyboardItem(QGraphicsObject *item) { m_keyboard_item = item; }
|
|
QGraphicsObject *keyboardItem() const { return m_keyboard_item; }
|
|
|
|
//methods related to element label Update Policy
|
|
void freezeElements(bool freeze);
|
|
void unfreezeElements();
|
|
void setFreezeNewElements(bool);
|
|
bool freezeNewElements();
|
|
|
|
//methods related to conductor label Update Policy
|
|
void freezeConductors(bool freeze);
|
|
void setFreezeNewConductors(bool);
|
|
bool freezeNewConductors();
|
|
|
|
//methods related to insertion and loading of folio sequential
|
|
void insertFolioSeqHash (QHash<QString, QStringList> *hash,
|
|
const QString& title,
|
|
const QString& seq,
|
|
NumerotationContext *nc);
|
|
void loadFolioSeqHash (QHash<QString, QStringList> *hash,
|
|
const QString& title, const QString& seq,
|
|
NumerotationContext *nc);
|
|
void changeZValue(QET::DepthOption option);
|
|
void setItemGroup(QGraphicsItem *item, const QUuid &group);
|
|
|
|
public slots:
|
|
void adjustSceneRect ();
|
|
void titleChanged();
|
|
void titleBlockTemplateChanged(const QString &);
|
|
void titleBlockTemplateRemoved(const QString &,
|
|
const QString & = QString());
|
|
void setTitleBlockTemplate(const QString &);
|
|
void loadElmtFolioSeq();
|
|
void loadCndFolioSeq();
|
|
|
|
// methods related to graphics items selection
|
|
void selectAll();
|
|
void deselectAll();
|
|
void invertSelection();
|
|
void selectAllConductors();
|
|
void selectAllTextFields();
|
|
void completeGroupSelection();
|
|
|
|
signals:
|
|
void showDiagram (Diagram *);
|
|
void usedTitleBlockTemplateChanged(const QString &);
|
|
void diagramTitleChanged(Diagram *);
|
|
|
|
/// Signal emitted when users wish to locate an element
|
|
/// from the diagram within elements collection
|
|
void findElementRequired(const ElementsLocation &);
|
|
|
|
void diagramActivated();
|
|
void diagramInformationChanged();
|
|
|
|
/// Emitted by setItemGroup(): an item joined or left a group
|
|
/// without the selection changing (#1144)
|
|
void itemGroupChanged();
|
|
};
|
|
Q_DECLARE_METATYPE(Diagram *)
|
|
|
|
/**
|
|
@brief Diagram::setConductor
|
|
Display or hide the conductor setter,
|
|
i.e. a dashed conductor stub which appears
|
|
when creating a conductor between two terminals.
|
|
@param adding true add conductor ,false remove conductor
|
|
*/
|
|
inline void Diagram::setConductor(bool adding) {
|
|
if (adding) {
|
|
if (!conductor_setter_ -> scene()) addItem(conductor_setter_);
|
|
} else {
|
|
if (conductor_setter_ -> scene()) removeItem(conductor_setter_);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::setConductorStart
|
|
Set the start point of the conductor setter.
|
|
@param start the point (in scene coordinates) which the newly created
|
|
conductor should start from.
|
|
*/
|
|
inline void Diagram::setConductorStart(QPointF start) {
|
|
conductor_setter_ -> setLine(QLineF(start, conductor_setter_ -> line().p2()));
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::setConductorStop
|
|
Set the end point of the conductor setter.
|
|
@param end the point (in scene coordinates) upon to which the newly created
|
|
conductor should be drawn.
|
|
*/
|
|
inline void Diagram::setConductorStop(QPointF end) {
|
|
conductor_setter_ -> setLine(QLineF(conductor_setter_ -> line().p1(), end));
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::setDisplayGrid
|
|
Set whether the diagram grid should be drawn.
|
|
@param dg true to render the grid, false otherwise.
|
|
*/
|
|
inline void Diagram::setDisplayGrid(bool dg) {
|
|
draw_grid_ = dg;
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::setInvertedLightness
|
|
Tell the diagram whether the view painting it will show the result
|
|
with its lightness inverted (PaletteGraphicsView on a dark palette).
|
|
drawBackground draws a softer grid in that case. Printing
|
|
and export never set this.
|
|
@param inverted
|
|
*/
|
|
inline void Diagram::setInvertedLightness(bool inverted) {
|
|
m_inverted_lightness = inverted;
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::displayGrid
|
|
@return draw_grid_ true if the grid is drawn, false otherwise.
|
|
*/
|
|
inline bool Diagram::displayGrid() {
|
|
return(draw_grid_);
|
|
}
|
|
|
|
inline void Diagram::setDisplayGuides(bool dg) {
|
|
if (draw_guides_ != dg) {
|
|
draw_guides_ = dg;
|
|
update();
|
|
}
|
|
}
|
|
|
|
inline bool Diagram::displayGuides() {
|
|
return(draw_guides_);
|
|
}
|
|
/**
|
|
@brief Diagram::setUseBorder
|
|
Set whether the diagram border (including rows/columns headers and the title
|
|
block) should be rendered along with the diagram. When set to false, the size
|
|
of the smallest rectangle containing all items is considered as the diagram
|
|
size.
|
|
@param ub true to take the border into account, false otherwise
|
|
*/
|
|
inline void Diagram::setUseBorder(bool ub) {
|
|
use_border_ = ub;
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::useBorder
|
|
@return use_border_ true if the border is rendered and take into account,
|
|
false otherwise.
|
|
*/
|
|
inline bool Diagram::useBorder() {
|
|
return(use_border_);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::setBorderOptions
|
|
Set the rendering options for the diagram border (including rows/columns
|
|
headers and the title block)
|
|
@param bo Enabled options ORed together
|
|
@see BorderOptions
|
|
*/
|
|
inline void Diagram::setBorderOptions(Diagram::BorderOptions bo) {
|
|
border_and_titleblock.displayBorder(!(bo & EmptyBorder));
|
|
border_and_titleblock.displayColumns(bo & Columns);
|
|
border_and_titleblock.displayTitleBlock(bo & TitleBlock);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::borderOptions
|
|
@return The rendering options for the diagram border
|
|
@see setBorderOptions
|
|
*/
|
|
inline Diagram::BorderOptions Diagram::borderOptions() {
|
|
BorderOptions options = EmptyBorder;
|
|
if (border_and_titleblock.titleBlockIsDisplayed())
|
|
options = (BorderOptions)(options|TitleBlock);
|
|
if (border_and_titleblock.columnsAreDisplayed())
|
|
options = (BorderOptions)(options|Columns);
|
|
return(options);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::qgiManager
|
|
@return the diagram graphics item manager
|
|
*/
|
|
inline QGIManager &Diagram::qgiManager() {
|
|
return(*qgi_manager_);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::drawTerminals
|
|
@return true if terminals are rendered, false otherwise
|
|
*/
|
|
inline bool Diagram::drawTerminals() const
|
|
{
|
|
return(draw_terminals_);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::drawTerminalNames
|
|
@return true if terminal names are rendered, false otherwise
|
|
*/
|
|
inline bool Diagram::drawTerminalNames() const
|
|
{
|
|
return(draw_terminal_names_);
|
|
}
|
|
|
|
/**
|
|
@brief Diagram::drawColoredConductors
|
|
@return true if conductors colors are rendered, false otherwise.
|
|
*/
|
|
inline bool Diagram::drawColoredConductors() const
|
|
{
|
|
return(draw_colored_conductors_);
|
|
}
|
|
|
|
#endif
|