1 /*
2  For general Scribus (>=1.3.2) copyright and licensing information please refer
3  to the COPYING file provided with the program. Following this notice may exist
4  a copyright and/or license notice that predates the release of Scribus 1.3.2
5  for which a new license (GPL+exception) is in place.
6  */
7 /***************************************************************************
8 *                                                                         *
9 *   This program is free software; you can redistribute it and/or modify  *
10 *   it under the terms of the GNU General Public License as published by  *
11 *   the Free Software Foundation; either version 2 of the License, or     *
12 *   (at your option) any later version.                                   *
13 *                                                                         *
14 ***************************************************************************/
15 
16 #ifndef OBSERVABLE_H
17 #define OBSERVABLE_H
18 
19 #include "scribusapi.h"
20 
21 #include <QObject>
22 #include <QSet>
23 #include <QVariant>
24 
25 #include "updatemanager.h"
26 
27 
28 //#include "observable_private.h"
29 struct SCRIBUS_API Private_Signal : public QObject
30 {
31 	Q_OBJECT
32 
33 public:
emitSignalPrivate_Signal34 	void emitSignal(QObject* what)
35 {
36 		emit changedObject(what);
37 }
38 
emitSignalPrivate_Signal39 void emitSignal(QVariant what)
40 {
41 	emit changedData(what);
42 }
43 
connectSignalPrivate_Signal44 bool connectSignal(QObject*, QObject* o, const char* slot)
45 {
46 	return QObject::connect(this, SIGNAL(changedObject(QObject*)), o, slot);
47 }
48 
disconnectSignalPrivate_Signal49 bool disconnectSignal(QObject*, QObject* o, const char* slot)
50 {
51 	return QObject::disconnect(this, SIGNAL(changedObject(QObject*)), o, slot);
52 }
53 
connectSignalPrivate_Signal54 bool connectSignal(QVariant, QObject* o, const char* slot)
55 {
56 	return QObject::connect(this, SIGNAL(changedData(QVariant)), o, slot);
57 }
58 
disconnectSignalPrivate_Signal59 bool disconnectSignal(QVariant, QObject* o, const char* slot)
60 {
61 	return QObject::disconnect(this, SIGNAL(changedData(QVariant)), o, slot);
62 }
63 
64 signals:
65 void changedObject(QObject* what);
66 void changedData(QVariant what);
67 };
68 
69 
70 
71 template<class OBSERVED>
72 struct Private_Memento : public UpdateMemento
73 {
Private_MementoPrivate_Memento74 	Private_Memento(OBSERVED data) : m_data(data), m_layout(false) {}
Private_MementoPrivate_Memento75 	Private_Memento(OBSERVED data, bool layout) : m_data(data), m_layout(layout) {}
76 
77 	OBSERVED m_data;
78 	bool     m_layout;
79 };
80 
81 
82 
83 /**
84   Implement this interface if you want to observe an observable but don't want to derive from QObject
85  */
86 template<class OBSERVED>
87 class SCRIBUS_API Observer {
88 public:
89 	virtual void changed(OBSERVED, bool doLayout) = 0;
~Observer()90 	virtual ~Observer() {}
91 };
92 
93 
94 
95 
96 /**
97  An MassObservable is basically just the source of a changed() signal.
98  Observers can register via the Qt signal/slot mechanism or via the above interface.
99 
100  The difference to Observable (below) is that a MassObservable doesn't report changes to
101  itself but to a bunch of SingleObservables.
102  When you call update() on the SingleObservable, it will tell the associated
103  MassObservable to notify all observers with the "changed" signal,
104  providing a pointer to the single Observable.
105 
106  The class parameter OBSERVED is usually a pointer to a subclass of SingleObservable.
107 
108  MassObservable does not inherit QObject since that makes it difficult to use it as a mixin class.
109  */
110 template<class OBSERVED>
111 class MassObservable : public UpdateManaged
112 {
113 	friend class UpdateManager;
114 
115 public:
116 	MassObservable(UpdateManager* um = nullptr);
117 	virtual ~MassObservable();
118 	/**
119 		Used if the UpdateManager is not available when the constructor is called
120 	*/
121 	void setUpdateManager(UpdateManager* um);
122 
123 	/**
124 		This method will be called by the SingleObservable.
125 	    If updates are enabled, it calls updateNow() directly, otherwise the undomanager
126 	    will take care of that.
127 	 */
128 	virtual void update(OBSERVED what);
129 
130 	/**
131 		Same as update, except layout will be update immediately
132 	 */
133 	virtual void updateLayout(OBSERVED what);
134 
135 	void connectObserver(Observer<OBSERVED>* o);
136 	void disconnectObserver(Observer<OBSERVED>* o);
137 
138 	bool connectObserver(QObject* o, const char* slot);
139 	bool disconnectObserver(QObject* o, const char* slot = 0);
140 
141 protected:
142 	/**
143 		This method will be called by the updatemanager or by update()
144 	 */
145 	virtual void updateNow(UpdateMemento* what);
146 
147 	QSet<Observer<OBSERVED>*> m_observers;
148 	Private_Signal* changedSignal;
149 	UpdateManager* m_um;
150 };
151 
152 
153 
154 
155 /**
156   This mixin class just provides the update() method.
157  */
158 template<class OBSERVED>
159 class SCRIBUS_API SingleObservable
160 {
161 public:
162 	/**
163 	  Connects this SingleObservale to the MassObservable
164 	 */
SingleObservable(MassObservable<OBSERVED * > * massObservable)165 	SingleObservable(MassObservable<OBSERVED*> * massObservable) : m_massObservable(massObservable) {}
166 
~SingleObservable()167 	virtual ~SingleObservable() {}
168 
setMassObservable(MassObservable<OBSERVED * > * massObservable)169 	void setMassObservable(MassObservable<OBSERVED*> * massObservable)
170 	{
171 		m_massObservable = massObservable;
172 	}
173 
update()174 	virtual void update()
175 	{
176 		m_massObservable->update(dynamic_cast<OBSERVED*>(this));
177 	}
178 
updateLayout()179 	virtual void updateLayout()
180 	{
181 		m_massObservable->updateLayout(dynamic_cast<OBSERVED*>(this));
182 	}
183 
184 private:
185 	MassObservable<OBSERVED*>* m_massObservable;
186 };
187 
188 
189 
190 
191 
192 /**
193   An Observable is basically just the source of a changed() signal.
194   Observers can register via the Qt signal/slot mechanism or via the above interface.
195 
196   When you call update(), all observers will receive the "changed" signal with a pointer
197   to the Observable (this).
198 
199   Observable is implemented as a MassObservable which.
200 
201   The class parameter OBSERVED should be the implementing class.
202  */
203 template<class OBSERVED>
204 class SCRIBUS_API Observable : public MassObservable<OBSERVED*>
205 {
206 public:
207 	Observable(UpdateManager* um = nullptr) : MassObservable<OBSERVED*>(um) {}
208 
update()209 	virtual void update()
210 	{
211 		MassObservable<OBSERVED*>::update(dynamic_cast<OBSERVED*>(this));
212 	}
213 private:
214 	using MassObservable<OBSERVED*>::update;
215 };
216 
217 
218 
219 // IMPLEMENTATION
220 
221 
222 
223 template<class OBSERVED>
MassObservable(UpdateManager * um)224 inline MassObservable<OBSERVED>::MassObservable(UpdateManager* um) : m_observers(), changedSignal(new Private_Signal()), m_um(um)
225 {
226 }
227 
228 template<class OBSERVED>
~MassObservable()229 inline MassObservable<OBSERVED>::~MassObservable()
230 {
231 	m_observers.clear();
232 	delete changedSignal;
233 }
234 
235 
236 template<class OBSERVED>
setUpdateManager(UpdateManager * um)237 inline void MassObservable<OBSERVED>::setUpdateManager(UpdateManager* um)
238 {
239 	m_um = um;
240 }
241 
242 
243 template<class OBSERVED>
update(OBSERVED what)244 inline void MassObservable<OBSERVED>::update(OBSERVED what)
245 {
246 	Private_Memento<OBSERVED>* memento = new Private_Memento<OBSERVED>(what);
247 	if (m_um == nullptr || m_um->requestUpdate(this, memento))
248 	{
249 		updateNow(memento);
250 	}
251 }
252 
253 template<class OBSERVED>
updateLayout(OBSERVED what)254 inline void MassObservable<OBSERVED>::updateLayout(OBSERVED what)
255 {
256 	Private_Memento<OBSERVED>* memento = new Private_Memento<OBSERVED>(what, true);
257 	if (m_um == nullptr || m_um->requestUpdate(this, memento))
258 		updateNow(memento);
259 }
260 
261 template<class OBSERVED>
updateNow(UpdateMemento * what)262 inline void MassObservable<OBSERVED>::updateNow(UpdateMemento* what)
263 {
264 	Private_Memento<OBSERVED>* memento = dynamic_cast<Private_Memento<OBSERVED>*>(what);
265 	if (!memento)
266 		qFatal("MassObservable<OBSERVED>::updateNow memento nullptr");
267 	foreach (Observer<OBSERVED>* obs, m_observers)
268 		obs->changed(memento->m_data, memento->m_layout);
269 	changedSignal->emitSignal(QVariant::fromValue(memento->m_data));
270 	delete memento;
271 }
272 
273 
274 template<class OBSERVED>
connectObserver(Observer<OBSERVED> * o)275 inline void MassObservable<OBSERVED>::connectObserver(Observer<OBSERVED>* o)
276 {
277 	m_observers.insert(o);
278 }
279 
280 template<class OBSERVED>
disconnectObserver(Observer<OBSERVED> * o)281 inline void MassObservable<OBSERVED>::disconnectObserver(Observer<OBSERVED>* o)
282 {
283 	m_observers.remove(o);
284 }
285 
286 
287 template <typename T>
Private_Init(T & dummy)288 inline void Private_Init(T& dummy) {}
289 
290 template <>
Private_Init(QObject * & dummy)291 inline void Private_Init(QObject*& dummy)
292 {
293 //	dummy->die_compiler_die();
294 	dummy = 0;
295 }
296 
297 
298 template<class OBSERVED>
connectObserver(QObject * o,const char * slot)299 inline bool MassObservable<OBSERVED>::connectObserver(QObject* o, const char* slot)
300 {
301 	OBSERVED dummy;
302 	Private_Init(dummy);
303 	return changedSignal->connectSignal(QVariant::fromValue(dummy), o, slot);
304 }
305 
306 template<class OBSERVED>
disconnectObserver(QObject * o,const char * slot)307 inline bool MassObservable<OBSERVED>::disconnectObserver(QObject* o, const char* slot)
308 {
309 	OBSERVED dummy;
310 	Private_Init(dummy);
311 	return changedSignal->disconnectSignal(QVariant::fromValue(dummy), o, slot);
312 }
313 
314 
315 #endif
316