From dc53be520b5233cffda1a05abc544c8f4221d99b Mon Sep 17 00:00:00 2001
From: Paolo Capriotti
Date: Fri, 20 Jul 2007 01:50:03 +0200
Subject: [PATCH] Introduced a new notification system for Settings changes.
The new system is still based on the QMetaObject facility, but does not
involve assigning priorities to Settings observers.
Instead, a very rudimental dependency enforcing mechanism is employed:
at connection time (i.e. when calling the onChange function) one can
specify an optional class name as a dependency.
Whenever an update is triggered, the systems ensures that dependencies
are honored, i.e. an object depending on a class "X" will be notified after
all observing objects of class "X".
The assumption here is that controlling dependency via types is sufficient
for our purposes.
As for the API, we now have to "onChange" methods, one that takes a dependency
(as a C-style string representing a class name) and one that does not.
The slot should be specified as a C-style string with just the method name
(no parentheses, no SLOT macro).
---
src/common.h | 9 +++--
src/graphicalgame.cpp | 2 +-
src/graphicalsystem.cpp | 2 +-
src/loader/theme.cpp | 2 +-
src/mastersettings.cpp | 89 +++++++++++++++++++++++++++++++++----------------
src/mastersettings.h | 44 ++++++++++++++++++------
6 files changed, 101 insertions(+), 47 deletions(-)
diff --git a/src/common.h b/src/common.h
index 3b88846..c01c196 100644
--- a/src/common.h
+++ b/src/common.h
@@ -28,9 +28,6 @@ template class Grid;
template class PointerGrid;
//typedef PointerGrid PieceGrid;
-#define FORALL(i, collection, type) \
- for (type::iterator i = (collection).begin(); i != (collection).end(); ++i)
-
std::ostream &operator <<(std::ostream &os, const QString& s);
@@ -52,8 +49,10 @@ inline void TRAP() {
#define M_PI 3.1415926
#endif
-#define ERROR(x) (std::cout << "Error: " << x << std::endl \
- << " in " << __PRETTY_FUNCTION__ << ", line " << __LINE__ << " of " << __FILE__ <setNotifier( static_cast(this) );
m_movelist->show();
}
- settings.onChange(this, SLOT(settingsChanged()));
+ settings.onChange(this, "settingsChanged", "Loader::Theme");
settingsChanged();
}
diff --git a/src/graphicalsystem.cpp b/src/graphicalsystem.cpp
index 3e7e040..0fd2ebf 100644
--- a/src/graphicalsystem.cpp
+++ b/src/graphicalsystem.cpp
@@ -46,7 +46,7 @@ GraphicalSystem::GraphicalSystem(ChessTable* view,
m_animator = m_variant->createAnimator(this);
- settings.onChange( this, SLOT(settingsChanged()));
+ settings.onChange(this, "settingsChanged", "Loader::Theme");
settingsChanged();
if (startingPosition)
diff --git a/src/loader/theme.cpp b/src/loader/theme.cpp
index e170848..9ee8ad5 100644
--- a/src/loader/theme.cpp
+++ b/src/loader/theme.cpp
@@ -41,7 +41,7 @@ Theme::Theme(const QString& lua_file)
if(m_lua_loader.error())
ERROR("Script load error: " << std::endl << m_lua_loader.errorString());
- settings.onChange(this, SLOT(onSettingsChanged()), -10);
+ settings.onChange(this, "onSettingsChanged");
onSettingsChanged();
}
diff --git a/src/mastersettings.cpp b/src/mastersettings.cpp
index 0700ff2..e1d2dda 100644
--- a/src/mastersettings.cpp
+++ b/src/mastersettings.cpp
@@ -55,10 +55,56 @@ MasterSettings::~MasterSettings() {
sync();
}
-void MasterSettings::onChange(QObject* obj, const char* slot, int prority) {
- std::cout << "register " << obj << " " << slot << std::endl;
- m_slots[prority].insert( std::pair(obj, slot) );
- connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(obj_destroyed(QObject*)));
+void MasterSettings::setupObserver(Observer& observer) {
+ std::cout << "adding observer: " << observer.object->metaObject()->className()
+ << "::" << wrap_cptr(observer.method)
+ << " (dep. " << wrap_cptr(observer.dependency) << ")" << std::endl;
+
+ connect(observer.object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*)));
+}
+
+void MasterSettings::onChange(QObject* obj, const char* method) {
+ m_observers.push_front(Observer(obj, method, 0));
+ setupObserver(m_observers.front());
+}
+
+void MasterSettings::onChange(QObject* obj, const char* method, const char* dependency) {
+ if (dependency) {
+ // go backwards through all existing observers, searching for something
+ // on which we depend.
+ for (ObserverList::reverse_iterator it = m_observers.rbegin();
+ it != m_observers.rend(); ++it) {
+ const char* observer_class = it->object->metaObject()->className();
+ if (observer_class && strcmp(observer_class, dependency) == 0) {
+ // we hit a dependency wall: we can't be notified
+ // before *it, so add the new Observer just here
+ ObserverList::iterator us = m_observers.insert(it.base(), Observer(obj, method, dependency));
+ setupObserver(*us);
+
+ // now check for a cyclic dependency
+ const char* this_class = obj->metaObject()->className();
+ for (ObserverList::iterator it2 = m_observers.begin(); it2 != us; ++it2) {
+ if (it2->dependency && strcmp(it2->dependency, this_class) == 0) {
+ // something which is notified before has us a dependency
+ // this means that there is a cyclic dependency it2 -> us -> it.
+ WARNING("Removing a cyclic dependency: " <<
+ it2->object->metaObject()->className() << " -> " <<
+ this_class << " -> " <<
+ observer_class);
+
+ // remove the cycle
+ it2->dependency = 0;
+ }
+ }
+
+ // done
+ return;
+ }
+ }
+ }
+
+ // no dependency
+ onChange(obj, method);
}
void MasterSettings::sync() {
@@ -73,36 +119,21 @@ void MasterSettings::sync() {
}
-void MasterSettings::obj_destroyed(QObject* obj) {
- for (SlotMap::iterator it = m_slots.begin(); it != m_slots.end(); /*++it*/ ) {
- SlotSet::iterator j = it->second.lower_bound( std::pair(obj, NULL) );
- while(j->first == obj)
- it->second.erase(j++);
-
- if(it->second.empty())
- m_slots.erase(it++);
- else
+void MasterSettings::objectDestroyed(QObject* obj) {
+ for (ObserverList::iterator it = m_observers.begin();
+ it != m_observers.end();) {
+ if (it->object == obj) {
+ it = m_observers.erase(it);
+ }
+ else {
++it;
+ }
}
}
void MasterSettings::changed() {
- for (SlotMap::iterator it = m_slots.begin(); it != m_slots.end(); ++it )
- for (SlotSet::iterator j = it->second.begin(); j != it->second.end(); ++j ) {
-
- //yes, moc is shit.
- char* mystr = (char*)alloca(strlen(j->second)+1);
- if(isdigit(j->second[0]))
- strcpy(mystr, j->second+1);
- else
- strcpy(mystr, j->second);
-
- if(char* par = index(mystr, '('))
- *par = '\0';
- if(char* par = index(mystr, ' '))
- *par = '\0';
-
- bool r = j->first->metaObject()->invokeMethod( j->first, mystr, Qt::DirectConnection);
+ foreach (Observer& observer, m_observers) {
+ observer.object->metaObject()->invokeMethod(observer.object, observer.method, Qt::DirectConnection);
}
sync();
}
diff --git a/src/mastersettings.h b/src/mastersettings.h
index 1ceb69b..8f12fde 100644
--- a/src/mastersettings.h
+++ b/src/mastersettings.h
@@ -13,8 +13,7 @@
#include
#include
-#include
-#include