diff --git a/misc/qet-mcp/test_qet_mcp.py b/misc/qet-mcp/test_qet_mcp.py index d9310e6c2..69291ff0a 100644 --- a/misc/qet-mcp/test_qet_mcp.py +++ b/misc/qet-mcp/test_qet_mcp.py @@ -3520,7 +3520,7 @@ class PlcIntegration(unittest.TestCase): @needs_examples class UuidIndexLookups(unittest.TestCase): - """qet.tableIndex() and qet.elementTextIndex(): a uuid in, the index the + """qet.tableIndex(), qet.elementTextIndex() and qet.folioIndex(): a uuid in, the index the other calls take out, as qet.textIndex() does for free texts. The index is what shifts when an earlier item is deleted; the uuid is what holds.""" @@ -3639,6 +3639,43 @@ class UuidIndexLookups(unittest.TestCase): self.assertIn(f"shows='{shows_b}'", out["lb"][out["ib"]]) self.assertEqual((out["bogus"], out["no_element"]), (-1, -1)) + def test_folio_index_follows_the_folio_across_a_removal(self): + """A file saved without folio uuids (tableau_domestique.qet) still has + one per folio once loaded, the same on every load; the index of the + third folio moves when the first is removed, its uuid does not.""" + body = """ + var out = {uuids: []}; + for (var i = 0; i < qet.folioCount(); i++) out.uuids.push(qet.folioUuid(i)); + out.found = out.uuids.map(function (u) { return qet.folioIndex(u); }); + out.bogus = qet.folioIndex("{00000000-0000-4000-8000-000000000000}"); + out.not_uuid = qet.folioIndex("folio 1"); + out.out_of_range = qet.folioUuid(qet.folioCount()); + qet.removeFolio(0); + out.third_after = qet.folioIndex(out.uuids[2]); + out.first_after = qet.folioIndex(out.uuids[0]);""" + project = Path(EXAMPLES) / "tableau_domestique.qet" + self.assertFalse(any(d.get("uuid") for d in ET.parse(project).getroot().iter("diagram"))) + out = self.run_script(project, body) + self.assertEqual(len(out["uuids"]), 5) + self.assertEqual(len(set(out["uuids"])), 5) + self.assertTrue(all(m._UUID_RE.fullmatch(u) for u in out["uuids"])) + self.assertEqual(out["found"], [0, 1, 2, 3, 4]) + self.assertEqual((out["bogus"], out["not_uuid"], out["out_of_range"]), (-1, -1, "")) + self.assertEqual((out["third_after"], out["first_after"]), (1, -1)) + self.assertEqual(self.run_script(project, body)["uuids"], out["uuids"]) + + def test_a_new_folio_keeps_its_uuid_through_a_save(self): + with tempfile.TemporaryDirectory() as tmp: + saved = Path(tmp) / "saved.qet" + out = self.run_script(Path(EXAMPLES) / "tableau_domestique.qet", f""" + var out = {{}}; + out.index = qet.addFolio(); + out.uuid = qet.folioUuid(out.index); + out.saved = qet.save({json.dumps(str(saved))});""") + self.assertTrue(out["saved"]) + in_file = [d.get("uuid") for d in ET.parse(saved).getroot().iter("diagram")] + self.assertEqual(in_file[out["index"]], out["uuid"]) + @needs_examples class CorpusIntegration(unittest.TestCase): diff --git a/sources/scripting/qetscriptapi.cpp b/sources/scripting/qetscriptapi.cpp index b3d083a3f..3c7c4c549 100644 --- a/sources/scripting/qetscriptapi.cpp +++ b/sources/scripting/qetscriptapi.cpp @@ -123,6 +123,37 @@ QString QetScriptApi::folioTitle(int index) const return diagrams.at(index)->title(); } +/** + @brief QetScriptApi::folioUuid + The uuid of the folio at @p index, or an empty string. + The uuid is what to hold across an edit; the index is what the other + calls take, and it shifts when a folio is added, removed or moved. +*/ +QString QetScriptApi::folioUuid(int index) const +{ + if (!m_project) return QString(); + const QList diagrams = m_project->diagrams(); + if (index < 0 || index >= diagrams.count()) return QString(); + return diagrams.at(index)->uuid().toString(); +} + +/** + @brief QetScriptApi::folioIndex + The current index of the folio carrying @p uuid, or -1. No two folios + of a project carry the same uuid: a clash is renewed on load. +*/ +int QetScriptApi::folioIndex(const QString &uuid) const +{ + if (!m_project) return -1; + const QUuid wanted(uuid); + if (wanted.isNull()) return -1; + const QList diagrams = m_project->diagrams(); + for (int i = 0 ; i < diagrams.count() ; ++i) { + if (diagrams.at(i)->uuid() == wanted) return i; + } + return -1; +} + int QetScriptApi::elementCount(int folioIndex) const { if (!m_project) return 0; diff --git a/sources/scripting/qetscriptapi.h b/sources/scripting/qetscriptapi.h index 533b70830..8aa634f01 100644 --- a/sources/scripting/qetscriptapi.h +++ b/sources/scripting/qetscriptapi.h @@ -353,6 +353,8 @@ class QetScriptApi : public QObject Q_INVOKABLE QString filePath() const; Q_INVOKABLE int folioCount() const; Q_INVOKABLE QString folioTitle(int index) const; + Q_INVOKABLE QString folioUuid(int index) const; + Q_INVOKABLE int folioIndex(const QString &uuid) const; Q_INVOKABLE int elementCount(int folioIndex) const; Q_INVOKABLE int conductorCount(int folioIndex) const;