mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-06 03:04:13 +02:00
Merge pull request #1282 from arummler/formula-improvements
Auto numeration: a lot of changes for elements, condutors, sheets
This commit is contained in:
@@ -324,6 +324,18 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/autoNum/numerotationcontext.h
|
||||
${QET_DIR}/sources/autoNum/renumberelementscommand.cpp
|
||||
${QET_DIR}/sources/autoNum/renumberelementscommand.h
|
||||
${QET_DIR}/sources/autoNum/autonumschemecommand.cpp
|
||||
${QET_DIR}/sources/autoNum/autonumschemecommand.h
|
||||
${QET_DIR}/sources/autoNum/elementautonumschemecommand.cpp
|
||||
${QET_DIR}/sources/autoNum/elementautonumschemecommand.h
|
||||
${QET_DIR}/sources/autoNum/ui/counterwarning.cpp
|
||||
${QET_DIR}/sources/autoNum/ui/counterwarning.h
|
||||
${QET_DIR}/sources/autoNum/ui/pastenumberingimport.cpp
|
||||
${QET_DIR}/sources/autoNum/ui/pastenumberingimport.h
|
||||
${QET_DIR}/sources/autoNum/ui/renumberpreviewdialog.cpp
|
||||
${QET_DIR}/sources/autoNum/ui/renumberpreviewdialog.h
|
||||
${QET_DIR}/sources/undocommand/freezeelementlabelcommand.cpp
|
||||
${QET_DIR}/sources/undocommand/freezeelementlabelcommand.h
|
||||
${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.cpp
|
||||
${QET_DIR}/sources/autoNum/ui/autonumberingdockwidget.h
|
||||
${QET_DIR}/sources/autoNum/ui/autonumberingmanagementw.cpp
|
||||
|
||||
+21
-1
@@ -1444,9 +1444,17 @@ OPS = {
|
||||
"add_autonum": ("addAutoNum", [("kind", "str"), ("name", "str"),
|
||||
("parts", "list")]),
|
||||
"remove_autonum": ("removeAutoNum", [("kind", "str"), ("name", "str")]),
|
||||
"rename_autonum": ("renameAutoNum", [("kind", "str"), ("name", "str"),
|
||||
("new_name", "str")]),
|
||||
"use_conductor_autonum": ("useConductorAutoNum", [("folio", "folio"), ("name", "str")]),
|
||||
"use_element_autonum": ("useElementAutoNum", [("name", "str")]),
|
||||
"number_element": ("numberElement", [("folio", "folio"), ("element", "elmt")]),
|
||||
"renumber_element_autonum": ("renumberElementAutoNum", [("name", "str")]),
|
||||
"free_element_numbers": ("freeElementNumbers", [("folio", "folio"), ("element", "elmt")]),
|
||||
"assign_element_number": ("assignElementNumber", [("folio", "folio"), ("element", "elmt"),
|
||||
("number", "num")]),
|
||||
"assign_element_autonum": ("assignElementAutoNum", [("name", "str"), ("folio", "folio"),
|
||||
("element", "elmt"), ("overwrite", "bool")]),
|
||||
# The text fields drawn on a symbol. Indexed within the element's own
|
||||
# list, which follows its definition and shifts on delete (and undo of a
|
||||
# delete puts the field back at the end).
|
||||
@@ -1959,7 +1967,7 @@ def _build_script(operations: list, output: str) -> str:
|
||||
raise ValueError(f"operation {i}: unknown folio property "
|
||||
f"{op.get('property')!r}; expected one of "
|
||||
f"{', '.join(FOLIO_PROPERTIES)}")
|
||||
if name in ("add_autonum", "remove_autonum") and op.get("kind") not in AUTONUM_KINDS:
|
||||
if name in ("add_autonum", "remove_autonum", "rename_autonum") and op.get("kind") not in AUTONUM_KINDS:
|
||||
raise ValueError(f"operation {i}: unknown kind {op.get('kind')!r}; "
|
||||
f"expected one of {', '.join(AUTONUM_KINDS)}")
|
||||
if name == "search_and_replace":
|
||||
@@ -4541,6 +4549,18 @@ TOOLS = [
|
||||
"use_conductor_autonum then makes new conductors on a folio "
|
||||
"take their number from it, so define and select it BEFORE the "
|
||||
"add_conductor ops it should number. For elements, "
|
||||
"renumber_element_autonum numbers an element context's elements again "
|
||||
"(an element with a frozen label keeps its label, and nobody else gets it); "
|
||||
"free_element_numbers lists the numbers an element of an element context may "
|
||||
"be given by hand, assign_element_number gives it one of them "
|
||||
"(it keeps following the context, its counter moves past the number); "
|
||||
"assign_element_autonum makes one element follow a named element context "
|
||||
"(refused when it holds a formula, frozen label or follows "
|
||||
"another one, unless overwrite is true); "
|
||||
"rename_autonum renames a context of any kind, what follows it (elements, or "
|
||||
"the folios of a conductor or folio context) keeps following it; "
|
||||
"remove_autonum refuses a context which elements (element kind) or "
|
||||
"folios (conductor and folio kinds) still follow. "
|
||||
"use_element_autonum selects the context and number_element applies "
|
||||
"it to one element AFTER it is placed (add_element does not number "
|
||||
"what it places); slaves and reports are refused, since they take "
|
||||
|
||||
@@ -441,6 +441,7 @@ class EditValidation(unittest.TestCase):
|
||||
"sort_terminal_strip": [{"op": "sort_terminal_strip", "strip": 0}],
|
||||
"add_autonum": [{"op": "add_autonum", "kind": "conductor", "name": "W", "parts": ["string:W"]}],
|
||||
"remove_autonum": [{"op": "remove_autonum", "kind": "conductor", "name": "W"}],
|
||||
"rename_autonum": [{"op": "rename_autonum", "kind": "element", "name": "EL", "new_name": "EL2"}],
|
||||
"use_conductor_autonum": [f, {"op": "use_conductor_autonum", "folio": "$f", "name": "W"}],
|
||||
"use_element_autonum": [{"op": "use_element_autonum", "name": "EL"}],
|
||||
"insert_folio": [{"op": "insert_folio", "id": "i", "position": 0}],
|
||||
@@ -458,6 +459,12 @@ class EditValidation(unittest.TestCase):
|
||||
"duplicate_elements": el + [{"op": "duplicate_elements", "id": "d", "folio": "$f",
|
||||
"elements": ["$e"], "to_folio": "$f", "x": 50, "y": 50}],
|
||||
"number_element": el + [{"op": "number_element", "folio": "$f", "element": "$e"}],
|
||||
"renumber_element_autonum": [{"op": "renumber_element_autonum", "name": "EL"}],
|
||||
"free_element_numbers": el + [{"op": "free_element_numbers", "folio": "$f", "element": "$e"}],
|
||||
"assign_element_number": el + [{"op": "assign_element_number", "folio": "$f",
|
||||
"element": "$e", "number": 3}],
|
||||
"assign_element_autonum": el + [{"op": "assign_element_autonum", "name": "EL", "folio": "$f",
|
||||
"element": "$e", "overwrite": False}],
|
||||
"add_table": [f, {"op": "add_table", "id": "t", "folio": "$f", "kind": "nomenclature",
|
||||
"name": "BOM", "query": "SELECT label FROM element_nomenclature_view"}],
|
||||
"set_table_position": [f, {"op": "add_table", "id": "t", "folio": "$f", "kind": "nomenclature",
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "autonumschemecommand.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../qetproject.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
AutoNumSchemeCommand::AutoNumSchemeCommand(QETProject *project, Kind kind) :
|
||||
m_project(project),
|
||||
m_kind(kind)
|
||||
{}
|
||||
|
||||
/// @return the titles of the schemes of @p kind, sorted
|
||||
QStringList AutoNumSchemeCommand::titles(const QETProject *project, Kind kind)
|
||||
{
|
||||
if (!project) {
|
||||
return {};
|
||||
}
|
||||
QStringList list = kind == Kind::Conductor ? project->conductorAutoNum().keys()
|
||||
: project->folioAutoNum().keys();
|
||||
list.sort(Qt::CaseInsensitive);
|
||||
return list;
|
||||
}
|
||||
|
||||
bool AutoNumSchemeCommand::contains(const QETProject *project, Kind kind, const QString &title)
|
||||
{
|
||||
if (!project) {
|
||||
return false;
|
||||
}
|
||||
return kind == Kind::Conductor ? project->conductorAutoNum().contains(title)
|
||||
: project->folioAutoNum().contains(title);
|
||||
}
|
||||
|
||||
NumerotationContext AutoNumSchemeCommand::contextOf(const QETProject *project, Kind kind, const QString &title)
|
||||
{
|
||||
if (!project) {
|
||||
return NumerotationContext();
|
||||
}
|
||||
return kind == Kind::Conductor ? project->conductorAutoNum(title)
|
||||
: project->folioAutoNum(title);
|
||||
}
|
||||
|
||||
/**
|
||||
@return the title of the scheme @p name clashes with, empty if none:
|
||||
the same name, whatever the case or the white space
|
||||
@param ignored_title : the scheme being renamed
|
||||
*/
|
||||
QString AutoNumSchemeCommand::nameClash(const QETProject *project, Kind kind,
|
||||
const QString &name, const QString &ignored_title)
|
||||
{
|
||||
const QString wanted = QETProject::normalizedAutoNumName(name);
|
||||
for (const QString &title : titles(project, kind)) {
|
||||
if (title == ignored_title) {
|
||||
continue;
|
||||
}
|
||||
if (QString::compare(QETProject::normalizedAutoNumName(title), wanted,
|
||||
Qt::CaseInsensitive) == 0) {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/// @return why @p name cannot be the name of a scheme, translated, empty if it can
|
||||
QString AutoNumSchemeCommand::nameProblem(const QETProject *project, Kind kind,
|
||||
const QString &name, const QString &ignored_title)
|
||||
{
|
||||
if (QETProject::normalizedAutoNumName(name).isEmpty()) {
|
||||
return tr("Le nom de la numérotation ne peut pas être vide.");
|
||||
}
|
||||
const QString clash = nameClash(project, kind, name, ignored_title);
|
||||
if (!clash.isEmpty()) {
|
||||
return tr("Une numérotation nommée « %1 » existe déjà.").arg(clash);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@return the folios which follow the scheme @p title
|
||||
@param references : every folio which names it, for a folio numbering,
|
||||
even if its number has been written into the folio field already (see
|
||||
the class description)
|
||||
*/
|
||||
QList<Diagram *> AutoNumSchemeCommand::usersOf(const QETProject *project, Kind kind,
|
||||
const QString &title, bool references)
|
||||
{
|
||||
QList<Diagram *> users;
|
||||
if (!project || title.isEmpty()) {
|
||||
return users;
|
||||
}
|
||||
for (Diagram *diagram : project->diagrams()) {
|
||||
if (kind == Kind::Conductor) {
|
||||
if (diagram->conductorsAutonumName() == title) {
|
||||
users << diagram;
|
||||
}
|
||||
} else if (diagram->border_and_titleblock.autoPageNum() == title
|
||||
&& (references || diagram->border_and_titleblock.folio().contains(QLatin1String("%autonum")))) {
|
||||
users << diagram;
|
||||
}
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
AutoNumSchemeCommand *AutoNumSchemeCommand::create(QETProject *project, Kind kind,
|
||||
const QString &title,
|
||||
const NumerotationContext &context,
|
||||
bool make_current)
|
||||
{
|
||||
if (!project || !nameProblem(project, kind, title).isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto *cmd = new AutoNumSchemeCommand(project, kind);
|
||||
cmd->m_after = Scheme{QETProject::normalizedAutoNumName(title), context};
|
||||
cmd->m_current_before = project->conductorCurrentAutoNum();
|
||||
cmd->m_current_after = (kind == Kind::Conductor && make_current)
|
||||
? cmd->m_after->title
|
||||
: cmd->m_current_before;
|
||||
cmd->setText(tr("Créer la numérotation %1").arg(cmd->m_after->title));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
Rename the scheme @p old_title to @p new_title and give it @p context.
|
||||
@param make_current : for conductors, make it the scheme new conductors
|
||||
take; otherwise it stays so if it was
|
||||
@return nullptr if there is no such scheme, the new name is not
|
||||
acceptable, or nothing changes
|
||||
*/
|
||||
AutoNumSchemeCommand *AutoNumSchemeCommand::edit(QETProject *project, Kind kind,
|
||||
const QString &old_title,
|
||||
const QString &new_title,
|
||||
const NumerotationContext &context,
|
||||
bool make_current)
|
||||
{
|
||||
if (!project || !contains(project, kind, old_title)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!nameProblem(project, kind, new_title, old_title).isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *cmd = new AutoNumSchemeCommand(project, kind);
|
||||
cmd->m_before = Scheme{old_title, contextOf(project, kind, old_title)};
|
||||
cmd->m_after = Scheme{QETProject::normalizedAutoNumName(new_title), context};
|
||||
cmd->m_current_before = project->conductorCurrentAutoNum();
|
||||
cmd->m_current_after = kind == Kind::Conductor
|
||||
? ((make_current || cmd->m_current_before == old_title)
|
||||
? cmd->m_after->title : cmd->m_current_before)
|
||||
: cmd->m_current_before;
|
||||
if (cmd->m_before->title != cmd->m_after->title) {
|
||||
cmd->m_users = [&] {
|
||||
QList<QPointer<Diagram>> list;
|
||||
for (Diagram *d : usersOf(project, kind, old_title, true)) list << d;
|
||||
return list;
|
||||
}();
|
||||
}
|
||||
|
||||
bool same_definition = cmd->m_before->context.size() == cmd->m_after->context.size();
|
||||
for (int i = 0 ; same_definition && i < cmd->m_before->context.size() ; ++i) {
|
||||
same_definition = cmd->m_before->context[i] == cmd->m_after->context[i];
|
||||
}
|
||||
if (cmd->m_before->title == cmd->m_after->title && same_definition
|
||||
&& cmd->m_current_before == cmd->m_current_after) {
|
||||
delete cmd;
|
||||
return nullptr;
|
||||
}
|
||||
if (cmd->m_before->title != cmd->m_after->title && same_definition) {
|
||||
cmd->setText(tr("Renommer la numérotation %1 en %2")
|
||||
.arg(cmd->m_before->title, cmd->m_after->title));
|
||||
} else {
|
||||
cmd->setText(tr("Modifier la numérotation %1").arg(cmd->m_after->title));
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
Remove the scheme @p title.
|
||||
@return nullptr if there is no such scheme or a folio still follows it
|
||||
*/
|
||||
AutoNumSchemeCommand *AutoNumSchemeCommand::remove(QETProject *project, Kind kind,
|
||||
const QString &title)
|
||||
{
|
||||
if (!project || !contains(project, kind, title)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!usersOf(project, kind, title).isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto *cmd = new AutoNumSchemeCommand(project, kind);
|
||||
cmd->m_before = Scheme{title, contextOf(project, kind, title)};
|
||||
cmd->m_current_before = project->conductorCurrentAutoNum();
|
||||
cmd->m_current_after = (kind == Kind::Conductor && cmd->m_current_before == title)
|
||||
? QString() : cmd->m_current_before;
|
||||
cmd->setText(tr("Supprimer la numérotation %1").arg(title));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// Let the windows which list the schemes read them again
|
||||
void AutoNumSchemeCommand::announce(bool added, bool removed)
|
||||
{
|
||||
if (m_kind == Kind::Conductor) {
|
||||
if (removed) m_project->conductorAutoNumRemoved();
|
||||
if (added) m_project->conductorAutoNumAdded();
|
||||
} else {
|
||||
if (removed) m_project->folioAutoNumRemoved();
|
||||
if (added) m_project->folioAutoNumAdded();
|
||||
}
|
||||
}
|
||||
|
||||
/// Give the scheme @p from the title @p to, and the folios which follow it
|
||||
void AutoNumSchemeCommand::rename(const QString &from, const QString &to)
|
||||
{
|
||||
const NumerotationContext context = contextOf(m_project, m_kind, from);
|
||||
if (m_kind == Kind::Conductor) {
|
||||
m_project->removeConductorAutoNum(from);
|
||||
m_project->addConductorAutoNum(to, context);
|
||||
if (m_project->conductorCurrentAutoNum() == from) {
|
||||
m_project->setCurrentConductorAutoNum(to);
|
||||
}
|
||||
for (Diagram *d : m_project->diagrams()) {
|
||||
//The highest folio number given, kept by scheme title
|
||||
for (auto *hash : {&d->m_cnd_unitfolio_max, &d->m_cnd_tenfolio_max, &d->m_cnd_hundredfolio_max}) {
|
||||
if (hash->contains(from)) {
|
||||
hash->insert(to, hash->take(from));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_project->removeFolioAutoNum(from);
|
||||
m_project->addFolioAutoNum(to, context);
|
||||
}
|
||||
for (const QPointer<Diagram> &d : std::as_const(m_users)) {
|
||||
if (!d) continue;
|
||||
if (m_kind == Kind::Conductor) {
|
||||
d->setConductorsAutonumName(to);
|
||||
} else {
|
||||
d->border_and_titleblock.setAutoPageNum(to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutoNumSchemeCommand::redo()
|
||||
{
|
||||
if (!m_project) return;
|
||||
|
||||
if (m_before && m_after && m_before->title != m_after->title) {
|
||||
rename(m_before->title, m_after->title);
|
||||
} else if (m_before && !m_after) {
|
||||
if (m_kind == Kind::Conductor) m_project->removeConductorAutoNum(m_before->title);
|
||||
else m_project->removeFolioAutoNum(m_before->title);
|
||||
}
|
||||
if (m_after) {
|
||||
if (m_kind == Kind::Conductor) m_project->addConductorAutoNum(m_after->title, m_after->context);
|
||||
else m_project->addFolioAutoNum(m_after->title, m_after->context);
|
||||
}
|
||||
if (m_kind == Kind::Conductor) {
|
||||
m_project->setCurrentConductorAutoNum(m_current_after);
|
||||
}
|
||||
announce(m_after.has_value(), m_before.has_value() && (!m_after || m_after->title != m_before->title));
|
||||
}
|
||||
|
||||
void AutoNumSchemeCommand::undo()
|
||||
{
|
||||
if (!m_project) return;
|
||||
|
||||
if (m_before && m_after && m_before->title != m_after->title) {
|
||||
rename(m_after->title, m_before->title);
|
||||
} else if (!m_before && m_after) {
|
||||
if (m_kind == Kind::Conductor) m_project->removeConductorAutoNum(m_after->title);
|
||||
else m_project->removeFolioAutoNum(m_after->title);
|
||||
}
|
||||
if (m_before) {
|
||||
if (m_kind == Kind::Conductor) m_project->addConductorAutoNum(m_before->title, m_before->context);
|
||||
else m_project->addFolioAutoNum(m_before->title, m_before->context);
|
||||
}
|
||||
if (m_kind == Kind::Conductor) {
|
||||
m_project->setCurrentConductorAutoNum(m_current_before);
|
||||
}
|
||||
announce(m_before.has_value(), m_after.has_value() && (!m_before || m_after->title != m_before->title));
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 AUTONUMSCHEMECOMMAND_H
|
||||
#define AUTONUMSCHEMECOMMAND_H
|
||||
|
||||
#include "numerotationcontext.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include <optional>
|
||||
|
||||
class Diagram;
|
||||
class QETProject;
|
||||
|
||||
/**
|
||||
@brief The AutoNumSchemeCommand class
|
||||
Undoable creation, edition (rename and/or new definition) and removal of
|
||||
one conductor or folio numbering scheme of a project.
|
||||
|
||||
A folio refers to the scheme it follows by its title, as the project file
|
||||
has always had it (the title of the conductor numbering a folio reads,
|
||||
the title of the folio numbering its title block follows). So a rename
|
||||
moves those references with it, in the same undo step, and a scheme which
|
||||
a folio still follows cannot be removed: the folio would be left reading
|
||||
a numbering which does not exist.
|
||||
|
||||
A folio numbering is applied once: the number is written into the folio
|
||||
field of the title block, in place of %autonum, and the title block keeps
|
||||
the name of the numbering it came from. So a folio really depends on a
|
||||
folio numbering only while its folio field still holds %autonum, and
|
||||
only then can the numbering not be removed; every title block which
|
||||
names it has its name moved by a rename.
|
||||
|
||||
Conductors keep the formula they were numbered with, and the numbers
|
||||
folios show are worked out from the scheme when they are drawn: a new
|
||||
definition changes what is numbered from then on, nothing is renumbered.
|
||||
|
||||
Counter changes made by numbering something are not edits of the scheme
|
||||
and do not come here.
|
||||
*/
|
||||
class AutoNumSchemeCommand : public QUndoCommand
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(AutoNumSchemeCommand)
|
||||
|
||||
public:
|
||||
enum class Kind { Conductor, Folio };
|
||||
|
||||
struct Scheme
|
||||
{
|
||||
QString title;
|
||||
NumerotationContext context;
|
||||
};
|
||||
|
||||
static QStringList titles(const QETProject *project, Kind kind);
|
||||
static bool contains(const QETProject *project, Kind kind, const QString &title);
|
||||
static NumerotationContext contextOf(const QETProject *project, Kind kind, const QString &title);
|
||||
static QString nameClash(const QETProject *project, Kind kind,
|
||||
const QString &name, const QString &ignored_title = QString());
|
||||
static QString nameProblem(const QETProject *project, Kind kind,
|
||||
const QString &name, const QString &ignored_title = QString());
|
||||
static QList<Diagram *> usersOf(const QETProject *project, Kind kind,
|
||||
const QString &title, bool references = false);
|
||||
|
||||
static AutoNumSchemeCommand *create(QETProject *project, Kind kind,
|
||||
const QString &title,
|
||||
const NumerotationContext &context,
|
||||
bool make_current = false);
|
||||
static AutoNumSchemeCommand *edit(QETProject *project, Kind kind,
|
||||
const QString &old_title,
|
||||
const QString &new_title,
|
||||
const NumerotationContext &context,
|
||||
bool make_current = false);
|
||||
static AutoNumSchemeCommand *remove(QETProject *project, Kind kind,
|
||||
const QString &title);
|
||||
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
AutoNumSchemeCommand(QETProject *project, Kind kind);
|
||||
void rename(const QString &from, const QString &to);
|
||||
void announce(bool added, bool removed);
|
||||
|
||||
QETProject *m_project = nullptr;
|
||||
Kind m_kind;
|
||||
std::optional<Scheme> m_before;
|
||||
std::optional<Scheme> m_after;
|
||||
QString m_current_before;
|
||||
QString m_current_after;
|
||||
QList<QPointer<Diagram>> m_users; ///< the folios which follow the scheme being renamed
|
||||
};
|
||||
|
||||
#endif // AUTONUMSCHEMECOMMAND_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 ELEMENTAUTONUMSCHEMECOMMAND_H
|
||||
#define ELEMENTAUTONUMSCHEMECOMMAND_H
|
||||
|
||||
#include "numerotationcontext.h"
|
||||
#include "renumberelementscommand.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QUndoCommand>
|
||||
#include <QUuid>
|
||||
#include <QVector>
|
||||
|
||||
#include <optional>
|
||||
|
||||
class Element;
|
||||
class QETProject;
|
||||
|
||||
/**
|
||||
@brief The ElementAutoNumSchemeCommand class
|
||||
Undoable creation, edition (rename and/or new definition) and removal
|
||||
of one element numbering scheme of a project, together with what it
|
||||
does to the elements following the scheme.
|
||||
|
||||
Elements follow a scheme by its uuid (QETInformation::ELMT_FORMULA_ID),
|
||||
so a rename touches no element. A new definition with a different
|
||||
formula rewrites the formula of every element following the scheme:
|
||||
when its sequential parts are the same (only the fixed text or the
|
||||
folio parts changed), each element keeps its numbers; otherwise the
|
||||
elements are numbered again, in folio and position order, as
|
||||
QETProject::renumberElementsBySchemeTitle() does.
|
||||
|
||||
A scheme whose formula would change cannot be edited while an element
|
||||
with a frozen label follows it: that element would keep a label and a
|
||||
formula the scheme no longer defines (see editBlockedBy(); edit()
|
||||
refuses). A rename, or a change of the counter, touches no formula and
|
||||
is allowed. renumber() leaves a frozen element exactly as it is,
|
||||
label, formula and numbers, and does not give its label to another
|
||||
element: the numbering goes past it (see reservedLabels()).
|
||||
|
||||
Counter changes made by placing elements are not edits of the scheme:
|
||||
they stay with SetAutoNumContextCommand.
|
||||
|
||||
Build one with create(), edit() or remove(); each returns nullptr when
|
||||
the change is refused or changes nothing. Show changedElementCount()
|
||||
to the user before pushing it when it is not zero.
|
||||
*/
|
||||
class ElementAutoNumSchemeCommand : public QUndoCommand
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(ElementAutoNumSchemeCommand)
|
||||
|
||||
public:
|
||||
struct Scheme
|
||||
{
|
||||
QString title;
|
||||
QUuid id;
|
||||
NumerotationContext context;
|
||||
};
|
||||
|
||||
static ElementAutoNumSchemeCommand *create(
|
||||
QETProject *project,
|
||||
const QString &title,
|
||||
const NumerotationContext &context,
|
||||
const QUuid &id = QUuid(),
|
||||
bool make_current = true);
|
||||
static ElementAutoNumSchemeCommand *edit(
|
||||
QETProject *project,
|
||||
const QString &old_title,
|
||||
const QString &new_title,
|
||||
const NumerotationContext &context,
|
||||
bool make_current = true);
|
||||
static ElementAutoNumSchemeCommand *remove(
|
||||
QETProject *project,
|
||||
const QString &title);
|
||||
|
||||
static QString nameProblem(const QETProject *project,
|
||||
const QString &name,
|
||||
const QString &ignored_title = QString());
|
||||
static bool sameSequentialParts(const QString &formula_a,
|
||||
const QString &formula_b);
|
||||
static NumerotationContext resetForRenumber(const NumerotationContext &context);
|
||||
static QVector<RenumberElementsCommand::ElementChange> renumberChanges(
|
||||
const QString &hash_key,
|
||||
const NumerotationContext &context,
|
||||
QVector<Element *> elements,
|
||||
const QString &formula,
|
||||
NumerotationContext *final_context,
|
||||
bool from_start = true,
|
||||
const QUuid &scheme_id = QUuid(),
|
||||
const QSet<QString> &reserved = QSet<QString>());
|
||||
static QSet<QString> reservedLabels(const QETProject *project,
|
||||
const QString &title,
|
||||
const QVector<Element *> &changing);
|
||||
static RenumberElementsCommand *renumber(QETProject *project,
|
||||
const QString &title,
|
||||
QVector<Element *> *frozen,
|
||||
const QString &text);
|
||||
static QVector<Element *> frozenFollowers(const QETProject *project,
|
||||
const QString &title);
|
||||
static QVector<Element *> editBlockedBy(const QETProject *project,
|
||||
const QString &title,
|
||||
const NumerotationContext &context);
|
||||
|
||||
/// Why assign() leaves an element as it is
|
||||
enum class Skipped {
|
||||
None,
|
||||
FollowsAlready, ///< already follows this scheme
|
||||
Frozen, ///< its label is frozen
|
||||
OtherFormula, ///< follows another scheme or has a formula of its own
|
||||
Linked ///< slave or report: takes its label from its master
|
||||
};
|
||||
static Skipped skipReason(const QETProject *project,
|
||||
const QString &title,
|
||||
const Element *element);
|
||||
/// What assign() would do with each of the given elements
|
||||
struct AssignPlan
|
||||
{
|
||||
QVector<Element *> todo; ///< given the scheme
|
||||
QVector<Element *> frozen; ///< a frozen label: left alone unless overwrite
|
||||
QVector<Element *> otherFormula; ///< follow or hold another formula: left alone unless overwrite
|
||||
int followsAlready = 0; ///< follow this scheme already
|
||||
int linked = 0; ///< slave or report: take their label from their master
|
||||
};
|
||||
static AssignPlan assignPlan(const QETProject *project,
|
||||
const QString &title,
|
||||
const QVector<Element *> &elements);
|
||||
static RenumberElementsCommand *assign(
|
||||
QETProject *project,
|
||||
const QString &title,
|
||||
const QVector<Element *> &elements,
|
||||
bool overwrite,
|
||||
int *skipped = nullptr,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
/// What a numbered element's frozen state becomes
|
||||
enum class FreezeRule {
|
||||
Unfrozen, ///< free: the user asked for this numbering
|
||||
NewElementPolicy ///< as an element just placed: frozen if the folio or project freezes new ones
|
||||
};
|
||||
static RenumberElementsCommand *numberElements(
|
||||
QETProject *project,
|
||||
const QString &title,
|
||||
const QVector<Element *> &elements,
|
||||
const QString &text,
|
||||
FreezeRule freeze = FreezeRule::Unfrozen,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
static QString schemeForFormula(const QETProject *project, const QString &formula);
|
||||
static QString followedScheme(const QETProject *project, const DiagramContext &info);
|
||||
static void writeCopiedSchemes(QDomDocument &document,
|
||||
QDomElement &root,
|
||||
const QETProject *project,
|
||||
const QVector<Element *> &copied);
|
||||
static QList<Scheme> copiedSchemes(const QDomElement &root);
|
||||
static QList<Scheme> missingForPaste(const QETProject *project,
|
||||
const QList<Scheme> &copied,
|
||||
const QList<Element *> &pasted);
|
||||
/// How the elements of a scheme are numbered by hand (see numberSupport())
|
||||
struct NumberSupport
|
||||
{
|
||||
bool supported = false;
|
||||
int partIndex = -1; ///< the one number part of the definition
|
||||
QString type; ///< "unit", "ten" or "hundred"
|
||||
int increase = 1;
|
||||
};
|
||||
struct GapRange
|
||||
{
|
||||
int from = 0;
|
||||
int to = 0;
|
||||
};
|
||||
/// A counter which would meet numbers already in use
|
||||
struct CounterConflict
|
||||
{
|
||||
int counter = 0; ///< the next number it would hand out
|
||||
int highest = 0; ///< the highest number in use
|
||||
int count = 0; ///< how many elements have a number of at least counter
|
||||
};
|
||||
static std::optional<CounterConflict> counterConflict(const QETProject *project,
|
||||
const QString &title,
|
||||
const NumerotationContext &proposed);
|
||||
static QSet<QString> labelsHeldBesides(const QETProject *project, const Element *element);
|
||||
static NumberSupport numberSupport(const NumerotationContext &context);
|
||||
static std::optional<int> numberOf(const NumberSupport &support, const Element *element);
|
||||
static QList<GapRange> gapRanges(const QETProject *project, const QString &title);
|
||||
static QList<int> freeNumbers(const QETProject *project, const QString &title,
|
||||
const Element *element, int limit = 500);
|
||||
static QString labelForNumber(const QETProject *project, const QString &title,
|
||||
Element *element, int number);
|
||||
static RenumberElementsCommand *assignNumber(QETProject *project,
|
||||
Element *element,
|
||||
int number,
|
||||
QString *problem = nullptr,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
static QMap<QString, QVector<Element *>> pastedSchemes(
|
||||
const QETProject *project,
|
||||
const QList<Element *> &elements);
|
||||
static void linkPasted(const QETProject *project,
|
||||
const QList<Element *> &elements);
|
||||
static int numberPasted(QETProject *project,
|
||||
const QMap<QString, QVector<Element *>> &schemes,
|
||||
QUndoCommand *parent);
|
||||
|
||||
/// Elements whose label and formula change
|
||||
int changedElementCount() const {return m_changes.size();}
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes() const {return m_changes;}
|
||||
std::optional<Scheme> before() const {return m_before;}
|
||||
std::optional<Scheme> after() const {return m_after;}
|
||||
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
explicit ElementAutoNumSchemeCommand(QETProject *project);
|
||||
|
||||
QETProject *m_project = nullptr;
|
||||
std::optional<Scheme> m_before;
|
||||
std::optional<Scheme> m_after;
|
||||
QString m_current_before;
|
||||
QString m_current_after;
|
||||
QVector<RenumberElementsCommand::ElementChange> m_changes;
|
||||
};
|
||||
|
||||
#endif // ELEMENTAUTONUMSCHEMECOMMAND_H
|
||||
@@ -26,8 +26,9 @@ RenumberElementsCommand::RenumberElementsCommand(
|
||||
QVector<ElementChange> changes,
|
||||
QHash<QString, NumerotationContext> old_ctx,
|
||||
QHash<QString, NumerotationContext> new_ctx,
|
||||
const QString &text)
|
||||
: QUndoCommand(text)
|
||||
const QString &text,
|
||||
QUndoCommand *parent)
|
||||
: QUndoCommand(text, parent)
|
||||
, project_(project)
|
||||
, changes_(std::move(changes))
|
||||
, old_ctx_(std::move(old_ctx))
|
||||
@@ -61,18 +62,23 @@ void RenumberElementsCommand::apply(bool use_new)
|
||||
|
||||
// Apply per-element changes
|
||||
for (const ElementChange &c : changes_) {
|
||||
if (!c.element) continue;
|
||||
|
||||
const bool frozen = use_new ? c.new_frozen : c.old_frozen;
|
||||
const auto &infos = use_new ? c.new_infos : c.old_infos;
|
||||
const auto &seq = use_new ? c.new_seq : c.old_seq;
|
||||
|
||||
// Temporarily unfreeze so that label/infos update correctly.
|
||||
const bool was_frozen = c.element->isFreezeLabel();
|
||||
if (was_frozen) c.element->freezeLabel(false);
|
||||
|
||||
c.element->rSequenceStruct() = seq;
|
||||
c.element->setElementInformations(infos);
|
||||
c.element->freezeLabel(frozen);
|
||||
applyChange(c, use_new);
|
||||
}
|
||||
}
|
||||
|
||||
void RenumberElementsCommand::applyChange(const ElementChange &c, bool use_new)
|
||||
{
|
||||
if (!c.element) return;
|
||||
|
||||
const bool frozen = use_new ? c.new_frozen : c.old_frozen;
|
||||
const auto &infos = use_new ? c.new_infos : c.old_infos;
|
||||
const auto &seq = use_new ? c.new_seq : c.old_seq;
|
||||
|
||||
// Temporarily unfreeze so that label/infos update correctly.
|
||||
const bool was_frozen = c.element->isFreezeLabel();
|
||||
if (was_frozen) c.element->freezeLabel(false);
|
||||
|
||||
c.element->rSequenceStruct() = seq;
|
||||
c.element->setElementInformations(infos);
|
||||
c.element->freezeLabel(frozen);
|
||||
}
|
||||
|
||||
@@ -51,11 +51,17 @@ public:
|
||||
QVector<ElementChange> changes,
|
||||
QHash<QString, NumerotationContext> old_ctx,
|
||||
QHash<QString, NumerotationContext> new_ctx,
|
||||
const QString &text);
|
||||
const QString &text,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
const QVector<ElementChange> &changes() const { return changes_; }
|
||||
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
/// Apply one change's old (@p use_new false) or new state.
|
||||
static void applyChange(const ElementChange &change, bool use_new);
|
||||
|
||||
private:
|
||||
void apply(bool use_new);
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
|
||||
#include "../../diagram.h"
|
||||
#include "../../diagramview.h"
|
||||
#include "../../qetgraphicsitem/element.h"
|
||||
#include "../../qeticons.h"
|
||||
#include "../elementautonumschemecommand.h"
|
||||
#include "counterwarning.h"
|
||||
#include "renumberpreviewdialog.h"
|
||||
#include "../../qetapp.h"
|
||||
#include "../../shortcutmanager.h"
|
||||
#include "../../titleblockproperties.h"
|
||||
@@ -29,7 +34,10 @@
|
||||
#include "../../undocommand/changetitleblockcommand.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSignalBlocker>
|
||||
#include <QSpinBox>
|
||||
|
||||
@@ -43,9 +51,127 @@ AutoNumberingDockWidget::AutoNumberingDockWidget(QWidget *parent) :
|
||||
ui(new Ui::AutoNumberingDockWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
//The element row gets a button to the left of its reset button
|
||||
//which gives the numbering shown to the selected elements
|
||||
QPushButton *reset = ui->m_element_reset_start_pb;
|
||||
ui->gridLayout->removeWidget(reset);
|
||||
auto *actions = new QWidget(this);
|
||||
auto *actions_layout = new QHBoxLayout(actions);
|
||||
actions_layout->setContentsMargins(0, 0, 0, 0);
|
||||
actions_layout->setSpacing(2);
|
||||
m_element_apply_pb = new QPushButton(QET::Icons::DialogOk, QString(), actions);
|
||||
m_element_apply_pb->setObjectName(QStringLiteral("m_element_apply_pb"));
|
||||
m_element_apply_pb->setMaximumWidth(24);
|
||||
m_element_apply_pb->setToolTip(tr("Appliquer cette numérotation aux éléments sélectionnés"));
|
||||
reset->setParent(actions);
|
||||
actions_layout->addWidget(m_element_apply_pb);
|
||||
actions_layout->addWidget(reset);
|
||||
ui->gridLayout->addWidget(actions, 3, 2);
|
||||
connect(m_element_apply_pb, &QPushButton::clicked,
|
||||
this, &AutoNumberingDockWidget::applyElementSchemeToSelection);
|
||||
connect(ui->m_element_cb, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, &AutoNumberingDockWidget::updateApplyEnabled);
|
||||
m_element_apply_pb->setEnabled(false);
|
||||
|
||||
this->setDisabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::applyElementSchemeToSelection
|
||||
Give the element numbering shown in the list to the elements selected
|
||||
in the current folio: each takes its formula and the next number, in
|
||||
folio and position order, and the counter moves on.
|
||||
|
||||
What would change is shown first. An element which holds another
|
||||
numbering, or whose label is frozen, is left as it is, unless the user
|
||||
asks to replace them too (a frozen one is then given back). One undo
|
||||
step.
|
||||
*/
|
||||
void AutoNumberingDockWidget::applyElementSchemeToSelection()
|
||||
{
|
||||
if (!m_project || m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
const QString caption = tr("Appliquer la numérotation");
|
||||
const QString title = ui->m_element_cb->currentText();
|
||||
DiagramView *view = m_project_view ? m_project_view->currentDiagram() : nullptr;
|
||||
Diagram *diagram = view ? view->diagram() : nullptr;
|
||||
if (!diagram || !m_project->elementAutoNum().contains(title)) {
|
||||
QMessageBox::information(this, caption,
|
||||
tr("Choisissez une numérotation d'éléments du projet."));
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Element *> selected;
|
||||
const auto items = diagram->selectedItems();
|
||||
for (QGraphicsItem *item : items) {
|
||||
if (auto *el = qgraphicsitem_cast<Element *>(item)) {
|
||||
selected << el;
|
||||
}
|
||||
}
|
||||
if (selected.isEmpty()) {
|
||||
QMessageBox::information(this, caption,
|
||||
tr("Sélectionnez d'abord des éléments dans le folio."));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto plan = ElementAutoNumSchemeCommand::assignPlan(m_project, title, selected);
|
||||
QVector<RenumberPreviewDialog::LeftAlone> left;
|
||||
for (Element *el : plan.frozen) {
|
||||
left.append({el, tr("(figé)")});
|
||||
}
|
||||
for (Element *el : plan.otherFormula) {
|
||||
left.append({el, tr("(autre numérotation)")});
|
||||
}
|
||||
const int replaceable = static_cast<int>(left.size());
|
||||
|
||||
auto *cmd = ElementAutoNumSchemeCommand::assign(m_project, title, selected, false);
|
||||
if (!cmd && !replaceable) {
|
||||
QMessageBox::information(
|
||||
this, caption,
|
||||
tr("Rien à appliquer : les éléments sélectionnés suivent déjà "
|
||||
"la numérotation « %1 », ou prennent leur nom d'un autre élément.").arg(title));
|
||||
return;
|
||||
}
|
||||
|
||||
const QString intro = cmd
|
||||
? tr("%n élément(s) vont recevoir la numérotation « %1 », dans l'ordre des folios "
|
||||
"et des positions.", "", static_cast<int>(plan.todo.size())).arg(title)
|
||||
: tr("Aucun élément ne peut recevoir la numérotation « %1 » sans remplacer "
|
||||
"ce qu'il a.").arg(title);
|
||||
const auto answer = RenumberPreviewDialog::ask(
|
||||
this, caption, intro,
|
||||
cmd ? cmd->changes() : QVector<RenumberElementsCommand::ElementChange>(),
|
||||
left,
|
||||
replaceable
|
||||
? tr("Remplacer aussi les %n élément(s) laissés comme ils sont", "", replaceable)
|
||||
: QString());
|
||||
|
||||
if (answer == RenumberPreviewDialog::Answer::Cancel) {
|
||||
delete cmd;
|
||||
return;
|
||||
}
|
||||
int applied = cmd ? static_cast<int>(cmd->changes().size()) : 0;
|
||||
if (answer == RenumberPreviewDialog::Answer::GoAndReplace) {
|
||||
delete cmd;
|
||||
cmd = ElementAutoNumSchemeCommand::assign(m_project, title, selected, true);
|
||||
applied = cmd ? static_cast<int>(cmd->changes().size()) : 0;
|
||||
}
|
||||
if (cmd) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
|
||||
const int not_given = static_cast<int>(selected.size()) - applied;
|
||||
if (not_given > 0) {
|
||||
QMessageBox::information(
|
||||
this, caption,
|
||||
tr("%n élément(s) numérotés.", "", applied)
|
||||
+ QLatin1Char('\n')
|
||||
+ tr("%n élément(s) sélectionnés sont restés comme ils sont.", "", not_given));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::~AutoNumberingDockWidget
|
||||
Destructor
|
||||
@@ -117,6 +243,8 @@ void AutoNumberingDockWidget::setProject(QETProject *project,
|
||||
disconnect(m_project, &QETProject::conductorAutoNumRemoved, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
disconnect(m_project, &QETProject::conductorAutoNumAdded, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
disconnect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::setConductorActive);
|
||||
disconnect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::followSelectionOf);
|
||||
followSelectionOf(nullptr);
|
||||
|
||||
//Element Signals
|
||||
disconnect(m_project, &QETProject::elementAutoNumRemoved, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
@@ -142,6 +270,8 @@ void AutoNumberingDockWidget::setProject(QETProject *project,
|
||||
connect(m_project, &QETProject::conductorAutoNumRemoved, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
connect(m_project, &QETProject::conductorAutoNumAdded, this, &AutoNumberingDockWidget::conductorAutoNumChanged);
|
||||
connect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::setConductorActive);
|
||||
connect(m_project_view, &ProjectView::diagramActivated, this, &AutoNumberingDockWidget::followSelectionOf);
|
||||
followSelectionOf(m_project_view->currentDiagram());
|
||||
|
||||
//Element Signals
|
||||
connect(m_project, &QETProject::elementAutoNumRemoved, this, &AutoNumberingDockWidget::elementAutoNumChanged);
|
||||
@@ -207,6 +337,56 @@ void AutoNumberingDockWidget::setContext()
|
||||
this->setActive();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::followSelectionOf
|
||||
Enable the apply button only while elements are selected in the folio
|
||||
shown: it gives a numbering to the selected elements, and with none
|
||||
selected there is nothing for it to do.
|
||||
@param view : the folio now shown, nullptr for none
|
||||
*/
|
||||
void AutoNumberingDockWidget::followSelectionOf(DiagramView *view)
|
||||
{
|
||||
Diagram *diagram = view ? view->diagram() : nullptr;
|
||||
if (m_selection_diagram) {
|
||||
disconnect(m_selection_diagram, &QGraphicsScene::selectionChanged,
|
||||
this, &AutoNumberingDockWidget::updateApplyEnabled);
|
||||
}
|
||||
m_selection_diagram = diagram;
|
||||
if (m_selection_diagram) {
|
||||
connect(m_selection_diagram, &QGraphicsScene::selectionChanged,
|
||||
this, &AutoNumberingDockWidget::updateApplyEnabled);
|
||||
}
|
||||
updateApplyEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::updateApplyEnabled
|
||||
The apply button needs a numbering to give and elements selected.
|
||||
*/
|
||||
void AutoNumberingDockWidget::updateApplyEnabled()
|
||||
{
|
||||
if (!m_element_apply_pb) {
|
||||
return;
|
||||
}
|
||||
bool elements_selected = false;
|
||||
if (m_selection_diagram) {
|
||||
const auto items = m_selection_diagram->selectedItems();
|
||||
for (QGraphicsItem *item : items) {
|
||||
if (qgraphicsitem_cast<Element *>(item)) {
|
||||
elements_selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_element_apply_pb->setEnabled(
|
||||
elements_selected && m_project && !m_project->isReadOnly()
|
||||
&& ui->m_element_cb->count() > 0);
|
||||
m_element_apply_pb->setToolTip(
|
||||
elements_selected
|
||||
? tr("Appliquer cette numérotation aux éléments sélectionnés")
|
||||
: tr("Sélectionnez d'abord des éléments dans le folio"));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief AutoNumberingDockWidget::setConductorActive
|
||||
@param dv: activated diagramview
|
||||
@@ -539,6 +719,12 @@ void AutoNumberingDockWidget::applyValueField(QComboBox *combo_box, QLineEdit *l
|
||||
}
|
||||
|
||||
context.replaceValue(index, typed);
|
||||
//An element counter put back on numbers in use: ask first
|
||||
if (category == AutoNumCategory::Element
|
||||
&& !CounterWarning::confirm(this, m_project, combo_box->currentText(), context)) {
|
||||
refreshRow(category);
|
||||
return;
|
||||
}
|
||||
storeContext(combo_box, category, context);
|
||||
refreshRow(category);
|
||||
}
|
||||
@@ -684,6 +870,12 @@ void AutoNumberingDockWidget::resetAutoNum(QComboBox *combo_box, AutoNumCategory
|
||||
context.replaceValue(i, QStringLiteral("a"));
|
||||
}
|
||||
|
||||
//Back to the first number, over numbers in use: ask first
|
||||
if (category == AutoNumCategory::Element
|
||||
&& !CounterWarning::confirm(this, m_project, combo_box->currentText(), context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
storeContext(combo_box, category, context);
|
||||
refreshRow(category);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
#include "../../qetproject.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QPointer>
|
||||
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
|
||||
namespace Ui {
|
||||
@@ -58,6 +60,9 @@ class AutoNumberingDockWidget : public QDockWidget
|
||||
void refreshValueFields();
|
||||
|
||||
void on_m_configure_pb_clicked();
|
||||
void applyElementSchemeToSelection();
|
||||
void updateApplyEnabled();
|
||||
void followSelectionOf(DiagramView *view);
|
||||
|
||||
void on_m_conductor_reset_start_pb_clicked();
|
||||
void on_m_element_reset_start_pb_clicked();
|
||||
@@ -126,6 +131,8 @@ class AutoNumberingDockWidget : public QDockWidget
|
||||
/// this replaces refreshValueField() at each of them.
|
||||
void refreshRow(AutoNumCategory category);
|
||||
|
||||
QPushButton *m_element_apply_pb = nullptr;
|
||||
QPointer<Diagram> m_selection_diagram; ///< the folio whose selection enables the apply button
|
||||
Ui::AutoNumberingDockWidget *ui;
|
||||
QETProject* m_project = nullptr;
|
||||
ProjectView* m_project_view = nullptr;
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "autonumberingmanagementw.h"
|
||||
#include "../elementautonumschemecommand.h"
|
||||
#include "renumberpreviewdialog.h"
|
||||
|
||||
#include "../../diagram.h"
|
||||
#include "../../qetgraphicsitem/element.h"
|
||||
#include "../../qetproject.h"
|
||||
#include "../numerotationcontextcommands.h"
|
||||
#include "formulaautonumberingw.h"
|
||||
@@ -122,7 +125,42 @@ void AutoNumberingManagementW::on_m_renumber_elements_pb_clicked()
|
||||
RenumberElementsDialog dlg(titles, this);
|
||||
if (dlg.exec() != QDialog::Accepted) return;
|
||||
|
||||
project_->renumberElementsBySchemeTitle(dlg.selectedSchemeTitle());
|
||||
const QString title = dlg.selectedSchemeTitle();
|
||||
QVector<Element *> frozen;
|
||||
auto *cmd = ElementAutoNumSchemeCommand::renumber(
|
||||
project_, title, &frozen,
|
||||
title.isEmpty() ? tr("Renuméroter les éléments")
|
||||
: tr("Renuméroter les éléments (%1)").arg(title));
|
||||
if (!cmd)
|
||||
{
|
||||
QMessageBox::information(
|
||||
this, tr("Renuméroter les éléments"),
|
||||
frozen.isEmpty()
|
||||
? tr("Aucun élément ne suit cette numérotation.")
|
||||
: tr("Les %n élément(s) qui suivent cette numérotation ont un nom "
|
||||
"figé : rien n'est renuméroté.", "", frozen.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
const int renumbered = static_cast<int>(cmd->changes().size());
|
||||
const int changed = RenumberPreviewDialog::changedLabelCount(cmd->changes());
|
||||
if (!RenumberPreviewDialog::confirm(
|
||||
this, tr("Renuméroter les éléments"),
|
||||
tr("%n élément(s) vont être renumérotés, à partir du premier numéro, "
|
||||
"dans l'ordre des folios et des positions.", "", renumbered),
|
||||
cmd->changes(), frozen)) {
|
||||
delete cmd;
|
||||
return;
|
||||
}
|
||||
project_->undoStack()->push(cmd);
|
||||
|
||||
QString summary = tr("%n élément(s) renumérotés, dont %1 avec un nouveau nom.", "", renumbered)
|
||||
.arg(changed);
|
||||
if (!frozen.isEmpty()) {
|
||||
summary += QLatin1Char('\n') + tr("%n élément(s) au nom figé n'ont pas été touchés, "
|
||||
"et leur numéro n'a pas été redonné.", "", frozen.size());
|
||||
}
|
||||
QMessageBox::information(this, tr("Renuméroter les éléments"), summary);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "counterwarning.h"
|
||||
|
||||
#include "../elementautonumschemecommand.h"
|
||||
#include "../../qetmessagebox.h"
|
||||
|
||||
/**
|
||||
@brief CounterWarning::confirm
|
||||
@param title : the element numbering
|
||||
@param proposed : its definition, with the counter to give it
|
||||
@return true if the counter meets no number in use, or the user goes
|
||||
on; false if the user gives up (and, with nobody to ask, always)
|
||||
*/
|
||||
bool CounterWarning::confirm(QWidget *parent,
|
||||
const QETProject *project,
|
||||
const QString &title,
|
||||
const NumerotationContext &proposed)
|
||||
{
|
||||
const auto conflict = ElementAutoNumSchemeCommand::counterConflict(project, title, proposed);
|
||||
if (!conflict) {
|
||||
return true;
|
||||
}
|
||||
const auto answer = QET::QetMessageBox::question(
|
||||
parent,
|
||||
tr("Compteur de la numérotation"),
|
||||
tr("Le prochain numéro serait %1, mais %n élément(s) de la numérotation « %2 » "
|
||||
"ont déjà un numéro égal ou supérieur (jusqu'à %3).", "", conflict->count)
|
||||
.arg(conflict->counter).arg(title).arg(conflict->highest)
|
||||
+ tr("\nLes nouveaux éléments sauteront les numéros déjà pris : ils ne recevront "
|
||||
"pas forcément les numéros à partir de %1.\n\nContinuer ?").arg(conflict->counter),
|
||||
QMessageBox::Yes | QMessageBox::Cancel,
|
||||
QMessageBox::Cancel);
|
||||
return answer == QMessageBox::Yes;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 COUNTERWARNING_H
|
||||
#define COUNTERWARNING_H
|
||||
|
||||
#include "../numerotationcontext.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
class QETProject;
|
||||
class QWidget;
|
||||
|
||||
/**
|
||||
@brief The CounterWarning class
|
||||
Asks before the counter of an element numbering is set to a number which
|
||||
elements of it have already: the next elements skip the numbers in use,
|
||||
so the next number is not the one asked for. The user may go on or give
|
||||
up.
|
||||
*/
|
||||
class CounterWarning
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(CounterWarning)
|
||||
|
||||
public:
|
||||
static bool confirm(QWidget *parent,
|
||||
const QETProject *project,
|
||||
const QString &title,
|
||||
const NumerotationContext &proposed);
|
||||
};
|
||||
|
||||
#endif // COUNTERWARNING_H
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "pastenumberingimport.h"
|
||||
|
||||
#include "../../diagram.h"
|
||||
#include "../../diagramcommands.h"
|
||||
#include "../../qetmessagebox.h"
|
||||
#include "../../qetproject.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
/// @return the numberings a copy carries, which pasted elements follow
|
||||
QList<ElementAutoNumSchemeCommand::Scheme> PasteNumberingImport::copiedBy(const QDomDocument &clipboard)
|
||||
{
|
||||
return ElementAutoNumSchemeCommand::copiedSchemes(clipboard.documentElement());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief PasteNumberingImport::push
|
||||
Push the paste of @p content on the undo stack of @p diagram, after
|
||||
asking whether to import the numberings of @p copied which the pasted
|
||||
elements follow and the project does not have.
|
||||
@param parent : parent of the question
|
||||
@param copied : the numberings the copy carries
|
||||
*/
|
||||
void PasteNumberingImport::push(QWidget *parent,
|
||||
Diagram *diagram,
|
||||
const DiagramContent &content,
|
||||
const QList<ElementAutoNumSchemeCommand::Scheme> &copied)
|
||||
{
|
||||
QETProject *project = diagram->project();
|
||||
auto *paste = new PasteDiagramCommand(diagram, content);
|
||||
|
||||
QList<ElementAutoNumSchemeCommand::Scheme> missing;
|
||||
if (project && !copied.isEmpty()
|
||||
&& QSettings().value("diagramcommands/autonumber-pasted-elements", true).toBool()) {
|
||||
missing = ElementAutoNumSchemeCommand::missingForPaste(project, copied, content.m_elements);
|
||||
}
|
||||
if (missing.isEmpty()) {
|
||||
diagram->undoStack().push(paste);
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList names;
|
||||
for (const auto &scheme : std::as_const(missing)) {
|
||||
names << QStringLiteral("« %1 »").arg(scheme.title);
|
||||
}
|
||||
const auto answer = QET::QetMessageBox::question(
|
||||
parent,
|
||||
tr("Numérotation absente de ce projet"),
|
||||
tr("Les éléments collés suivent %n numérotation(s) qui n'existe(nt) pas dans ce projet : %1.\n\n"
|
||||
"Les importer ? Les éléments recevront alors les prochains numéros, à partir de 1. "
|
||||
"Sinon ils seront collés sans numérotation.", "", static_cast<int>(missing.size()))
|
||||
.arg(names.join(QStringLiteral(", "))),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
diagram->undoStack().push(paste);
|
||||
return;
|
||||
}
|
||||
|
||||
diagram->undoStack().beginMacro(paste->text());
|
||||
for (const auto &scheme : std::as_const(missing))
|
||||
{
|
||||
//Under its own name, or one which is free here
|
||||
QString title = scheme.title;
|
||||
for (int n = 2 ; !project->elementAutoNumNameClash(title).isEmpty() ; ++n) {
|
||||
title = QStringLiteral("%1 (%2)").arg(scheme.title).arg(n);
|
||||
}
|
||||
if (auto *cmd = ElementAutoNumSchemeCommand::create(
|
||||
project, title,
|
||||
ElementAutoNumSchemeCommand::resetForRenumber(scheme.context),
|
||||
scheme.id, false)) {
|
||||
diagram->undoStack().push(cmd);
|
||||
}
|
||||
}
|
||||
diagram->undoStack().push(paste);
|
||||
diagram->undoStack().endMacro();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 PASTENUMBERINGIMPORT_H
|
||||
#define PASTENUMBERINGIMPORT_H
|
||||
|
||||
#include "../elementautonumschemecommand.h"
|
||||
#include "../../diagramcontent.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QList>
|
||||
|
||||
class Diagram;
|
||||
class QWidget;
|
||||
|
||||
/**
|
||||
@brief The PasteNumberingImport class
|
||||
Pastes elements copied from another project, which may follow element
|
||||
numberings this project does not have.
|
||||
|
||||
Such an element would be left with a formula which names no numbering
|
||||
here, and with a label nothing explains. So the user is asked, once
|
||||
for all the numberings missing, whether to import them. Imported, they
|
||||
number the pasted elements from their first number (a number of the other
|
||||
project means nothing here); not imported, the elements are pasted
|
||||
without a numbering.
|
||||
|
||||
The import and the paste are one undo step.
|
||||
*/
|
||||
class PasteNumberingImport
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(PasteNumberingImport)
|
||||
|
||||
public:
|
||||
static void push(QWidget *parent,
|
||||
Diagram *diagram,
|
||||
const DiagramContent &content,
|
||||
const QList<ElementAutoNumSchemeCommand::Scheme> &copied);
|
||||
|
||||
static QList<ElementAutoNumSchemeCommand::Scheme> copiedBy(const QDomDocument &clipboard);
|
||||
};
|
||||
|
||||
#endif // PASTENUMBERINGIMPORT_H
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "renumberpreviewdialog.h"
|
||||
|
||||
#include "../../diagram.h"
|
||||
#include "../../qetgraphicsitem/element.h"
|
||||
#include "../../qetinformation.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFont>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
QString labelOf(const DiagramContext &info)
|
||||
{
|
||||
return info.value(QETInformation::ELMT_LABEL).toString();
|
||||
}
|
||||
|
||||
QString folioOf(const Element *element)
|
||||
{
|
||||
return element && element->diagram()
|
||||
? QString::number(element->diagram()->folioIndex() + 1)
|
||||
: QString();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief RenumberPreviewDialog::changedLabelCount
|
||||
@return how many of @p changes give their element another label
|
||||
*/
|
||||
int RenumberPreviewDialog::changedLabelCount(
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes)
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto &change : changes) {
|
||||
if (labelOf(change.old_infos) != labelOf(change.new_infos)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief RenumberPreviewDialog::ask
|
||||
@param intro : text above the list
|
||||
@param changes : what the operation would do to elements
|
||||
@param left_alone : the elements it leaves as they are, each with the
|
||||
reason shown in place of its new name
|
||||
@param replace_text : when not empty, a third button of that text
|
||||
which goes on and also replaces the elements left alone
|
||||
@return what the user chose
|
||||
*/
|
||||
RenumberPreviewDialog::Answer RenumberPreviewDialog::ask(
|
||||
QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &intro,
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes,
|
||||
const QVector<LeftAlone> &left_alone,
|
||||
const QString &replace_text)
|
||||
{
|
||||
QDialog dialog(parent);
|
||||
dialog.setWindowTitle(title);
|
||||
dialog.resize(640, 460);
|
||||
auto *layout = new QVBoxLayout(&dialog);
|
||||
|
||||
const int changed = changedLabelCount(changes);
|
||||
QString text = intro;
|
||||
if (changes.size() > changed) {
|
||||
text += QLatin1Char('\n') + tr("%n élément(s) gardent le nom qu'ils ont.", "", changes.size() - changed);
|
||||
}
|
||||
if (!left_alone.isEmpty()) {
|
||||
text += QLatin1Char('\n') + tr("%n élément(s) restent comme ils sont.", "", left_alone.size());
|
||||
}
|
||||
auto *intro_label = new QLabel(text, &dialog);
|
||||
intro_label->setWordWrap(true);
|
||||
layout->addWidget(intro_label);
|
||||
|
||||
auto *table = new QTableWidget(&dialog);
|
||||
table->setColumnCount(4);
|
||||
table->setHorizontalHeaderLabels({tr("Folio"), tr("Élément"), tr("Nom actuel"), tr("Nouveau nom")});
|
||||
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table->verticalHeader()->setVisible(false);
|
||||
table->horizontalHeader()->setStretchLastSection(true);
|
||||
table->setAlternatingRowColors(true);
|
||||
|
||||
const auto add_row = [table](const QStringList &cells, bool grey) {
|
||||
const int row = table->rowCount();
|
||||
table->insertRow(row);
|
||||
for (int column = 0 ; column < cells.size() ; ++column) {
|
||||
auto *item = new QTableWidgetItem(cells.at(column));
|
||||
if (grey) {
|
||||
QFont font = item->font();
|
||||
font.setItalic(true);
|
||||
item->setFont(font);
|
||||
item->setForeground(QBrush(Qt::gray));
|
||||
}
|
||||
table->setItem(row, column, item);
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &change : changes) {
|
||||
const QString before = labelOf(change.old_infos);
|
||||
const QString after = labelOf(change.new_infos);
|
||||
if (before != after) {
|
||||
add_row({folioOf(change.element),
|
||||
change.element ? change.element->name() : QString(),
|
||||
before, after}, false);
|
||||
}
|
||||
}
|
||||
for (const LeftAlone &left : left_alone) {
|
||||
if (left.element) {
|
||||
add_row({folioOf(left.element), left.element->name(),
|
||||
labelOf(left.element->elementInformations()), left.note}, true);
|
||||
}
|
||||
}
|
||||
table->resizeColumnsToContents();
|
||||
layout->addWidget(table, 1);
|
||||
|
||||
//With no label to change, OK would do nothing: only Cancel (and, where
|
||||
//there is one, the button which replaces what is left alone) is offered
|
||||
const bool something_to_do = !changes.isEmpty();
|
||||
auto *buttons = new QDialogButtonBox(
|
||||
something_to_do ? (QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
|
||||
: QDialogButtonBox::Cancel, &dialog);
|
||||
Answer answer = Answer::Cancel;
|
||||
QPushButton *replace = nullptr;
|
||||
if (!replace_text.isEmpty()) {
|
||||
replace = buttons->addButton(replace_text, QDialogButtonBox::ActionRole);
|
||||
}
|
||||
QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||
QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
if (replace) {
|
||||
QObject::connect(replace, &QPushButton::clicked, &dialog, [&]() {
|
||||
answer = Answer::GoAndReplace;
|
||||
dialog.accept();
|
||||
});
|
||||
}
|
||||
layout->addWidget(buttons);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted && answer != Answer::GoAndReplace) {
|
||||
answer = Answer::Go;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief RenumberPreviewDialog::confirm
|
||||
@param intro : text above the list
|
||||
@param changes : what the operation would do to elements
|
||||
@param frozen : the elements it leaves as they are, a frozen label
|
||||
@return true if the user agrees to go on
|
||||
*/
|
||||
bool RenumberPreviewDialog::confirm(
|
||||
QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &intro,
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes,
|
||||
const QVector<Element *> &frozen)
|
||||
{
|
||||
QVector<LeftAlone> left;
|
||||
for (Element *el : frozen) {
|
||||
left.append({el, tr("(figé)")});
|
||||
}
|
||||
return ask(parent, title, intro, changes, left) == Answer::Go;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 RENUMBERPREVIEWDIALOG_H
|
||||
#define RENUMBERPREVIEWDIALOG_H
|
||||
|
||||
#include "../renumberelementscommand.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QVector>
|
||||
|
||||
class Element;
|
||||
class QWidget;
|
||||
|
||||
/**
|
||||
@brief The RenumberPreviewDialog class
|
||||
Shows what a numbering operation is about to do to the labels of
|
||||
elements -- each changed element with its label now and the one it
|
||||
would get -- and the elements with a frozen label it leaves as they are,
|
||||
and asks to go on or not. Nothing is changed by the dialog itself.
|
||||
*/
|
||||
class RenumberPreviewDialog
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(RenumberPreviewDialog)
|
||||
|
||||
public:
|
||||
/// An element an operation leaves as it is, and why
|
||||
struct LeftAlone
|
||||
{
|
||||
Element *element = nullptr;
|
||||
QString note;
|
||||
};
|
||||
enum class Answer { Cancel, Go, GoAndReplace };
|
||||
|
||||
static Answer ask(
|
||||
QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &intro,
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes,
|
||||
const QVector<LeftAlone> &left_alone,
|
||||
const QString &replace_text = QString());
|
||||
|
||||
static bool confirm(
|
||||
QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &intro,
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes,
|
||||
const QVector<Element *> &frozen);
|
||||
|
||||
static int changedLabelCount(
|
||||
const QVector<RenumberElementsCommand::ElementChange> &changes);
|
||||
};
|
||||
|
||||
#endif // RENUMBERPREVIEWDIALOG_H
|
||||
@@ -18,13 +18,17 @@
|
||||
#include "selectautonumw.h"
|
||||
|
||||
#include "../assignvariables.h"
|
||||
#include "../numerotationcontextcommands.h"
|
||||
#include "formulaautonumberingw.h"
|
||||
#include "numparteditorw.h"
|
||||
#include "ui_formulaautonumberingw.h"
|
||||
#include "ui_selectautonumw.h"
|
||||
|
||||
#include "../../qeticons.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::SelectAutonumW
|
||||
@@ -39,6 +43,24 @@ SelectAutonumW::SelectAutonumW(int type, QWidget *parent) :
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->m_comboBox->lineEdit()->setClearButtonEnabled(true);
|
||||
|
||||
//The add button goes under the last part, at the left; each part
|
||||
//has its own remove and move buttons at its right. The one remove
|
||||
//button above the parts, which could only remove the last one, is
|
||||
//not needed any more.
|
||||
ui->remove_button->hide();
|
||||
ui->horizontalLayout->removeWidget(ui->add_button);
|
||||
m_add_row = new QWidget(this);
|
||||
auto *add_layout = new QHBoxLayout(m_add_row);
|
||||
add_layout->setContentsMargins(0, 0, 0, 0);
|
||||
ui->add_button->setParent(m_add_row);
|
||||
add_layout->addWidget(ui->add_button);
|
||||
add_layout->addStretch();
|
||||
ui->editor_layout->addWidget(m_add_row);
|
||||
ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Annuler"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Reset)->setToolTip(
|
||||
tr("Revenir à la définition enregistrée"));
|
||||
|
||||
if (m_edited_type == 0)
|
||||
{
|
||||
m_feaw = new FormulaAutonumberingW();
|
||||
@@ -103,28 +125,125 @@ void SelectAutonumW::setContext(const NumerotationContext &context)
|
||||
{
|
||||
m_context = context;
|
||||
|
||||
qDeleteAll(num_part_list_);
|
||||
num_part_list_.clear();
|
||||
clearPartRows();
|
||||
|
||||
if (m_context.size() == 0) { //@context contain nothing, build a default numPartEditor
|
||||
on_add_button_clicked();
|
||||
insertPartRow(new NumPartEditorW(m_edited_type, this));
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<m_context.size(); ++i) { //build with the content of @context
|
||||
NumPartEditorW *part= new NumPartEditorW(m_context, i, m_edited_type, this);
|
||||
connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
|
||||
num_part_list_ << part;
|
||||
ui -> editor_layout -> addWidget(part);
|
||||
insertPartRow(new NumPartEditorW(m_context, i, m_edited_type, this));
|
||||
}
|
||||
}
|
||||
|
||||
num_part_list_.size() == 1 ?
|
||||
ui -> remove_button -> setDisabled(true):
|
||||
ui -> remove_button -> setEnabled (true);
|
||||
|
||||
updatePartButtons();
|
||||
applyEnable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::insertPartRow
|
||||
Show @p part as the last part of the definition, with the buttons which
|
||||
remove it and move it up or down at its right.
|
||||
*/
|
||||
void SelectAutonumW::insertPartRow(NumPartEditorW *part)
|
||||
{
|
||||
connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
|
||||
|
||||
PartRow r;
|
||||
r.part = part;
|
||||
r.row = new QWidget(this);
|
||||
auto *layout = new QHBoxLayout(r.row);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(2);
|
||||
part->setParent(r.row);
|
||||
layout->addWidget(part, 1);
|
||||
|
||||
const auto make_button = [&](const QIcon &icon, const QString &tip) {
|
||||
auto *button = new QToolButton(r.row);
|
||||
button->setIcon(icon);
|
||||
button->setToolTip(tip);
|
||||
button->setAutoRaise(true);
|
||||
layout->addWidget(button);
|
||||
return button;
|
||||
};
|
||||
r.up = make_button(QET::Icons::GoUp, tr("Monter cette variable"));
|
||||
r.down = make_button(QET::Icons::GoDown, tr("Descendre cette variable"));
|
||||
r.remove = make_button(QET::Icons::EditDelete, tr("Supprimer cette variable"));
|
||||
QWidget *row = r.row;
|
||||
connect(r.up, &QToolButton::clicked, this, [this, row]() { movePartRow(row, -1); });
|
||||
connect(r.down, &QToolButton::clicked, this, [this, row]() { movePartRow(row, +1); });
|
||||
connect(r.remove, &QToolButton::clicked, this, [this, row]() { removePartRow(row); });
|
||||
|
||||
m_rows << r;
|
||||
num_part_list_ << part;
|
||||
//Before the add button, which stays under the last part
|
||||
ui->editor_layout->insertWidget(ui->editor_layout->count() - 1, r.row);
|
||||
updatePartButtons();
|
||||
}
|
||||
|
||||
/// Take away every part of the definition shown
|
||||
void SelectAutonumW::clearPartRows()
|
||||
{
|
||||
for (const PartRow &r : std::as_const(m_rows)) {
|
||||
delete r.row; //with the part, which it holds
|
||||
}
|
||||
m_rows.clear();
|
||||
num_part_list_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::updatePartButtons
|
||||
The first part cannot move up, the last cannot move down, and the only
|
||||
one cannot be removed: those buttons are greyed out.
|
||||
*/
|
||||
void SelectAutonumW::updatePartButtons()
|
||||
{
|
||||
for (int i = 0 ; i < m_rows.size() ; ++i) {
|
||||
m_rows.at(i).up->setEnabled(i > 0);
|
||||
m_rows.at(i).down->setEnabled(i < m_rows.size() - 1);
|
||||
m_rows.at(i).remove->setEnabled(m_rows.size() > 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the part shown in @p row; the definition keeps at least one
|
||||
void SelectAutonumW::removePartRow(QWidget *row)
|
||||
{
|
||||
if (m_rows.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0 ; i < m_rows.size() ; ++i) {
|
||||
if (m_rows.at(i).row == row) {
|
||||
const PartRow r = m_rows.takeAt(i);
|
||||
num_part_list_.removeAll(r.part);
|
||||
delete r.row; //with the part, which it holds
|
||||
break;
|
||||
}
|
||||
}
|
||||
updatePartButtons();
|
||||
applyEnable();
|
||||
}
|
||||
|
||||
/// Move the part shown in @p row up (-1) or down (+1)
|
||||
void SelectAutonumW::movePartRow(QWidget *row, int step)
|
||||
{
|
||||
for (int i = 0 ; i < m_rows.size() ; ++i) {
|
||||
if (m_rows.at(i).row != row) {
|
||||
continue;
|
||||
}
|
||||
const int to = i + step;
|
||||
if (to < 0 || to >= m_rows.size()) {
|
||||
return;
|
||||
}
|
||||
m_rows.swapItemsAt(i, to);
|
||||
num_part_list_.swapItemsAt(i, to);
|
||||
ui->editor_layout->removeWidget(row);
|
||||
ui->editor_layout->insertWidget(1 + to, row); //after the header of the columns
|
||||
break;
|
||||
}
|
||||
updatePartButtons();
|
||||
applyEnable();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::toNumContext
|
||||
@return the content to num_part_list to NumerotationContext
|
||||
@@ -143,12 +262,8 @@ NumerotationContext SelectAutonumW::toNumContext() const
|
||||
*/
|
||||
void SelectAutonumW::on_add_button_clicked()
|
||||
{
|
||||
applyEnable(false);
|
||||
NumPartEditorW *part = new NumPartEditorW(m_edited_type, this);
|
||||
connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
|
||||
num_part_list_ << part;
|
||||
ui -> editor_layout -> addWidget(part);
|
||||
ui -> remove_button -> setEnabled(true);
|
||||
insertPartRow(new NumPartEditorW(m_edited_type, this));
|
||||
applyEnable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,16 +272,9 @@ void SelectAutonumW::on_add_button_clicked()
|
||||
*/
|
||||
void SelectAutonumW::on_remove_button_clicked()
|
||||
{
|
||||
//remove if @num_part_list contains more than one item
|
||||
if (num_part_list_.size() > 1) {
|
||||
NumPartEditorW *part = num_part_list_.takeLast();
|
||||
// deliberately not disconnecting as not possible to resolve with lambda and will happen automatically when the object "part" is destroyed.
|
||||
delete part;
|
||||
if (num_part_list_.size() == 1) {
|
||||
ui -> remove_button -> setDisabled(true);
|
||||
}
|
||||
if (!m_rows.isEmpty()) {
|
||||
removePartRow(m_rows.last().row);
|
||||
}
|
||||
applyEnable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,6 +400,8 @@ void SelectAutonumW::on_buttonBox_clicked(QAbstractButton *button)
|
||||
*/
|
||||
void SelectAutonumW::applyEnable(bool b)
|
||||
{
|
||||
//Apply and Cancel both stand for a change: grey until there is one
|
||||
ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(b);
|
||||
if (b){
|
||||
bool valid= true;
|
||||
foreach (NumPartEditorW *npe, num_part_list_)
|
||||
@@ -331,28 +441,6 @@ void SelectAutonumW::contextToFormula()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::on_m_next_pb_clicked
|
||||
Increase NumerotationContext
|
||||
*/
|
||||
void SelectAutonumW::on_m_next_pb_clicked()
|
||||
{
|
||||
NumerotationContextCommands ncc (toNumContext());
|
||||
setContext(ncc.next());
|
||||
applyEnable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::on_m_previous_pb_clicked
|
||||
Decrease NumerotationContext
|
||||
*/
|
||||
void SelectAutonumW::on_m_previous_pb_clicked()
|
||||
{
|
||||
NumerotationContextCommands ncc (toNumContext());
|
||||
setContext(ncc.previous());
|
||||
applyEnable(true);
|
||||
}
|
||||
|
||||
void SelectAutonumW::on_m_comboBox_currentTextChanged(const QString &arg1)
|
||||
{
|
||||
Q_UNUSED(arg1);
|
||||
@@ -363,3 +451,29 @@ void SelectAutonumW::on_m_remove_pb_clicked()
|
||||
{
|
||||
emit removeClicked();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief SelectAutonumW::setExplicitNaming
|
||||
Make the list of numberings a plain choice: no typing a name into it
|
||||
(an easy way to create a numbering by accident, or one with an empty
|
||||
name), but a button to create a numbering and one to rename the shown
|
||||
one, which emit newClicked() and renameClicked() for the owner to ask
|
||||
for the name.
|
||||
*/
|
||||
void SelectAutonumW::setExplicitNaming()
|
||||
{
|
||||
ui->m_comboBox->setEditable(false);
|
||||
auto *row = ui->horizontalLayout_2;
|
||||
const int index = row->indexOf(ui->m_remove_pb);
|
||||
|
||||
auto *new_pb = new QPushButton(QET::Icons::Add, QString(), this);
|
||||
new_pb->setToolTip(tr("Nouvelle numérotation…"));
|
||||
connect(new_pb, &QPushButton::clicked, this, &SelectAutonumW::newClicked);
|
||||
|
||||
auto *rename_pb = new QPushButton(QET::Icons::EditRename, QString(), this);
|
||||
rename_pb->setToolTip(tr("Renommer la numérotation…"));
|
||||
connect(rename_pb, &QPushButton::clicked, this, &SelectAutonumW::renameClicked);
|
||||
|
||||
row->insertWidget(index, rename_pb);
|
||||
row->insertWidget(index, new_pb);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class NumPartEditorW;
|
||||
class QAbstractButton;
|
||||
class FormulaAutonumberingW;
|
||||
class QComboBox;
|
||||
class QToolButton;
|
||||
class QHBoxLayout;
|
||||
|
||||
namespace Ui {
|
||||
class SelectAutonumW;
|
||||
@@ -52,10 +54,14 @@ class SelectAutonumW : public QWidget
|
||||
void contextToFormula ();
|
||||
QString formula();
|
||||
QComboBox *contextComboBox() const;
|
||||
void setExplicitNaming();
|
||||
|
||||
signals:
|
||||
void applyPressed();
|
||||
void removeClicked();
|
||||
/// Only with setExplicitNaming()
|
||||
void newClicked();
|
||||
void renameClicked();
|
||||
|
||||
//SLOT
|
||||
private slots:
|
||||
@@ -63,16 +69,30 @@ class SelectAutonumW : public QWidget
|
||||
void on_remove_button_clicked();
|
||||
void on_buttonBox_clicked(QAbstractButton *);
|
||||
void applyEnable (bool = true);
|
||||
void on_m_next_pb_clicked();
|
||||
void on_m_previous_pb_clicked();
|
||||
void on_m_comboBox_currentTextChanged(const QString &arg1);
|
||||
|
||||
//ATTRIBUTES
|
||||
void on_m_remove_pb_clicked();
|
||||
|
||||
|
||||
private:
|
||||
/// A part of the definition, with the buttons at its right
|
||||
struct PartRow
|
||||
{
|
||||
QWidget *row = nullptr;
|
||||
NumPartEditorW *part = nullptr;
|
||||
QToolButton *up = nullptr;
|
||||
QToolButton *down = nullptr;
|
||||
QToolButton *remove = nullptr;
|
||||
};
|
||||
void insertPartRow(NumPartEditorW *part);
|
||||
void removePartRow(QWidget *row);
|
||||
void movePartRow(QWidget *row, int step);
|
||||
void updatePartButtons();
|
||||
void clearPartRows();
|
||||
Ui::SelectAutonumW *ui;
|
||||
QList <NumPartEditorW *> num_part_list_;
|
||||
QList <PartRow> m_rows;
|
||||
QWidget *m_add_row = nullptr; ///< holds the add button, under the last part
|
||||
NumerotationContext m_context;
|
||||
FormulaAutonumberingW *m_feaw;
|
||||
FormulaAutonumberingW *m_fcaw;
|
||||
|
||||
@@ -158,32 +158,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_previous_pb">
|
||||
<property name="toolTip">
|
||||
<string>Précédent</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-left"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_next_pb">
|
||||
<property name="toolTip">
|
||||
<string>Suivant</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-right"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
@@ -227,6 +201,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="value_label">
|
||||
<property name="toolTip">
|
||||
<string>Pour un numéro : le prochain numéro attribué. Pour un texte : le texte fixe.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Valeur</string>
|
||||
</property>
|
||||
|
||||
@@ -133,6 +133,8 @@ class BorderTitleBlock : public QObject
|
||||
return (additional_fields_); }
|
||||
/// @return the value of the title block
|
||||
QString autoPageNum() const { return(btb_auto_page_num_); }
|
||||
/// The title of the folio numbering this folio follows, nothing else changes
|
||||
void setAutoPageNum(const QString &title) { btb_auto_page_num_ = title; }
|
||||
/// @return the value of the total number of folios
|
||||
int folioTotal() const { return(folio_total_);}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "diagram.h"
|
||||
#include "autoNum/elementautonumschemecommand.h"
|
||||
|
||||
#include "ElementsCollection/elementcollectionhandler.h"
|
||||
#include "TerminalStrip/GraphicsItem/terminalstripitem.h"
|
||||
@@ -1290,6 +1291,13 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
||||
std::stable_sort(list_conductors.begin(), list_conductors.end(),
|
||||
[](Conductor *a, Conductor *b) { return conductorSortKey(a) < conductorSortKey(b); });
|
||||
|
||||
// A copy carries the numberings its elements follow: pasted into
|
||||
// another project, which does not know them, it can offer to import
|
||||
// them (see PasteNumberingImport)
|
||||
if (is_copy_command) {
|
||||
ElementAutoNumSchemeCommand::writeCopiedSchemes(document, dom_root, m_project, list_elements);
|
||||
}
|
||||
|
||||
// correspondence table between the addresses of the terminals and their ids
|
||||
// table de correspondance entre les adresses des bornes et leurs ids
|
||||
QHash<Terminal *, int> table_adr_id;
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
*/
|
||||
#include "diagramcommands.h"
|
||||
|
||||
#include "autoNum/elementautonumschemecommand.h"
|
||||
#include "diagram.h"
|
||||
#include "itemgroups.h"
|
||||
#include "qetproject.h"
|
||||
#include "qetgraphicsitem/conductortextitem.h"
|
||||
#include "qetgraphicsitem/diagramimageitem.h"
|
||||
#include "qetgraphicsitem/dynamicelementtextitem.h"
|
||||
@@ -29,6 +31,19 @@
|
||||
#include "qetinformation.h"
|
||||
#include "qgimanager.h"
|
||||
|
||||
namespace {
|
||||
/// Is @p element among those the numberings in @p schemes are given to?
|
||||
bool pasted_schemes_has(const QMap<QString, QVector<Element *>> &schemes, const Element *element)
|
||||
{
|
||||
for (auto it = schemes.constBegin() ; it != schemes.constEnd() ; ++it) {
|
||||
if (it.value().contains(const_cast<Element *>(element))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief PasteDiagramCommand::PasteDiagramCommand
|
||||
Constructor
|
||||
@@ -64,6 +79,10 @@ void PasteDiagramCommand::undo()
|
||||
{
|
||||
diagram -> showMe();
|
||||
|
||||
//The numbering of the pasted elements, if any: gives the numbers
|
||||
//back to the numberings they came from
|
||||
QUndoCommand::undo();
|
||||
|
||||
foreach(QGraphicsItem *item, content.items(filter))
|
||||
diagram->removeItem(item);
|
||||
}
|
||||
@@ -98,6 +117,18 @@ void PasteDiagramCommand::redo()
|
||||
e->initLink(elmts_list);
|
||||
}
|
||||
|
||||
//The numberings the pasted elements follow, known now: the label
|
||||
//erasing below empties their formula. Pasted elements get the next
|
||||
//number of their numbering, as placed ones do, instead of the
|
||||
//label of the element they were copied from.
|
||||
const bool autonumber = m_autonumber && settings.value(
|
||||
"diagramcommands/autonumber-pasted-elements", true).toBool();
|
||||
QETProject *project = diagram->project();
|
||||
QMap<QString, QVector<Element *>> pasted_schemes;
|
||||
if (project && autonumber) {
|
||||
pasted_schemes = ElementAutoNumSchemeCommand::pastedSchemes(project, elmts_list);
|
||||
}
|
||||
|
||||
//make new uuid for every pasted conductor, because old uuid are
|
||||
//the uuid of the copied conductor
|
||||
const QList <Conductor *> all_pasted_conductors = content.conductors();
|
||||
@@ -221,6 +252,44 @@ void PasteDiagramCommand::redo()
|
||||
}
|
||||
}
|
||||
|
||||
//What the pasted elements keep of their numbering names a
|
||||
//numbering of this project, or none (a paste from another
|
||||
//project, or from a file written before the ids)
|
||||
if (project) {
|
||||
ElementAutoNumSchemeCommand::linkPasted(project, elmts_list);
|
||||
if (autonumber)
|
||||
{
|
||||
//A formula which names no numbering of this project is
|
||||
//not kept: it would stand for a numbering which does
|
||||
//not exist here, with a label which is not its result.
|
||||
//The label is what the erase preference made of it.
|
||||
for (Element *e : elmts_list)
|
||||
{
|
||||
const DiagramContext &info = e->elementInformations();
|
||||
if (e->linkType() == Element::Slave || (e->linkType() & Element::AllReport)
|
||||
|| info.value(QETInformation::ELMT_FORMULA).toString().isEmpty()
|
||||
|| !ElementAutoNumSchemeCommand::followedScheme(project, info).isEmpty()
|
||||
|| pasted_schemes_has(pasted_schemes, e)) {
|
||||
continue;
|
||||
}
|
||||
DiagramContext dc = info;
|
||||
dc.addValue(QETInformation::ELMT_FORMULA, QString());
|
||||
for (DynamicElementTextItem *deti : e->dynamicTextItems())
|
||||
deti->m_block_alignment = true;
|
||||
for (auto *group : e->textGroups())
|
||||
group->blockAlignmentUpdate(true);
|
||||
e->setElementInformations(dc);
|
||||
for (DynamicElementTextItem *deti : e->dynamicTextItems())
|
||||
deti->m_block_alignment = false;
|
||||
for (auto *group : e->textGroups())
|
||||
group->blockAlignmentUpdate(false);
|
||||
}
|
||||
}
|
||||
if (!pasted_schemes.isEmpty()) {
|
||||
ElementAutoNumSchemeCommand::numberPasted(project, pasted_schemes, this);
|
||||
}
|
||||
}
|
||||
|
||||
//Pasted groups become new groups: the members of one source
|
||||
//group all get the same new uuid, never the source's, or the
|
||||
//copy would join the original's group. After the elements got
|
||||
@@ -250,6 +319,10 @@ void PasteDiagramCommand::redo()
|
||||
}
|
||||
}
|
||||
|
||||
//The numbering of the pasted elements, if any, on the first redo
|
||||
//as well as on the next ones
|
||||
QUndoCommand::redo();
|
||||
|
||||
const QList<QGraphicsItem *> qgis_list = content.items();
|
||||
for (QGraphicsItem *qgi : qgis_list)
|
||||
qgi -> setSelected(true);
|
||||
|
||||
@@ -42,6 +42,12 @@ class PasteDiagramCommand : public QUndoCommand {
|
||||
public:
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
/**
|
||||
Whether pasted elements which follow an element numbering are
|
||||
numbered by the paste (default true, if the preference allows it).
|
||||
Off for a caller which numbers them itself.
|
||||
*/
|
||||
void setAutoNumbering(bool enabled) {m_autonumber = enabled;}
|
||||
|
||||
// attributes
|
||||
private:
|
||||
@@ -53,6 +59,8 @@ class PasteDiagramCommand : public QUndoCommand {
|
||||
int filter;
|
||||
/// prevent the first call to redo()
|
||||
bool first_redo;
|
||||
/// number the pasted elements which follow an element numbering
|
||||
bool m_autonumber = true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -274,7 +274,10 @@ void DiagramEventAddMacro::addMacro(QPointF final_pos)
|
||||
QSettings settings;
|
||||
bool saved_erase = settings.value("diagramcommands/erase-label-on-copy", true).toBool();
|
||||
settings.setValue("diagramcommands/erase-label-on-copy", false);
|
||||
m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, pasted_content));
|
||||
//and does not number them either: the labels are the ones saved
|
||||
auto *paste = new PasteDiagramCommand(m_diagram, pasted_content);
|
||||
paste->setAutoNumbering(false);
|
||||
m_diagram->undoStack().push(paste);
|
||||
settings.setValue("diagramcommands/erase-label-on-copy", saved_erase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "diagrameventaddpaste.h"
|
||||
#include "../autoNum/ui/pastenumberingimport.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../diagramcommands.h"
|
||||
@@ -74,6 +75,7 @@
|
||||
|
||||
//Load items at their original XML coordinates.
|
||||
m_diagram->fromXml(document_xml, QPointF(), false, &m_content);
|
||||
m_copied_schemes = PasteNumberingImport::copiedBy(document_xml);
|
||||
|
||||
if (db) {
|
||||
db->blockSignals(false);
|
||||
@@ -385,7 +387,10 @@ void DiagramEventAddPaste::commit()
|
||||
m_finished = true;
|
||||
m_running = false;
|
||||
|
||||
m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, m_content));
|
||||
//Asks whether to import the numberings the copy brings, if the
|
||||
//project has not got them
|
||||
PasteNumberingImport::push(m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(),
|
||||
m_diagram, m_content, m_copied_schemes);
|
||||
emit finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "diagrameventinterface.h"
|
||||
#include "../diagramcontent.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
@@ -90,6 +91,8 @@ class DiagramEventAddPaste : public DiagramEventInterface
|
||||
void removeItems();
|
||||
|
||||
DiagramContent m_content;
|
||||
/// The numberings the pasted copy carries
|
||||
QList<ElementAutoNumSchemeCommand::Scheme> m_copied_schemes;
|
||||
///Each movable item's position relative to the group's top left,
|
||||
///taken once so repeated moves cannot accumulate rounding drift.
|
||||
QHash<QGraphicsItem *, QPointF> m_relative_pos;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "diagramview.h"
|
||||
#include "autoNum/ui/pastenumberingimport.h"
|
||||
#include "cellruler.h"
|
||||
#include "lastusedstyle.h"
|
||||
#include "utils/colordialogdoubleclick.h"
|
||||
@@ -572,7 +573,10 @@ void DiagramView::paste(const QPointF &pos, QClipboard::Mode clipboard_mode) {
|
||||
if (content_pasted.count())
|
||||
{
|
||||
m_diagram -> clearSelection();
|
||||
m_diagram -> undoStack().push(new PasteDiagramCommand(m_diagram, content_pasted));
|
||||
//Asks whether to import the numberings the copy brings, if the
|
||||
//project has not got them
|
||||
PasteNumberingImport::push(this, m_diagram, content_pasted,
|
||||
PasteNumberingImport::copiedBy(document_xml));
|
||||
adjustSceneRect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "elementspanelwidget.h"
|
||||
#include "autoNum/elementautonumschemecommand.h"
|
||||
#include "diagram.h"
|
||||
#include "qetgraphicsitem/conductor.h"
|
||||
#include "editor/ui/qetelementeditor.h"
|
||||
@@ -699,6 +700,14 @@ void ElementsPanelWidget::duplicateDiagram()
|
||||
elmt->initLink(new_elements);
|
||||
}
|
||||
|
||||
// The numberings the copies follow, known before the label
|
||||
// erasing below empties their formula
|
||||
const bool autonumber = QSettings().value(
|
||||
"diagramcommands/autonumber-pasted-elements", true).toBool();
|
||||
const QMap<QString, QVector<Element *>> copy_schemes = autonumber
|
||||
? ElementAutoNumSchemeCommand::pastedSchemes(project, new_elements)
|
||||
: QMap<QString, QVector<Element *>>();
|
||||
|
||||
for (QGraphicsItem *item : new_diagram->items()) {
|
||||
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
||||
// The XML round-trip kept the source elements' uuids. Give the
|
||||
@@ -835,6 +844,18 @@ void ElementsPanelWidget::duplicateDiagram()
|
||||
}
|
||||
}
|
||||
|
||||
// The copies follow the numberings of the project: they get the
|
||||
// next numbers, as pasted elements do. This duplication is not
|
||||
// undoable, so neither is the numbering: it is done at once.
|
||||
ElementAutoNumSchemeCommand::linkPasted(project, new_elements);
|
||||
if (!copy_schemes.isEmpty())
|
||||
{
|
||||
QUndoCommand numbering;
|
||||
if (ElementAutoNumSchemeCommand::numberPasted(project, copy_schemes, &numbering)) {
|
||||
numbering.redo();
|
||||
}
|
||||
}
|
||||
|
||||
// Groups too: a group of the copy is its own, so selecting it
|
||||
// is the same on both folios but the database tells them apart.
|
||||
QHash<QUuid, QUuid> renewed_groups;
|
||||
|
||||
@@ -427,7 +427,7 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
|
||||
lines << QETInformation::translatedInfoKey(key) + ": " + info.value(key).toString();
|
||||
}
|
||||
for (const QString &key : info.keys()) {
|
||||
if (key == "formula") continue;
|
||||
if (key == "formula" || key == QETInformation::ELMT_FORMULA_ID) continue;
|
||||
QString translated = QETInformation::translatedInfoKey(key);
|
||||
if (lines.contains(translated + ": " + info.value(key).toString())) continue;
|
||||
if (info.value(key).toString().isEmpty()) continue;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "../qetproject.h"
|
||||
#include "../PropertiesEditor/propertieseditordialog.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
#include "../autoNum/numerotationcontextcommands.h"
|
||||
#include "../diagram.h"
|
||||
#include "../diagramcommands.h"
|
||||
@@ -1495,8 +1496,41 @@ void Element::setGroupIndexForElement(Element *elmt, int index)
|
||||
If new information is different of current infotmation emit elementInfoChange
|
||||
@param dc
|
||||
*/
|
||||
namespace {
|
||||
/**
|
||||
An element follows a numbering scheme only through a formula: an
|
||||
information context whose formula is emptied (paste with labels
|
||||
erased, the formula deleted by hand...) no longer names one.
|
||||
*/
|
||||
void dropOrphanFormulaId(DiagramContext &dc)
|
||||
{
|
||||
if (dc.contains(QETInformation::ELMT_FORMULA_ID)
|
||||
&& dc.value(QETInformation::ELMT_FORMULA).toString().isEmpty()) {
|
||||
dc.remove(QETInformation::ELMT_FORMULA_ID);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief Element::setFormulaSchemeId
|
||||
Make this element follow the element numbering scheme with uuid @p id,
|
||||
or none if @p id is null, without touching anything else: no label
|
||||
update, no signal, no undo. For QETProject when it ties freshly loaded
|
||||
elements to their schemes.
|
||||
*/
|
||||
void Element::setFormulaSchemeId(const QUuid &id)
|
||||
{
|
||||
if (id.isNull()) {
|
||||
m_data.m_informations.remove(QETInformation::ELMT_FORMULA_ID);
|
||||
} else {
|
||||
m_data.m_informations.addValue(QETInformation::ELMT_FORMULA_ID,
|
||||
id.toString(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::setElementInformations(DiagramContext dc)
|
||||
{
|
||||
dropOrphanFormulaId(dc);
|
||||
if (m_data.m_informations == dc) {
|
||||
return;
|
||||
}
|
||||
@@ -1549,6 +1583,7 @@ ElementData Element::elementData() const
|
||||
*/
|
||||
void Element::setElementData(ElementData data)
|
||||
{
|
||||
dropOrphanFormulaId(data.m_informations);
|
||||
auto old_info = m_data.m_informations;
|
||||
auto old_plc = m_data.m_type == ElementData::Master && m_data.m_master_type == ElementData::PLC
|
||||
? m_data.plcMasterData() : ElementData::PlcMasterData();
|
||||
@@ -1776,15 +1811,48 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
|
||||
QString element_currentAutoNum = diagram()
|
||||
->project()
|
||||
->elementCurrentAutoNum();
|
||||
const QUuid scheme_id = diagram()->project()->elementAutoNumId(element_currentAutoNum);
|
||||
setFormulaSchemeId(formula.isEmpty() ? QUuid() : scheme_id);
|
||||
NumerotationContext nc = diagram()
|
||||
->project()
|
||||
->elementAutoNum(element_currentAutoNum);
|
||||
NumerotationContextCommands ncc (nc);
|
||||
|
||||
//The number given is the first whose label no other element of
|
||||
//the project carries: a counter set back, or an element which
|
||||
//was given a number by hand, must not make a second element
|
||||
//with the same label. The counter then goes on from there.
|
||||
NumerotationContext given = nc;
|
||||
if (!formula.isEmpty())
|
||||
{
|
||||
const QSet<QString> held = ElementAutoNumSchemeCommand::labelsHeldBesides(
|
||||
diagram()->project(), this);
|
||||
for (int attempt = 0 ; !held.isEmpty() && attempt < 100000 ; ++attempt)
|
||||
{
|
||||
autonum::sequentialNumbers probe;
|
||||
autonum::setSequential(formula, probe, given, diagram(), element_currentAutoNum);
|
||||
const QString candidate = autonum::AssignVariables::formulaToLabel(
|
||||
formula, probe, diagram(), this, nullptr);
|
||||
if (candidate.isEmpty() || !held.contains(candidate)) {
|
||||
break;
|
||||
}
|
||||
NumerotationContextCommands step (given);
|
||||
const NumerotationContext advanced = step.next();
|
||||
bool moved = advanced.size() != given.size();
|
||||
for (int i = 0 ; !moved && i < given.size() ; ++i) {
|
||||
moved = advanced.itemAt(i) != given.itemAt(i);
|
||||
}
|
||||
if (!moved) {
|
||||
break; //a numbering which cannot go on: nothing to skip to
|
||||
}
|
||||
given = advanced;
|
||||
}
|
||||
}
|
||||
NumerotationContextCommands ncc (given);
|
||||
|
||||
m_autoNum_seq.clear();
|
||||
autonum::setSequential(formula,
|
||||
m_autoNum_seq,
|
||||
nc,
|
||||
given,
|
||||
diagram(),
|
||||
element_currentAutoNum);
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ class Element : public QetGraphicsItem
|
||||
autonum::sequentialNumbers& rSequenceStruct()
|
||||
{return m_autoNum_seq;}
|
||||
void setUpFormula(bool code_letter = true, QUndoCommand *parent_undo = nullptr);
|
||||
void setFormulaSchemeId(const QUuid &id);
|
||||
void setPrefix(QString);
|
||||
QString getPrefix() const;
|
||||
void freezeLabel(bool freeze);
|
||||
|
||||
@@ -40,6 +40,13 @@ namespace QETInformation
|
||||
/** Default information related to element **/
|
||||
static QString ELMT_LABEL = "label";
|
||||
static QString ELMT_FORMULA = "formula";
|
||||
/**
|
||||
Uuid of the project's element numbering scheme the element's
|
||||
formula follows. Internal: never shown, edited, exported or
|
||||
printed as an information of its own; it only exists alongside a
|
||||
non-empty ELMT_FORMULA (see Element::setElementInformations()).
|
||||
*/
|
||||
static QString ELMT_FORMULA_ID = "formula_id";
|
||||
static QString ELMT_COMMENT = "comment";
|
||||
static QString ELMT_FUNCTION = "function";
|
||||
static QString ELMT_DESCRIPTION = "description";
|
||||
|
||||
+259
-110
@@ -23,6 +23,7 @@
|
||||
#include "autoNum/numerotationcontext.h"
|
||||
#include "autoNum/numerotationcontextcommands.h"
|
||||
#include "autoNum/renumberelementscommand.h"
|
||||
#include "autoNum/elementautonumschemecommand.h"
|
||||
#include "diagram.h"
|
||||
#include "qetgraphicsitem/element.h"
|
||||
#include "qetapp.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "ui/importelementdialog.h"
|
||||
#include "TerminalStrip/terminalstrip.h"
|
||||
#include "qetxml.h"
|
||||
#include "qetinformation.h"
|
||||
#include "qetversion.h"
|
||||
#include "undocommand/adddiagramcommand.h"
|
||||
|
||||
@@ -43,27 +45,6 @@
|
||||
#include <QTimer>
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief Reset numeric fields of a NumerotationContext so renumbering starts at 1.
|
||||
* Keeps non-numeric parts (string/idfolio/folio/plant/locmach/elementline/elementcolumn/elementprefix) unchanged.
|
||||
*/
|
||||
NumerotationContext resetContextForRenumber(const NumerotationContext &tmpl)
|
||||
{
|
||||
NumerotationContext out = tmpl;
|
||||
for (int i = 0; i < out.size(); ++i) {
|
||||
const QStringList parts = out.itemAt(i);
|
||||
if (parts.isEmpty()) continue;
|
||||
const QString type = parts.at(0);
|
||||
if (out.keyIsNumber(type)) {
|
||||
out.replaceValue(i, QStringLiteral("1"));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#include <QtDebug>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
@@ -884,9 +865,8 @@ void QETProject::setCurrrentElementAutonum(QString autoNum) {
|
||||
@brief QETProject::renumberElementsBySchemeTitle
|
||||
Renumber existing elements by element autonumbering scheme title.
|
||||
|
||||
Elements do not store the scheme title; they store a "formula" (elementInformations["formula"]).
|
||||
This method matches elements to schemes by comparing the stored formula with the formula derived
|
||||
from each scheme's NumerotationContext.
|
||||
Elements follow a scheme by its uuid (elementInformations["formula_id"]),
|
||||
see elementsUsingElementAutoNum().
|
||||
|
||||
If scheme_title is empty, all schemes are renumbered. Otherwise only that scheme is renumbered.
|
||||
The operation is undoable.
|
||||
@@ -894,93 +874,14 @@ void QETProject::setCurrrentElementAutonum(QString autoNum) {
|
||||
void QETProject::renumberElementsBySchemeTitle(const QString &scheme_title)
|
||||
{
|
||||
if (!m_undo_stack) return;
|
||||
if (isReadOnly()) return;
|
||||
|
||||
// Build map: scheme title -> canonical formula
|
||||
QHash<QString, QString> scheme_formula;
|
||||
for (const QString &k : m_element_autonum.keys()) {
|
||||
if (!scheme_title.isEmpty() && k != scheme_title) continue;
|
||||
scheme_formula.insert(k, autonum::numerotationContextToFormula(m_element_autonum.value(k)));
|
||||
auto *cmd = ElementAutoNumSchemeCommand::renumber(
|
||||
this, scheme_title, nullptr,
|
||||
scheme_title.isEmpty() ? tr("Renumber elements")
|
||||
: tr("Renumber elements (%1)").arg(scheme_title));
|
||||
if (cmd) {
|
||||
m_undo_stack->push(cmd);
|
||||
}
|
||||
if (scheme_formula.isEmpty()) return;
|
||||
|
||||
// Collect elements per scheme by formula match
|
||||
QHash<QString, QVector<Element*>> by_key;
|
||||
for (Diagram *d : diagrams()) {
|
||||
if (!d) continue;
|
||||
const auto items = d->items();
|
||||
for (QGraphicsItem *it : items) {
|
||||
auto *el = qgraphicsitem_cast<Element*>(it);
|
||||
if (!el) continue;
|
||||
if (el->linkType() == Element::Slave || (el->linkType() & Element::AllReport))
|
||||
continue;
|
||||
|
||||
const QString el_formula = el->elementInformations().value(QStringLiteral("formula")).toString();
|
||||
if (el_formula.isEmpty()) continue;
|
||||
|
||||
QString matched_key;
|
||||
for (auto itf = scheme_formula.constBegin(); itf != scheme_formula.constEnd(); ++itf) {
|
||||
if (itf.value() == el_formula) { matched_key = itf.key(); break; }
|
||||
}
|
||||
if (matched_key.isEmpty()) continue;
|
||||
by_key[matched_key].append(el);
|
||||
}
|
||||
}
|
||||
if (by_key.isEmpty()) return;
|
||||
|
||||
QVector<RenumberElementsCommand::ElementChange> changes;
|
||||
QHash<QString, NumerotationContext> old_ctx;
|
||||
QHash<QString, NumerotationContext> new_ctx;
|
||||
|
||||
for (auto it = by_key.constBegin(); it != by_key.constEnd(); ++it) {
|
||||
old_ctx.insert(it.key(), m_element_autonum.value(it.key()));
|
||||
}
|
||||
|
||||
for (auto it = by_key.begin(); it != by_key.end(); ++it) {
|
||||
const QString key = it.key();
|
||||
auto &elements = it.value();
|
||||
std::sort(elements.begin(), elements.end(), [](Element *a, Element *b){ return comparPos(a, b); });
|
||||
|
||||
NumerotationContext base_tmpl = m_element_autonum.value(key);
|
||||
NumerotationContext nc = resetContextForRenumber(base_tmpl);
|
||||
NumerotationContextCommands ncc(nc);
|
||||
|
||||
for (Element *el : elements) {
|
||||
RenumberElementsCommand::ElementChange ch;
|
||||
ch.element = el;
|
||||
ch.old_infos = el->elementInformations();
|
||||
ch.old_seq = el->sequenceStruct();
|
||||
ch.old_frozen = el->isFreezeLabel();
|
||||
ch.new_frozen = ch.old_frozen; // preserve frozen state
|
||||
|
||||
const QString formula = ch.old_infos.value(QStringLiteral("formula")).toString();
|
||||
autonum::sequentialNumbers new_seq;
|
||||
new_seq.clear();
|
||||
autonum::setSequential(formula, new_seq, nc, el->diagram(), key);
|
||||
|
||||
DiagramContext new_infos = ch.old_infos;
|
||||
new_infos.addValue(QStringLiteral("label"), autonum::AssignVariables::formulaToLabel(formula, new_seq, el->diagram(), el, nullptr));
|
||||
ch.new_infos = new_infos;
|
||||
ch.new_seq = new_seq;
|
||||
changes.append(ch);
|
||||
|
||||
// advance
|
||||
nc = ncc.next();
|
||||
ncc = NumerotationContextCommands(nc);
|
||||
}
|
||||
|
||||
new_ctx.insert(key, nc);
|
||||
}
|
||||
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
auto *cmd = new RenumberElementsCommand(
|
||||
this,
|
||||
changes,
|
||||
old_ctx,
|
||||
new_ctx,
|
||||
scheme_title.isEmpty() ? tr("Renumber elements") : tr("Renumber elements (%1)").arg(scheme_title));
|
||||
m_undo_stack->push(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1043,11 +944,232 @@ void QETProject::addConductorAutoNum(const QString& key, const NumerotationConte
|
||||
*/
|
||||
void QETProject::addElementAutoNum(const QString& key, const NumerotationContext& context)
|
||||
{
|
||||
addElementAutoNum(key, context, QUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::addElementAutoNum
|
||||
Add or replace the element numbering scheme @p key.
|
||||
A scheme that already exists keeps its uuid unless @p id is given;
|
||||
a new one takes @p id, or a new uuid when @p id is null or already
|
||||
used by another scheme.
|
||||
@param key : title of the scheme
|
||||
@param context : its numerotation context
|
||||
@param id : its uuid, null to keep or create one
|
||||
*/
|
||||
void QETProject::addElementAutoNum(const QString &key,
|
||||
const NumerotationContext &context,
|
||||
const QUuid &id)
|
||||
{
|
||||
QUuid scheme_id = id;
|
||||
if (!scheme_id.isNull()) {
|
||||
const QString owner = elementAutoNumTitle(scheme_id);
|
||||
if (!owner.isEmpty() && owner != key) {
|
||||
scheme_id = QUuid();
|
||||
}
|
||||
}
|
||||
if (scheme_id.isNull()) {
|
||||
scheme_id = m_element_autonum_id.value(key);
|
||||
}
|
||||
if (scheme_id.isNull()) {
|
||||
scheme_id = QUuid::createUuid();
|
||||
}
|
||||
m_element_autonum_id.insert(key, scheme_id);
|
||||
m_element_autonum.insert(key, context);
|
||||
emit elementAutoNumAdded(key);
|
||||
emit autoNumContextUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::elementAutoNumId
|
||||
@return the uuid of the element numbering scheme @p title, null if
|
||||
there is no such scheme
|
||||
*/
|
||||
QUuid QETProject::elementAutoNumId(const QString &title) const
|
||||
{
|
||||
return m_element_autonum_id.value(title);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::elementAutoNumTitle
|
||||
@return the title of the element numbering scheme with uuid @p id,
|
||||
empty if there is none
|
||||
*/
|
||||
QString QETProject::elementAutoNumTitle(const QUuid &id) const
|
||||
{
|
||||
if (id.isNull()) {
|
||||
return QString();
|
||||
}
|
||||
for (auto it = m_element_autonum_id.constBegin();
|
||||
it != m_element_autonum_id.constEnd(); ++it) {
|
||||
if (it.value() == id) {
|
||||
return it.key();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::renameElementAutoNum
|
||||
Give the element numbering scheme @p old_title the title @p new_title.
|
||||
It keeps its uuid, so the elements following it are not touched;
|
||||
the folios' per-scheme maxima of folio sequential numbers and the
|
||||
project's current scheme follow the new title.
|
||||
Not undoable by itself: see ElementAutoNumSchemeCommand.
|
||||
@return false if there is no scheme @p old_title or a scheme
|
||||
@p new_title already exists
|
||||
*/
|
||||
bool QETProject::renameElementAutoNum(const QString &old_title, const QString &new_title)
|
||||
{
|
||||
if (old_title == new_title) {
|
||||
return m_element_autonum.contains(old_title);
|
||||
}
|
||||
if (!m_element_autonum.contains(old_title)
|
||||
|| m_element_autonum.contains(new_title)
|
||||
|| new_title.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_element_autonum.insert(new_title, m_element_autonum.take(old_title));
|
||||
m_element_autonum_id.insert(new_title, m_element_autonum_id.take(old_title));
|
||||
if (m_current_element_autonum == old_title) {
|
||||
m_current_element_autonum = new_title;
|
||||
}
|
||||
|
||||
for (Diagram *d : std::as_const(m_diagrams_list)) {
|
||||
if (!d) continue;
|
||||
for (auto *hash : {&d->m_elmt_unitfolio_max,
|
||||
&d->m_elmt_tenfolio_max,
|
||||
&d->m_elmt_hundredfolio_max}) {
|
||||
if (hash->contains(old_title)) {
|
||||
hash->insert(new_title, hash->take(old_title));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit elementAutoNumRemoved(old_title);
|
||||
emit elementAutoNumAdded(new_title);
|
||||
emit autoNumContextUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::normalizedAutoNumName
|
||||
@return @p name as it is stored as the title of a numbering scheme
|
||||
*/
|
||||
QString QETProject::normalizedAutoNumName(const QString &name)
|
||||
{
|
||||
return name.simplified();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::elementAutoNumNameClash
|
||||
Two element numbering schemes may not have the same name, compared
|
||||
without regard to case or surrounding white space, so that the name
|
||||
alone identifies a scheme for the user and for scripts.
|
||||
@param name : the name to check
|
||||
@param ignored_title : a scheme not to compare with (the one being
|
||||
renamed)
|
||||
@return the title of the existing scheme @p name clashes with, empty
|
||||
if none
|
||||
*/
|
||||
QString QETProject::elementAutoNumNameClash(const QString &name,
|
||||
const QString &ignored_title) const
|
||||
{
|
||||
const QString wanted = normalizedAutoNumName(name);
|
||||
for (auto it = m_element_autonum.constBegin();
|
||||
it != m_element_autonum.constEnd(); ++it) {
|
||||
if (it.key() == ignored_title) {
|
||||
continue;
|
||||
}
|
||||
if (QString::compare(normalizedAutoNumName(it.key()), wanted,
|
||||
Qt::CaseInsensitive) == 0) {
|
||||
return it.key();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::elementsUsingElementAutoNum
|
||||
@return the elements whose label follows the element numbering
|
||||
scheme @p title, in no particular order
|
||||
*/
|
||||
QVector<Element *> QETProject::elementsUsingElementAutoNum(const QString &title) const
|
||||
{
|
||||
QVector<Element *> list;
|
||||
const QUuid id = elementAutoNumId(title);
|
||||
if (id.isNull()) {
|
||||
return list;
|
||||
}
|
||||
for (Diagram *d : m_diagrams_list) {
|
||||
if (!d) continue;
|
||||
const auto items = d->items();
|
||||
for (QGraphicsItem *it : items) {
|
||||
auto *el = qgraphicsitem_cast<Element *>(it);
|
||||
if (!el) continue;
|
||||
const DiagramContext &info = el->elementInformations();
|
||||
if (info.value(QETInformation::ELMT_FORMULA).toString().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (QUuid(info.value(QETInformation::ELMT_FORMULA_ID).toString()) == id) {
|
||||
list << el;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::linkElementsToElementAutoNums
|
||||
Called once the diagrams of a file are loaded. Makes every element's
|
||||
ELMT_FORMULA_ID name an element numbering scheme of this project:
|
||||
- an id naming one of the schemes is kept;
|
||||
- an id naming none (the scheme was renamed by a version of
|
||||
QElectroTech that lost the ids, or the element was pasted from
|
||||
another project) and, in a file written before the ids existed,
|
||||
an element with no id at all, are linked to the one scheme whose
|
||||
formula is the element's formula; with no such scheme, or more than
|
||||
one, the element is left unlinked.
|
||||
The label is not touched: nothing it is built from changes.
|
||||
*/
|
||||
void QETProject::linkElementsToElementAutoNums()
|
||||
{
|
||||
QHash<QString, QStringList> titles_by_formula;
|
||||
for (auto it = m_element_autonum.constBegin();
|
||||
it != m_element_autonum.constEnd(); ++it) {
|
||||
titles_by_formula[autonum::numerotationContextToFormula(it.value())] << it.key();
|
||||
}
|
||||
|
||||
for (Diagram *d : std::as_const(m_diagrams_list)) {
|
||||
if (!d) continue;
|
||||
const auto items = d->items();
|
||||
for (QGraphicsItem *it : items) {
|
||||
auto *el = qgraphicsitem_cast<Element *>(it);
|
||||
if (!el) continue;
|
||||
const DiagramContext &info = el->elementInformations();
|
||||
const QString formula = info.value(QETInformation::ELMT_FORMULA).toString();
|
||||
const bool has_id = info.contains(QETInformation::ELMT_FORMULA_ID);
|
||||
const QUuid id(info.value(QETInformation::ELMT_FORMULA_ID).toString());
|
||||
|
||||
if (formula.isEmpty()) {
|
||||
if (has_id) el->setFormulaSchemeId(QUuid());
|
||||
continue;
|
||||
}
|
||||
if (!elementAutoNumTitle(id).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!has_id && !m_legacy_element_autonums) {
|
||||
continue; //A formula typed by hand
|
||||
}
|
||||
const QStringList matches = titles_by_formula.value(formula);
|
||||
el->setFormulaSchemeId(matches.size() == 1
|
||||
? elementAutoNumId(matches.first())
|
||||
: QUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::addFolioAutoNum
|
||||
Add a new folio numerotation context. If key already exist,
|
||||
@@ -1077,6 +1199,7 @@ void QETProject::removeConductorAutoNum(const QString& key) {
|
||||
void QETProject::removeElementAutoNum(const QString& key)
|
||||
{
|
||||
m_element_autonum.remove(key);
|
||||
m_element_autonum_id.remove(key);
|
||||
emit elementAutoNumRemoved(key);
|
||||
}
|
||||
|
||||
@@ -1934,6 +2057,9 @@ void QETProject::readProjectXml(QDomDocument &xml_project)
|
||||
readDiagramsXml(xml_project);
|
||||
const qint64 diagrams_ms = phase_timer.restart();
|
||||
|
||||
//Tie the elements to the numbering schemes they follow
|
||||
linkElementsToElementAutoNums();
|
||||
|
||||
//Load the terminal strip
|
||||
readTerminalStripXml(xml_project);
|
||||
const qint64 strips_ms = phase_timer.restart();
|
||||
@@ -2206,7 +2332,25 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project)
|
||||
{
|
||||
NumerotationContext nc;
|
||||
nc.fromXml(elmt);
|
||||
m_element_autonum.insert(elmt.attribute(QStringLiteral("title")), nc);
|
||||
const QString title = elmt.attribute(QStringLiteral("title"));
|
||||
QUuid id(elmt.attribute(QStringLiteral("id")));
|
||||
if (id.isNull() || !elementAutoNumTitle(id).isEmpty()) {
|
||||
//Saved before schemes had an id: derive one, the
|
||||
//same on every load of the file.
|
||||
m_legacy_element_autonums = true;
|
||||
//Of this project: another project may have a numbering of the
|
||||
//same name, which is not the same numbering
|
||||
id = derivedItemUuid(QStringLiteral("element_autonum"),
|
||||
m_uuid.toString() + QLatin1Char('\n') + title);
|
||||
}
|
||||
m_element_autonum.insert(title, nc);
|
||||
m_element_autonum_id.insert(title, id);
|
||||
}
|
||||
//The id is authoritative, the title is kept for older versions
|
||||
const QString current_title = elementAutoNumTitle(
|
||||
QUuid(element_autonums.attribute(QStringLiteral("current_autonum_id"))));
|
||||
if (!current_title.isEmpty()) {
|
||||
m_current_element_autonum = current_title;
|
||||
}
|
||||
}
|
||||
// Read guides from XML (if missing, e.g. in old projects, list stays empty)
|
||||
@@ -2372,6 +2516,10 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element)
|
||||
//Export Element Autonums
|
||||
QDomElement element_autonums = xml_document.createElement("element_autonums");
|
||||
element_autonums.setAttribute("current_autonum", m_current_element_autonum);
|
||||
if (!elementAutoNumId(m_current_element_autonum).isNull()) {
|
||||
element_autonums.setAttribute("current_autonum_id",
|
||||
elementAutoNumId(m_current_element_autonum).toString());
|
||||
}
|
||||
element_autonums.setAttribute("freeze_new_elements", m_freeze_new_elements ? "true" : "false");
|
||||
QStringList element_autonum_keys = elementAutoNum().keys();
|
||||
element_autonum_keys.sort();
|
||||
@@ -2379,6 +2527,7 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element)
|
||||
QDomElement element_autonum = elementAutoNum(key).toXml(xml_document, "element_autonum");
|
||||
if (key != "" && elementAutoNumFormula(key) != "") {
|
||||
element_autonum.setAttribute("title", key);
|
||||
element_autonum.setAttribute("id", elementAutoNumId(key).toString());
|
||||
element_autonum.setAttribute("formula", elementAutoNumFormula(key));
|
||||
element_autonums.appendChild(element_autonum);
|
||||
}
|
||||
|
||||
+26
-3
@@ -39,11 +39,14 @@
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QUuid>
|
||||
#include <QVector>
|
||||
#include <QFuture>
|
||||
|
||||
#include <array>
|
||||
|
||||
class Diagram;
|
||||
class Element;
|
||||
class ElementsLocation;
|
||||
class QETResult;
|
||||
class TitleBlockTemplate;
|
||||
@@ -179,12 +182,26 @@ class QETProject : public QObject
|
||||
QString elementCurrentAutoNum() const;
|
||||
void setCurrrentElementAutonum(QString autoNum);
|
||||
|
||||
//Identity of the element numbering schemes. The title is the
|
||||
//name shown to the user and the lookup key of the API; the uuid
|
||||
//is what an element's ELMT_FORMULA_ID refers to, so a scheme can
|
||||
//be renamed or edited without its elements losing track of it.
|
||||
void addElementAutoNum(const QString &key,
|
||||
const NumerotationContext &context,
|
||||
const QUuid &id);
|
||||
QUuid elementAutoNumId(const QString &title) const;
|
||||
QString elementAutoNumTitle(const QUuid &id) const;
|
||||
bool renameElementAutoNum(const QString &old_title, const QString &new_title);
|
||||
QString elementAutoNumNameClash(const QString &name,
|
||||
const QString &ignored_title = QString()) const;
|
||||
static QString normalizedAutoNumName(const QString &name);
|
||||
QVector<Element *> elementsUsingElementAutoNum(const QString &title) const;
|
||||
|
||||
/**
|
||||
* @brief Renumber existing elements by element autonumbering scheme.
|
||||
*
|
||||
* Elements do not store the scheme title but they store the corresponding formula.
|
||||
* This operation matches elements to schemes by comparing the stored formula
|
||||
* with the formula derived from the scheme's NumerotationContext.
|
||||
* Elements follow a scheme by its uuid (QETInformation::ELMT_FORMULA_ID),
|
||||
* see elementsUsingElementAutoNum().
|
||||
*
|
||||
* If @p scheme_title is empty, all schemes are renumbered.
|
||||
* If @p scheme_title is non-empty, only that scheme is renumbered.
|
||||
@@ -374,7 +391,13 @@ class QETProject : public QObject
|
||||
QHash <QString, NumerotationContext> m_folio_autonum;
|
||||
/// Element Auto Numbering
|
||||
QHash <QString, NumerotationContext> m_element_autonum; //Title and NumContext hash
|
||||
/// Title -> uuid of each element numbering scheme
|
||||
QHash <QString, QUuid> m_element_autonum_id;
|
||||
QString m_current_element_autonum;
|
||||
/// True when the loaded file had element numbering schemes
|
||||
/// saved without an id (written before ids existed)
|
||||
bool m_legacy_element_autonums = false;
|
||||
void linkElementsToElementAutoNums();
|
||||
bool m_auto_conductor = true;
|
||||
WireHops::Mode m_wire_hops = WireHops::Mode::None;
|
||||
WiringRules::Settings m_wiring_rules;
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
#include "../TerminalStrip/physicalterminal.h"
|
||||
#include "../TerminalStrip/realterminal.h"
|
||||
#include "../TerminalStrip/terminalstrip.h"
|
||||
#include "../autoNum/autonumschemecommand.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../autoNum/numerotationcontext.h"
|
||||
#include "../borderproperties.h"
|
||||
@@ -609,6 +611,11 @@ bool QetScriptApi::setInfoKey(int folioIndex, const QString &elementUuid,
|
||||
log(QStringLiteral("qet.%1: empty information key").arg(caller));
|
||||
return false;
|
||||
}
|
||||
if (key == QETInformation::ELMT_FORMULA_ID) {
|
||||
log(QStringLiteral("qet.%1: \"%2\" is internal, follow a numbering with numberElement()")
|
||||
.arg(caller, key));
|
||||
return false;
|
||||
}
|
||||
Element *element = findElement(folioIndex, elementUuid);
|
||||
if (!element) return false;
|
||||
|
||||
@@ -616,6 +623,10 @@ bool QetScriptApi::setInfoKey(int folioIndex, const QString &elementUuid,
|
||||
if (old_info.value(key).toString() == value) return true; // nothing to push
|
||||
DiagramContext new_info = old_info;
|
||||
new_info.addValue(key, value);
|
||||
// A formula written by hand follows no numbering scheme
|
||||
if (key == QETInformation::ELMT_FORMULA) {
|
||||
new_info.remove(QETInformation::ELMT_FORMULA_ID);
|
||||
}
|
||||
|
||||
auto *cmd = new ChangeElementInformationCommand(element, old_info, new_info);
|
||||
m_project->undoStack()->push(cmd);
|
||||
@@ -3110,9 +3121,108 @@ bool QetScriptApi::addAutoNum(const QString &kind, const QString &name, const QS
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == QLatin1String("conductor")) m_project->addConductorAutoNum(name, context);
|
||||
else if (kind == QLatin1String("element")) m_project->addElementAutoNum(name, context);
|
||||
else m_project->addFolioAutoNum(name, context);
|
||||
if (kind == QLatin1String("element")) {
|
||||
// Through the undo stack, and an existing scheme is edited
|
||||
// rather than replaced: the elements following it follow
|
||||
// the new definition (see ElementAutoNumSchemeCommand)
|
||||
ElementAutoNumSchemeCommand *cmd = nullptr;
|
||||
if (m_project->elementAutoNum().contains(name)) {
|
||||
const int frozen = ElementAutoNumSchemeCommand::editBlockedBy(m_project, name, context).size();
|
||||
if (frozen) {
|
||||
log(QStringLiteral("qet.addAutoNum: %1 element(s) with a frozen label follow '%2', "
|
||||
"its formula cannot change").arg(frozen).arg(name));
|
||||
return false;
|
||||
}
|
||||
if (const auto conflict = ElementAutoNumSchemeCommand::counterConflict(m_project, name, context)) {
|
||||
log(QStringLiteral("qet.addAutoNum: the counter %1 of '%2' is at or below %3 number(s) in use "
|
||||
"(up to %4); new elements will skip them")
|
||||
.arg(conflict->counter).arg(name).arg(conflict->count).arg(conflict->highest));
|
||||
}
|
||||
cmd = ElementAutoNumSchemeCommand::edit(m_project, name, name, context, false);
|
||||
} else {
|
||||
const QString problem = ElementAutoNumSchemeCommand::nameProblem(m_project, name);
|
||||
if (!problem.isEmpty()) {
|
||||
log(QStringLiteral("qet.addAutoNum: %1").arg(problem));
|
||||
return false;
|
||||
}
|
||||
cmd = ElementAutoNumSchemeCommand::create(m_project, name, context, QUuid(), false);
|
||||
}
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
// Conductor and folio numberings: undoable too, an existing one is
|
||||
// edited, and a new name is unique and not empty
|
||||
const auto scheme_kind = kind == QLatin1String("conductor")
|
||||
? AutoNumSchemeCommand::Kind::Conductor
|
||||
: AutoNumSchemeCommand::Kind::Folio;
|
||||
AutoNumSchemeCommand *cmd = nullptr;
|
||||
if (AutoNumSchemeCommand::contains(m_project, scheme_kind, name)) {
|
||||
cmd = AutoNumSchemeCommand::edit(m_project, scheme_kind, name, name, context);
|
||||
} else {
|
||||
const QString problem = AutoNumSchemeCommand::nameProblem(m_project, scheme_kind, name);
|
||||
if (!problem.isEmpty()) {
|
||||
log(QStringLiteral("qet.addAutoNum: %1").arg(problem));
|
||||
return false;
|
||||
}
|
||||
cmd = AutoNumSchemeCommand::create(m_project, scheme_kind, name, context);
|
||||
}
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::renameAutoNum
|
||||
Rename a numbering context. What follows it follows it under its new
|
||||
name: the elements for an element numbering, the folios for a conductor
|
||||
or folio numbering. Undoable.
|
||||
*/
|
||||
bool QetScriptApi::renameAutoNum(const QString &kind, const QString &name, const QString &newName)
|
||||
{
|
||||
if (!m_project) return false;
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.renameAutoNum: project is read-only"));
|
||||
return false;
|
||||
}
|
||||
if (kind != QLatin1String("element") && kind != QLatin1String("conductor")
|
||||
&& kind != QLatin1String("folio")) {
|
||||
log(QStringLiteral("qet.renameAutoNum: unknown kind '%1'; expected conductor, element or folio").arg(kind));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kind != QLatin1String("element"))
|
||||
{
|
||||
// The folios which follow it follow it under its new name
|
||||
const auto scheme_kind = kind == QLatin1String("conductor")
|
||||
? AutoNumSchemeCommand::Kind::Conductor
|
||||
: AutoNumSchemeCommand::Kind::Folio;
|
||||
if (!AutoNumSchemeCommand::contains(m_project, scheme_kind, name)) {
|
||||
log(QStringLiteral("qet.renameAutoNum: no %1 auto-numbering named '%2'").arg(kind, name));
|
||||
return false;
|
||||
}
|
||||
const QString problem = AutoNumSchemeCommand::nameProblem(m_project, scheme_kind, newName, name);
|
||||
if (!problem.isEmpty()) {
|
||||
log(QStringLiteral("qet.renameAutoNum: %1").arg(problem));
|
||||
return false;
|
||||
}
|
||||
auto *cmd = AutoNumSchemeCommand::edit(
|
||||
m_project, scheme_kind, name, newName,
|
||||
AutoNumSchemeCommand::contextOf(m_project, scheme_kind, name));
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_project->elementAutoNum().contains(name)) {
|
||||
log(QStringLiteral("qet.renameAutoNum: no element auto-numbering named '%1'").arg(name));
|
||||
return false;
|
||||
}
|
||||
const QString problem = ElementAutoNumSchemeCommand::nameProblem(m_project, newName, name);
|
||||
if (!problem.isEmpty()) {
|
||||
log(QStringLiteral("qet.renameAutoNum: %1").arg(problem));
|
||||
return false;
|
||||
}
|
||||
auto *cmd = ElementAutoNumSchemeCommand::edit(
|
||||
m_project, name, newName, m_project->elementAutoNum(name), false);
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3129,9 +3239,30 @@ bool QetScriptApi::removeAutoNum(const QString &kind, const QString &name)
|
||||
log(QStringLiteral("qet.removeAutoNum: no %1 auto-numbering named '%2'").arg(kind, name));
|
||||
return false;
|
||||
}
|
||||
if (kind == QLatin1String("conductor")) m_project->removeConductorAutoNum(name);
|
||||
else if (kind == QLatin1String("element")) m_project->removeElementAutoNum(name);
|
||||
else m_project->removeFolioAutoNum(name);
|
||||
if (kind == QLatin1String("element")) {
|
||||
const int used = m_project->elementsUsingElementAutoNum(name).size();
|
||||
if (used) {
|
||||
log(QStringLiteral("qet.removeAutoNum: %1 element(s) follow '%2', it cannot be removed")
|
||||
.arg(used).arg(name));
|
||||
return false;
|
||||
}
|
||||
if (auto *cmd = ElementAutoNumSchemeCommand::remove(m_project, name)) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const auto scheme_kind = kind == QLatin1String("conductor")
|
||||
? AutoNumSchemeCommand::Kind::Conductor
|
||||
: AutoNumSchemeCommand::Kind::Folio;
|
||||
const int used = AutoNumSchemeCommand::usersOf(m_project, scheme_kind, name).size();
|
||||
if (used) {
|
||||
log(QStringLiteral("qet.removeAutoNum: %1 folio(s) follow '%2', it cannot be removed")
|
||||
.arg(used).arg(name));
|
||||
return false;
|
||||
}
|
||||
if (auto *cmd = AutoNumSchemeCommand::remove(m_project, scheme_kind, name)) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3732,7 +3863,8 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
|
||||
element->setUpFormula(true);
|
||||
const DiagramContext new_info = element->elementInformations();
|
||||
if (new_info.value(QETInformation::ELMT_LABEL) == old_info.value(QETInformation::ELMT_LABEL)
|
||||
&& new_info.value(QStringLiteral("formula")) == old_info.value(QStringLiteral("formula"))) {
|
||||
&& new_info.value(QStringLiteral("formula")) == old_info.value(QStringLiteral("formula"))
|
||||
&& new_info.value(QETInformation::ELMT_FORMULA_ID) == old_info.value(QETInformation::ELMT_FORMULA_ID)) {
|
||||
stack->endMacro();
|
||||
return false;
|
||||
}
|
||||
@@ -3742,6 +3874,129 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::renumberElementAutoNum
|
||||
Number again, from the first number, the elements following the
|
||||
element auto-numbering @p name, in folio and position order, as the
|
||||
"Renumber" button of the project's numberings does. Undoable, one
|
||||
step.
|
||||
|
||||
An element whose label is frozen is left as it is, and its label is
|
||||
not given to another element.
|
||||
@return how many elements were left as they are because their label
|
||||
is frozen, -1 if there is no such numbering or the project is read-only
|
||||
*/
|
||||
int QetScriptApi::renumberElementAutoNum(const QString &name)
|
||||
{
|
||||
if (!m_project) return -1;
|
||||
const QString caller = QStringLiteral("renumberElementAutoNum");
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
|
||||
return -1;
|
||||
}
|
||||
if (!m_project->elementAutoNum().contains(name)) {
|
||||
log(QStringLiteral("qet.%1: no element auto-numbering named '%2'").arg(caller, name));
|
||||
return -1;
|
||||
}
|
||||
QVector<Element *> frozen;
|
||||
if (auto *cmd = ElementAutoNumSchemeCommand::renumber(
|
||||
m_project, name, &frozen,
|
||||
QObject::tr("Renuméroter les éléments (%1)").arg(name))) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
return static_cast<int>(frozen.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::freeElementNumbers
|
||||
The numbers an element which follows an element auto-numbering may be
|
||||
given by hand with assignElementNumber(): from 1 to a few past the
|
||||
highest in use, those no other element of the numbering carries and which
|
||||
would not give it the label of another element. Empty if the element
|
||||
follows no auto-numbering, or one whose numbers are not a single sequence
|
||||
(a folio number, a cycle or letters in the definition).
|
||||
*/
|
||||
QVariantList QetScriptApi::freeElementNumbers(int folioIndex, const QString &elementUuid)
|
||||
{
|
||||
QVariantList list;
|
||||
if (!m_project) return list;
|
||||
Element *element = findElement(folioIndex, elementUuid);
|
||||
if (!element) return list;
|
||||
const QString title = m_project->elementAutoNumTitle(
|
||||
QUuid(element->elementInformations().value(QETInformation::ELMT_FORMULA_ID).toString()));
|
||||
for (int n : ElementAutoNumSchemeCommand::freeNumbers(m_project, title, element)) {
|
||||
list << n;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::assignElementNumber
|
||||
Give an element which follows an element auto-numbering the number
|
||||
@p number of it, which must be free (see freeElementNumbers()): its label
|
||||
is the formula worked out with that number and it keeps following the
|
||||
numbering. If the counter of the numbering is at or below @p number it
|
||||
moves past it. Undoable, one step.
|
||||
*/
|
||||
bool QetScriptApi::assignElementNumber(int folioIndex, const QString &elementUuid, int number)
|
||||
{
|
||||
if (!m_project) return false;
|
||||
const QString caller = QStringLiteral("assignElementNumber");
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
|
||||
return false;
|
||||
}
|
||||
Element *element = findElement(folioIndex, elementUuid);
|
||||
if (!element) return false;
|
||||
|
||||
QString problem;
|
||||
auto *cmd = ElementAutoNumSchemeCommand::assignNumber(m_project, element, number, &problem);
|
||||
if (!cmd) {
|
||||
log(QStringLiteral("qet.%1: %2").arg(caller, problem));
|
||||
return false;
|
||||
}
|
||||
m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::assignElementAutoNum
|
||||
Make an element follow the element auto-numbering @p name: it gets
|
||||
the formula and the next number of that numbering, which moves on.
|
||||
The same command as picking the numbering in the element's
|
||||
information window. Undoable, one step.
|
||||
|
||||
The element is left alone, and false returned, when it follows that
|
||||
numbering already, is a slave or a report, or when something would be
|
||||
lost: a frozen label, or a formula it follows or holds already, unless
|
||||
@p overwrite is true. A label typed by hand, with no formula, is
|
||||
replaced.
|
||||
*/
|
||||
bool QetScriptApi::assignElementAutoNum(const QString &name, int folioIndex,
|
||||
const QString &elementUuid, bool overwrite)
|
||||
{
|
||||
if (!m_project) return false;
|
||||
const QString caller = QStringLiteral("assignElementAutoNum");
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
|
||||
return false;
|
||||
}
|
||||
if (!m_project->elementAutoNum().contains(name)) {
|
||||
log(QStringLiteral("qet.%1: no element auto-numbering named '%2'").arg(caller, name));
|
||||
return false;
|
||||
}
|
||||
Element *element = findElement(folioIndex, elementUuid);
|
||||
if (!element) return false;
|
||||
|
||||
auto *cmd = ElementAutoNumSchemeCommand::assign(m_project, name, {element}, overwrite);
|
||||
if (!cmd) {
|
||||
log(QStringLiteral("qet.%1: the element was left as it is").arg(caller));
|
||||
return false;
|
||||
}
|
||||
m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::duplicateElements
|
||||
Copy elements, and the conductors between them, to another place.
|
||||
@@ -3751,6 +4006,10 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
|
||||
elements are selected for the moment and the previous selection restored
|
||||
before returning; the paste is Diagram::fromXml() at the position,
|
||||
followed by one PasteDiagramCommand so it is a single undo step.
|
||||
|
||||
As for a paste in the editor, the copies of elements which follow an
|
||||
element numbering get the next numbers of it (the preference "number
|
||||
pasted elements"), and the numbering moves on; undoing gives them back.
|
||||
@return the uuids of the new elements, or an empty list on failure
|
||||
*/
|
||||
QStringList QetScriptApi::duplicateElements(int fromFolioIndex, const QStringList &elementUuids,
|
||||
|
||||
@@ -523,9 +523,15 @@ class QetScriptApi : public QObject
|
||||
Q_INVOKABLE QStringList autoNums(const QString &kind) const;
|
||||
Q_INVOKABLE bool addAutoNum(const QString &kind, const QString &name, const QStringList &parts);
|
||||
Q_INVOKABLE bool removeAutoNum(const QString &kind, const QString &name);
|
||||
Q_INVOKABLE bool renameAutoNum(const QString &kind, const QString &name, const QString &newName);
|
||||
Q_INVOKABLE bool useConductorAutoNum(int folioIndex, const QString &name);
|
||||
Q_INVOKABLE bool useElementAutoNum(const QString &name);
|
||||
Q_INVOKABLE bool numberElement(int folioIndex, const QString &elementUuid);
|
||||
Q_INVOKABLE int renumberElementAutoNum(const QString &name);
|
||||
Q_INVOKABLE QVariantList freeElementNumbers(int folioIndex, const QString &elementUuid);
|
||||
Q_INVOKABLE bool assignElementNumber(int folioIndex, const QString &elementUuid, int number);
|
||||
Q_INVOKABLE bool assignElementAutoNum(const QString &name, int folioIndex,
|
||||
const QString &elementUuid, bool overwrite);
|
||||
|
||||
// -- images, embedded in the project --
|
||||
Q_INVOKABLE QStringList images(int folioIndex) const;
|
||||
|
||||
@@ -131,6 +131,7 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) :
|
||||
}
|
||||
ui->m_use_gesture_trackpad->setChecked(settings.value("diagramview/gestures", false).toBool());
|
||||
ui->m_save_label_paste->setChecked(settings.value("diagramcommands/erase-label-on-copy", true).toBool());
|
||||
ui->m_autonumber_pasted->setChecked(settings.value("diagramcommands/autonumber-pasted-elements", true).toBool());
|
||||
ui->m_enable_scripting->setChecked(QetSettings::scriptingEnabled());
|
||||
#ifdef QET_HAS_SCRIPTING
|
||||
if (QetSettings::scriptingForcedByEnvironment()) {
|
||||
@@ -322,6 +323,7 @@ void GeneralConfigurationPage::applyConf()
|
||||
|
||||
//DIAGRAM COMMAND
|
||||
settings.setValue("diagramcommands/erase-label-on-copy", ui->m_save_label_paste->isChecked());
|
||||
settings.setValue("diagramcommands/autonumber-pasted-elements", ui->m_autonumber_pasted->isChecked());
|
||||
|
||||
//SCRIPTING
|
||||
//Left alone while the environment forces it on: the box is disabled
|
||||
|
||||
@@ -327,6 +327,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="m_autonumber_pasted">
|
||||
<property name="text">
|
||||
<string>Numéroter automatiquement les éléments collés ou dupliqués qui suivent une numérotation</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Un élément collé reçoit le numéro suivant de sa numérotation au lieu du nom de l'élément copié, et la numérotation avance.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="m_save_label_paste">
|
||||
<property name="text">
|
||||
@@ -354,7 +364,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -1373,6 +1383,7 @@ Vous pouvez spécifier ici la valeur par défaut de ce champ pour les éléments
|
||||
<tabstop>m_use_windows_mode_rb</tabstop>
|
||||
<tabstop>m_use_tab_mode_rb</tabstop>
|
||||
<tabstop>m_save_label_paste</tabstop>
|
||||
<tabstop>m_autonumber_pasted</tabstop>
|
||||
<tabstop>m_use_folio_label</tabstop>
|
||||
<tabstop>m_border_0</tabstop>
|
||||
<tabstop>m_autosave_sb</tabstop>
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
#include "projectconfigpages.h"
|
||||
|
||||
#include "../autoNum/autonumschemecommand.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
#include "../autoNum/ui/counterwarning.h"
|
||||
#include "../autoNum/ui/renumberpreviewdialog.h"
|
||||
#include "../autoNum/numerotationcontext.h"
|
||||
#include "../autoNum/ui/autonumberingmanagementw.h"
|
||||
#include "../autoNum/ui/folioautonumbering.h"
|
||||
@@ -28,6 +32,8 @@
|
||||
#include "../qetproject.h"
|
||||
#include "../wiringrules.h"
|
||||
#include "../wiringruleswarning.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../diagram.h"
|
||||
#include "../borderpropertieswidget.h"
|
||||
#include "../conductorpropertieswidget.h"
|
||||
#include "../diagramcontextwidget.h"
|
||||
@@ -38,6 +44,7 @@
|
||||
//#include "ui_autonumberingmanagementw.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
Constructor
|
||||
@@ -450,6 +457,33 @@ QIcon ProjectAutoNumConfigPage::icon() const
|
||||
void ProjectAutoNumConfigPage::applyProjectConf()
|
||||
{}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
A tab of a numbering list over a table of the folios which follow the
|
||||
numbering shown
|
||||
*/
|
||||
QWidget *schemeTab(SelectAutonumW *saw, QGroupBox *&box, QTableWidget *&table)
|
||||
{
|
||||
auto *tab = new QWidget();
|
||||
auto *layout = new QVBoxLayout(tab);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(saw);
|
||||
|
||||
box = new QGroupBox(tab);
|
||||
auto *box_layout = new QVBoxLayout(box);
|
||||
table = new QTableWidget(0, 2, box);
|
||||
table->setHorizontalHeaderLabels({QObject::tr("Folio"), QObject::tr("Titre")});
|
||||
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table->verticalHeader()->setVisible(false);
|
||||
table->horizontalHeader()->setStretchLastSection(true);
|
||||
table->setAlternatingRowColors(true);
|
||||
box_layout->addWidget(table);
|
||||
layout->addWidget(box, 1);
|
||||
return tab;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::initWidgets
|
||||
Init some widget of this page
|
||||
@@ -457,6 +491,7 @@ void ProjectAutoNumConfigPage::applyProjectConf()
|
||||
void ProjectAutoNumConfigPage::initWidgets()
|
||||
{
|
||||
QTabWidget *tab_widget = new QTabWidget(this);
|
||||
m_tab_widget = tab_widget;
|
||||
|
||||
//Management tab
|
||||
m_amw = new AutoNumberingManagementW(project());
|
||||
@@ -464,15 +499,44 @@ void ProjectAutoNumConfigPage::initWidgets()
|
||||
|
||||
//Conductor tab
|
||||
m_saw_conductor = new SelectAutonumW(1);
|
||||
tab_widget->addTab(m_saw_conductor, tr("Conducteurs"));
|
||||
m_saw_conductor->setExplicitNaming();
|
||||
tab_widget->addTab(schemeTab(m_saw_conductor, m_conductor_users_box, m_conductor_users), tr("Conducteurs"));
|
||||
|
||||
//Element tab
|
||||
m_saw_element = new SelectAutonumW(0);
|
||||
tab_widget->addTab(m_saw_element, tr("Eléments"));
|
||||
m_saw_element->setExplicitNaming();
|
||||
auto *element_tab = new QWidget(this);
|
||||
auto *element_layout = new QVBoxLayout(element_tab);
|
||||
element_layout->setContentsMargins(0, 0, 0, 0);
|
||||
element_layout->addWidget(m_saw_element);
|
||||
|
||||
//The elements which follow the numbering shown: what an edit or a
|
||||
//renumbering would touch, and which of them stop it
|
||||
m_element_users_box = new QGroupBox(element_tab);
|
||||
auto *users_layout = new QVBoxLayout(m_element_users_box);
|
||||
m_element_users = new QTableWidget(0, 5, m_element_users_box);
|
||||
m_element_users->setHorizontalHeaderLabels({tr("N°"), tr("Nom"), tr("Folio"), tr("Élément"), tr("Figé")});
|
||||
m_element_users->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_element_users->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_element_users->verticalHeader()->setVisible(false);
|
||||
m_element_users->horizontalHeader()->setStretchLastSection(true);
|
||||
m_element_users->setAlternatingRowColors(true);
|
||||
users_layout->addWidget(m_element_users);
|
||||
m_assign_number_pb = new QPushButton(tr("Attribuer un numéro libre…"), m_element_users_box);
|
||||
m_assign_number_pb->setToolTip(tr("Donner à l'élément sélectionné un numéro que personne n'a : "
|
||||
"il garde sa numérotation, seul son numéro change."));
|
||||
m_assign_number_pb->setEnabled(false);
|
||||
users_layout->addWidget(m_assign_number_pb, 0, Qt::AlignLeft);
|
||||
connect(m_assign_number_pb, &QPushButton::clicked, this, &ProjectAutoNumConfigPage::assignFreeNumber);
|
||||
connect(m_element_users, &QTableWidget::itemSelectionChanged,
|
||||
this, &ProjectAutoNumConfigPage::updateAssignNumberButton);
|
||||
element_layout->addWidget(m_element_users_box, 1);
|
||||
tab_widget->addTab(element_tab, tr("Eléments"));
|
||||
|
||||
//Folio Tab
|
||||
m_saw_folio = new SelectAutonumW(2);
|
||||
tab_widget->addTab(m_saw_folio, tr("Folios"));
|
||||
m_saw_folio->setExplicitNaming();
|
||||
tab_widget->addTab(schemeTab(m_saw_folio, m_folio_users_box, m_folio_users), tr("Folios"));
|
||||
|
||||
//AutoNumbering Tab
|
||||
m_faw = new FolioAutonumberingW(project());
|
||||
@@ -509,16 +573,13 @@ void ProjectAutoNumConfigPage::readValuesFromProject()
|
||||
m_saw_folio->contextComboBox()->clear();
|
||||
|
||||
//Conductor Tab
|
||||
const QStringList strlc(m_project->conductorAutoNum().keys());
|
||||
m_saw_conductor->contextComboBox()->addItems(strlc);
|
||||
refreshSchemes(SchemeKind::Conductor, m_project->conductorCurrentAutoNum());
|
||||
|
||||
//Element Tab
|
||||
const QStringList strle(m_project->elementAutoNum().keys());
|
||||
m_saw_element->contextComboBox()->addItems(strle);
|
||||
refreshElementSchemes(m_project->elementCurrentAutoNum());
|
||||
|
||||
//Folio Tab
|
||||
const QStringList strlf(m_project->folioAutoNum().keys());
|
||||
m_saw_folio->contextComboBox()->addItems(strlf);
|
||||
refreshSchemes(SchemeKind::Folio, QString());
|
||||
|
||||
//Folio AutoNumbering Tab
|
||||
m_faw->setContext(m_project->folioAutoNum().keys());
|
||||
@@ -547,16 +608,22 @@ void ProjectAutoNumConfigPage::buildConnections()
|
||||
//Conductor Tab
|
||||
connect(m_saw_conductor, &SelectAutonumW::applyPressed, this, &ProjectAutoNumConfigPage::saveContextConductor);
|
||||
connect(m_saw_conductor, &SelectAutonumW::removeClicked, this, &ProjectAutoNumConfigPage::removeContextConductor);
|
||||
connect(m_saw_conductor, &SelectAutonumW::newClicked, this, &ProjectAutoNumConfigPage::newContextConductor);
|
||||
connect(m_saw_conductor, &SelectAutonumW::renameClicked, this, &ProjectAutoNumConfigPage::renameContextConductor);
|
||||
connect(m_saw_conductor->contextComboBox(), &QComboBox::textActivated, this, &ProjectAutoNumConfigPage::updateContextConductor);
|
||||
|
||||
//Element Tab
|
||||
connect(m_saw_element, &SelectAutonumW::applyPressed, this, &ProjectAutoNumConfigPage::saveContextElement);
|
||||
connect(m_saw_element, &SelectAutonumW::removeClicked, this, &ProjectAutoNumConfigPage::removeContextElement);
|
||||
connect(m_saw_element, &SelectAutonumW::newClicked, this, &ProjectAutoNumConfigPage::newContextElement);
|
||||
connect(m_saw_element, &SelectAutonumW::renameClicked, this, &ProjectAutoNumConfigPage::renameContextElement);
|
||||
connect(m_saw_element->contextComboBox(), &QComboBox::textActivated, this, &ProjectAutoNumConfigPage::updateContextElement);
|
||||
|
||||
//Folio Tab
|
||||
connect(m_saw_folio, &SelectAutonumW::applyPressed, this, &ProjectAutoNumConfigPage::saveContextFolio);
|
||||
connect(m_saw_folio, &SelectAutonumW::removeClicked, this, &ProjectAutoNumConfigPage::removeContextFolio);
|
||||
connect(m_saw_folio, &SelectAutonumW::newClicked, this, &ProjectAutoNumConfigPage::newContextFolio);
|
||||
connect(m_saw_folio, &SelectAutonumW::renameClicked, this, &ProjectAutoNumConfigPage::renameContextFolio);
|
||||
connect(m_saw_folio->contextComboBox(), &QComboBox::textActivated, this, &ProjectAutoNumConfigPage::updateContextFolio);
|
||||
|
||||
// Auto Folio Numbering
|
||||
@@ -572,18 +639,12 @@ void ProjectAutoNumConfigPage::buildConnections()
|
||||
@param str : key of context stored in project
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::updateContextConductor(const QString& str) {
|
||||
if (str == tr("Nom de la nouvelle numérotation")) m_saw_conductor -> setContext(NumerotationContext());
|
||||
else m_saw_conductor ->setContext(m_project->conductorAutoNum(str));
|
||||
m_saw_conductor->setContext(AutoNumSchemeCommand::contextOf(m_project, SchemeKind::Conductor, str));
|
||||
refreshSchemeUsers(SchemeKind::Conductor);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::updateContext_folio
|
||||
Display the current selected context for folio
|
||||
@param str : key of context stored in project
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::updateContextFolio(const QString& str) {
|
||||
if (str == tr("Nom de la nouvelle numérotation")) m_saw_folio -> setContext(NumerotationContext());
|
||||
else m_saw_folio ->setContext(m_project->folioAutoNum(str));
|
||||
m_saw_folio->setContext(AutoNumSchemeCommand::contextOf(m_project, SchemeKind::Folio, str));
|
||||
refreshSchemeUsers(SchemeKind::Folio);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -593,7 +654,7 @@ void ProjectAutoNumConfigPage::updateContextFolio(const QString& str) {
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::updateContextElement(const QString& str)
|
||||
{
|
||||
if (str == tr("Nom de la nouvelle numérotation"))
|
||||
if (str.isEmpty() || !m_project->elementAutoNum().contains(str))
|
||||
{
|
||||
m_saw_element->setContext(NumerotationContext());
|
||||
}
|
||||
@@ -601,6 +662,306 @@ void ProjectAutoNumConfigPage::updateContextElement(const QString& str)
|
||||
{
|
||||
m_saw_element->setContext(m_project->elementAutoNum(str));
|
||||
}
|
||||
refreshElementUsers();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::refreshElementUsers
|
||||
List, in folio and position order, the elements which follow the
|
||||
element numbering shown, with their label and whether it is frozen.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::refreshElementUsers()
|
||||
{
|
||||
if (!m_element_users || !m_element_users_box) {
|
||||
return;
|
||||
}
|
||||
const QString title = m_saw_element->contextComboBox()->currentText();
|
||||
QVector<Element *> users = m_project->elementsUsingElementAutoNum(title);
|
||||
std::sort(users.begin(), users.end(),
|
||||
[](Element *a, Element *b) { return comparPos(a, b); });
|
||||
|
||||
//With a single sequence of numbers the elements are listed by number,
|
||||
//and the numbers no element has between the lowest and the highest
|
||||
//are shown too: where an element was deleted or given another number
|
||||
using Support = ElementAutoNumSchemeCommand::NumberSupport;
|
||||
const Support support = m_project->elementAutoNum().contains(title)
|
||||
? ElementAutoNumSchemeCommand::numberSupport(m_project->elementAutoNum().value(title))
|
||||
: Support();
|
||||
struct Entry {
|
||||
int number = 0;
|
||||
int to = 0; ///< the end of a range of free numbers
|
||||
Element *element = nullptr;
|
||||
};
|
||||
QVector<Entry> entries;
|
||||
QVector<Entry> unnumbered;
|
||||
for (Element *el : std::as_const(users)) {
|
||||
const auto number = support.supported ? ElementAutoNumSchemeCommand::numberOf(support, el)
|
||||
: std::nullopt;
|
||||
if (support.supported && !number) {
|
||||
unnumbered << Entry{0, 0, el};
|
||||
} else {
|
||||
entries << Entry{number.value_or(0), 0, el};
|
||||
}
|
||||
}
|
||||
if (support.supported) {
|
||||
for (const auto &gap : ElementAutoNumSchemeCommand::gapRanges(m_project, title)) {
|
||||
entries << Entry{gap.from, gap.to, nullptr};
|
||||
}
|
||||
std::stable_sort(entries.begin(), entries.end(),
|
||||
[](const Entry &a, const Entry &b) { return a.number < b.number; });
|
||||
entries += unnumbered;
|
||||
}
|
||||
|
||||
int frozen = 0;
|
||||
int gaps = 0;
|
||||
m_element_users->setRowCount(0);
|
||||
m_element_rows.clear();
|
||||
for (const Entry &entry : std::as_const(entries))
|
||||
{
|
||||
const int row = m_element_users->rowCount();
|
||||
m_element_users->insertRow(row);
|
||||
m_element_rows << QPointer<Element>(entry.element);
|
||||
|
||||
QStringList cells;
|
||||
if (entry.element)
|
||||
{
|
||||
const bool is_frozen = entry.element->isFreezeLabel();
|
||||
if (is_frozen) ++frozen;
|
||||
const auto number = support.supported ? ElementAutoNumSchemeCommand::numberOf(support, entry.element)
|
||||
: std::nullopt;
|
||||
cells = QStringList{
|
||||
number ? QString::number(*number) : QString(),
|
||||
entry.element->elementInformations().value(QStringLiteral("label")).toString(),
|
||||
entry.element->diagram() ? QString::number(entry.element->diagram()->folioIndex() + 1) : QString(),
|
||||
entry.element->name(),
|
||||
is_frozen ? tr("figé") : QString()};
|
||||
}
|
||||
else
|
||||
{
|
||||
++gaps;
|
||||
cells = QStringList{
|
||||
entry.to > entry.number ? QStringLiteral("%1 – %2").arg(entry.number).arg(entry.to)
|
||||
: QString::number(entry.number),
|
||||
tr("— libre —"), QString(),
|
||||
tr("aucun élément n'a ce numéro (supprimé ou renuméroté)"), QString()};
|
||||
}
|
||||
for (int column = 0 ; column < cells.size() ; ++column) {
|
||||
auto *item = new QTableWidgetItem(cells.at(column));
|
||||
if (!entry.element) {
|
||||
QFont font = item->font();
|
||||
font.setItalic(true);
|
||||
item->setFont(font);
|
||||
item->setForeground(QBrush(Qt::gray));
|
||||
}
|
||||
m_element_users->setItem(row, column, item);
|
||||
}
|
||||
}
|
||||
m_element_users->setColumnHidden(0, !support.supported);
|
||||
m_element_users->resizeColumnsToContents();
|
||||
m_element_users_box->setTitle(
|
||||
users.isEmpty()
|
||||
? tr("Aucun élément ne suit cette numérotation")
|
||||
: tr("%n élément(s) suivent cette numérotation, dont %1 au nom figé", "", users.size())
|
||||
.arg(frozen)
|
||||
+ (gaps ? tr(" ; %n numéro(s) sans élément", "", gaps) : QString()));
|
||||
updateAssignNumberButton();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::updateAssignNumberButton
|
||||
The button which gives an element a free number needs an element row
|
||||
selected, in a numbering whose numbers are one sequence.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::updateAssignNumberButton()
|
||||
{
|
||||
if (!m_assign_number_pb) {
|
||||
return;
|
||||
}
|
||||
const int row = m_element_users->currentRow();
|
||||
const Element *element = row >= 0 ? m_element_rows.value(row).data() : nullptr;
|
||||
const QString title = m_saw_element->contextComboBox()->currentText();
|
||||
const bool supported = m_project->elementAutoNum().contains(title)
|
||||
&& ElementAutoNumSchemeCommand::numberSupport(m_project->elementAutoNum().value(title)).supported;
|
||||
m_assign_number_pb->setEnabled(element && supported && !m_project->isReadOnly());
|
||||
m_assign_number_pb->setToolTip(
|
||||
!supported
|
||||
? tr("Cette numérotation a plusieurs numéros (ou un numéro par folio) : "
|
||||
"on ne peut pas en choisir un à la main.")
|
||||
: tr("Donner à l'élément sélectionné un numéro que personne n'a : "
|
||||
"il garde sa numérotation, seul son numéro change."));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::assignFreeNumber
|
||||
Give the element selected in the table a free number, chosen in the list
|
||||
of those which are, each with the label it would give. The element keeps
|
||||
following the numbering. One undo step.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::assignFreeNumber()
|
||||
{
|
||||
const int row = m_element_users->currentRow();
|
||||
Element *element = row >= 0 ? m_element_rows.value(row).data() : nullptr;
|
||||
const QString title = m_saw_element->contextComboBox()->currentText();
|
||||
const QString caption = tr("Attribuer un numéro libre");
|
||||
if (!element || m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
if (element->isFreezeLabel()) {
|
||||
QMessageBox::information(this, caption,
|
||||
tr("Le nom de cet élément est figé : dégelez-le d'abord."));
|
||||
return;
|
||||
}
|
||||
const QList<int> numbers = ElementAutoNumSchemeCommand::freeNumbers(m_project, title, element);
|
||||
if (numbers.isEmpty()) {
|
||||
QMessageBox::information(this, caption, tr("Aucun numéro n'est libre."));
|
||||
return;
|
||||
}
|
||||
QStringList items;
|
||||
for (int n : numbers) {
|
||||
items << tr("%1 → %2").arg(n).arg(ElementAutoNumSchemeCommand::labelForNumber(
|
||||
m_project, title, element, n));
|
||||
}
|
||||
bool ok = false;
|
||||
const QString chosen = QInputDialog::getItem(
|
||||
this, caption,
|
||||
tr("Numéro libre pour l'élément « %1 » (%2) :")
|
||||
.arg(element->elementInformations().value(QStringLiteral("label")).toString(), element->name()),
|
||||
items, 0, false, &ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
QString problem;
|
||||
auto *cmd = ElementAutoNumSchemeCommand::assignNumber(
|
||||
m_project, element, numbers.at(items.indexOf(chosen)), &problem);
|
||||
if (!cmd) {
|
||||
QMessageBox::warning(this, caption, problem);
|
||||
return;
|
||||
}
|
||||
m_project->undoStack()->push(cmd);
|
||||
updateContextElement(title); // the counter may have moved; lists the elements again
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::refreshElementSchemes
|
||||
Fill the list of element numberings from the project and show
|
||||
@p selected, or the first one if there is no such numbering
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::refreshElementSchemes(const QString &selected)
|
||||
{
|
||||
QComboBox *cb = m_saw_element->contextComboBox();
|
||||
const QSignalBlocker blocker(cb);
|
||||
cb->clear();
|
||||
QStringList titles(m_project->elementAutoNum().keys());
|
||||
titles.sort(Qt::CaseInsensitive);
|
||||
cb->addItems(titles);
|
||||
const int index = cb->findText(selected);
|
||||
cb->setCurrentIndex(index >= 0 ? index : 0);
|
||||
updateContextElement(cb->currentText());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::askElementSchemeName
|
||||
Ask the user for the name of an element numbering until it is an
|
||||
acceptable one or the user gives up.
|
||||
@param title : title of the dialog
|
||||
@param name : name proposed first
|
||||
@param ignored_title : the numbering being renamed, if any
|
||||
@return the name, empty if the user cancelled
|
||||
*/
|
||||
QString ProjectAutoNumConfigPage::askElementSchemeName(const QString &title,
|
||||
QString name,
|
||||
const QString &ignored_title)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
bool ok = false;
|
||||
name = QInputDialog::getText(this, title, tr("Nom de la numérotation :"),
|
||||
QLineEdit::Normal, name, &ok);
|
||||
if (!ok) {
|
||||
return QString();
|
||||
}
|
||||
const QString problem = ElementAutoNumSchemeCommand::nameProblem(
|
||||
m_project, name, ignored_title);
|
||||
if (problem.isEmpty()) {
|
||||
return QETProject::normalizedAutoNumName(name);
|
||||
}
|
||||
QMessageBox::warning(this, title, problem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::pushElementSchemeCommand
|
||||
Push @p cmd on the project's undo stack, after the user agreed when it
|
||||
changes the label of elements.
|
||||
@return true if pushed; @p cmd is deleted otherwise
|
||||
*/
|
||||
bool ProjectAutoNumConfigPage::pushElementSchemeCommand(ElementAutoNumSchemeCommand *cmd)
|
||||
{
|
||||
if (!cmd) {
|
||||
return false;
|
||||
}
|
||||
if (const int changed = cmd->changedElementCount())
|
||||
{
|
||||
if (!RenumberPreviewDialog::confirm(
|
||||
this,
|
||||
tr("Modifier la numérotation"),
|
||||
tr("%n élément(s) suivent cette numérotation et vont "
|
||||
"changer de formule et de nom.", "", changed),
|
||||
cmd->changes(), {})) {
|
||||
delete cmd;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_project->undoStack()->push(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::newContextElement
|
||||
Create an element numbering under a name asked to the user, with a
|
||||
default definition, and show it to be defined.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::newContextElement()
|
||||
{
|
||||
if (m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
const QString name = askElementSchemeName(tr("Nouvelle numérotation"),
|
||||
QString(), QString());
|
||||
if (name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_saw_element->setContext(NumerotationContext());
|
||||
if (pushElementSchemeCommand(ElementAutoNumSchemeCommand::create(
|
||||
m_project, name,
|
||||
m_saw_element->toNumContext(),
|
||||
QUuid(), false))) {
|
||||
refreshElementSchemes(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::renameContextElement
|
||||
Rename the shown element numbering; the elements following it keep
|
||||
following it.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::renameContextElement()
|
||||
{
|
||||
const QString old_title = m_saw_element->contextComboBox()->currentText();
|
||||
if (m_project->isReadOnly() || !m_project->elementAutoNum().contains(old_title)) {
|
||||
return;
|
||||
}
|
||||
const QString name = askElementSchemeName(tr("Renommer la numérotation"),
|
||||
old_title, old_title);
|
||||
if (name.isEmpty() || name == old_title) {
|
||||
return;
|
||||
}
|
||||
if (pushElementSchemeCommand(ElementAutoNumSchemeCommand::edit(
|
||||
m_project, old_title, name,
|
||||
m_project->elementAutoNum(old_title),
|
||||
false))) {
|
||||
refreshElementSchemes(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,28 +970,49 @@ void ProjectAutoNumConfigPage::updateContextElement(const QString& str)
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::saveContextElement()
|
||||
{
|
||||
// If the text is the default text "Name of new numerotation" save the edited context
|
||||
// With the the name "No name"
|
||||
if (m_saw_element->contextComboBox()->currentText() == tr("Nom de la nouvelle numérotation"))
|
||||
{
|
||||
QString title(tr("Sans nom"));
|
||||
|
||||
m_project->addElementAutoNum (title, m_saw_element -> toNumContext());
|
||||
m_project->setCurrrentElementAutonum(title);
|
||||
m_saw_element->contextComboBox()->addItem(tr("Sans nom"));
|
||||
if (m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
// If the text isn't yet to the autonum of the project, add this new item to the combo box.
|
||||
else if ( !m_project -> elementAutoNum().contains( m_saw_element->contextComboBox()->currentText()))
|
||||
const QString title = m_saw_element->contextComboBox()->currentText();
|
||||
ElementAutoNumSchemeCommand *cmd = nullptr;
|
||||
QString shown = title;
|
||||
if (!m_project->elementAutoNum().contains(title))
|
||||
{
|
||||
m_project->addElementAutoNum(m_saw_element->contextComboBox()->currentText(), m_saw_element->toNumContext());
|
||||
m_project->setCurrrentElementAutonum(m_saw_element->contextComboBox()->currentText());
|
||||
m_saw_element->contextComboBox()->addItem(m_saw_element->contextComboBox()->currentText());
|
||||
//No numbering yet: the definition needs a name
|
||||
shown = askElementSchemeName(tr("Nouvelle numérotation"),
|
||||
QString(), QString());
|
||||
if (shown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
cmd = ElementAutoNumSchemeCommand::create(
|
||||
m_project, shown, m_saw_element->toNumContext());
|
||||
}
|
||||
// Else, the text already exist in the autonum of the project, just update the context
|
||||
else
|
||||
{
|
||||
m_project->addElementAutoNum (m_saw_element->contextComboBox() -> currentText(), m_saw_element -> toNumContext());
|
||||
m_project->setCurrrentElementAutonum(m_saw_element->contextComboBox()->currentText());
|
||||
const NumerotationContext wanted = m_saw_element->toNumContext();
|
||||
if (const int frozen = ElementAutoNumSchemeCommand::editBlockedBy(
|
||||
m_project, title, wanted).size())
|
||||
{
|
||||
QMessageBox::warning(
|
||||
this, tr("Modifier la numérotation"),
|
||||
tr("La numérotation « %1 » ne peut pas être modifiée : "
|
||||
"%n élément(s) au nom figé la suivent.\n"
|
||||
"Dégelez-les d'abord (voir la liste), ou renommez seulement la "
|
||||
"numérotation.", "", frozen).arg(title));
|
||||
refreshElementSchemes(title); //Back to the definition the project has
|
||||
return;
|
||||
}
|
||||
//A counter put back on numbers in use: the next elements skip them
|
||||
if (!CounterWarning::confirm(this, m_project, title, wanted))
|
||||
{
|
||||
refreshElementSchemes(title); //Back to the definition the project has
|
||||
return;
|
||||
}
|
||||
cmd = ElementAutoNumSchemeCommand::edit(
|
||||
m_project, title, title, wanted);
|
||||
}
|
||||
if (pushElementSchemeCommand(cmd)) {
|
||||
refreshElementSchemes(shown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,7 +1118,7 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
bool exists = false;
|
||||
switch (i) {
|
||||
case 0: exists = m_project->conductorAutoNum().contains(title); break;
|
||||
case 1: exists = m_project->elementAutoNum().contains(title); break;
|
||||
case 1: exists = !m_project->elementAutoNumNameClash(title).isEmpty(); break;
|
||||
default: exists = m_project->folioAutoNum().contains(title); break;
|
||||
}
|
||||
|
||||
@@ -750,6 +1132,7 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
item->setCheckState(exists ? Qt::Unchecked : Qt::Checked);
|
||||
item->setData(Qt::UserRole, i);
|
||||
item->setData(Qt::UserRole + 1, title);
|
||||
item->setData(Qt::UserRole + 3, entry.attribute(QStringLiteral("id")));
|
||||
|
||||
NumerotationContext nc;
|
||||
nc.fromXml(entry);
|
||||
@@ -778,7 +1161,8 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
return;
|
||||
}
|
||||
|
||||
int imported = 0, skipped = 0, conductors = 0;
|
||||
int imported = 0, skipped = 0, conductors = 0, relabelled = 0, blocked = 0;
|
||||
m_project->undoStack()->beginMacro(tr("Importer des numérotations"));
|
||||
for (int row = 0 ; row < list->count() ; ++row)
|
||||
{
|
||||
QListWidgetItem *item = list->item(row);
|
||||
@@ -791,9 +1175,9 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
|
||||
bool exists = false;
|
||||
switch (category) {
|
||||
case 0: exists = m_project->conductorAutoNum().contains(title); break;
|
||||
case 1: exists = m_project->elementAutoNum().contains(title); break;
|
||||
default: exists = m_project->folioAutoNum().contains(title); break;
|
||||
case 0: exists = !AutoNumSchemeCommand::nameClash(m_project, SchemeKind::Conductor, title).isEmpty(); break;
|
||||
case 1: exists = !m_project->elementAutoNumNameClash(title).isEmpty(); break;
|
||||
default: exists = !AutoNumSchemeCommand::nameClash(m_project, SchemeKind::Folio, title).isEmpty(); break;
|
||||
}
|
||||
if (exists && !overwrite_cb->isChecked()) {
|
||||
++skipped;
|
||||
@@ -804,14 +1188,54 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
contexts.at(item->data(Qt::UserRole + 2).toInt());
|
||||
switch (category) {
|
||||
case 0:
|
||||
m_project->addConductorAutoNum(title, nc);
|
||||
{
|
||||
const QString clash = AutoNumSchemeCommand::nameClash(m_project, SchemeKind::Conductor, title);
|
||||
auto *cmd = clash.isEmpty()
|
||||
? AutoNumSchemeCommand::create(m_project, SchemeKind::Conductor, title, nc)
|
||||
: AutoNumSchemeCommand::edit(m_project, SchemeKind::Conductor, clash, clash, nc);
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
++conductors;
|
||||
break;
|
||||
case 1: m_project->addElementAutoNum(title, nc); break;
|
||||
default: m_project->addFolioAutoNum(title, nc); break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
//Undoable, and an existing numbering is edited, not
|
||||
//replaced: the elements following it follow the
|
||||
//imported definition. A new one keeps the id it has
|
||||
//in the other project when free here, so elements
|
||||
//pasted from there follow it too.
|
||||
const QString clash = m_project->elementAutoNumNameClash(title);
|
||||
if (!clash.isEmpty()
|
||||
&& !ElementAutoNumSchemeCommand::editBlockedBy(m_project, clash, nc).isEmpty()) {
|
||||
//Elements with a frozen label follow it: kept as it is
|
||||
++blocked;
|
||||
continue;
|
||||
}
|
||||
ElementAutoNumSchemeCommand *cmd = clash.isEmpty()
|
||||
? ElementAutoNumSchemeCommand::create(
|
||||
m_project, title, nc,
|
||||
QUuid(item->data(Qt::UserRole + 3).toString()), false)
|
||||
: ElementAutoNumSchemeCommand::edit(
|
||||
m_project, clash, clash, nc, false);
|
||||
if (cmd) {
|
||||
relabelled += cmd->changedElementCount();
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
const QString clash = AutoNumSchemeCommand::nameClash(m_project, SchemeKind::Folio, title);
|
||||
auto *cmd = clash.isEmpty()
|
||||
? AutoNumSchemeCommand::create(m_project, SchemeKind::Folio, title, nc)
|
||||
: AutoNumSchemeCommand::edit(m_project, SchemeKind::Folio, clash, clash, nc);
|
||||
if (cmd) m_project->undoStack()->push(cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
++imported;
|
||||
}
|
||||
m_project->undoStack()->endMacro();
|
||||
|
||||
readValuesFromProject();
|
||||
if (conductors) {
|
||||
@@ -820,10 +1244,17 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
|
||||
QMessageBox::information(
|
||||
this, tr("Import terminé"),
|
||||
skipped ? tr("%1 numérotation(s) importée(s), "
|
||||
"%2 conservée(s) telles quelles.")
|
||||
.arg(imported).arg(skipped)
|
||||
: tr("%1 numérotation(s) importée(s).").arg(imported));
|
||||
(skipped ? tr("%1 numérotation(s) importée(s), "
|
||||
"%2 conservée(s) telles quelles.")
|
||||
.arg(imported).arg(skipped)
|
||||
: tr("%1 numérotation(s) importée(s).").arg(imported))
|
||||
+ (blocked ? QStringLiteral("\n")
|
||||
+ tr("%n numérotation(s) suivie(s) par des éléments au nom figé "
|
||||
"n'ont pas été remplacées.", "", blocked)
|
||||
: QString())
|
||||
+ (relabelled ? QStringLiteral("\n")
|
||||
+ tr("%n élément(s) ont changé de formule.", "", relabelled)
|
||||
: QString()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -832,15 +1263,23 @@ void ProjectAutoNumConfigPage::importFromProject()
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::removeContextElement()
|
||||
{
|
||||
//if default text, return
|
||||
if (m_saw_element->contextComboBox()->currentText() == tr("Nom de la nouvelle numérotation"))
|
||||
const QString title = m_saw_element->contextComboBox()->currentText();
|
||||
if (m_project->isReadOnly() || !m_project->elementAutoNum().contains(title)) {
|
||||
return;
|
||||
m_project->removeElementAutoNum (m_saw_element->contextComboBox()->currentText());
|
||||
m_saw_element->contextComboBox()->removeItem (m_saw_element->contextComboBox()->currentIndex());
|
||||
// removeItem() removes the current selection programmatically but
|
||||
// textActivated() does not react to (by design, see buildConnections()).
|
||||
// Refresh the displayed pattern explicitly so it matches the new selection.
|
||||
updateContextElement(m_saw_element->contextComboBox()->currentText());
|
||||
}
|
||||
if (const int used = m_project->elementsUsingElementAutoNum(title).size())
|
||||
{
|
||||
QMessageBox::information(
|
||||
this, tr("Supprimer la numérotation"),
|
||||
tr("%n élément(s) suivent la numérotation « %1 », elle ne "
|
||||
"peut pas être supprimée.\nDonnez-leur une autre "
|
||||
"numérotation ou un nom fixe d'abord.", "", used)
|
||||
.arg(title));
|
||||
return;
|
||||
}
|
||||
if (pushElementSchemeCommand(ElementAutoNumSchemeCommand::remove(m_project, title))) {
|
||||
refreshElementSchemes(m_project->elementCurrentAutoNum());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -849,52 +1288,16 @@ void ProjectAutoNumConfigPage::removeContextElement()
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::saveContextConductor()
|
||||
{
|
||||
// If the text is the default text "Name of new numerotation" save the edited context
|
||||
// With the the name "No name"
|
||||
if (m_saw_conductor->contextComboBox()-> currentText() == tr("Nom de la nouvelle numérotation"))
|
||||
{
|
||||
m_project->addConductorAutoNum (tr("Sans nom"), m_saw_conductor -> toNumContext());
|
||||
project()->setCurrentConductorAutoNum(tr("Sans nom"));
|
||||
m_saw_conductor->contextComboBox()-> addItem(tr("Sans nom"));
|
||||
}
|
||||
// If the text isn't yet to the autonum of the project, add this new item to the combo box.
|
||||
else if ( !m_project -> conductorAutoNum().contains( m_saw_conductor->contextComboBox()->currentText()))
|
||||
{
|
||||
project()->addConductorAutoNum(m_saw_conductor->contextComboBox()->currentText(), m_saw_conductor->toNumContext());
|
||||
project()->setCurrentConductorAutoNum(m_saw_conductor->contextComboBox()->currentText());
|
||||
m_saw_conductor->contextComboBox()-> addItem(m_saw_conductor->contextComboBox()->currentText());
|
||||
}
|
||||
// Else, the text already exist in the autonum of the project, just update the context
|
||||
else
|
||||
{
|
||||
project()->setCurrentConductorAutoNum(m_saw_conductor->contextComboBox()->currentText());
|
||||
m_project->addConductorAutoNum (m_saw_conductor->contextComboBox()-> currentText(), m_saw_conductor -> toNumContext());
|
||||
}
|
||||
project()->conductorAutoNumAdded();
|
||||
saveScheme(SchemeKind::Conductor);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::saveContext_folio
|
||||
Save the current displayed folio context in project
|
||||
@brief ProjectAutoNumConfigPage::saveContextFolio
|
||||
Apply the definition shown to the folio numbering shown
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::saveContextFolio()
|
||||
{
|
||||
// If the text is the default text "Name of new numerotation" save the edited context
|
||||
// With the the name "No name"
|
||||
if (m_saw_folio->contextComboBox() -> currentText() == tr("Nom de la nouvelle numérotation")) {
|
||||
m_project->addFolioAutoNum (tr("Sans nom"), m_saw_folio -> toNumContext());
|
||||
m_saw_folio->contextComboBox() -> addItem(tr("Sans nom"));
|
||||
}
|
||||
// If the text isn't yet to the autonum of the project, add this new item to the combo box.
|
||||
else if ( !m_project -> folioAutoNum().contains( m_saw_folio->contextComboBox()->currentText())) {
|
||||
project()->addFolioAutoNum(m_saw_folio->contextComboBox()->currentText(), m_saw_folio->toNumContext());
|
||||
m_saw_folio->contextComboBox() -> addItem(m_saw_folio->contextComboBox()->currentText());
|
||||
}
|
||||
// Else, the text already exist in the autonum of the project, just update the context
|
||||
else {
|
||||
m_project->addFolioAutoNum (m_saw_folio->contextComboBox() -> currentText(), m_saw_folio -> toNumContext());
|
||||
}
|
||||
project()->folioAutoNumAdded();
|
||||
saveScheme(SchemeKind::Folio);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1024,32 +1427,201 @@ void ProjectAutoNumConfigPage::applyManagement()
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::removeContextConductor()
|
||||
{
|
||||
//if default text, return
|
||||
if ( m_saw_conductor->contextComboBox()-> currentText() == tr("Nom de la nouvelle numérotation") ) return;
|
||||
m_project -> removeConductorAutoNum (m_saw_conductor->contextComboBox()-> currentText() );
|
||||
m_saw_conductor->contextComboBox()-> removeItem (m_saw_conductor->contextComboBox()-> currentIndex() );
|
||||
// removeItem() removes the current selection programmatically but
|
||||
// textActivated() does not react to (by design, see buildConnections()).
|
||||
// Refresh the displayed pattern explicitly so it matches the new selection.
|
||||
updateContextConductor(m_saw_conductor->contextComboBox()->currentText());
|
||||
project()->conductorAutoNumRemoved();
|
||||
removeScheme(SchemeKind::Conductor);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::removeContext_folio
|
||||
Remove from project the current folio numerotation context
|
||||
@brief ProjectAutoNumConfigPage::removeContextFolio
|
||||
Remove from project the folio numerotation context shown
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::removeContextFolio()
|
||||
{
|
||||
//if default text, return
|
||||
if ( m_saw_folio->contextComboBox() -> currentText() == tr("Nom de la nouvelle numérotation") ) return;
|
||||
m_project -> removeFolioAutoNum (m_saw_folio->contextComboBox() -> currentText() );
|
||||
m_saw_folio->contextComboBox() -> removeItem (m_saw_folio->contextComboBox() -> currentIndex() );
|
||||
// removeItem() removes the current selection programmatically but
|
||||
// textActivated() does not react to (by design, see buildConnections()).
|
||||
// Refresh the displayed pattern explicitly so it matches the new selection.
|
||||
updateContextFolio(m_saw_folio->contextComboBox()->currentText());
|
||||
project()->folioAutoNumRemoved();
|
||||
removeScheme(SchemeKind::Folio);
|
||||
}
|
||||
|
||||
void ProjectAutoNumConfigPage::newContextConductor() {newScheme(SchemeKind::Conductor);}
|
||||
void ProjectAutoNumConfigPage::renameContextConductor() {renameScheme(SchemeKind::Conductor);}
|
||||
void ProjectAutoNumConfigPage::newContextFolio() {newScheme(SchemeKind::Folio);}
|
||||
void ProjectAutoNumConfigPage::renameContextFolio() {renameScheme(SchemeKind::Folio);}
|
||||
|
||||
SelectAutonumW *ProjectAutoNumConfigPage::sawFor(SchemeKind kind) const
|
||||
{
|
||||
return kind == SchemeKind::Conductor ? m_saw_conductor : m_saw_folio;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::refreshSchemes
|
||||
Fill the list of numberings of @p kind from the project and show
|
||||
@p selected, or the first one if there is no such numbering
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::refreshSchemes(SchemeKind kind, const QString &selected)
|
||||
{
|
||||
QComboBox *cb = sawFor(kind)->contextComboBox();
|
||||
{
|
||||
const QSignalBlocker blocker(cb);
|
||||
cb->clear();
|
||||
cb->addItems(AutoNumSchemeCommand::titles(m_project, kind));
|
||||
const int index = cb->findText(selected);
|
||||
cb->setCurrentIndex(index >= 0 ? index : 0);
|
||||
}
|
||||
sawFor(kind)->setContext(AutoNumSchemeCommand::contextOf(m_project, kind, cb->currentText()));
|
||||
refreshSchemeUsers(kind);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::refreshSchemeUsers
|
||||
List the folios which follow the numbering of @p kind shown
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::refreshSchemeUsers(SchemeKind kind)
|
||||
{
|
||||
QTableWidget *table = kind == SchemeKind::Conductor ? m_conductor_users : m_folio_users;
|
||||
QGroupBox *box = kind == SchemeKind::Conductor ? m_conductor_users_box : m_folio_users_box;
|
||||
if (!table || !box) {
|
||||
return;
|
||||
}
|
||||
const QString title = sawFor(kind)->contextComboBox()->currentText();
|
||||
//A folio numbering is named by the title blocks it numbered, whose
|
||||
//folio field holds the number since: they are what is listed
|
||||
const QList<Diagram *> users = AutoNumSchemeCommand::usersOf(
|
||||
m_project, kind, title, kind == SchemeKind::Folio);
|
||||
|
||||
table->setRowCount(0);
|
||||
for (Diagram *d : users) {
|
||||
const int row = table->rowCount();
|
||||
table->insertRow(row);
|
||||
table->setItem(row, 0, new QTableWidgetItem(QString::number(d->folioIndex() + 1)));
|
||||
table->setItem(row, 1, new QTableWidgetItem(d->border_and_titleblock.title()));
|
||||
}
|
||||
table->resizeColumnsToContents();
|
||||
if (kind == SchemeKind::Folio) {
|
||||
box->setTitle(users.isEmpty()
|
||||
? tr("Aucun folio ne nomme cette numérotation")
|
||||
: tr("%n folio(s) nomment cette numérotation", "", users.size()));
|
||||
} else {
|
||||
box->setTitle(users.isEmpty()
|
||||
? tr("Aucun folio ne suit cette numérotation")
|
||||
: tr("%n folio(s) suivent cette numérotation", "", users.size()));
|
||||
}
|
||||
}
|
||||
|
||||
QString ProjectAutoNumConfigPage::askSchemeName(SchemeKind kind, const QString &title,
|
||||
QString name, const QString &ignored_title)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
bool ok = false;
|
||||
name = QInputDialog::getText(this, title, tr("Nom de la numérotation :"),
|
||||
QLineEdit::Normal, name, &ok);
|
||||
if (!ok) {
|
||||
return QString();
|
||||
}
|
||||
const QString problem = AutoNumSchemeCommand::nameProblem(m_project, kind, name, ignored_title);
|
||||
if (problem.isEmpty()) {
|
||||
return QETProject::normalizedAutoNumName(name);
|
||||
}
|
||||
QMessageBox::warning(this, title, problem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::newScheme
|
||||
Create a numbering under a name asked to the user, and show it to be defined.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::newScheme(SchemeKind kind)
|
||||
{
|
||||
if (m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
const QString name = askSchemeName(kind, tr("Nouvelle numérotation"), QString(), QString());
|
||||
if (name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sawFor(kind)->setContext(NumerotationContext());
|
||||
if (auto *cmd = AutoNumSchemeCommand::create(m_project, kind, name,
|
||||
sawFor(kind)->toNumContext(), true)) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
refreshSchemes(kind, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::renameScheme
|
||||
Rename the numbering shown; the folios which follow it follow it under its new name.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::renameScheme(SchemeKind kind)
|
||||
{
|
||||
const QString old_title = sawFor(kind)->contextComboBox()->currentText();
|
||||
if (m_project->isReadOnly() || !AutoNumSchemeCommand::contains(m_project, kind, old_title)) {
|
||||
return;
|
||||
}
|
||||
const QString name = askSchemeName(kind, tr("Renommer la numérotation"), old_title, old_title);
|
||||
if (name.isEmpty() || name == old_title) {
|
||||
return;
|
||||
}
|
||||
if (auto *cmd = AutoNumSchemeCommand::edit(
|
||||
m_project, kind, old_title, name,
|
||||
AutoNumSchemeCommand::contextOf(m_project, kind, old_title))) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
refreshSchemes(kind, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::saveScheme
|
||||
Give the numbering shown the definition shown; with no numbering yet,
|
||||
create one under a name asked to the user. A conductor numbering applied
|
||||
becomes the one new conductors take.
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::saveScheme(SchemeKind kind)
|
||||
{
|
||||
if (m_project->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
const QString title = sawFor(kind)->contextComboBox()->currentText();
|
||||
const NumerotationContext wanted = sawFor(kind)->toNumContext();
|
||||
QString shown = title;
|
||||
AutoNumSchemeCommand *cmd = nullptr;
|
||||
if (!AutoNumSchemeCommand::contains(m_project, kind, title))
|
||||
{
|
||||
shown = askSchemeName(kind, tr("Nouvelle numérotation"), QString(), QString());
|
||||
if (shown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
cmd = AutoNumSchemeCommand::create(m_project, kind, shown, wanted, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = AutoNumSchemeCommand::edit(m_project, kind, title, title, wanted, true);
|
||||
}
|
||||
if (cmd) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
}
|
||||
refreshSchemes(kind, shown);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectAutoNumConfigPage::removeScheme
|
||||
Remove the numbering shown, unless a folio still follows it
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::removeScheme(SchemeKind kind)
|
||||
{
|
||||
const QString title = sawFor(kind)->contextComboBox()->currentText();
|
||||
if (m_project->isReadOnly() || !AutoNumSchemeCommand::contains(m_project, kind, title)) {
|
||||
return;
|
||||
}
|
||||
if (const int used = AutoNumSchemeCommand::usersOf(m_project, kind, title).size())
|
||||
{
|
||||
QMessageBox::information(
|
||||
this, tr("Supprimer la numérotation"),
|
||||
tr("%n folio(s) suivent la numérotation « %1 », elle ne peut pas être "
|
||||
"supprimée.\nDonnez-leur une autre numérotation d'abord.", "", used)
|
||||
.arg(title));
|
||||
return;
|
||||
}
|
||||
if (auto *cmd = AutoNumSchemeCommand::remove(m_project, kind, title)) {
|
||||
m_project->undoStack()->push(cmd);
|
||||
refreshSchemes(kind, kind == SchemeKind::Conductor ? m_project->conductorCurrentAutoNum() : QString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1059,5 +1631,7 @@ void ProjectAutoNumConfigPage::removeContextFolio()
|
||||
*/
|
||||
void ProjectAutoNumConfigPage::changeToTab(int i)
|
||||
{
|
||||
qDebug()<<"Q_UNUSED"<<i;
|
||||
if (m_tab_widget && i >= 0 && i < m_tab_widget->count()) {
|
||||
m_tab_widget->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#ifndef PROJECTCONFIGPAGES_H
|
||||
#define PROJECTCONFIGPAGES_H
|
||||
#include "configpage.h"
|
||||
#include <QPointer>
|
||||
#include <QVector>
|
||||
#include "../../autoNum/autonumschemecommand.h"
|
||||
|
||||
class QLabel;
|
||||
class QComboBox;
|
||||
@@ -33,6 +36,11 @@ class DiagramContextWidget;
|
||||
class ReportPropertieWidget;
|
||||
class XRefPropertiesWidget;
|
||||
class SelectAutonumW;
|
||||
class QTabWidget;
|
||||
class QGroupBox;
|
||||
class Element;
|
||||
class QTableWidget;
|
||||
class ElementAutoNumSchemeCommand;
|
||||
class FolioAutonumberingW;
|
||||
class FormulaAutonumberingW;
|
||||
class AutoNumberingManagementW;
|
||||
@@ -168,11 +176,37 @@ class ProjectAutoNumConfigPage : public ProjectConfigPage {
|
||||
void updateContextElement(const QString&);//element
|
||||
void saveContextElement();
|
||||
void removeContextElement();
|
||||
void newContextElement();
|
||||
void renameContextElement();
|
||||
void refreshElementSchemes(const QString &selected);
|
||||
void refreshElementUsers();
|
||||
void assignFreeNumber();
|
||||
void updateAssignNumberButton();
|
||||
void newContextConductor();
|
||||
void renameContextConductor();
|
||||
void newContextFolio();
|
||||
void renameContextFolio();
|
||||
QString askElementSchemeName(const QString &title, QString name,
|
||||
const QString &ignored_title);
|
||||
bool pushElementSchemeCommand(ElementAutoNumSchemeCommand *cmd);
|
||||
void importFromProject();
|
||||
|
||||
void applyAutoNum();
|
||||
void applyManagement();
|
||||
|
||||
private:
|
||||
//Conductor and folio numberings
|
||||
using SchemeKind = AutoNumSchemeCommand::Kind;
|
||||
SelectAutonumW *sawFor(SchemeKind kind) const;
|
||||
void refreshSchemes(SchemeKind kind, const QString &selected);
|
||||
void refreshSchemeUsers(SchemeKind kind);
|
||||
QString askSchemeName(SchemeKind kind, const QString &title, QString name,
|
||||
const QString &ignored_title);
|
||||
void newScheme(SchemeKind kind);
|
||||
void renameScheme(SchemeKind kind);
|
||||
void saveScheme(SchemeKind kind);
|
||||
void removeScheme(SchemeKind kind);
|
||||
|
||||
signals:
|
||||
void setAutoNum(QString);
|
||||
void setAutoNum(int,int);
|
||||
@@ -184,6 +218,15 @@ class ProjectAutoNumConfigPage : public ProjectConfigPage {
|
||||
SelectAutonumW *m_saw_conductor;
|
||||
SelectAutonumW *m_saw_folio;
|
||||
SelectAutonumW *m_saw_element;
|
||||
QTabWidget *m_tab_widget = nullptr;
|
||||
QGroupBox *m_conductor_users_box = nullptr;
|
||||
QTableWidget *m_conductor_users = nullptr;
|
||||
QGroupBox *m_folio_users_box = nullptr;
|
||||
QTableWidget *m_folio_users = nullptr;
|
||||
QGroupBox *m_element_users_box = nullptr;
|
||||
QTableWidget *m_element_users = nullptr;
|
||||
QPushButton *m_assign_number_pb = nullptr;
|
||||
QVector<QPointer<Element>> m_element_rows; ///< the element of each row of the table, none for a gap
|
||||
FolioAutonumberingW *m_faw;
|
||||
AutoNumberingManagementW *m_amw;
|
||||
QPushButton *m_import_pb = nullptr;
|
||||
|
||||
@@ -18,7 +18,13 @@
|
||||
#include "elementinfowidget.h"
|
||||
#include "../qet.h"
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
#include "../undocommand/freezeelementlabelcommand.h"
|
||||
#include "../diagram.h"
|
||||
#include "../materiallist/materiallist.h"
|
||||
#include "../materiallist/materialselectiondialog.h"
|
||||
@@ -28,6 +34,7 @@
|
||||
#include "../dataBase/projectdatabase.h"
|
||||
#include "../qetinformation.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../ui/projectpropertiesdialog.h"
|
||||
#include "../ui_elementinfowidget.h"
|
||||
#include "../undocommand/changeelementinformationcommand.h"
|
||||
#include "customelementinfopartwidget.h"
|
||||
@@ -113,13 +120,76 @@ void ElementInfoWidget::apply()
|
||||
*/
|
||||
QUndoCommand* ElementInfoWidget::associatedUndo() const
|
||||
{
|
||||
const auto new_info = currentInfo();
|
||||
auto new_info = currentInfo();
|
||||
const auto old_info = m_element -> elementInformations();
|
||||
|
||||
if (old_info != new_info)
|
||||
return (new ChangeElementInformationCommand(m_element, old_info, new_info));
|
||||
const bool was_frozen = m_element->isFreezeLabel();
|
||||
const bool want_frozen = m_freeze_cb ? m_freeze_cb->isChecked() : was_frozen;
|
||||
|
||||
return nullptr;
|
||||
//A numbering chosen in the list is given to the element the way
|
||||
//a placed one gets it: its formula, and the next number of the
|
||||
//numbering, which moves on. That is a command of its own, first,
|
||||
//so the other edited fields are changed on top of its result.
|
||||
const QString chosen = chosenScheme();
|
||||
const bool assign_wanted = !chosen.isEmpty()
|
||||
&& chosen != followedScheme()
|
||||
&& m_element->diagram();
|
||||
//A number picked among the free ones, for an element which keeps its numbering
|
||||
const bool number_wanted = !assign_wanted && m_number_cb && m_number_row
|
||||
&& !m_number_row->isHidden() && m_number_cb->isEnabled()
|
||||
&& m_number_cb->currentData().toInt() > 0
|
||||
&& m_number_cb->currentData().toInt() != m_number_current
|
||||
&& m_element->diagram();
|
||||
|
||||
if (!assign_wanted && !number_wanted && was_frozen == want_frozen)
|
||||
{
|
||||
if (old_info != new_info)
|
||||
return (new ChangeElementInformationCommand(m_element, old_info, new_info));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *macro = new QUndoCommand(
|
||||
QObject::tr("Modifier les informations de l'élément : %1")
|
||||
.arg(m_element->name()));
|
||||
DiagramContext base_info = old_info;
|
||||
bool state_frozen = was_frozen;
|
||||
|
||||
if (assign_wanted || number_wanted)
|
||||
{
|
||||
RenumberElementsCommand *assign = assign_wanted
|
||||
? ElementAutoNumSchemeCommand::assign(
|
||||
m_element->diagram()->project(), chosen,
|
||||
{m_element.data()}, true, nullptr, macro)
|
||||
: ElementAutoNumSchemeCommand::assignNumber(
|
||||
m_element->diagram()->project(), m_element.data(),
|
||||
m_number_cb->currentData().toInt(), nullptr, macro);
|
||||
if (assign && !assign->changes().isEmpty())
|
||||
{
|
||||
const auto &change = assign->changes().first();
|
||||
base_info = change.new_infos;
|
||||
state_frozen = change.new_frozen;
|
||||
for (const QString &key : {QETInformation::ELMT_FORMULA,
|
||||
QETInformation::ELMT_FORMULA_ID,
|
||||
QETInformation::ELMT_LABEL}) {
|
||||
if (base_info.contains(key)) {
|
||||
new_info.addValue(key, base_info.value(key), key != QETInformation::ELMT_FORMULA_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (base_info != new_info) {
|
||||
new ChangeElementInformationCommand(m_element, base_info, new_info, macro);
|
||||
}
|
||||
if (state_frozen != want_frozen) {
|
||||
new FreezeElementLabelCommand(m_element, state_frozen, want_frozen, macro);
|
||||
}
|
||||
|
||||
if (macro->childCount() == 0) {
|
||||
delete macro;
|
||||
return nullptr;
|
||||
}
|
||||
return macro;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,6 +247,12 @@ void ElementInfoWidget::enableLiveEdit()
|
||||
if (m_exclude_from_bom_cb) {
|
||||
connect(m_exclude_from_bom_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
if (m_freeze_cb) {
|
||||
connect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
if (m_number_cb) {
|
||||
connect(m_number_cb, QOverload<int>::of(&QComboBox::activated), this, &ElementInfoWidget::apply);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,6 +268,12 @@ void ElementInfoWidget::disableLiveEdit()
|
||||
if (m_potential_isolating_cb) {
|
||||
disconnect(m_potential_isolating_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
if (m_freeze_cb) {
|
||||
disconnect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
if (m_number_cb) {
|
||||
disconnect(m_number_cb, QOverload<int>::of(&QComboBox::activated), this, &ElementInfoWidget::apply);
|
||||
}
|
||||
if (m_exclude_from_bom_cb) {
|
||||
disconnect(m_exclude_from_bom_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||
}
|
||||
@@ -226,6 +308,9 @@ void ElementInfoWidget::buildInterface()
|
||||
}
|
||||
|
||||
setupMaterialButtons();
|
||||
setupSchemeRow();
|
||||
setupNumberRow();
|
||||
setupFreezeRow();
|
||||
|
||||
m_add_custom_property_btn = new QPushButton(tr("Ajouter une propriété personnalisée"), this);
|
||||
connect(m_add_custom_property_btn, &QPushButton::clicked, this, [this]() { addCustomProperty(); });
|
||||
@@ -273,7 +358,8 @@ QStringList ElementInfoWidget::predefinedKeys() const
|
||||
? QETInformation::terminalElementInfoKeys()
|
||||
: QETInformation::elementInfoKeys();
|
||||
|
||||
keys << QStringLiteral("auto_num_locked")
|
||||
keys << QETInformation::ELMT_FORMULA_ID
|
||||
<< QStringLiteral("auto_num_locked")
|
||||
<< QStringLiteral("potential_isolating")
|
||||
<< QStringLiteral("exclude_from_bom");
|
||||
|
||||
@@ -573,6 +659,12 @@ void ElementInfoWidget::updateUi()
|
||||
for (ElementInfoPartWidget *eipw : m_eipw_list) {
|
||||
eipw -> setText (element_info[eipw->key()].toString());
|
||||
}
|
||||
refreshSchemeRow();
|
||||
if (m_freeze_cb) {
|
||||
m_freeze_cb->setChecked(m_element->isFreezeLabel());
|
||||
updateFreezeRow();
|
||||
}
|
||||
refreshNumberRow();
|
||||
updateSuggestions();
|
||||
|
||||
// Rebuild the custom-property rows to match whatever
|
||||
@@ -658,8 +750,362 @@ DiagramContext ElementInfoWidget::currentInfo() const
|
||||
if (m_exclude_from_bom_cb) {
|
||||
info_.addValue(QStringLiteral("exclude_from_bom"), m_exclude_from_bom_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||
}
|
||||
|
||||
//The element keeps following its numbering scheme as long as its
|
||||
//formula is left as it is; a formula edited by hand is its own.
|
||||
const DiagramContext &elmt_info = m_element->elementInformations();
|
||||
if (elmt_info.contains(QETInformation::ELMT_FORMULA_ID)
|
||||
&& !info_.value(QETInformation::ELMT_FORMULA).toString().isEmpty()
|
||||
&& info_.value(QETInformation::ELMT_FORMULA) == elmt_info.value(QETInformation::ELMT_FORMULA)) {
|
||||
info_.addValue(QETInformation::ELMT_FORMULA_ID,
|
||||
elmt_info.value(QETInformation::ELMT_FORMULA_ID), false);
|
||||
}
|
||||
return info_;
|
||||
}
|
||||
/**
|
||||
@brief ElementInfoWidget::setupSchemeRow
|
||||
Add above the formula the list of the numberings of the project, the
|
||||
formula of the element being the one of the numbering picked there:
|
||||
no formula to type, a numbering to choose, and a button to open the
|
||||
numberings without leaving this window.
|
||||
*/
|
||||
void ElementInfoWidget::setupSchemeRow()
|
||||
{
|
||||
ElementInfoPartWidget *formula = infoPartWidgetForKey(QETInformation::ELMT_FORMULA);
|
||||
if (!formula || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_scheme_row = new QWidget(this);
|
||||
auto *layout = new QHBoxLayout(m_scheme_row);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(new QLabel(tr("Numérotation automatique"), m_scheme_row));
|
||||
|
||||
m_scheme_cb = new QComboBox(m_scheme_row);
|
||||
m_scheme_cb->setObjectName(QStringLiteral("m_scheme_cb"));
|
||||
m_scheme_cb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
layout->addWidget(m_scheme_cb, 1);
|
||||
|
||||
auto *open = new QPushButton(tr("…"), m_scheme_row);
|
||||
open->setToolTip(tr("Ouvrir les numérotations d'éléments du projet"));
|
||||
open->setMaximumWidth(32);
|
||||
layout->addWidget(open);
|
||||
|
||||
ui->scroll_vlayout->insertWidget(ui->scroll_vlayout->indexOf(formula), m_scheme_row);
|
||||
|
||||
//The formula is shown, not typed: it comes from the numbering
|
||||
formula->setDisabled(true);
|
||||
|
||||
connect(m_scheme_cb, QOverload<int>::of(&QComboBox::activated),
|
||||
this, &ElementInfoWidget::schemeChosen);
|
||||
connect(m_scheme_cb, QOverload<int>::of(&QComboBox::activated),
|
||||
this, &ElementInfoWidget::updateNumberRow);
|
||||
connect(open, &QPushButton::clicked, this, &ElementInfoWidget::openSchemePage);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::setupFreezeRow
|
||||
Add below the label a check box which freezes it: a frozen label is
|
||||
not touched by the numbering of elements, and its number is not given
|
||||
to another element.
|
||||
*/
|
||||
void ElementInfoWidget::setupFreezeRow()
|
||||
{
|
||||
ElementInfoPartWidget *label = infoPartWidgetForKey(QETInformation::ELMT_LABEL);
|
||||
if (!label || !m_element) {
|
||||
return;
|
||||
}
|
||||
m_freeze_cb = new QCheckBox(tr("Figer le nom"), this);
|
||||
m_freeze_cb->setToolTip(tr("Un nom figé n'est pas changé par la numérotation automatique, "
|
||||
"et son numéro n'est pas donné à un autre élément."));
|
||||
ui->scroll_vlayout->insertWidget(ui->scroll_vlayout->indexOf(label) + 1, m_freeze_cb);
|
||||
connect(m_freeze_cb, &QCheckBox::toggled, this, &ElementInfoWidget::updateNumberRow);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::updateFreezeRow
|
||||
Freezing means something for a label a formula gives: the box can be
|
||||
ticked when there is one, and always unticked, so that a frozen
|
||||
element can be given back.
|
||||
*/
|
||||
void ElementInfoWidget::updateFreezeRow()
|
||||
{
|
||||
ElementInfoPartWidget *formula = infoPartWidgetForKey(QETInformation::ELMT_FORMULA);
|
||||
if (!m_freeze_cb || !formula) {
|
||||
return;
|
||||
}
|
||||
m_freeze_cb->setEnabled(!formula->text().isEmpty() || m_freeze_cb->isChecked());
|
||||
updateNumberRow();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::setupNumberRow
|
||||
Add under the numbering a list of the numbers the element may be given
|
||||
by hand: its own, then the free ones, each with the label it would give.
|
||||
For an element which follows a numbering whose numbers are one sequence.
|
||||
*/
|
||||
void ElementInfoWidget::setupNumberRow()
|
||||
{
|
||||
if (!m_scheme_row || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
m_number_row = new QWidget(this);
|
||||
auto *layout = new QHBoxLayout(m_number_row);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(new QLabel(tr("Numéro"), m_number_row));
|
||||
m_number_cb = new QComboBox(m_number_row);
|
||||
m_number_cb->setObjectName(QStringLiteral("m_number_cb"));
|
||||
m_number_cb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
m_number_cb->setToolTip(tr("Seuls les numéros libres sont proposés : un élément qui doit garder son "
|
||||
"numéro réel peut le retrouver si personne ne l'a."));
|
||||
layout->addWidget(m_number_cb, 1);
|
||||
ui->scroll_vlayout->insertWidget(ui->scroll_vlayout->indexOf(m_scheme_row) + 1, m_number_row);
|
||||
m_number_row->hide();
|
||||
connect(m_number_cb, QOverload<int>::of(&QComboBox::activated), this, &ElementInfoWidget::numberChosen);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::refreshNumberRow
|
||||
Fill the list of numbers: the element's own, then the free ones. Hidden
|
||||
when the element follows no numbering, or one whose numbers are not one
|
||||
sequence.
|
||||
*/
|
||||
void ElementInfoWidget::refreshNumberRow()
|
||||
{
|
||||
if (!m_number_cb || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
QETProject *project = m_element->diagram()->project();
|
||||
const QString title = followedScheme();
|
||||
const auto support = title.isEmpty()
|
||||
? ElementAutoNumSchemeCommand::NumberSupport()
|
||||
: ElementAutoNumSchemeCommand::numberSupport(project->elementAutoNum().value(title));
|
||||
|
||||
const QSignalBlocker blocker(m_number_cb);
|
||||
m_number_cb->clear();
|
||||
m_number_current = -1;
|
||||
if (!support.supported) {
|
||||
m_number_row->hide();
|
||||
return;
|
||||
}
|
||||
if (const auto own = ElementAutoNumSchemeCommand::numberOf(support, m_element)) {
|
||||
m_number_current = *own;
|
||||
m_number_cb->addItem(tr("%1 (actuel)").arg(*own), *own);
|
||||
} else {
|
||||
m_number_cb->addItem(tr("— (numéro inconnu)"), 0);
|
||||
}
|
||||
for (int n : ElementAutoNumSchemeCommand::freeNumbers(project, title, m_element)) {
|
||||
if (n == m_number_current) continue;
|
||||
m_number_cb->addItem(tr("%1 → %2").arg(n).arg(
|
||||
ElementAutoNumSchemeCommand::labelForNumber(project, title, m_element, n)), n);
|
||||
}
|
||||
m_number_cb->setCurrentIndex(0);
|
||||
m_number_row->show();
|
||||
updateNumberRow();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::updateNumberRow
|
||||
A number is chosen only for an element which keeps its numbering and
|
||||
whose name is not frozen.
|
||||
*/
|
||||
void ElementInfoWidget::updateNumberRow()
|
||||
{
|
||||
if (!m_number_cb || !m_number_row || m_number_row->isHidden()) {
|
||||
return;
|
||||
}
|
||||
const bool keeps = chosenScheme() == followedScheme() && !chosenScheme().isEmpty();
|
||||
const bool frozen = m_freeze_cb ? m_freeze_cb->isChecked() : m_element->isFreezeLabel();
|
||||
m_number_cb->setEnabled(keeps && !frozen);
|
||||
m_number_cb->setToolTip(
|
||||
frozen ? tr("Le nom est figé : dégelez-le pour changer son numéro.")
|
||||
: !keeps ? tr("Le numéro se choisit quand l'élément garde sa numérotation.")
|
||||
: tr("Seuls les numéros libres sont proposés : un élément qui doit garder son "
|
||||
"numéro réel peut le retrouver si personne ne l'a."));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::numberChosen
|
||||
Show the label the number picked gives, in the field of the label.
|
||||
*/
|
||||
void ElementInfoWidget::numberChosen()
|
||||
{
|
||||
ElementInfoPartWidget *label = infoPartWidgetForKey(QETInformation::ELMT_LABEL);
|
||||
if (!label || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
const int number = m_number_cb->currentData().toInt();
|
||||
if (number <= 0 || number == m_number_current) {
|
||||
label->setText(m_element->elementInformations().value(QETInformation::ELMT_LABEL).toString());
|
||||
return;
|
||||
}
|
||||
label->setText(ElementAutoNumSchemeCommand::labelForNumber(
|
||||
m_element->diagram()->project(), followedScheme(), m_element, number));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::followedScheme
|
||||
@return the title of the numbering the element follows, empty if none
|
||||
*/
|
||||
QString ElementInfoWidget::followedScheme() const
|
||||
{
|
||||
if (!m_element || !m_element->diagram()) {
|
||||
return QString();
|
||||
}
|
||||
return m_element->diagram()->project()->elementAutoNumTitle(
|
||||
QUuid(m_element->elementInformations()
|
||||
.value(QETInformation::ELMT_FORMULA_ID).toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::chosenScheme
|
||||
@return the title of the numbering picked in the list, empty if the
|
||||
element is to follow none
|
||||
*/
|
||||
QString ElementInfoWidget::chosenScheme() const
|
||||
{
|
||||
const QString data = m_scheme_cb ? m_scheme_cb->currentData().toString() : QString();
|
||||
return data == QLatin1String("\x01") ? QString() : data;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::refreshSchemeRow
|
||||
Fill the list with the numberings of the project and show the one the
|
||||
element follows. An element with a formula of its own, followed from
|
||||
no numbering, gets an entry for it, so that it is neither hidden nor
|
||||
lost by looking at it.
|
||||
*/
|
||||
void ElementInfoWidget::refreshSchemeRow()
|
||||
{
|
||||
if (!m_scheme_cb || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
QETProject *project = m_element->diagram()->project();
|
||||
|
||||
const QSignalBlocker blocker(m_scheme_cb);
|
||||
m_scheme_cb->clear();
|
||||
m_scheme_cb->addItem(tr("Aucune (nom saisi à la main)"), QString());
|
||||
|
||||
QStringList titles(project->elementAutoNum().keys());
|
||||
titles.sort(Qt::CaseInsensitive);
|
||||
for (const QString &title : titles) {
|
||||
m_scheme_cb->addItem(title, title);
|
||||
}
|
||||
|
||||
const QString followed = followedScheme();
|
||||
if (!followed.isEmpty()) {
|
||||
m_scheme_cb->setCurrentIndex(m_scheme_cb->findData(followed));
|
||||
m_scheme_index = m_scheme_cb->currentIndex();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString formula = m_element->elementInformations()
|
||||
.value(QETInformation::ELMT_FORMULA).toString();
|
||||
if (!formula.isEmpty()) {
|
||||
//Same data as "none" would clash: the entry is for display only
|
||||
m_scheme_cb->insertItem(1, tr("Formule propre : %1").arg(formula), QStringLiteral("\x01"));
|
||||
m_scheme_cb->setCurrentIndex(1);
|
||||
}
|
||||
m_scheme_index = m_scheme_cb->currentIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::schemeChosen
|
||||
The user picked an entry of the list.
|
||||
|
||||
The formula shown is the one of the numbering picked; the label is
|
||||
typed only when there is no formula. Leaving a numbering, or the
|
||||
element's own formula, for "none" also empties the label: what stays
|
||||
would pass for a number the numbering gave, and could be given again
|
||||
to another element. Going back to what the element has at present
|
||||
gives its label back.
|
||||
|
||||
An element whose label is frozen is changed only if the user agrees.
|
||||
*/
|
||||
void ElementInfoWidget::schemeChosen()
|
||||
{
|
||||
ElementInfoPartWidget *formula = infoPartWidgetForKey(QETInformation::ELMT_FORMULA);
|
||||
ElementInfoPartWidget *label = infoPartWidgetForKey(QETInformation::ELMT_LABEL);
|
||||
if (!formula || !label || !m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DiagramContext info = m_element->elementInformations();
|
||||
const QString own_formula = info.value(QETInformation::ELMT_FORMULA).toString();
|
||||
const bool own_entry = m_scheme_cb->currentData().toString() == QLatin1String("\x01");
|
||||
const QString chosen = chosenScheme();
|
||||
const bool unchanged = own_entry
|
||||
|| (!chosen.isEmpty() && chosen == followedScheme())
|
||||
|| (chosen.isEmpty() && own_formula.isEmpty());
|
||||
|
||||
const bool frozen_now = m_freeze_cb ? m_freeze_cb->isChecked() : m_element->isFreezeLabel();
|
||||
if (!unchanged && frozen_now)
|
||||
{
|
||||
const auto answer = QET::QetMessageBox::question(
|
||||
this,
|
||||
tr("Nom figé"),
|
||||
tr("Le nom de cet élément est figé.\n"
|
||||
"Changer sa numérotation le remplacera ou l'effacera.\n\nContinuer ?"),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
m_scheme_cb->setCurrentIndex(m_scheme_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_scheme_index = m_scheme_cb->currentIndex();
|
||||
//The user agreed: the element is no longer protected
|
||||
if (!unchanged && m_freeze_cb) {
|
||||
m_freeze_cb->setChecked(false);
|
||||
}
|
||||
|
||||
//Setting the fields one by one must not apply each step
|
||||
const bool live = m_live_edit;
|
||||
if (live) disableLiveEdit();
|
||||
|
||||
if (unchanged) {
|
||||
formula->setText(own_formula);
|
||||
label->setText(info.value(QETInformation::ELMT_LABEL).toString());
|
||||
} else if (chosen.isEmpty()) {
|
||||
formula->setText(QString());
|
||||
if (!own_formula.isEmpty()) {
|
||||
label->setText(QString());
|
||||
}
|
||||
} else {
|
||||
formula->setText(m_element->diagram()->project()->elementAutoNumFormula(chosen));
|
||||
}
|
||||
updateFreezeRow();
|
||||
|
||||
if (live) {
|
||||
enableLiveEdit();
|
||||
apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::openSchemePage
|
||||
Open the numberings of the project, on the one of elements, then show
|
||||
again the list, which may have changed.
|
||||
*/
|
||||
void ElementInfoWidget::openSchemePage()
|
||||
{
|
||||
if (!m_element || !m_element->diagram()) {
|
||||
return;
|
||||
}
|
||||
const QString before = chosenScheme();
|
||||
|
||||
ProjectPropertiesDialog ppd(m_element->diagram()->project(), this);
|
||||
ppd.setCurrentPage(ProjectPropertiesDialog::Autonum);
|
||||
ppd.changeToElement();
|
||||
ppd.exec();
|
||||
|
||||
refreshSchemeRow();
|
||||
const int index = m_scheme_cb->findData(before);
|
||||
if (index >= 0) {
|
||||
m_scheme_cb->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementInfoWidget::firstActivated
|
||||
Slot activated when this widget is show.
|
||||
|
||||
@@ -29,7 +29,9 @@ class ElementInfoPartWidget;
|
||||
class CustomElementInfoPartWidget;
|
||||
class ChangeElementInformationCommand;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QWidget;
|
||||
struct MaterialRecord;
|
||||
|
||||
namespace Ui {
|
||||
@@ -71,6 +73,18 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
||||
ElementInfoPartWidget *infoPartWidgetForKey(const QString &key) const;
|
||||
QStringList predefinedKeys() const;
|
||||
void updateSuggestions();
|
||||
void setupSchemeRow();
|
||||
void setupFreezeRow();
|
||||
void setupNumberRow();
|
||||
void refreshNumberRow();
|
||||
void updateNumberRow();
|
||||
void numberChosen();
|
||||
void updateFreezeRow();
|
||||
void refreshSchemeRow();
|
||||
void schemeChosen();
|
||||
void openSchemePage();
|
||||
QString chosenScheme() const;
|
||||
QString followedScheme() const;
|
||||
|
||||
private slots:
|
||||
void firstActivated();
|
||||
@@ -86,6 +100,13 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
||||
QPushButton *m_add_custom_property_btn = nullptr;
|
||||
QCheckBox *m_potential_isolating_cb = nullptr;
|
||||
QCheckBox *m_exclude_from_bom_cb = nullptr;
|
||||
QComboBox *m_scheme_cb = nullptr;
|
||||
QCheckBox *m_freeze_cb = nullptr;
|
||||
QWidget *m_number_row = nullptr;
|
||||
QComboBox *m_number_cb = nullptr;
|
||||
int m_number_current = -1; ///< the number the element has, -1 if unknown
|
||||
int m_scheme_index = 0;
|
||||
QWidget *m_scheme_row = nullptr;
|
||||
bool m_first_activation;
|
||||
bool m_ui_builded = false;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "jumptoelementdialog.h"
|
||||
#include "../qetinformation.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../diagramview.h"
|
||||
@@ -99,6 +100,10 @@ void JumpToElementDialog::buildCandidates()
|
||||
search_parts << label << name;
|
||||
const DiagramContext infos = element->elementInformations();
|
||||
for (const QString &key : infos.keys()) {
|
||||
//An internal id, not something the user can read or type
|
||||
if (key == QETInformation::ELMT_FORMULA_ID) {
|
||||
continue;
|
||||
}
|
||||
search_parts << infos.value(key).toString();
|
||||
}
|
||||
candidate.search_text = search_parts.join(QLatin1Char(' ')).toLower();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "multipastedialog.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../conductorautonumerotation.h"
|
||||
#include "../diagram.h"
|
||||
@@ -105,22 +106,28 @@ void MultiPasteDialog::on_m_button_box_accepted()
|
||||
{
|
||||
m_diagram->undoStack().beginMacro(tr("Multi-collage"));
|
||||
|
||||
QSettings settings;
|
||||
bool erase_label = settings.value("diagramcommands/erase-label-on-copy", true).toBool();
|
||||
//Ensure when 'auto_num' is checked, the settings 'save_label' is to true.
|
||||
//Because in the class PasteDiagramCommand, if the settings 'save_label' is to false,
|
||||
//the function redo of PasteDiagramCommand, clear the formula and the label of the pasted element
|
||||
//and so the auto_num below do nothing (there is not a formula to compare)
|
||||
if(ui->m_auto_num_cb->isChecked())
|
||||
settings.setValue("diagramcommands/erase-label-on-copy", false);
|
||||
|
||||
QETProject *project = m_diagram->project();
|
||||
|
||||
//The element numberings the copies follow, read now: the paste
|
||||
//erases the formulas of its elements, unless the preference
|
||||
//keeps them, and the numbering below is meant to work either way
|
||||
QList<QMap<QString, QVector<Element *>>> copy_schemes;
|
||||
for(const DiagramContent &dc : std::as_const(m_pasted_content_list))
|
||||
{
|
||||
copy_schemes << (ui->m_auto_num_cb->isChecked()
|
||||
? ElementAutoNumSchemeCommand::pastedSchemes(project, dc.m_elements)
|
||||
: QMap<QString, QVector<Element *>>());
|
||||
}
|
||||
|
||||
m_diagram->clearSelection();
|
||||
m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, m_pasted_content));
|
||||
//The copies are numbered by this dialog, copy by copy, below
|
||||
auto *paste = new PasteDiagramCommand(m_diagram, m_pasted_content);
|
||||
paste->setAutoNumbering(false);
|
||||
m_diagram->undoStack().push(paste);
|
||||
|
||||
for(DiagramContent dc : m_pasted_content_list)
|
||||
for(int copy = 0 ; copy < m_pasted_content_list.size() ; ++copy)
|
||||
{
|
||||
const DiagramContent &dc = m_pasted_content_list.at(copy);
|
||||
QList<Element *> pasted_elements = dc.m_elements;
|
||||
//Sort the list element by there pos (top -> bottom)
|
||||
std::sort(pasted_elements.begin(), pasted_elements.end(), [](Element *a, Element *b){return (a->pos().y() < b->pos().y());});
|
||||
@@ -147,32 +154,21 @@ void MultiPasteDialog::on_m_button_box_accepted()
|
||||
}
|
||||
}
|
||||
|
||||
//Set up the label of element
|
||||
//Instead of use the current autonum of project,
|
||||
//we try to fetch the same formula of the pasted element, in the several autonum of the project
|
||||
//for apply the good formula for each elements
|
||||
if(ui->m_auto_num_cb->isChecked())
|
||||
//Number the elements of this copy with the numberings they
|
||||
//follow: the next numbers, in the order of the Renumber
|
||||
//button, the counters moving on, so that the next copy goes
|
||||
//on from there. The project's current numbering is not
|
||||
//changed, and labels which other elements keep are not given.
|
||||
if(!copy_schemes.at(copy).isEmpty())
|
||||
{
|
||||
for(Element *elmt : pasted_elements)
|
||||
{
|
||||
QString formula = elmt->elementInformations()["formula"].toString();
|
||||
if(!formula.isEmpty())
|
||||
{
|
||||
QHash <QString, NumerotationContext> autonums = m_diagram->project()->elementAutoNum();
|
||||
QHashIterator<QString, NumerotationContext> hash_iterator(autonums);
|
||||
|
||||
while(hash_iterator.hasNext())
|
||||
{
|
||||
hash_iterator.next();
|
||||
if(autonum::numerotationContextToFormula(hash_iterator.value()) == formula)
|
||||
{
|
||||
m_diagram->project()->setCurrrentElementAutonum(hash_iterator.key());
|
||||
elmt->setUpFormula();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto *numbering = new QUndoCommand(tr("Numéroter les éléments collés"));
|
||||
ElementAutoNumSchemeCommand::numberPasted(project, copy_schemes.at(copy), numbering);
|
||||
if(numbering->childCount())
|
||||
m_diagram->undoStack().push(numbering);
|
||||
else
|
||||
delete numbering;
|
||||
}
|
||||
|
||||
//Like elements, we compare formula of pasted conductor with the autonums available in the project.
|
||||
if(ui->m_auto_num_cond_cb->isChecked())
|
||||
{
|
||||
@@ -216,7 +212,6 @@ void MultiPasteDialog::on_m_button_box_accepted()
|
||||
|
||||
m_diagram->adjustSceneRect();
|
||||
m_accept = true;
|
||||
settings.setValue("diagramcommands/erase-label-on-copy", erase_label);
|
||||
m_diagram->undoStack().endMacro();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,3 +93,15 @@ void ProjectPropertiesDialog::changeToFolio()
|
||||
m_properties_dialog->pages.at(2));
|
||||
autoNumPage->changeToTab(3);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectPropertiesDialog::changeToElement
|
||||
Change the current displayed tab to the element numberings tab.
|
||||
*/
|
||||
void ProjectPropertiesDialog::changeToElement()
|
||||
{
|
||||
ProjectAutoNumConfigPage *autoNumPage =
|
||||
static_cast <ProjectAutoNumConfigPage*>(
|
||||
m_properties_dialog->pages.at(2));
|
||||
autoNumPage->changeToTab(2);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class ProjectPropertiesDialog : public QObject {
|
||||
void exec();
|
||||
void setCurrentPage(ProjectPropertiesDialog::Page);
|
||||
void changeToFolio();
|
||||
void changeToElement();
|
||||
|
||||
private:
|
||||
ConfigDialog *m_properties_dialog;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "freezeelementlabelcommand.h"
|
||||
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
|
||||
FreezeElementLabelCommand::FreezeElementLabelCommand(Element *element,
|
||||
bool old_frozen,
|
||||
bool new_frozen,
|
||||
QUndoCommand *parent) :
|
||||
QUndoCommand(new_frozen ? QObject::tr("Figer le nom de l'élément")
|
||||
: QObject::tr("Dégeler le nom de l'élément"),
|
||||
parent),
|
||||
m_element(element),
|
||||
m_old_frozen(old_frozen),
|
||||
m_new_frozen(new_frozen)
|
||||
{}
|
||||
|
||||
void FreezeElementLabelCommand::undo()
|
||||
{
|
||||
if (m_element) {
|
||||
m_element->freezeLabel(m_old_frozen);
|
||||
}
|
||||
}
|
||||
|
||||
void FreezeElementLabelCommand::redo()
|
||||
{
|
||||
if (m_element) {
|
||||
m_element->freezeLabel(m_new_frozen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 FREEZEELEMENTLABELCOMMAND_H
|
||||
#define FREEZEELEMENTLABELCOMMAND_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QUndoCommand>
|
||||
|
||||
class Element;
|
||||
|
||||
/**
|
||||
@brief The FreezeElementLabelCommand class
|
||||
Freezes or unfreezes the label of an element: a frozen label is not
|
||||
touched by the numbering of elements, and its number is not given to
|
||||
another element.
|
||||
*/
|
||||
class FreezeElementLabelCommand : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
FreezeElementLabelCommand(Element *element,
|
||||
bool old_frozen,
|
||||
bool new_frozen,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
QPointer<Element> m_element;
|
||||
bool m_old_frozen;
|
||||
bool m_new_frozen;
|
||||
};
|
||||
|
||||
#endif // FREEZEELEMENTLABELCOMMAND_H
|
||||
@@ -645,6 +645,21 @@ target_compile_definitions(tst_derivedwireuuid PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# Element numbering schemes carry an id and elements follow them by it:
|
||||
# a legacy file gets stable ids on --resave; through --run, rename, edit,
|
||||
# refused removal and undo/redo keep the elements following the scheme.
|
||||
if(QET_HAS_SCRIPTING)
|
||||
add_executable(
|
||||
tst_elementautonumids
|
||||
tst_elementautonumids.cpp)
|
||||
add_test(NAME tst_elementautonumids COMMAND tst_elementautonumids)
|
||||
add_dependencies(tst_elementautonumids qelectrotech)
|
||||
target_link_libraries(tst_elementautonumids PRIVATE Qt::Test Qt::Xml)
|
||||
target_compile_definitions(tst_elementautonumids PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
endif()
|
||||
|
||||
# Saving a project that was just saved changes nothing: runs the real
|
||||
# binary's --resave twice on every project in examples/, and on one whose
|
||||
# title block holds a single-space value (#973).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user