diff --git a/doomsday/client/include/ui/styledlogsinkformatter.h b/doomsday/client/include/ui/styledlogsinkformatter.h index 54f14a0d5a..ba3a2d3aed 100644 --- a/doomsday/client/include/ui/styledlogsinkformatter.h +++ b/doomsday/client/include/ui/styledlogsinkformatter.h @@ -42,8 +42,7 @@ class StyledLogSinkFormatter : public de::LogSink::IFormatter void setOmitSectionIfNonDev(bool omit); private: - de::LogEntry::Flags _format; - bool _omitSectionIfNonDev; + DENG2_PRIVATE(d) }; #endif // DENG_CLIENT_STYLEDLOGSINKFORMATTER_H diff --git a/doomsday/client/modules/appconfig.de b/doomsday/client/modules/appconfig.de index d9ed41c62e..776bd4ea08 100644 --- a/doomsday/client/modules/appconfig.de +++ b/doomsday/client/modules/appconfig.de @@ -35,7 +35,8 @@ def setDefaults(d) gui.setDefaults(d) # Additional Log defaults. - d.log.filterBySubsystem = False + d.log.filterBySubsystem = False + d.log.showMetadata = False record d.alert d.alert.generic = Log.WARNING d.alert.resource = Log.WARNING diff --git a/doomsday/client/src/ui/dialogs/logsettingsdialog.cpp b/doomsday/client/src/ui/dialogs/logsettingsdialog.cpp index 7454c9a2d1..a6a222bfb8 100644 --- a/doomsday/client/src/ui/dialogs/logsettingsdialog.cpp +++ b/doomsday/client/src/ui/dialogs/logsettingsdialog.cpp @@ -48,6 +48,7 @@ DENG2_PIMPL(LogSettingsDialog) { ui::ListData levels; VariableToggleWidget *separately; + VariableToggleWidget *metadata; FoldPanelWidget *fold; GridLayout foldLayout; IndirectRule *columnWidth; ///< Sync column width in and out of the fold. @@ -72,6 +73,10 @@ DENG2_PIMPL(LogSettingsDialog) new VariableToggleWidget(tr("Filter by Subsystem"), App::config()["log.filterBySubsystem"])); + self.area().add(metadata = + new VariableToggleWidget(tr("Show Metadata"), + App::config()["log.showMetadata"])); + levels << new ChoiceItem( tr("1 - X.Verbose"), LogEntry::XVerbose) << new ChoiceItem( tr("2 - Verbose"), LogEntry::Verbose ) << new ChoiceItem( tr("3 - Message"), LogEntry::Message ) @@ -230,7 +235,9 @@ LogSettingsDialog::LogSettingsDialog(String const &name) << *d->domWidgets[0].alert << Const(0); layout.append(*d->separately, 3); - layout.append(*d->fold, 4); + layout.append(*d->fold, 4) + << Const(0); + layout.append(*d->metadata, 3); // Fold's layout is complete. d->fold->content().rule().setSize(d->foldLayout.width(), d->foldLayout.height()); diff --git a/doomsday/client/src/ui/styledlogsinkformatter.cpp b/doomsday/client/src/ui/styledlogsinkformatter.cpp index f9a28cfe9a..19cac3c8d9 100644 --- a/doomsday/client/src/ui/styledlogsinkformatter.cpp +++ b/doomsday/client/src/ui/styledlogsinkformatter.cpp @@ -17,29 +17,71 @@ */ #include "ui/styledlogsinkformatter.h" +#include +#include +#include using namespace de; -StyledLogSinkFormatter::StyledLogSinkFormatter() - : _omitSectionIfNonDev(true) +static char const *VAR_METADATA = "log.showMetadata"; + +DENG2_PIMPL(StyledLogSinkFormatter) +, DENG2_OBSERVES(Variable, Change) { - _format = LogEntry::Styled | LogEntry::OmitLevel; + LogEntry::Flags format; + bool observe; + bool omitSectionIfNonDev; + bool showMetadata; + + Instance(Public *i, bool observeVars) + : Base(i) + , observe(observeVars) + , omitSectionIfNonDev(true) + , showMetadata(false) + { + if(observe) + { + showMetadata = App::config().getb(VAR_METADATA); + App::config()[VAR_METADATA].audienceForChange() += this; + } + } -#ifndef _DEBUG - // No metadata in release builds. - _format |= LogEntry::Simple | LogEntry::OmitDomain; -#endif + ~Instance() + { + if(observe) + { + App::config()[VAR_METADATA].audienceForChange() -= this; + } + } + + void variableValueChanged(Variable &, Value const &newValue) + { + showMetadata = newValue.isTrue(); + } +}; + +StyledLogSinkFormatter::StyledLogSinkFormatter() + : d(new Instance(this, true /*observe*/)) +{ + d->format = LogEntry::Styled | LogEntry::OmitLevel; } StyledLogSinkFormatter::StyledLogSinkFormatter(LogEntry::Flags const &formatFlags) - : _format(formatFlags), - _omitSectionIfNonDev(true) -{} + : d(new Instance(this, false /*don't observe*/)) +{ + d->format = formatFlags; +} LogSink::IFormatter::Lines StyledLogSinkFormatter::logEntryToTextLines(LogEntry const &entry) { - LogEntry::Flags form = _format; - if(_omitSectionIfNonDev && !(entry.context() & LogEntry::Dev)) + LogEntry::Flags form = d->format; + + if(!d->showMetadata) + { + form |= LogEntry::Simple | LogEntry::OmitDomain; + } + + if(d->omitSectionIfNonDev && !(entry.context() & LogEntry::Dev)) { // The sections refer to names of native code functions, etc. // These are relevant only to developers. Non-dev messages must be @@ -54,5 +96,5 @@ LogSink::IFormatter::Lines StyledLogSinkFormatter::logEntryToTextLines(LogEntry void StyledLogSinkFormatter::setOmitSectionIfNonDev(bool omit) { - _omitSectionIfNonDev = omit; + d->omitSectionIfNonDev = omit; } diff --git a/doomsday/libdeng2/include/de/core/app.h b/doomsday/libdeng2/include/de/core/app.h index 9a5f1cda29..b8e7e1ca21 100644 --- a/doomsday/libdeng2/include/de/core/app.h +++ b/doomsday/libdeng2/include/de/core/app.h @@ -234,6 +234,8 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange) */ static Archive &persistentData(); + static bool hasPersistentData(); + /** * Returns the application's current native working directory. */ diff --git a/doomsday/libdeng2/include/de/core/logbuffer.h b/doomsday/libdeng2/include/de/core/logbuffer.h index d722dc5b08..614651bce1 100644 --- a/doomsday/libdeng2/include/de/core/logbuffer.h +++ b/doomsday/libdeng2/include/de/core/logbuffer.h @@ -150,12 +150,19 @@ class DENG2_PUBLIC LogBuffer : public QObject, public Lockable, DENG2_OBSERVES(F */ void enableFlushing(bool yes = true); + enum OutputChangeBehavior { + FlushFirstToOldOutputs, + DontFlush + }; + /** * Sets the path of the file used for writing log entries to. * - * @param path Path of the file. + * @param path Path of the file. + * @param behavior What to do with existing unflushed entries. */ - void setOutputFile(String const &path); + void setOutputFile(String const &path, + OutputChangeBehavior behavior = FlushFirstToOldOutputs); /** * Returns the path of the file used for log output. diff --git a/doomsday/libdeng2/src/core/app.cpp b/doomsday/libdeng2/src/core/app.cpp index ef0783a7b7..a33a3b3d2d 100644 --- a/doomsday/libdeng2/src/core/app.cpp +++ b/doomsday/libdeng2/src/core/app.cpp @@ -446,6 +446,11 @@ Archive &App::persistentData() return *persist; } +bool App::hasPersistentData() +{ + return DENG2_APP->d->persistentData != 0; +} + NativePath App::currentWorkPath() { return NativePath::workPath(); diff --git a/doomsday/libdeng2/src/core/config.cpp b/doomsday/libdeng2/src/core/config.cpp index a6e0b20d4e..d39b960dc5 100644 --- a/doomsday/libdeng2/src/core/config.cpp +++ b/doomsday/libdeng2/src/core/config.cpp @@ -93,19 +93,27 @@ void Config::read() LOG_DEBUG("Found serialized Config:\n") << names(); // If the saved config is from a different version, rerun the script. - Value const &oldVersion = names()["__version__"].value(); - d->setOldVersion(oldVersion); - if(oldVersion.compare(*version)) + if(names().has("__version__")) { - // Version mismatch: store the old version in a separate variable. - d->config.globals().add(new Variable("__oldversion__", oldVersion.duplicate(), + Value const &oldVersion = names()["__version__"].value(); + d->setOldVersion(oldVersion); + if(oldVersion.compare(*version)) + { + // Version mismatch: store the old version in a separate variable. + d->config.globals().add(new Variable("__oldversion__", oldVersion.duplicate(), Variable::AllowArray | Variable::ReadOnly)); - shouldRunScript = true; + shouldRunScript = true; + } + else + { + // Versions match. + LOG_MSG("") << d->refuge.path() << " matches version " << version->asText(); + } } else { - // Versions match. - LOG_MSG("") << d->refuge.path() << " matches version " << version->asText(); + // Don't know what version this is, run script to be sure. + shouldRunScript = true; } // Also check the timestamp of written config vs. the config script. diff --git a/doomsday/libdeng2/src/core/logbuffer.cpp b/doomsday/libdeng2/src/core/logbuffer.cpp index e77aade084..cd79d9fd9e 100644 --- a/doomsday/libdeng2/src/core/logbuffer.cpp +++ b/doomsday/libdeng2/src/core/logbuffer.cpp @@ -80,6 +80,7 @@ DENG2_PIMPL_NOREF(LogBuffer) , outSink(QtDebugMsg) , errSink(QtWarningMsg) #endif + , lastFlushedAt(Time::invalidTime()) , autoFlushTimer(0) { // Standard output enabled by default. @@ -96,6 +97,23 @@ DENG2_PIMPL_NOREF(LogBuffer) delete fileLogSink; } + void enableAutoFlush(bool yes) + { + DENG2_ASSERT(qApp); + if(yes) + { + if(!autoFlushTimer->isActive()) + { + // Every now and then the buffer will be flushed. + autoFlushTimer->start(FLUSH_INTERVAL * 1000); + } + } + else + { + autoFlushTimer->stop(); + } + } + void disposeFileLogSink() { if(fileLogSink) @@ -190,20 +208,13 @@ void LogBuffer::add(LogEntry *entry) // We will not flush the new entry as it likely has not yet been given // all its arguments. - if(d->lastFlushedAt.since() > FLUSH_INTERVAL) + if(d->lastFlushedAt.isValid() && d->lastFlushedAt.since() > FLUSH_INTERVAL) { flush(); } d->entries.push_back(entry); d->toBeFlushed.push_back(entry); - - // Should we start autoflush? - if(!d->autoFlushTimer->isActive() && qApp) - { - // Every now and then the buffer will be flushed. - d->autoFlushTimer->start(FLUSH_INTERVAL * 1000); - } } void LogBuffer::enableStandardOutput(bool yes) @@ -219,13 +230,18 @@ void LogBuffer::enableStandardOutput(bool yes) void LogBuffer::enableFlushing(bool yes) { d->flushingEnabled = yes; + d->enableAutoFlush(true); } -void LogBuffer::setOutputFile(String const &path) +void LogBuffer::setOutputFile(String const &path, OutputChangeBehavior behavior) { DENG2_GUARD(this); - flush(); + if(behavior == FlushFirstToOldOutputs) + { + flush(); + } + d->disposeFileLogSink(); if(d->outputFile) diff --git a/doomsday/libdeng2/src/data/refuge.cpp b/doomsday/libdeng2/src/data/refuge.cpp index 5098724dc4..80cd9e2d32 100644 --- a/doomsday/libdeng2/src/data/refuge.cpp +++ b/doomsday/libdeng2/src/data/refuge.cpp @@ -66,17 +66,27 @@ Refuge::~Refuge() void Refuge::read() { - Reader(App::persistentData().entryBlock(d->persistentPath)).withHeader() >> d->names; + if(App::hasPersistentData()) + { + Reader(App::persistentData().entryBlock(d->persistentPath)).withHeader() >> d->names; + } } void Refuge::write() const { - Writer(App::persistentData().entryBlock(d->persistentPath)).withHeader() << d->names; + if(App::hasPersistentData()) + { + Writer(App::persistentData().entryBlock(d->persistentPath)).withHeader() << d->names; + } } Time Refuge::lastWrittenAt() const { - return App::persistentData().entryStatus(d->persistentPath).modifiedAt; + if(App::hasPersistentData()) + { + return App::persistentData().entryStatus(d->persistentPath).modifiedAt; + } + return Time::invalidTime(); } Record &Refuge::names()