Let a script rename the project and reshape a folio's frame

setProjectTitle(title)
  folioBorder(folio, prop)  setFolioBorder(folio, prop, value)

The frame is the grid of columns and rows around a folio: columns,
column-width, display-columns, rows, row-height, display-rows -- the six
fields the folio properties panel offers -- through ChangeBorderCommand,
so it undoes like a hand edit. The title block's header sizes, which the
panel does not offer, are left alone. Changing the project title is not
undoable, because the application sets it directly too.

Counts are 1 to 99 and sizes 1 to 1000. The panel's upper limits are the
same; its lower limit is 0, which is not offered: a grid with no columns
has no use here and 0 was not tested, so it is refused rather than
assumed safe. The extremes that are offered (99 x 99 cells, widths from 1
to 1000) were exported to PNG without a hang or crash. Fractions, out of
range values, an unknown property and a bad folio are refused with a
reason.

Verified: ten columns of 40 and four rows of 100, undo of the last change,
and the folio and the renamed project read back after a reload. My first
reload check read the wrong folio and briefly looked like the border was
not persisted; the file itself had the right values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-21 22:51:55 +12:00
parent 2772b690e8
commit f087820757
2 changed files with 107 additions and 0 deletions
+95
View File
@@ -41,6 +41,7 @@
#include "../TerminalStrip/terminalstrip.h"
#include "../autoNum/assignvariables.h"
#include "../autoNum/numerotationcontext.h"
#include "../borderproperties.h"
#include "../diagramcommands.h"
#include "../qetgraphicsitem/terminal.h"
#include "../qetgraphicsitem/terminalelement.h"
@@ -2283,6 +2284,100 @@ QStringList QetScriptApi::duplicateElements(int fromFolioIndex, const QStringLis
return created;
}
bool QetScriptApi::setProjectTitle(const QString &title)
{
if (!m_project) return false;
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.setProjectTitle: project is read-only"));
return false;
}
m_project->setTitle(title);
return m_project->title() == title;
}
namespace {
const QStringList &folioBorderNames()
{
static const QStringList n{QStringLiteral("columns"), QStringLiteral("column-width"),
QStringLiteral("display-columns"), QStringLiteral("rows"), QStringLiteral("row-height"),
QStringLiteral("display-rows")};
return n;
}
} // namespace
QString QetScriptApi::folioBorder(int folioIndex, const QString &property) const
{
if (!m_project) return QString();
const QList<Diagram *> diagrams = m_project->diagrams();
if (folioIndex < 0 || folioIndex >= diagrams.count()) return QString();
const BorderProperties b = diagrams.at(folioIndex)->border_and_titleblock.exportBorder();
if (property == QLatin1String("columns")) return QString::number(b.columns_count);
if (property == QLatin1String("column-width")) return QString::number(b.columns_width);
if (property == QLatin1String("display-columns")) return b.display_columns ? QStringLiteral("true") : QStringLiteral("false");
if (property == QLatin1String("rows")) return QString::number(b.rows_count);
if (property == QLatin1String("row-height")) return QString::number(b.rows_height);
if (property == QLatin1String("display-rows")) return b.display_rows ? QStringLiteral("true") : QStringLiteral("false");
return QString();
}
/**
@brief QetScriptApi::setFolioBorder
Change one field of a folio's frame through ChangeBorderCommand, as the
folio properties panel does. Counts are whole numbers from 1 to 99 and
sizes are from 1 to 1000. The panel's own upper limits are 99 and 1000;
its lower limit is 0, which is deliberately not offered -- a grid with
no columns, or columns of no width, has no use here and 0 was not
tested, so it is left refused rather than assumed safe. The extremes
that are offered (99 x 99 cells, widths from 1 to 1000) were exported to
PNG and did not hang or crash.
*/
bool QetScriptApi::setFolioBorder(int folioIndex, const QString &property, const QString &value)
{
if (!m_project) return false;
const QString caller = QStringLiteral("setFolioBorder");
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
return false;
}
if (!folioBorderNames().contains(property)) {
log(QStringLiteral("qet.%1: unknown property '%2'; expected one of %3")
.arg(caller, property, folioBorderNames().join(QStringLiteral(", "))));
return false;
}
const QList<Diagram *> diagrams = m_project->diagrams();
if (folioIndex < 0 || folioIndex >= diagrams.count()) return false;
Diagram *diagram = diagrams.at(folioIndex);
const BorderProperties old_b = diagram->border_and_titleblock.exportBorder();
BorderProperties new_b = old_b;
bool ok = false;
if (property == QLatin1String("columns") || property == QLatin1String("rows")) {
const int n = value.toInt(&ok);
if (!ok || n < 1 || n > 99) {
log(QStringLiteral("qet.%1: %2 must be a whole number from 1 to 99, not '%3'").arg(caller, property, value));
return false;
}
(property == QLatin1String("columns") ? new_b.columns_count : new_b.rows_count) = n;
} else if (property == QLatin1String("column-width") || property == QLatin1String("row-height")) {
const double d = value.toDouble(&ok);
if (!ok || d < 1 || d > 1000) {
log(QStringLiteral("qet.%1: %2 must be a number from 1 to 1000, not '%3'").arg(caller, property, value));
return false;
}
(property == QLatin1String("column-width") ? new_b.columns_width : new_b.rows_height) = d;
} else {
const QString v = value.toLower();
if (v != QLatin1String("true") && v != QLatin1String("false")) {
log(QStringLiteral("qet.%1: %2 is true or false, not '%3'").arg(caller, property, value));
return false;
}
(property == QLatin1String("display-columns") ? new_b.display_columns : new_b.display_rows) = (v == QLatin1String("true"));
}
if (new_b == old_b) return true;
m_project->undoStack()->push(new ChangeBorderCommand(diagram, old_b, new_b));
return true;
}
int QetScriptApi::addFolio()
{
if (!m_project) return -1;
+12
View File
@@ -203,6 +203,13 @@ class DynamicElementTextItem;
cannot be told apart. A conductor is copied
only if both its ends are among the copied elements. The previous
selection is put back afterwards, since copying works by selecting.
- @b Project title and folio frame: setProjectTitle(), and the grid that
frames each folio -- columns and rows, their size, and whether the
headers show (columns, column-width, display-columns, rows, row-height,
display-rows) -- through ChangeBorderCommand. These are the six fields
the folio properties panel offers; the title block's header sizes,
which it does not, are left alone. Changing the project title is not
undoable: the application sets it directly too.
- @b Images: place a picture from a file. The pixels are copied into
the project, which stores them inline in the .qet -- the saved file
does not refer to the original path, so it opens on another machine,
@@ -398,6 +405,11 @@ class QetScriptApi : public QObject
Q_INVOKABLE QStringList duplicateElements(int fromFolioIndex, const QStringList &elementUuids,
int toFolioIndex, double x, double y);
// -- the project title, and each folio's frame (grid of columns and rows) --
Q_INVOKABLE bool setProjectTitle(const QString &title);
Q_INVOKABLE QString folioBorder(int folioIndex, const QString &property) const;
Q_INVOKABLE bool setFolioBorder(int folioIndex, const QString &property, const QString &value);
// -- folios --
Q_INVOKABLE int addFolio();
Q_INVOKABLE bool setFolioTitle(int folioIndex, const QString &title);