/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/******************************************************************************
*
* FILE
* $Archive: /Commando/Code/wwlib/Notify.h $
*
* DESCRIPTION
* These templates provide implementation of the Subject-Observer pattern.
*
* PROGRAMMER
* Steve Clinard
* $Author: Denzil_l $
*
* VERSION INFO
* $Modtime: 11/13/01 10:49a $
* $Revision: 8 $
*
******************************************************************************/
#ifndef __NOTIFY_H__
#define __NOTIFY_H__
// Reduce warning level for STL
#if defined(_MSC_VER)
#pragma warning(push, 3)
#endif
#include
#include
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#include
template class Notifier;
template class Observer;
template class Observer
{
public:
typedef std::vector< Notifier* > NotifierColl;
Observer() :
mNotifiers(NULL)
{}
virtual ~Observer()
{StopObserving();}
//! Handle event notification
virtual void HandleNotification(Event&) = 0;
//! Notifier has ended notification of this event
virtual void NotificationEnded(const Notifier& notifier)
{
NotifierColl::iterator pos = std::find(mNotifiers.begin(),
mNotifiers.end(), ¬ifier);
if (pos != mNotifiers.end())
{
mNotifiers.erase(pos);
}
}
//! Request notification of this event
virtual void NotifyMe(Notifier& notifier)
{notifier.AddObserver(*this);}
//! Stop observing event
void StopObserving()
{
while (mNotifiers.size() > 0)
{
Notifier* notifier = mNotifiers.back();
assert(notifier && "ERROR: NULL pointer in collection.");
notifier->RemoveObserver(*this);
}
}
protected:
Observer(const Observer& observer);
const Observer& operator=(const Observer&);
private:
friend class Notifier;
NotifierColl mNotifiers;
};
#define DECLARE_OBSERVER(Event) \
virtual void NotifyMe(Notifier& observer) \
{Notifier::AddObserver(observer);}
template class Notifier
{
public:
typedef std::vector< Observer* > ObserverColl;
Notifier()
{}
virtual ~Notifier()
{
for (int index = mObservers.size(); index--;)
{
mObservers[index]->NotificationEnded(*this);
}
}
//! Send event notification to all observers of this event.
virtual void NotifyObservers(Event& event)
{
for (unsigned int index = 0; index < mObservers.size(); index++)
{
mObservers[index]->HandleNotification(event);
}
}
//! Add an observer of this event
virtual void AddObserver(Observer& observer)
{
ObserverColl::iterator pos = std::find(mObservers.begin(),
mObservers.end(), &observer);
if (pos == mObservers.end())
{
observer.mNotifiers.push_back(this);
mObservers.push_back(&observer);
}
}
//! Remove an observer of this event
virtual void RemoveObserver(Observer& observer)
{
ObserverColl::iterator pos = std::find(mObservers.begin(),
mObservers.end(), &observer);
if (pos != mObservers.end())
{
observer.NotificationEnded(*this);
mObservers.erase(pos);
}
}
virtual bool HasObservers(void) const
{return !mObservers.empty();}
private:
//! Observer collection
ObserverColl mObservers;
};
#define DECLARE_NOTIFIER(Event) \
virtual void NotifyObservers(Event& event) \
{Notifier::NotifyObservers(event);} \
virtual void AddObserver(Observer& observer) \
{Notifier::AddObserver(observer);} \
virtual void RemoveObserver(Observer& observer) \
{Notifier::RemoveObserver(observer);}
/*-----------------------------------------------------------------------------
* The following templates are useful for defining unique types to use as
* Events from types such as strings or integers.
*---------------------------------------------------------------------------*/
/* TypedEvent
*
* The first type (T) must be a class or other unique type. This need not
* be a "real" class. It could be a forward declared class, which is enough
* to make the template class unique.
*
* The second type (V) is the event data. "UString" and "int" are obvious
* choices.
*
* Typedef'ing the template class is a good thing to do.
*/
template
class TypedEvent
{
public:
TypedEvent(V& value) :
mValue(value)
{}
inline V& operator()()
{return mValue;}
inline V& Subject(void)
{return mValue;}
protected:
V& mValue;
};
template
class TypedEventPtr
{
public:
TypedEventPtr(O* subject) :
mSubject(subject)
{}
inline O* Subject(void)
{return mSubject;}
inline O* operator()()
{return mSubject;}
protected:
O* mSubject;
};
template
class TypedActionPtr :
public TypedEventPtr
{
public:
A GetAction(void) const
{return mAction;}
TypedActionPtr(A action, O* data) :
TypedEventPtr(data),
mAction(action)
{}
~TypedActionPtr()
{}
protected:
A mAction;
};
template
class TypedEventPair
{
public:
TypedEventPair(A itemA, B itemB) :
mItemA(itemA),
mItemB(itemB)
{}
inline A GetItemA(void)
{return mItemA;}
inline B GetItemB(void)
{return mItemB;}
protected:
A mItemA;
B mItemB;
};
#endif // __NOTIFY_H__