Never use a folio grid step below 1 from the settings

The folio grid steps, diagrameditor/Xgrid and Ygrid, are read from the
settings in six places with a plain toInt() and used as they come. The
preferences page cannot store anything below 1, but a hand-edited or
damaged settings file can hold 0, a negative number or text:

- Diagram::snapToGrid() divides by the step, so 0 is a SIGFPE on the
  first click that places a symbol;
- Diagram::drawBackground() runs "while (g_x % xGrid)" and then loops
  "gx += xGrid", so 0 crashes every repaint and a negative step never
  ends;
- the paste, duplicate and align paths divide by it or multiply with it.

Add foliogrid.h, a header-only helper: FolioGrid::step() reads a
settings entry and returns the built-in step (Diagram::xGrid, 10) when
the entry is missing, not a number, below 1 or above what an int
holds (QVariant::toInt() wraps such a value around). Every reader of
the two keys goes through it; the settings page, which only writes the
spin box values, is unchanged.

No file-format change, and no change for any step the preferences page
can produce.

Tests: tst_foliogrid covers the helper: 1, 10, "7", int max are kept;
missing, 0, -5, "ten", "", "nan", 99999999999 (as text and as a
number) and int max + 1 fall back; 7.9 rounds to 8 as before; and the
two settings keys through a QSettings scope of the test's own. The
helper is new, so the test cannot fail on master; the six readers are
the replacements in the diff.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Beat Hangartner <beat@hangartners.ch>
This commit is contained in:
Beat Hangartner
2026-10-08 06:04:35 +02:00
parent 51675c8a65
commit ede7bd99e1
7 changed files with 178 additions and 26 deletions
@@ -19,6 +19,7 @@
#include "../autoNum/ui/pastenumberingimport.h"
#include "../diagram.h"
#include "../foliogrid.h"
#include "../diagramcommands.h"
#include "../qetapp.h"
#include "../qetdiagrameditor.h"
@@ -108,10 +109,8 @@
}
const QPointF top_left = items_rect.topLeft();
QSettings settings;
const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
Diagram::xGrid).toInt();
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
Diagram::yGrid).toInt();
const int xGrid = FolioGrid::step(settings.value(FolioGrid::x_key), Diagram::xGrid);
const int yGrid = FolioGrid::step(settings.value(FolioGrid::y_key), Diagram::yGrid);
const auto snapGrid = [xGrid, yGrid](const QPointF &p) -> QPointF {
return QPointF(
qRound(p.x() / xGrid) * xGrid,
@@ -287,10 +286,8 @@ void DiagramEventAddPaste::showHint()
void DiagramEventAddPaste::moveTo(const QPointF &scene_pos)
{
QSettings settings;
const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"),
Diagram::xGrid).toInt();
const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"),
Diagram::yGrid).toInt();
const int xGrid = FolioGrid::step(settings.value(FolioGrid::x_key), Diagram::xGrid);
const int yGrid = FolioGrid::step(settings.value(FolioGrid::y_key), Diagram::yGrid);
const auto snapGrid = [xGrid, yGrid](const QPointF &p) -> QPointF {
return QPointF(