From 90209edd1a681bca6492b5765f09c6e0bef3707d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= Date: Sun, 2 Mar 2014 19:41:34 +0200 Subject: [PATCH] Refactor|libdeng2: Pimpl-friendly audiences Defined a new set of audience macros that can be used in a pimpl friendly manner, i.e., without public member variables. They require adding accessor methods for the audiences, though (using new macros). Applied the pimpl idiom to Asset, Clock, File, and Variable. --- doomsday/libdeng2/include/de/core/app.h | 6 +- doomsday/libdeng2/include/de/core/asset.h | 7 +- doomsday/libdeng2/include/de/core/clock.h | 5 +- doomsday/libdeng2/include/de/core/loop.h | 2 +- doomsday/libdeng2/include/de/data/bank.h | 4 +- .../libdeng2/include/de/data/escapeparser.h | 4 +- doomsday/libdeng2/include/de/data/observers.h | 72 +++++++++ doomsday/libdeng2/include/de/data/property.h | 4 +- doomsday/libdeng2/include/de/data/record.h | 2 +- doomsday/libdeng2/include/de/data/variable.h | 21 ++- doomsday/libdeng2/include/de/filesys/file.h | 36 +---- doomsday/libdeng2/include/de/widgets/action.h | 7 +- doomsday/libdeng2/include/de/widgets/widget.h | 8 +- doomsday/libdeng2/src/core/app.cpp | 14 +- doomsday/libdeng2/src/core/asset.cpp | 47 ++++-- doomsday/libdeng2/src/core/clock.cpp | 24 ++- doomsday/libdeng2/src/core/logbuffer.cpp | 4 +- doomsday/libdeng2/src/core/loop.cpp | 6 +- .../src/core/monospacelogsinkformatter.cpp | 4 +- doomsday/libdeng2/src/core/textapp.cpp | 2 +- doomsday/libdeng2/src/data/bank.cpp | 17 ++- doomsday/libdeng2/src/data/escapeparser.cpp | 12 +- doomsday/libdeng2/src/data/record.cpp | 16 +- doomsday/libdeng2/src/data/recordvalue.cpp | 8 +- doomsday/libdeng2/src/data/refvalue.cpp | 4 +- doomsday/libdeng2/src/data/variable.cpp | 124 ++++++++++----- .../libdeng2/src/filesys/archiveentryfile.cpp | 4 +- doomsday/libdeng2/src/filesys/archivefeed.cpp | 6 +- doomsday/libdeng2/src/filesys/file.cpp | 141 +++++++++++++----- doomsday/libdeng2/src/filesys/folder.cpp | 4 +- doomsday/libdeng2/src/filesys/libraryfile.cpp | 4 +- doomsday/libdeng2/src/filesys/nativefile.cpp | 4 +- .../libdeng2/src/filesys/packagefolder.cpp | 4 +- doomsday/libdeng2/src/scriptsys/function.cpp | 4 +- .../libdeng2/src/scriptsys/scriptsystem.cpp | 4 +- doomsday/libdeng2/src/widgets/action.cpp | 12 +- doomsday/libdeng2/src/widgets/widget.cpp | 22 ++- 37 files changed, 457 insertions(+), 212 deletions(-) diff --git a/doomsday/libdeng2/include/de/core/app.h b/doomsday/libdeng2/include/de/core/app.h index 8ed456c5c7..db3c5db033 100644 --- a/doomsday/libdeng2/include/de/core/app.h +++ b/doomsday/libdeng2/include/de/core/app.h @@ -69,17 +69,17 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange) /** * Notified when application startup has been fully completed. */ - DENG2_DEFINE_AUDIENCE(StartupComplete, void appStartupCompleted()) + DENG2_DEFINE_AUDIENCE2(StartupComplete, void appStartupCompleted()) /** * Notified before the current game is unloaded. */ - DENG2_DEFINE_AUDIENCE(GameUnload, void aboutToUnloadGame(game::Game const &gameBeingUnloaded)) + DENG2_DEFINE_AUDIENCE2(GameUnload, void aboutToUnloadGame(game::Game const &gameBeingUnloaded)) /** * Notified after the current game has been changed. */ - DENG2_DEFINE_AUDIENCE(GameChange, void currentGameChanged(game::Game const &newGame)) + DENG2_DEFINE_AUDIENCE2(GameChange, void currentGameChanged(game::Game const &newGame)) public: /** diff --git a/doomsday/libdeng2/include/de/core/asset.h b/doomsday/libdeng2/include/de/core/asset.h index eec86d9913..ec782f5dc8 100644 --- a/doomsday/libdeng2/include/de/core/asset.h +++ b/doomsday/libdeng2/include/de/core/asset.h @@ -51,15 +51,16 @@ class DENG2_PUBLIC Asset /** * Notified whenever the state of the asset changes. */ - DENG2_DEFINE_AUDIENCE(StateChange, void assetStateChanged(Asset &)) + DENG2_DEFINE_AUDIENCE2(StateChange, void assetStateChanged(Asset &)) /** * Notified when the asset is destroyed. */ - DENG2_DEFINE_AUDIENCE(Deletion, void assetDeleted(Asset &)) + DENG2_DEFINE_AUDIENCE2(Deletion, void assetDeleted(Asset &)) public: Asset(State initialState = NotReady); + Asset(Asset const &other); virtual ~Asset(); void setState(State s); @@ -72,7 +73,7 @@ class DENG2_PUBLIC Asset virtual bool isReady() const; private: - State _state; + DENG2_PRIVATE(d) }; /** diff --git a/doomsday/libdeng2/include/de/core/clock.h b/doomsday/libdeng2/include/de/core/clock.h index 57bf0bd3a1..87e06b24a6 100644 --- a/doomsday/libdeng2/include/de/core/clock.h +++ b/doomsday/libdeng2/include/de/core/clock.h @@ -36,7 +36,7 @@ class DENG2_PUBLIC Clock * Notified whenever the time of the clock changes. The audience members * will be notified in unspecified order. */ - DENG2_DEFINE_AUDIENCE(TimeChange, void timeChanged(Clock const &)) + DENG2_DEFINE_AUDIENCE2(TimeChange, void timeChanged(Clock const &)) /** * Notified whenever the time of the clock changes. The entire priority @@ -73,8 +73,7 @@ class DENG2_PUBLIC Clock static Time const &appTime(); private: - Time _startedAt; - Time _time; + DENG2_PRIVATE(d) static Clock *_appClock; }; diff --git a/doomsday/libdeng2/include/de/core/loop.h b/doomsday/libdeng2/include/de/core/loop.h index a4932244a5..27d9df7d0f 100644 --- a/doomsday/libdeng2/include/de/core/loop.h +++ b/doomsday/libdeng2/include/de/core/loop.h @@ -39,7 +39,7 @@ class DENG2_PUBLIC Loop : public QObject /** * Audience to be notified each time the loop iterates. */ - DENG2_DEFINE_AUDIENCE(Iteration, void loopIteration()) + DENG2_DEFINE_AUDIENCE2(Iteration, void loopIteration()) public: /** diff --git a/doomsday/libdeng2/include/de/data/bank.h b/doomsday/libdeng2/include/de/data/bank.h index 84529de04e..4b1d1df0a7 100644 --- a/doomsday/libdeng2/include/de/data/bank.h +++ b/doomsday/libdeng2/include/de/data/bank.h @@ -176,13 +176,13 @@ class DENG2_PUBLIC Bank * Notified when a data item has been loaded to memory (cache level * InMemory). May be called from the background thread, if one is running. */ - DENG2_DEFINE_AUDIENCE(Load, void bankLoaded(DotPath const &path)) + DENG2_DEFINE_AUDIENCE2(Load, void bankLoaded(DotPath const &path)) /** * Notified when a data item's cache level changes (in addition to the Load * notification). */ - DENG2_DEFINE_AUDIENCE(CacheLevel, void bankCacheLevelChanged(DotPath const &path, CacheLevel level)) + DENG2_DEFINE_AUDIENCE2(CacheLevel, void bankCacheLevelChanged(DotPath const &path, CacheLevel level)) public: /** diff --git a/doomsday/libdeng2/include/de/data/escapeparser.h b/doomsday/libdeng2/include/de/data/escapeparser.h index 4dcb0cd0c9..f606bba1c6 100644 --- a/doomsday/libdeng2/include/de/data/escapeparser.h +++ b/doomsday/libdeng2/include/de/data/escapeparser.h @@ -38,7 +38,7 @@ class DENG2_PUBLIC EscapeParser * * @param range Range in the original text. */ - DENG2_DEFINE_AUDIENCE(PlainText, void handlePlainText(Rangei const &range)) + DENG2_DEFINE_AUDIENCE2(PlainText, void handlePlainText(Rangei const &range)) /** * Called during parsing when an escape sequence has been parsed. @@ -46,7 +46,7 @@ class DENG2_PUBLIC EscapeParser * * @param range Range in the original text. */ - DENG2_DEFINE_AUDIENCE(EscapeSequence, void handleEscapeSequence(Rangei const &range)) + DENG2_DEFINE_AUDIENCE2(EscapeSequence, void handleEscapeSequence(Rangei const &range)) public: EscapeParser(); diff --git a/doomsday/libdeng2/include/de/data/observers.h b/doomsday/libdeng2/include/de/data/observers.h index dee0ea4ade..9c06c404da 100644 --- a/doomsday/libdeng2/include/de/data/observers.h +++ b/doomsday/libdeng2/include/de/data/observers.h @@ -61,6 +61,24 @@ typedef de::Observers Name##Audience; \ extern Name##Audience audienceFor##Name; +#define DENG2_DECLARE_AUDIENCE_METHOD(Name) \ + typedef de::Observers Name##Audience; \ + Name##Audience &audienceFor##Name(); \ + Name##Audience const &audienceFor##Name() const; + +#define DENG2_AUDIENCE_METHOD(ClassName, Name) \ + ClassName::Name##Audience &ClassName::audienceFor##Name() { return d->audienceFor##Name; } \ + ClassName::Name##Audience const &ClassName::audienceFor##Name() const { return d->audienceFor##Name; } + +#define DENG2_AUDIENCE_METHOD_INLINE(Name) \ + typedef de::Observers Name##Audience; \ + Name##Audience _audienceFor##Name; \ + Name##Audience &audienceFor##Name() { return _audienceFor##Name; } \ + Name##Audience const &audienceFor##Name() const { return _audienceFor##Name; } + +#define DENG2_PIMPL_AUDIENCE(Name) \ + Name##Audience audienceFor##Name; + /** * Macro for defining an observer interface containing a single method. * @@ -73,6 +91,14 @@ DENG2_DECLARE_AUDIENCE(Name, Method) \ DENG2_AUDIENCE(Name) +#define DENG2_DEFINE_AUDIENCE2(Name, Method) \ + DENG2_DECLARE_AUDIENCE(Name, Method) \ + DENG2_DECLARE_AUDIENCE_METHOD(Name) + +#define DENG2_DEFINE_AUDIENCE_INLINE(Name, Method) \ + DENG2_DECLARE_AUDIENCE(Name, Method) \ + DENG2_AUDIENCE_METHOD_INLINE(Name) + /** * Macro that can be used in class declarations to specify which audiences the class * can belong to. @@ -101,6 +127,9 @@ #define DENG2_FOR_AUDIENCE(Name, Var) \ DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name) +#define DENG2_FOR_AUDIENCE2(Name, Var) \ + DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name()) + /** * Macro for looping through the public audience members from inside a private * implementation. @@ -111,12 +140,55 @@ #define DENG2_FOR_PUBLIC_AUDIENCE(Name, Var) \ DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, self.audienceFor##Name) +#define DENG2_FOR_PUBLIC_AUDIENCE2(Name, Var) \ + DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name) + namespace de { /** * Template for observer sets. The template type should be an interface * implemented by all the observers. @ingroup data * + * @par How to use the non-pimpl audience macros + * + * These examples explain how to create an audience called "Deletion". In general, + * audience names should be nouns like this so they can be used in the form + * "audience for (something)". + * + * In a class declaration, define the audience in the @c public section of the class: + *
DENG2_DEFINE_AUDIENCE(Deletion, ...interface-function...)
+ * + * This will generate a public member variable called @c audienceForDeletion that + * can be directly manipulated by other objects. + * + * Note that because the audience is created as a public member variable, this can + * easily lead to ABI backwards compatibility issues down the road if there is + * need to make changes to the class. + * + * @par How to use the pimpl audience macros + * + * Another set of macros is provided for declaring and defining a pimpl-friendly + * audience. The caveat is that you'll need to separately declare accessor methods + * and define the audience inside the private implementation of the class. + * + * First, define the audience in the @c public section of the class: + *
DENG2_DEFINE_AUDIENCE2(Deletion, ...interface-function...)
+ * + * This works like DENG2_DEFINE_AUDIENCE, but without a public member variable. + * Instead, accessor methods are declared for accessing the audience. + * + * Then, inside the private implementation (@c Instance struct), define the audience: + *
DENG2_PIMPL_AUDIENCE(Deletion)
+ * + * Finally, define the accessor methods (for instance, just before the constructor + * of the class): + *
DENG2_AUDIENCE_METHOD(ClassName, Deletion)
+ * + * It is recommended to keep the DENG2_PIMPL_AUDIENCE and DENG2_AUDIENCE_METHOD + * macros close together in the source file for easier maintenance. The former + * could be at the end of the @c Instance struct while the latter is immediately + * following the struct. + * * @par Thread-safety * * Observers and Observers::Loop lock the observer set separately for reading diff --git a/doomsday/libdeng2/include/de/data/property.h b/doomsday/libdeng2/include/de/data/property.h index fc04d818e4..7204d4ab55 100644 --- a/doomsday/libdeng2/include/de/data/property.h +++ b/doomsday/libdeng2/include/de/data/property.h @@ -69,12 +69,12 @@ class BaseProperty void setValue(ValueType const &v) { \ if(_value == v) return; \ _value = v; \ - DENG2_FOR_AUDIENCE(Change, i) i->valueOf ## PropName ## Changed(); \ + DENG2_FOR_AUDIENCE2(Change, i) i->valueOf ## PropName ## Changed(); \ } \ PropName &operator = (ValueType const &v) { setValue(v); return *this; } \ PropName &operator += (ValueType const &v) { setValue(_value + v); return *this; } \ PropName &operator -= (ValueType const &v) { setValue(_value - v); return *this; } \ - DENG2_DEFINE_AUDIENCE(Change, void valueOf ## PropName ## Changed()) \ + DENG2_DEFINE_AUDIENCE_INLINE(Change, void valueOf ## PropName ## Changed()) \ }; #define DENG2_PROPERTY(PropName, ValueType) \ diff --git a/doomsday/libdeng2/include/de/data/record.h b/doomsday/libdeng2/include/de/data/record.h index 625733fcb4..232eae6ffd 100644 --- a/doomsday/libdeng2/include/de/data/record.h +++ b/doomsday/libdeng2/include/de/data/record.h @@ -63,7 +63,7 @@ class DENG2_PUBLIC Record : public ISerializable, public LogEntry::Arg::Base, typedef std::pair KeyValue; typedef QList List; - DENG2_DEFINE_AUDIENCE(Deletion, void recordBeingDeleted(Record &record)) + DENG2_DEFINE_AUDIENCE2(Deletion, void recordBeingDeleted(Record &record)) public: Record(); diff --git a/doomsday/libdeng2/include/de/data/variable.h b/doomsday/libdeng2/include/de/data/variable.h index 0fc5d6e962..212558cfbd 100644 --- a/doomsday/libdeng2/include/de/data/variable.h +++ b/doomsday/libdeng2/include/de/data/variable.h @@ -126,7 +126,7 @@ class DENG2_PUBLIC Variable : public ISerializable /** * Returns the name of the variable. */ - String const &name() const { return _name; } + String const &name() const; /** * Sets the value of the variable. @@ -159,12 +159,15 @@ class DENG2_PUBLIC Variable : public ISerializable */ Value &value(); + Value *valuePtr(); + Value const *valuePtr() const; + /** * Returns the value of the variable. */ template Type &value() { - Type *v = dynamic_cast(_value); + Type *v = dynamic_cast(valuePtr()); if(!v) { /// @throw TypeError Casting to Type failed. throw TypeError("Variable::value", @@ -193,7 +196,7 @@ class DENG2_PUBLIC Variable : public ISerializable */ template Type const &value() const { - Type const *v = dynamic_cast(_value); + Type const *v = dynamic_cast(valuePtr()); if(!v) { /// @throw TypeError Casting to Type failed. throw TypeError("Variable::value", @@ -265,7 +268,7 @@ class DENG2_PUBLIC Variable : public ISerializable * * @param variable Variable. */ - DENG2_DEFINE_AUDIENCE(Deletion, void variableBeingDeleted(Variable &variable)) + DENG2_DEFINE_AUDIENCE2(Deletion, void variableBeingDeleted(Variable &variable)) /** * The value of the variable has changed. @@ -273,16 +276,10 @@ class DENG2_PUBLIC Variable : public ISerializable * @param variable Variable. * @param newValue New value of the variable. */ - DENG2_DEFINE_AUDIENCE(Change, void variableValueChanged(Variable &variable, Value const &newValue)) + DENG2_DEFINE_AUDIENCE2(Change, void variableValueChanged(Variable &variable, Value const &newValue)) private: - String _name; - - /// Value of the variable. - Value *_value; - - /// Mode flags. - Flags _mode; + DENG2_PRIVATE(d) }; Q_DECLARE_OPERATORS_FOR_FLAGS(Variable::Flags) diff --git a/doomsday/libdeng2/include/de/filesys/file.h b/doomsday/libdeng2/include/de/filesys/file.h index 630ca84d55..7982b81d52 100644 --- a/doomsday/libdeng2/include/de/filesys/file.h +++ b/doomsday/libdeng2/include/de/filesys/file.h @@ -82,7 +82,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream * * @param file The file object being deleted. */ - DENG2_DEFINE_AUDIENCE(Deletion, void fileBeingDeleted(File const &file)) + DENG2_DEFINE_AUDIENCE2(Deletion, void fileBeingDeleted(File const &file)) /** * Stores the status of a file (size, time of last modification). @@ -187,7 +187,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream static FileSystem &fileSystem(); /// Returns the name of the file. - String const &name() const { return _name; } + String const &name() const; /** * Returns a textual description of the file, intended only for humans. @@ -210,12 +210,12 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream /** * Sets the parent folder of this file. */ - void setParent(Folder *parent) { _parent = parent; } + void setParent(Folder *parent); /** * Returns the parent folder. May be NULL. */ - Folder *parent() const { return _parent; } + Folder *parent() const; /** * Sets the origin Feed of the File. The origin feed is the feed that is able @@ -233,7 +233,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream * Returns the origin Feed of the File. * @see setOriginFeed() */ - Feed *originFeed() const { return _originFeed; } + Feed *originFeed() const; /** * Sets the source file of this file. The source is where this file is @@ -306,10 +306,10 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream virtual void setMode(Flags const &newMode); /// Returns the file information (const). - Record const &info() const { return _info; } + Record const &info() const; /// Returns the file information. - Record &info() { return _info; } + Record &info(); /** * Makes sure that the file has write access. @@ -333,27 +333,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream explicit File(String const &name = ""); private: - /// The parent folder. - Folder *_parent; - - /// The source file (NULL for non-interpreted files). - File *_source; - - /// Feed that generated the file. This feed is called upon when the file needs - /// to be pruned. May also be NULL. - Feed *_originFeed; - - /// Name of the file. - String _name; - - /// Status of the file. - Status _status; - - /// Mode flags. - Flags _mode; - - /// File information. - Record _info; + DENG2_PRIVATE(d) }; Q_DECLARE_OPERATORS_FOR_FLAGS(File::Flags) diff --git a/doomsday/libdeng2/include/de/widgets/action.h b/doomsday/libdeng2/include/de/widgets/action.h index e353e7a58b..287a09d3b3 100644 --- a/doomsday/libdeng2/include/de/widgets/action.h +++ b/doomsday/libdeng2/include/de/widgets/action.h @@ -39,7 +39,9 @@ class DENG2_PUBLIC Action : public Counted /** * Audience to be notified when the action is triggerd. */ - DENG2_DEFINE_AUDIENCE(Triggered, void actionTriggered(Action &)) + DENG2_DEFINE_AUDIENCE2(Triggered, void actionTriggered(Action &)) + + Action(); /** * Perform the action this instance represents. Derived classes must call @@ -52,6 +54,9 @@ class DENG2_PUBLIC Action : public Counted protected: virtual ~Action(); // ref counted, hence not publicly deletable + +private: + DENG2_PRIVATE(d) }; } // namespace de diff --git a/doomsday/libdeng2/include/de/widgets/widget.h b/doomsday/libdeng2/include/de/widgets/widget.h index eef4b5319b..ac532f5684 100644 --- a/doomsday/libdeng2/include/de/widgets/widget.h +++ b/doomsday/libdeng2/include/de/widgets/widget.h @@ -85,22 +85,22 @@ class DENG2_PUBLIC Widget /** * Notified when the widget is about to be deleted. */ - DENG2_DEFINE_AUDIENCE(Deletion, void widgetBeingDeleted(Widget &widget)) + DENG2_DEFINE_AUDIENCE2(Deletion, void widgetBeingDeleted(Widget &widget)) /** * Notified when the widget's parent changes. */ - DENG2_DEFINE_AUDIENCE(ParentChange, void widgetParentChanged(Widget &child, Widget *oldParent, Widget *newParent)) + DENG2_DEFINE_AUDIENCE2(ParentChange, void widgetParentChanged(Widget &child, Widget *oldParent, Widget *newParent)) /** * Notified when a child is added to the widget. */ - DENG2_DEFINE_AUDIENCE(ChildAddition, void widgetChildAdded(Widget &child)) + DENG2_DEFINE_AUDIENCE2(ChildAddition, void widgetChildAdded(Widget &child)) /** * Notified after a child has been removed from the widget. */ - DENG2_DEFINE_AUDIENCE(ChildRemoval, void widgetChildRemoved(Widget &child)) + DENG2_DEFINE_AUDIENCE2(ChildRemoval, void widgetChildRemoved(Widget &child)) public: Widget(String const &name = ""); diff --git a/doomsday/libdeng2/src/core/app.cpp b/doomsday/libdeng2/src/core/app.cpp index fc6ac798ce..f323b09762 100644 --- a/doomsday/libdeng2/src/core/app.cpp +++ b/doomsday/libdeng2/src/core/app.cpp @@ -136,12 +136,12 @@ DENG2_PIMPL(App) appModule.addArray("audienceForGameChange"); // game change observers scriptSys.addNativeModule("App", appModule); - self.audienceForGameChange += scriptAudienceForGameChange; + audienceForGameChange += scriptAudienceForGameChange; } ~Instance() { - clock.audienceForTimeChange -= self; + clock.audienceForTimeChange() -= self; if(config) { @@ -252,8 +252,16 @@ DENG2_PIMPL(App) logFilter.setAllowDev(LogEntry::AllDomains, false); } } + + DENG2_PIMPL_AUDIENCE(StartupComplete) + DENG2_PIMPL_AUDIENCE(GameUnload) + DENG2_PIMPL_AUDIENCE(GameChange) }; +DENG2_AUDIENCE_METHOD(App, StartupComplete) +DENG2_AUDIENCE_METHOD(App, GameUnload) +DENG2_AUDIENCE_METHOD(App, GameChange) + App::App(NativePath const &appFilePath, QStringList args) : d(new Instance(this, args)) { @@ -560,7 +568,7 @@ void App::initSubsystems(SubsystemInitFlags flags) d->clock.setTime(Time::currentHighPerformanceTime()); // Now we can start observing progress of time. - d->clock.audienceForTimeChange += this; + d->clock.audienceForTimeChange() += this; LOG_VERBOSE("libdeng2::App %s subsystems initialized.") << Version().asText(); } diff --git a/doomsday/libdeng2/src/core/asset.cpp b/doomsday/libdeng2/src/core/asset.cpp index 49164ed71f..b6312ebb07 100644 --- a/doomsday/libdeng2/src/core/asset.cpp +++ b/doomsday/libdeng2/src/core/asset.cpp @@ -20,21 +20,38 @@ namespace de { -Asset::Asset(State initialState) : _state(initialState) +DENG2_PIMPL_NOREF(Asset) +{ + State state; + + Instance(State s) : state(s) {} + Instance(Instance const &other) : state(other.state) {} + + DENG2_PIMPL_AUDIENCE(StateChange) + DENG2_PIMPL_AUDIENCE(Deletion) +}; + +DENG2_AUDIENCE_METHOD(Asset, StateChange) +DENG2_AUDIENCE_METHOD(Asset, Deletion) + +Asset::Asset(State initialState) : d(new Instance(initialState)) +{} + +Asset::Asset(Asset const &other) : d(new Instance(*other.d)) {} Asset::~Asset() { - DENG2_FOR_AUDIENCE(Deletion, i) i->assetDeleted(*this); + DENG2_FOR_AUDIENCE2(Deletion, i) i->assetDeleted(*this); } void Asset::setState(State s) { - State old = _state; - _state = s; - if(old != _state) + State old = d->state; + d->state = s; + if(old != d->state) { - DENG2_FOR_AUDIENCE(StateChange, i) i->assetStateChanged(*this); + DENG2_FOR_AUDIENCE2(StateChange, i) i->assetStateChanged(*this); } } @@ -45,12 +62,12 @@ void Asset::setState(bool assetReady) Asset::State Asset::state() const { - return _state; + return d->state; } bool Asset::isReady() const { - return _state == Ready; + return d->state == Ready; } //---------------------------------------------------------------------------- @@ -95,7 +112,7 @@ AssetGroup::AssetGroup() : d(new Instance) AssetGroup::~AssetGroup() { // We are about to be deleted. - audienceForStateChange.clear(); + audienceForStateChange().clear(); clear(); } @@ -109,8 +126,8 @@ void AssetGroup::clear() { DENG2_FOR_EACH(Members, i, d->deps) { - i->first->audienceForDeletion -= this; - i->first->audienceForStateChange -= this; + i->first->audienceForDeletion() -= this; + i->first->audienceForStateChange() -= this; } d->deps.clear(); @@ -120,15 +137,15 @@ void AssetGroup::clear() void AssetGroup::insert(Asset const &asset, Policy policy) { d->deps[&asset] = policy; - asset.audienceForDeletion += this; - asset.audienceForStateChange += this; + asset.audienceForDeletion() += this; + asset.audienceForStateChange() += this; d->update(*this); } void AssetGroup::remove(Asset const &asset) { - asset.audienceForDeletion -= this; - asset.audienceForStateChange -= this; + asset.audienceForDeletion() -= this; + asset.audienceForStateChange() -= this; d->deps.erase(&asset); d->update(*this); } diff --git a/doomsday/libdeng2/src/core/clock.cpp b/doomsday/libdeng2/src/core/clock.cpp index 381317c17a..0d9ac59e07 100644 --- a/doomsday/libdeng2/src/core/clock.cpp +++ b/doomsday/libdeng2/src/core/clock.cpp @@ -20,9 +20,19 @@ namespace de { +DENG2_PIMPL_NOREF(Clock) +{ + Time startedAt; + Time time; + + DENG2_PIMPL_AUDIENCE(TimeChange) +}; + +DENG2_AUDIENCE_METHOD(Clock, TimeChange) + Clock *Clock::_appClock = 0; -Clock::Clock() +Clock::Clock() : d(new Instance) {} Clock::~Clock() @@ -30,9 +40,9 @@ Clock::~Clock() void Clock::setTime(Time const ¤tTime) { - bool changed = (_time != currentTime); + bool changed = (d->time != currentTime); - _time = currentTime; + d->time = currentTime; if(changed) { @@ -40,23 +50,23 @@ void Clock::setTime(Time const ¤tTime) { i->timeChanged(*this); } - DENG2_FOR_AUDIENCE(TimeChange, i) i->timeChanged(*this); + DENG2_FOR_AUDIENCE2(TimeChange, i) i->timeChanged(*this); } } void Clock::advanceTime(TimeDelta const &span) { - setTime(_time + span); + setTime(d->time + span); } TimeDelta Clock::elapsed() const { - return _time - _startedAt; + return d->time - d->startedAt; } Time const &Clock::time() const { - return _time; + return d->time; } void Clock::setAppClock(Clock *c) diff --git a/doomsday/libdeng2/src/core/logbuffer.cpp b/doomsday/libdeng2/src/core/logbuffer.cpp index 2bd0e6cbbe..e77aade084 100644 --- a/doomsday/libdeng2/src/core/logbuffer.cpp +++ b/doomsday/libdeng2/src/core/logbuffer.cpp @@ -230,7 +230,7 @@ void LogBuffer::setOutputFile(String const &path) if(d->outputFile) { - d->outputFile->audienceForDeletion -= this; + d->outputFile->audienceForDeletion() -= this; d->outputFile = 0; } @@ -238,7 +238,7 @@ void LogBuffer::setOutputFile(String const &path) { d->outputFile = &App::rootFolder().replaceFile(path); d->outputFile->setMode(File::Write); - d->outputFile->audienceForDeletion += this; + d->outputFile->audienceForDeletion() += this; // Add a sink for the file. d->fileLogSink = new FileLogSink(*d->outputFile); diff --git a/doomsday/libdeng2/src/core/loop.cpp b/doomsday/libdeng2/src/core/loop.cpp index 5bac69d247..456186f34c 100644 --- a/doomsday/libdeng2/src/core/loop.cpp +++ b/doomsday/libdeng2/src/core/loop.cpp @@ -50,8 +50,12 @@ DENG2_PIMPL(Loop) { loopSingleton = 0; } + + DENG2_PIMPL_AUDIENCE(Iteration) }; +DENG2_AUDIENCE_METHOD(Loop, Iteration) + Loop::Loop() : d(new Instance(this)) {} @@ -102,7 +106,7 @@ void Loop::nextLoopIteration() { if(d->running) { - DENG2_FOR_AUDIENCE(Iteration, i) i->loopIteration(); + DENG2_FOR_AUDIENCE2(Iteration, i) i->loopIteration(); } } catch(Error const &er) diff --git a/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp b/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp index d11f5b7ac8..4df429fb24 100644 --- a/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp +++ b/doomsday/libdeng2/src/core/monospacelogsinkformatter.cpp @@ -40,8 +40,8 @@ struct TabFiller TabFiller(String const &text) : hasTabs(false) { - esc.audienceForPlainText += this; - esc.audienceForEscapeSequence += this; + esc.audienceForPlainText() += this; + esc.audienceForEscapeSequence() += this; // Break the entire message into lines, excluding all escape codes // except for tabs. diff --git a/doomsday/libdeng2/src/core/textapp.cpp b/doomsday/libdeng2/src/core/textapp.cpp index f908bbbc82..55b504a335 100644 --- a/doomsday/libdeng2/src/core/textapp.cpp +++ b/doomsday/libdeng2/src/core/textapp.cpp @@ -29,7 +29,7 @@ DENG2_PIMPL(TextApp) Instance(Public *i) : Base(i) { - loop.audienceForIteration += self; + loop.audienceForIteration() += self; // In text-based apps, we can limit the loop frequency. loop.setRate(35); diff --git a/doomsday/libdeng2/src/data/bank.cpp b/doomsday/libdeng2/src/data/bank.cpp index ceadea9b78..22bf2bfaa3 100644 --- a/doomsday/libdeng2/src/data/bank.cpp +++ b/doomsday/libdeng2/src/data/bank.cpp @@ -111,6 +111,7 @@ class Cache : public Lockable } // namespace internal + DENG2_PIMPL(Bank), DENG2_OBSERVES(Loop, Iteration) // notifications from other threads sent via main Loop { @@ -545,7 +546,7 @@ DENG2_OBSERVES(Loop, Iteration) // notifications from other threads sent via mai ~Instance() { - Loop::appLoop().audienceForIteration -= this; + Loop::appLoop().audienceForIteration() -= this; destroySerialCache(); } @@ -669,13 +670,13 @@ DENG2_OBSERVES(Loop, Iteration) // notifications from other threads sent via mai notifications.put(new Notification(notif)); if(isThreaded()) { - Loop::appLoop().audienceForIteration += this; + Loop::appLoop().audienceForIteration() += this; } } void loopIteration() { - Loop::appLoop().audienceForIteration -= this; + Loop::appLoop().audienceForIteration() -= this; performNotifications(); } @@ -695,14 +696,14 @@ DENG2_OBSERVES(Loop, Iteration) // notifications from other threads sent via mai switch(nt.kind) { case Notification::Loaded: - DENG2_FOR_PUBLIC_AUDIENCE(Load, i) + DENG2_FOR_PUBLIC_AUDIENCE2(Load, i) { i->bankLoaded(nt.path); } break; case Notification::CacheChanged: - DENG2_FOR_PUBLIC_AUDIENCE(CacheLevel, i) + DENG2_FOR_PUBLIC_AUDIENCE2(CacheLevel, i) { DENG2_ASSERT(nt.cache != 0); @@ -714,8 +715,14 @@ DENG2_OBSERVES(Loop, Iteration) // notifications from other threads sent via mai break; } } + + DENG2_PIMPL_AUDIENCE(Load) + DENG2_PIMPL_AUDIENCE(CacheLevel) }; +DENG2_AUDIENCE_METHOD(Bank, Load) +DENG2_AUDIENCE_METHOD(Bank, CacheLevel) + Bank::Bank(Flags const &flags, String const &hotStorageLocation) : d(new Instance(this, flags)) { diff --git a/doomsday/libdeng2/src/data/escapeparser.cpp b/doomsday/libdeng2/src/data/escapeparser.cpp index 2305015a96..afd59709b9 100644 --- a/doomsday/libdeng2/src/data/escapeparser.cpp +++ b/doomsday/libdeng2/src/data/escapeparser.cpp @@ -24,8 +24,14 @@ DENG2_PIMPL_NOREF(EscapeParser) { String original; String plain; + + DENG2_PIMPL_AUDIENCE(PlainText) + DENG2_PIMPL_AUDIENCE(EscapeSequence) }; +DENG2_AUDIENCE_METHOD(EscapeParser, PlainText) +DENG2_AUDIENCE_METHOD(EscapeParser, EscapeSequence) + EscapeParser::EscapeParser() : d(new Instance) {} @@ -44,7 +50,7 @@ void EscapeParser::parse(String const &textWithEscapes) // Empty ranges are ignored. if(range.size() > 0) { - DENG2_FOR_AUDIENCE(PlainText, i) + DENG2_FOR_AUDIENCE2(PlainText, i) { i->handlePlainText(range); } @@ -75,7 +81,7 @@ void EscapeParser::parse(String const &textWithEscapes) break; } - DENG2_FOR_AUDIENCE(EscapeSequence, i) + DENG2_FOR_AUDIENCE2(EscapeSequence, i) { i->handleEscapeSequence(Rangei(range.end + 1, range.end + escLen)); } @@ -89,7 +95,7 @@ void EscapeParser::parse(String const &textWithEscapes) range.end = d->original.size(); if(range.size() > 0) { - DENG2_FOR_AUDIENCE(PlainText, i) + DENG2_FOR_AUDIENCE2(PlainText, i) { i->handlePlainText(range); } diff --git a/doomsday/libdeng2/src/data/record.cpp b/doomsday/libdeng2/src/data/record.cpp index 95d9bc4ab7..18211a6288 100644 --- a/doomsday/libdeng2/src/data/record.cpp +++ b/doomsday/libdeng2/src/data/record.cpp @@ -170,8 +170,12 @@ DENG2_PIMPL(Record) } } } + + DENG2_PIMPL_AUDIENCE(Deletion) }; +DENG2_AUDIENCE_METHOD(Record, Deletion) + Record::Record() : d(new Instance(*this)) {} @@ -184,7 +188,7 @@ Record::Record(Record const &other) Record::~Record() { - DENG2_FOR_AUDIENCE(Deletion, i) i->recordBeingDeleted(*this); + DENG2_FOR_AUDIENCE2(Deletion, i) i->recordBeingDeleted(*this); clear(); } @@ -194,7 +198,7 @@ void Record::clear() { DENG2_FOR_EACH(Members, i, d->members) { - i.value()->audienceForDeletion -= this; + i.value()->audienceForDeletion() -= this; delete i.value(); } d->members.clear(); @@ -209,7 +213,7 @@ void Record::copyMembersFrom(Record const &other, CopyBehavior behavior) i.key().startsWith("__")) continue; Variable *var = new Variable(*i.value()); - var->audienceForDeletion += this; + var->audienceForDeletion() += this; d->members[i.key()] = var; } } @@ -254,14 +258,14 @@ Variable &Record::add(Variable *variable) // Delete the previous variable with this name. delete d->members[variable->name()]; } - var->audienceForDeletion += this; + var->audienceForDeletion() += this; d->members[variable->name()] = var.release(); return *variable; } Variable *Record::remove(Variable &variable) { - variable.audienceForDeletion -= this; + variable.audienceForDeletion() -= this; d->members.remove(variable.name()); return &variable; } @@ -565,7 +569,7 @@ void Record::operator << (Reader &from) // Observe all members for deletion. DENG2_FOR_EACH(Members, i, d->members) { - i.value()->audienceForDeletion += this; + i.value()->audienceForDeletion() += this; } } diff --git a/doomsday/libdeng2/src/data/recordvalue.cpp b/doomsday/libdeng2/src/data/recordvalue.cpp index e92bed2f15..eb5856a675 100644 --- a/doomsday/libdeng2/src/data/recordvalue.cpp +++ b/doomsday/libdeng2/src/data/recordvalue.cpp @@ -36,7 +36,7 @@ RecordValue::RecordValue(Record *record, OwnershipFlags o) if(!_ownership.testFlag(OwnsRecord)) { // If we don't own it, someone may delete the record. - _record->audienceForDeletion += this; + _record->audienceForDeletion() += this; } } @@ -44,7 +44,7 @@ RecordValue::RecordValue(Record &record) : _record(&record), _ownership(0), _oldOwnership(0) { // Someone may delete the record. - _record->audienceForDeletion += this; + _record->audienceForDeletion() += this; } RecordValue::~RecordValue() @@ -72,7 +72,7 @@ void RecordValue::setRecord(Record *record) } else if(_record) { - _record->audienceForDeletion -= this; + _record->audienceForDeletion() -= this; } _record = record; @@ -81,7 +81,7 @@ void RecordValue::setRecord(Record *record) if(_record) { // Since we don't own it, someone may delete the record. - _record->audienceForDeletion += this; + _record->audienceForDeletion() += this; } } diff --git a/doomsday/libdeng2/src/data/refvalue.cpp b/doomsday/libdeng2/src/data/refvalue.cpp index ef1d8de1cb..5f2a431c02 100644 --- a/doomsday/libdeng2/src/data/refvalue.cpp +++ b/doomsday/libdeng2/src/data/refvalue.cpp @@ -27,7 +27,7 @@ RefValue::RefValue(Variable *variable) : _variable(variable) { if(_variable) { - _variable->audienceForDeletion.add(this); + _variable->audienceForDeletion() += this; } } @@ -35,7 +35,7 @@ RefValue::~RefValue() { if(_variable) { - _variable->audienceForDeletion.remove(this); + _variable->audienceForDeletion() -= this; } } diff --git a/doomsday/libdeng2/src/data/variable.cpp b/doomsday/libdeng2/src/data/variable.cpp index ef1dd483f8..92a979aebd 100644 --- a/doomsday/libdeng2/src/data/variable.cpp +++ b/doomsday/libdeng2/src/data/variable.cpp @@ -31,29 +31,67 @@ #include "de/Writer" #include "de/Log" -using namespace de; +namespace de { + +DENG2_PIMPL_NOREF(Variable) +{ + String name; + + /// Value of the variable. + Value *value; + + /// Mode flags. + Flags mode; + + Instance() : value(0) {} + + Instance(Instance const &other) + : name (other.name) + , value(other.value->duplicate()) + , mode (other.mode) + {} + + ~Instance() + { + delete value; + } + + DENG2_PIMPL_AUDIENCE(Deletion) + DENG2_PIMPL_AUDIENCE(Change) +}; + +DENG2_AUDIENCE_METHOD(Variable, Deletion) +DENG2_AUDIENCE_METHOD(Variable, Change) Variable::Variable(String const &name, Value *initial, Flags const &m) - : _name(name), _value(0), _mode(m) + : d(new Instance) { + d->name = name; + d->mode = m; + std::auto_ptr v(initial); if(!initial) { v.reset(new NoneValue); } - verifyName(_name); + verifyName(d->name); verifyValid(*v); - _value = v.release(); + d->value = v.release(); } Variable::Variable(Variable const &other) - : ISerializable(), _name(other._name), _value(other._value->duplicate()), _mode(other._mode) + : ISerializable() + , d(new Instance(*other.d)) {} Variable::~Variable() { - DENG2_FOR_AUDIENCE(Deletion, i) i->variableBeingDeleted(*this); - delete _value; + DENG2_FOR_AUDIENCE2(Deletion, i) i->variableBeingDeleted(*this); +} + +String const &Variable::name() const +{ + return d->name; } Variable &Variable::operator = (Value *v) @@ -72,11 +110,11 @@ Variable &Variable::set(Value *v) verifyWritable(*v); verifyValid(*v); - QScopedPointer oldValue(_value); // old value deleted afterwards - _value = val.take(); + QScopedPointer oldValue(d->value); // old value deleted afterwards + d->value = val.take(); // We'll only determine if actual change occurred if someone is interested. - if(!audienceForChange.isEmpty()) + if(!audienceForChange().isEmpty()) { bool notify = true; try @@ -91,7 +129,7 @@ Variable &Variable::set(Value *v) if(notify) { - DENG2_FOR_AUDIENCE(Change, i) i->variableValueChanged(*this, *_value); + DENG2_FOR_AUDIENCE2(Change, i) i->variableValueChanged(*this, *d->value); } } return *this; @@ -105,14 +143,24 @@ Variable &Variable::set(Value const &v) Value const &Variable::value() const { - DENG2_ASSERT(_value != 0); - return *_value; + DENG2_ASSERT(d->value != 0); + return *d->value; } Value &Variable::value() { - DENG2_ASSERT(_value != 0); - return *_value; + DENG2_ASSERT(d->value != 0); + return *d->value; +} + +Value *Variable::valuePtr() +{ + return d->value; +} + +Value const *Variable::valuePtr() const +{ + return d->value; } Record const &Variable::valueAsRecord() const @@ -142,30 +190,30 @@ Variable::operator ddouble () const Variable::Flags Variable::mode() const { - return _mode; + return d->mode; } void Variable::setMode(Flags const &flags, FlagOp operation) { - applyFlagOperation(_mode, flags, operation); + applyFlagOperation(d->mode, flags, operation); } Variable &Variable::setReadOnly() { - _mode |= ReadOnly; + d->mode |= ReadOnly; return *this; } bool Variable::isValid(Value const &v) const { /// @todo Make sure this actually works and add func, record, ref. - if((dynamic_cast(&v) && !_mode.testFlag(AllowNone)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowNumber)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowText)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowArray)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowDictionary)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowBlock)) || - (dynamic_cast(&v) && !_mode.testFlag(AllowTime))) + if((dynamic_cast(&v) && !d->mode.testFlag(AllowNone)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowNumber)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowText)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowArray)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowDictionary)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowBlock)) || + (dynamic_cast(&v) && !d->mode.testFlag(AllowTime))) { return false; } @@ -179,16 +227,16 @@ void Variable::verifyValid(Value const &v) const { /// @throw InvalidError Value @a v is not allowed by the variable. throw InvalidError("Variable::verifyValid", - "Value type is not allowed by the variable '" + _name + "'"); + "Value type is not allowed by the variable '" + d->name + "'"); } } void Variable::verifyWritable(Value const &attemptedNewValue) { - if(_mode & ReadOnly) + if(d->mode & ReadOnly) { - if(_value && typeid(*_value) == typeid(attemptedNewValue) && - !_value->compare(attemptedNewValue)) + if(d->value && typeid(*d->value) == typeid(attemptedNewValue) && + !d->value->compare(attemptedNewValue)) { // This is ok: the value doesn't change. return; @@ -196,7 +244,7 @@ void Variable::verifyWritable(Value const &attemptedNewValue) /// @throw ReadOnlyError The variable is in read-only mode. throw ReadOnlyError("Variable::verifyWritable", - "Variable '" + _name + "' is in read-only mode"); + "Variable '" + d->name + "' is in read-only mode"); } } @@ -211,26 +259,28 @@ void Variable::verifyName(String const &s) void Variable::operator >> (Writer &to) const { - if(!_mode.testFlag(NoSerialize)) + if(!d->mode.testFlag(NoSerialize)) { - to << _name << duint32(_mode) << *_value; + to << d->name << duint32(d->mode) << *d->value; } } void Variable::operator << (Reader &from) { duint32 modeFlags = 0; - from >> _name >> modeFlags; - _mode = Flags(modeFlags); - delete _value; + from >> d->name >> modeFlags; + d->mode = Flags(modeFlags); + delete d->value; try { - _value = Value::constructFrom(from); + d->value = Value::constructFrom(from); } catch(Error const &) { // Always need to have a value. - _value = new NoneValue(); + d->value = new NoneValue(); throw; } } + +} // namespace de diff --git a/doomsday/libdeng2/src/filesys/archiveentryfile.cpp b/doomsday/libdeng2/src/filesys/archiveentryfile.cpp index a348b8ab1f..e6ee375dd4 100644 --- a/doomsday/libdeng2/src/filesys/archiveentryfile.cpp +++ b/doomsday/libdeng2/src/filesys/archiveentryfile.cpp @@ -32,8 +32,8 @@ ArchiveEntryFile::~ArchiveEntryFile() { DENG2_GUARD(this); - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); - audienceForDeletion.clear(); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); + audienceForDeletion().clear(); deindex(); } diff --git a/doomsday/libdeng2/src/filesys/archivefeed.cpp b/doomsday/libdeng2/src/filesys/archivefeed.cpp index ded3d3e066..b6227402e8 100644 --- a/doomsday/libdeng2/src/filesys/archivefeed.cpp +++ b/doomsday/libdeng2/src/filesys/archivefeed.cpp @@ -48,7 +48,7 @@ DENG2_PIMPL(ArchiveFeed) Instance(Public *feed, File &f) : Base(feed), file(&f), arch(0), parentFeed(0) { - file->audienceForDeletion += this; + file->audienceForDeletion() += this; // If the file happens to be a byte array file, we can use it // directly to store the Archive. @@ -75,14 +75,14 @@ DENG2_PIMPL(ArchiveFeed) Instance(Public *feed, ArchiveFeed &parentFeed, String const &path) : Base(feed), file(parentFeed.d->file), arch(0), basePath(path), parentFeed(&parentFeed) { - file->audienceForDeletion += this; + file->audienceForDeletion() += this; } ~Instance() { if(file) { - file->audienceForDeletion -= this; + file->audienceForDeletion() -= this; } if(arch) diff --git a/doomsday/libdeng2/src/filesys/file.cpp b/doomsday/libdeng2/src/filesys/file.cpp index 79dda257b4..96564b2671 100644 --- a/doomsday/libdeng2/src/filesys/file.cpp +++ b/doomsday/libdeng2/src/filesys/file.cpp @@ -26,38 +26,71 @@ #include "de/NumberValue" #include "de/Guard" -using namespace de; +namespace de { -File::File(String const &fileName) - : _parent(0), _originFeed(0), _name(fileName) +DENG2_PIMPL_NOREF(File) { - _source = this; + /// The parent folder. + Folder *parent; + + /// The source file (NULL for non-interpreted files). + File *source; + + /// Feed that generated the file. This feed is called upon when the file needs + /// to be pruned. May also be NULL. + Feed *originFeed; + + /// Name of the file. + String name; + + /// Status of the file. + Status status; + + /// Mode flags. + Flags mode; + + /// File information. + Record info; + + Instance(String const &fileName) + : parent(0) + , originFeed(0) + , name(fileName) {} + + DENG2_PIMPL_AUDIENCE(Deletion) +}; + +DENG2_AUDIENCE_METHOD(File, Deletion) + +File::File(String const &fileName) : d(new Instance(fileName)) +{ + d->source = this; // Create the default set of info variables common to all files. - _info.add(new Variable("name", new Accessor(*this, Accessor::NAME), Accessor::VARIABLE_MODE)); - _info.add(new Variable("path", new Accessor(*this, Accessor::PATH), Accessor::VARIABLE_MODE)); - _info.add(new Variable("type", new Accessor(*this, Accessor::TYPE), Accessor::VARIABLE_MODE)); - _info.add(new Variable("size", new Accessor(*this, Accessor::SIZE), Accessor::VARIABLE_MODE)); - _info.add(new Variable("modifiedAt", new Accessor(*this, Accessor::MODIFIED_AT), Accessor::VARIABLE_MODE)); + d->info.add(new Variable("name", new Accessor(*this, Accessor::NAME), Accessor::VARIABLE_MODE)); + d->info.add(new Variable("path", new Accessor(*this, Accessor::PATH), Accessor::VARIABLE_MODE)); + d->info.add(new Variable("type", new Accessor(*this, Accessor::TYPE), Accessor::VARIABLE_MODE)); + d->info.add(new Variable("size", new Accessor(*this, Accessor::SIZE), Accessor::VARIABLE_MODE)); + d->info.add(new Variable("modifiedAt", new Accessor(*this, Accessor::MODIFIED_AT), Accessor::VARIABLE_MODE)); } File::~File() { DENG2_GUARD(this); - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); flush(); - if(_source != this) + if(d->source != this) { // If we own a source, get rid of it. - delete _source; - _source = 0; + delete d->source; + d->source = 0; } - if(_parent) + if(d->parent) { // Remove from parent folder. - _parent->remove(this); + d->parent->remove(this); } deindex(); } @@ -80,6 +113,11 @@ FileSystem &File::fileSystem() return DENG2_APP->fileSystem(); } +String const &File::name() const +{ + return d->name; +} + String File::description() const { DENG2_GUARD(this); @@ -109,11 +147,26 @@ String File::describe() const return "abstract File"; } +void File::setParent(Folder *parent) +{ + d->parent = parent; +} + +Folder *File::parent() const +{ + return d->parent; +} + void File::setOriginFeed(Feed *feed) { DENG2_GUARD(this); - _originFeed = feed; + d->originFeed = feed; +} + +Feed *File::originFeed() const +{ + return d->originFeed; } String const File::path() const @@ -121,7 +174,7 @@ String const File::path() const DENG2_GUARD(this); String thePath = name(); - for(Folder *i = _parent; i; i = i->_parent) + for(Folder *i = d->parent; i; i = i->d->parent) { thePath = i->name() / thePath; } @@ -132,34 +185,34 @@ void File::setSource(File *source) { DENG2_GUARD(this); - if(_source != this) + if(d->source != this) { // Delete the old source. - delete _source; + delete d->source; } - _source = source; + d->source = source; } File const *File::source() const { DENG2_GUARD(this); - if(_source != this) + if(d->source != this) { - return _source->source(); + return d->source->source(); } - return _source; + return d->source; } File *File::source() { DENG2_GUARD(this); - if(_source != this) + if(d->source != this) { - return _source->source(); + return d->source->source(); } - return _source; + return d->source; } void File::setStatus(Status const &status) @@ -167,13 +220,13 @@ void File::setStatus(Status const &status) DENG2_GUARD(this); // The source file status is the official one. - if(this != _source) + if(this != d->source) { - _source->setStatus(status); + d->source->setStatus(status); } else { - _status = status; + d->status = status; } } @@ -181,36 +234,46 @@ File::Status const &File::status() const { DENG2_GUARD(this); - if(this != _source) + if(this != d->source) { - return _source->status(); + return d->source->status(); } - return _status; + return d->status; } void File::setMode(Flags const &newMode) { DENG2_GUARD(this); - if(this != _source) + if(this != d->source) { - _source->setMode(newMode); + d->source->setMode(newMode); } else { - _mode = newMode; + d->mode = newMode; } } +Record const &File::info() const +{ + return d->info; +} + +Record &File::info() +{ + return d->info; +} + File::Flags const &File::mode() const { DENG2_GUARD(this); - if(this != _source) + if(this != d->source) { - return _source->mode(); + return d->source->mode(); } - return _mode; + return d->mode; } void File::verifyWriteAccess() @@ -289,3 +352,5 @@ Value *File::Accessor::duplicateContent() const } return new TextValue(*this); } + +} // namespace de diff --git a/doomsday/libdeng2/src/filesys/folder.cpp b/doomsday/libdeng2/src/filesys/folder.cpp index bfd4b25340..1475617a88 100644 --- a/doomsday/libdeng2/src/filesys/folder.cpp +++ b/doomsday/libdeng2/src/filesys/folder.cpp @@ -40,8 +40,8 @@ Folder::~Folder() { DENG2_GUARD(this); - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); - audienceForDeletion.clear(); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); + audienceForDeletion().clear(); deindex(); diff --git a/doomsday/libdeng2/src/filesys/libraryfile.cpp b/doomsday/libdeng2/src/filesys/libraryfile.cpp index c069b9533a..3595557a73 100644 --- a/doomsday/libdeng2/src/filesys/libraryfile.cpp +++ b/doomsday/libdeng2/src/filesys/libraryfile.cpp @@ -34,8 +34,8 @@ LibraryFile::LibraryFile(File *source) LibraryFile::~LibraryFile() { - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); - audienceForDeletion.clear(); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); + audienceForDeletion().clear(); deindex(); delete _library; diff --git a/doomsday/libdeng2/src/filesys/nativefile.cpp b/doomsday/libdeng2/src/filesys/nativefile.cpp index 7567cbdadd..5df855fdc9 100644 --- a/doomsday/libdeng2/src/filesys/nativefile.cpp +++ b/doomsday/libdeng2/src/filesys/nativefile.cpp @@ -31,8 +31,8 @@ NativeFile::~NativeFile() { DENG2_GUARD(this); - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); - audienceForDeletion.clear(); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); + audienceForDeletion().clear(); close(); deindex(); diff --git a/doomsday/libdeng2/src/filesys/packagefolder.cpp b/doomsday/libdeng2/src/filesys/packagefolder.cpp index f771fcbc6d..7595a92c7f 100644 --- a/doomsday/libdeng2/src/filesys/packagefolder.cpp +++ b/doomsday/libdeng2/src/filesys/packagefolder.cpp @@ -29,8 +29,8 @@ PackageFolder::PackageFolder(File &sourceArchiveFile, String const &name) : Fold PackageFolder::~PackageFolder() { - DENG2_FOR_AUDIENCE(Deletion, i) i->fileBeingDeleted(*this); - audienceForDeletion.clear(); + DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this); + audienceForDeletion().clear(); deindex(); } diff --git a/doomsday/libdeng2/src/scriptsys/function.cpp b/doomsday/libdeng2/src/scriptsys/function.cpp index 787cbadf5f..c59a78d21c 100644 --- a/doomsday/libdeng2/src/scriptsys/function.cpp +++ b/doomsday/libdeng2/src/scriptsys/function.cpp @@ -96,7 +96,7 @@ Function::~Function() if(d->globals) { // Stop observing the namespace. - d->globals->audienceForDeletion.remove(this); + d->globals->audienceForDeletion() -= this; } } @@ -230,7 +230,7 @@ void Function::setGlobals(Record *globals) if(!d->globals) { d->globals = globals; - d->globals->audienceForDeletion.add(this); + d->globals->audienceForDeletion() += this; } /* else if(d->globals != globals) diff --git a/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp b/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp index 482c7acc45..c11789a4c5 100644 --- a/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp +++ b/doomsday/libdeng2/src/scriptsys/scriptsystem.cpp @@ -85,14 +85,14 @@ DENG2_PIMPL(ScriptSystem), DENG2_OBSERVES(Record, Deletion) DENG2_FOR_EACH(NativeModules, i, nativeModules) { - i.value()->audienceForDeletion -= this; + i.value()->audienceForDeletion() -= this; } } void addNativeModule(String const &name, Record &module) { nativeModules.insert(name, &module); // not owned - module.audienceForDeletion += this; + module.audienceForDeletion() += this; } void recordBeingDeleted(Record &record) diff --git a/doomsday/libdeng2/src/widgets/action.cpp b/doomsday/libdeng2/src/widgets/action.cpp index 57b0ba3d4c..c5da823433 100644 --- a/doomsday/libdeng2/src/widgets/action.cpp +++ b/doomsday/libdeng2/src/widgets/action.cpp @@ -20,12 +20,22 @@ namespace de { +DENG2_PIMPL_NOREF(Action) +{ + DENG2_PIMPL_AUDIENCE(Triggered) +}; + +DENG2_AUDIENCE_METHOD(Action, Triggered) + +Action::Action() : d(new Instance) +{} + Action::~Action() {} void Action::trigger() { - DENG2_FOR_AUDIENCE(Triggered, i) + DENG2_FOR_AUDIENCE2(Triggered, i) { i->actionTriggered(*this); } diff --git a/doomsday/libdeng2/src/widgets/widget.cpp b/doomsday/libdeng2/src/widgets/widget.cpp index 0e0c3998c3..3fa0d0099a 100644 --- a/doomsday/libdeng2/src/widgets/widget.cpp +++ b/doomsday/libdeng2/src/widgets/widget.cpp @@ -60,8 +60,18 @@ DENG2_PIMPL(Widget) } index.clear(); } + + DENG2_PIMPL_AUDIENCE(Deletion) + DENG2_PIMPL_AUDIENCE(ParentChange) + DENG2_PIMPL_AUDIENCE(ChildAddition) + DENG2_PIMPL_AUDIENCE(ChildRemoval) }; +DENG2_AUDIENCE_METHOD(Widget, Deletion) +DENG2_AUDIENCE_METHOD(Widget, ParentChange) +DENG2_AUDIENCE_METHOD(Widget, ChildAddition) +DENG2_AUDIENCE_METHOD(Widget, ChildRemoval) + Widget::Widget(String const &name) : d(new Instance(this, name)) {} @@ -72,7 +82,7 @@ Widget::~Widget() root().setFocus(0); } - audienceForParentChange.clear(); + audienceForParentChange().clear(); // Remove from parent automatically. if(d->parent) @@ -81,7 +91,7 @@ Widget::~Widget() } // Notify everyone else. - DENG2_FOR_AUDIENCE(Deletion, i) i->widgetBeingDeleted(*this); + DENG2_FOR_AUDIENCE2(Deletion, i) i->widgetBeingDeleted(*this); } Id Widget::id() const @@ -272,11 +282,11 @@ Widget &Widget::add(Widget *child) } // Notify. - DENG2_FOR_AUDIENCE(ChildAddition, i) + DENG2_FOR_AUDIENCE2(ChildAddition, i) { i->widgetChildAdded(*child); } - DENG2_FOR_EACH_OBSERVER(ParentChangeAudience, i, child->audienceForParentChange) + DENG2_FOR_EACH_OBSERVER(ParentChangeAudience, i, child->audienceForParentChange()) { i->widgetParentChanged(*child, 0, this); } @@ -309,11 +319,11 @@ Widget *Widget::remove(Widget &child) } // Notify. - DENG2_FOR_AUDIENCE(ChildRemoval, i) + DENG2_FOR_AUDIENCE2(ChildRemoval, i) { i->widgetChildRemoved(child); } - DENG2_FOR_EACH_OBSERVER(ParentChangeAudience, i, child.audienceForParentChange) + DENG2_FOR_EACH_OBSERVER(ParentChangeAudience, i, child.audienceForParentChange()) { i->widgetParentChanged(child, this, 0); }