/* For general Scribus (>=1.3.2) copyright and licensing information please refer to the COPYING file provided with the program. Following this notice may exist a copyright and/or license notice that predates the release of Scribus 1.3.2 for which a new license (GPL+exception) is in place. */ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef OBSERVABLE_H #define OBSERVABLE_H #include "scribusapi.h" #include #include #include #include "updatemanager.h" //#include "observable_private.h" struct SCRIBUS_API Private_Signal : public QObject { Q_OBJECT; public: void emitSignal(QObject* what) { emit changedObject(what); } void emitSignal(QVariant what) { emit changedData(what); } bool connectSignal(QObject*, QObject* o, const char* slot) { return QObject::connect(this, SIGNAL(changedObject(QObject*)), o, slot); } bool disconnectSignal(QObject*, QObject* o, const char* slot) { return QObject::disconnect(this, SIGNAL(changedObject(QObject*)), o, slot); } bool connectSignal(QVariant, QObject* o, const char* slot) { return QObject::connect(this, SIGNAL(changedData(QVariant)), o, slot); } bool disconnectSignal(QVariant, QObject* o, const char* slot) { return QObject::disconnect(this, SIGNAL(changedData(QVariant)), o, slot); } signals: void changedObject(QObject* what); void changedData(QVariant what); }; template struct Private_Memento : public UpdateMemento { Private_Memento(OBSERVED data) : m_data(data) {}; OBSERVED m_data; }; /** Implement this interface if you want to observe an observable but dont want to derive from QObject */ template class SCRIBUS_API Observer { public: virtual void changed(OBSERVED) = 0; virtual ~Observer() {} }; /** An MassObservable is basically just the source of a changed() signal. Observers can register via the Qt signal/slot mechanism or via the above interface. The difference to Observable (below) is that a MassObservable doesnt report changes to itself but to a bunch of SingleObservables. When you call update() on the SingleObservable, it will tell the associated MassObservable to notify all observers with the "changed" signal, providing a pointer to the single Observable. The class parameter OBSERVED is usually a pointer to a subclass of SingleObservable. MassObservable does not inherit QObject since that makes it difficult to use it as a mixin class. */ template class MassObservable : public UpdateManaged { friend class UpdateManager; public: MassObservable(UpdateManager* um = NULL); virtual ~MassObservable(); /** Used if the UpdateManager is not available when the constructor is called */ void setUpdateManager(UpdateManager* um); /** This method will be called by the SingleObservable. If updates are enabled, it calls updateNow() directly, otherwise the undomanager will take care of that. */ virtual void update(OBSERVED what); void connectObserver(Observer* o); void disconnectObserver(Observer* o); bool connectObserver(QObject* o, const char* slot); bool disconnectObserver(QObject* o, const char* slot = 0); protected: /** This method will be called by the updatemanager or by update() */ virtual void updateNow(UpdateMemento* what); QSet*> m_observers; Private_Signal* changedSignal; UpdateManager* m_um; }; /** This mixin class just provides the update() method. */ template class SCRIBUS_API SingleObservable { public: /** Connects this SingleObservale to the MassObservable */ SingleObservable(MassObservable * massObservable) : m_massObservable(massObservable) {}; virtual ~SingleObservable() {}; void setMassObservable(MassObservable * massObservable) { m_massObservable = massObservable; } virtual void update() { m_massObservable->update(dynamic_cast(this)); } private: MassObservable* m_massObservable; }; /** An Observable is basically just the source of a changed() signal. Observers can register via the Qt signal/slot mechanism or via the above interface. When you call update(), all observers will receive the "changed" signal with a pointer to the Observable (this). Observable is implemented as a MassObservable which. The class parameter OBSERVED should be the implementing class. */ template class SCRIBUS_API Observable : public MassObservable { public: Observable(UpdateManager* um = NULL) : MassObservable(um) {}; virtual void update() { MassObservable::update(dynamic_cast(this)); } }; // IMPLEMENTATION template inline MassObservable::MassObservable(UpdateManager* um) : m_observers(), changedSignal(new Private_Signal()), m_um(um) { } template inline MassObservable::~MassObservable() { m_observers.clear(); delete changedSignal; } template inline void MassObservable::setUpdateManager(UpdateManager* um) { m_um = um; } template inline void MassObservable::update(OBSERVED what) { Private_Memento* memento = new Private_Memento(what); if (m_um == NULL || m_um->requestUpdate(this, memento)) { updateNow(memento); } } template inline void MassObservable::updateNow(UpdateMemento* what) { Private_Memento* memento = dynamic_cast*>(what); foreach (Observer* obs, m_observers) { obs->changed(memento->m_data); } changedSignal->emitSignal(QVariant::fromValue(memento->m_data)); delete memento; } template inline void MassObservable::connectObserver(Observer* o) { m_observers.insert(o); } template inline void MassObservable::disconnectObserver(Observer* o) { m_observers.remove(o); } template inline void Private_Init(T& dummy) {} template <> inline void Private_Init(QObject*& dummy) { // dummy->die_compiler_die(); dummy = 0; } template inline bool MassObservable::connectObserver(QObject* o, const char* slot) { OBSERVED dummy; Private_Init(dummy); return changedSignal->connectSignal(QVariant::fromValue(dummy), o, slot); } template inline bool MassObservable::disconnectObserver(QObject* o, const char* slot) { OBSERVED dummy; Private_Init(dummy); return changedSignal->disconnectSignal(QVariant::fromValue(dummy), o, slot); } #endif