Make the font restoration counters safe against nested project loads

DialogWaiting pumps the event loop while the folios of a project are
built, so a second openAndAddProject() can run to completion nested
inside the first one (drop on another editor window, queued open) and
the plain reset/read counters would then report the wrong numbers.

Replace them with a RAII counting window (FontRestorationScope): the
constructor keeps the enclosing counts aside, the destructor restores
them. The nesting is strictly LIFO - the nested load completes inside
the pump of the outer one - so each load reports exactly its own
numbers, and the early-return paths of openAndAddProject() restore the
outer window automatically.

Suggested by ispyisail in the review of the reporting change.
This commit is contained in:
Dieter Mayer
2026-08-01 11:13:57 +02:00
parent 0fa2591c4f
commit 3bc2e8e712
3 changed files with 49 additions and 18 deletions
+6 -4
View File
@@ -1171,8 +1171,10 @@ bool QETDiagramEditor::openAndAddProject(
//Per-project window for the font counters reported below; the folios //Per-project window for the font counters reported below; the folios
//(and with them the stored font descriptions) are built between here //(and with them the stored font descriptions) are built between here
//and the end of addProject(). //and the end of addProject(). RAII, because DialogWaiting pumps the
QETUtils::resetFontRestorationCounters(); //event loop during the load: a nested openAndAddProject() gets its
//own window and this one resumes unharmed.
QETUtils::FontRestorationScope font_scope;
QETProject *project = new QETProject(filepath); QETProject *project = new QETProject(filepath);
if (project -> state() != QETProject::Ok) if (project -> state() != QETProject::Ok)
@@ -1203,8 +1205,8 @@ bool QETDiagramEditor::openAndAddProject(
//Report font descriptions which could not be read as-is (written by //Report font descriptions which could not be read as-is (written by
//an incompatible Qt version or corrupted), so the user learns about //an incompatible Qt version or corrupted), so the user learns about
//it from somewhere else than the console. See issue #553. //it from somewhere else than the console. See issue #553.
const int salvaged_fonts = QETUtils::salvagedFontCount(); const int salvaged_fonts = font_scope.salvaged();
const int unreadable_fonts = QETUtils::unreadableFontCount(); const int unreadable_fonts = font_scope.unreadable();
if (salvaged_fonts || unreadable_fonts) if (salvaged_fonts || unreadable_fonts)
{ {
qInfo().nospace() << "Project font descriptions: " qInfo().nospace() << "Project font descriptions: "
+20 -11
View File
@@ -304,33 +304,42 @@ bool QETUtils::fontFromString(QFont &font, const QString &description)
} }
/** /**
* @brief QETUtils::resetFontRestorationCounters * @brief QETUtils::FontRestorationScope::FontRestorationScope
* Reset the counters incremented by fontFromString(), to be called before * Open a fresh counting window: the enclosing window's counts are kept
* loading a project so the numbers reported afterwards are per-project. * aside and restored by the destructor, so a project load nested inside
* another one (through the event loop) reports its own numbers only.
*/ */
void QETUtils::resetFontRestorationCounters() QETUtils::FontRestorationScope::FontRestorationScope() :
m_outer_salvaged(salvaged_font_count),
m_outer_unreadable(unreadable_font_count)
{ {
salvaged_font_count = 0; salvaged_font_count = 0;
unreadable_font_count = 0; unreadable_font_count = 0;
} }
QETUtils::FontRestorationScope::~FontRestorationScope()
{
salvaged_font_count = m_outer_salvaged;
unreadable_font_count = m_outer_unreadable;
}
/** /**
* @brief QETUtils::salvagedFontCount * @brief QETUtils::FontRestorationScope::salvaged
* @return How many font descriptions fontFromString() restored from a * @return How many font descriptions fontFromString() restored from a
* foreign or corrupt format since the counters were last reset. Such * foreign or corrupt format since this window was opened. Such descriptions
* descriptions are rewritten in the stable format on the next save. * are rewritten in the stable format on the next save.
*/ */
int QETUtils::salvagedFontCount() int QETUtils::FontRestorationScope::salvaged() const
{ {
return salvaged_font_count; return salvaged_font_count;
} }
/** /**
* @brief QETUtils::unreadableFontCount * @brief QETUtils::FontRestorationScope::unreadable
* @return How many font descriptions fontFromString() could not restore at * @return How many font descriptions fontFromString() could not restore at
* all since the counters were last reset (the caller's default font applies). * all since this window was opened (the caller's default font applies).
*/ */
int QETUtils::unreadableFontCount() int QETUtils::FontRestorationScope::unreadable() const
{ {
return unreadable_font_count; return unreadable_font_count;
} }
+23 -3
View File
@@ -34,9 +34,29 @@ namespace QETUtils
void pixelSizedFont (QFont &font); void pixelSizedFont (QFont &font);
QString fontToString (const QFont &font); QString fontToString (const QFont &font);
bool fontFromString (QFont &font, const QString &description); bool fontFromString (QFont &font, const QString &description);
void resetFontRestorationCounters ();
int salvagedFontCount (); /**
int unreadableFontCount (); RAII counting window for the font descriptions fontFromString()
salvages or fails to read: construction opens a fresh window, the
destructor restores the enclosing one. Windows nest strictly LIFO,
which covers project loads re-entered through the event loop
(DialogWaiting pumps it while the folios are built).
*/
class FontRestorationScope
{
public:
FontRestorationScope();
~FontRestorationScope();
FontRestorationScope(const FontRestorationScope &) = delete;
FontRestorationScope &operator=(const FontRestorationScope &) = delete;
int salvaged() const;
int unreadable() const;
private:
int m_outer_salvaged;
int m_outer_unreadable;
};
bool sortBeginIntString(const QString &str_a, const QString &str_b); bool sortBeginIntString(const QString &str_a, const QString &str_b);