diff options
author | brad kittenbrink <brad@lindenlab.com> | 2009-07-15 17:54:11 -0700 |
---|---|---|
committer | brad kittenbrink <brad@lindenlab.com> | 2009-07-15 17:54:11 -0700 |
commit | 61c27f2663aee60ba6eaeed57341915c8b8b5097 (patch) | |
tree | 9ea7781fbafb5860d5376a791ac2aa3e7368d25e /indra | |
parent | 796ac35ca503090b3923c942df11f0153fd23693 (diff) |
Oops, LLNotificationsListener can't just be forward declared under gcc.
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llui/llnotifications.h | 1825 |
1 files changed, 912 insertions, 913 deletions
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 971d11db97..93cdcbeefd 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -1,913 +1,912 @@ -/**
-* @file llnotifications.h
-* @brief Non-UI manager and support for keeping a prioritized list of notifications
-* @author Q (with assistance from Richard and Coco)
-*
-* $LicenseInfo:firstyear=2008&license=viewergpl$
-*
-* Copyright (c) 2008-2009, Linden Research, Inc.
-*
-* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
-*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
-*
-* By copying, modifying or distributing this software, you acknowledge
-* that you have read and understood your obligations described above,
-* and agree to abide by those obligations.
-*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
-* $/LicenseInfo$
-*/
-
-#ifndef LL_LLNOTIFICATIONS_H
-#define LL_LLNOTIFICATIONS_H
-
-/**
- * This system is intended to provide a singleton mechanism for adding
- * notifications to one of an arbitrary set of event channels.
- *
- * Controlling JIRA: DEV-9061
- *
- * Every notification has (see code for full list):
- * - a textual name, which is used to look up its template in the XML files
- * - a payload, which is a block of LLSD
- * - a channel, which is normally extracted from the XML files but
- * can be overridden.
- * - a timestamp, used to order the notifications
- * - expiration time -- if nonzero, specifies a time after which the
- * notification will no longer be valid.
- * - a callback name and a couple of status bits related to callbacks (see below)
- *
- * There is a management class called LLNotifications, which is an LLSingleton.
- * The class maintains a collection of all of the notifications received
- * or processed during this session, and also manages the persistence
- * of those notifications that must be persisted.
- *
- * We also have Channels. A channel is a view on a collection of notifications;
- * The collection is defined by a filter function that controls which
- * notifications are in the channel, and its ordering is controlled by
- * a comparator.
- *
- * There is a hierarchy of channels; notifications flow down from
- * the management class (LLNotifications, which itself inherits from
- * The channel base class) to the individual channels.
- * Any change to notifications (add, delete, modify) is
- * automatically propagated through the channel hierarchy.
- *
- * We provide methods for adding a new notification, for removing
- * one, and for managing channels. Channels are relatively cheap to construct
- * and maintain, so in general, human interfaces should use channels to
- * select and manage their lists of notifications.
- *
- * We also maintain a collection of templates that are loaded from the
- * XML file of template translations. The system supports substitution
- * of named variables from the payload into the XML file.
- *
- * By default, only the "unknown message" template is built into the system.
- * It is not an error to add a notification that's not found in the
- * template system, but it is logged.
- *
- */
-
-#include <string>
-#include <list>
-#include <vector>
-#include <map>
-#include <set>
-#include <iomanip>
-#include <sstream>
-
-#include <boost/utility.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/type_traits.hpp>
-
-// we want to minimize external dependencies, but this one is important
-#include "llsd.h"
-
-// and we need this to manage the notification callbacks
-#include "llevents.h"
-#include "llfunctorregistry.h"
-#include "llui.h"
-#include "llmemory.h"
-
-class LLNotification;
-typedef boost::shared_ptr<LLNotification> LLNotificationPtr;
-
-
-typedef enum e_notification_priority
-{
- NOTIFICATION_PRIORITY_UNSPECIFIED,
- NOTIFICATION_PRIORITY_LOW,
- NOTIFICATION_PRIORITY_NORMAL,
- NOTIFICATION_PRIORITY_HIGH,
- NOTIFICATION_PRIORITY_CRITICAL
-} ENotificationPriority;
-
-typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
-
-typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
-typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
-
-// context data that can be looked up via a notification's payload by the display logic
-// derive from this class to implement specific contexts
-class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
-{
-public:
- LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
- {
- }
-
- virtual ~LLNotificationContext() {}
-
- LLSD asLLSD() const
- {
- return getKey();
- }
-
-private:
-
-};
-
-// Contains notification form data, such as buttons and text fields along with
-// manipulator functions
-class LLNotificationForm
-{
- LOG_CLASS(LLNotificationForm);
-
-public:
- typedef enum e_ignore_type
- {
- IGNORE_NO,
- IGNORE_WITH_DEFAULT_RESPONSE,
- IGNORE_WITH_LAST_RESPONSE,
- IGNORE_SHOW_AGAIN
- } EIgnoreType;
-
- LLNotificationForm();
- LLNotificationForm(const LLSD& sd);
- LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node);
-
- LLSD asLLSD() const;
-
- S32 getNumElements() { return mFormData.size(); }
- LLSD getElement(S32 index) { return mFormData.get(index); }
- LLSD getElement(const std::string& element_name);
- bool hasElement(const std::string& element_name);
- void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD());
- void formatElements(const LLSD& substitutions);
- // appends form elements from another form serialized as LLSD
- void append(const LLSD& sub_form);
- std::string getDefaultOption();
-
- EIgnoreType getIgnoreType() { return mIgnore; }
- std::string getIgnoreMessage() { return mIgnoreMsg; }
-
-private:
- LLSD mFormData;
- EIgnoreType mIgnore;
- std::string mIgnoreMsg;
-};
-
-typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
-
-// This is the class of object read from the XML file (notifications.xml,
-// from the appropriate local language directory).
-struct LLNotificationTemplate
-{
- LLNotificationTemplate();
- // the name of the notification -- the key used to identify it
- // Ideally, the key should follow variable naming rules
- // (no spaces or punctuation).
- std::string mName;
- // The type of the notification
- // used to control which queue it's stored in
- std::string mType;
- // The text used to display the notification. Replaceable parameters
- // are enclosed in square brackets like this [].
- std::string mMessage;
- // The label for the notification; used for
- // certain classes of notification (those with a window and a window title).
- // Also used when a notification pops up underneath the current one.
- // Replaceable parameters can be used in the label.
- std::string mLabel;
- // The name of the icon image. This should include an extension.
- std::string mIcon;
- // This is the Highlander bit -- "There Can Be Only One"
- // An outstanding notification with this bit set
- // is updated by an incoming notification with the same name,
- // rather than creating a new entry in the queue.
- // (used for things like progress indications, or repeating warnings
- // like "the grid is going down in N minutes")
- bool mUnique;
- // if we want to be unique only if a certain part of the payload is constant
- // specify the field names for the payload. The notification will only be
- // combined if all of the fields named in the context are identical in the
- // new and the old notification; otherwise, the notification will be
- // duplicated. This is to support suppressing duplicate offers from the same
- // sender but still differentiating different offers. Example: Invitation to
- // conference chat.
- std::vector<std::string> mUniqueContext;
- // If this notification expires automatically, this value will be
- // nonzero, and indicates the number of seconds for which the notification
- // will be valid (a teleport offer, for example, might be valid for
- // 300 seconds).
- U32 mExpireSeconds;
- // if the offer expires, one of the options is chosen automatically
- // based on its "value" parameter. This controls which one.
- // If expireSeconds is specified, expireOption should also be specified.
- U32 mExpireOption;
- // if the notification contains a url, it's stored here (and replaced
- // into the message where [_URL] is found)
- std::string mURL;
- // if there's a URL in the message, this controls which option visits
- // that URL. Obsolete this and eliminate the buttons for affected
- // messages when we allow clickable URLs in the UI
- U32 mURLOption;
-
- U32 mURLOpenExternally;
- //This is a flag that tells if the url needs to open externally dispite
- //what the user setting is.
-
- // does this notification persist across sessions? if so, it will be
- // serialized to disk on first receipt and read on startup
- bool mPersist;
- // This is the name of the default functor, if present, to be
- // used for the notification's callback. It is optional, and used only if
- // the notification is constructed without an identified functor.
- std::string mDefaultFunctor;
- // The form data associated with a given notification (buttons, text boxes, etc)
- LLNotificationFormPtr mForm;
- // default priority for notifications of this type
- ENotificationPriority mPriority;
- // UUID of the audio file to be played when this notification arrives
- // this is loaded as a name, but looked up to get the UUID upon template load.
- // If null, it wasn't specified.
- LLUUID mSoundEffect;
-};
-
-// we want to keep a map of these by name, and it's best to manage them
-// with smart pointers
-typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;
-
-/**
- * @class LLNotification
- * @brief The object that expresses the details of a notification
- *
- * We make this noncopyable because
- * we want to manage these through LLNotificationPtr, and only
- * ever create one instance of any given notification.
- *
- * The enable_shared_from_this flag ensures that if we construct
- * a smart pointer from a notification, we'll always get the same
- * shared pointer.
- */
-class LLNotification :
- boost::noncopyable,
- public boost::enable_shared_from_this<LLNotification>
-{
-LOG_CLASS(LLNotification);
-friend class LLNotifications;
-
-public:
- // parameter object used to instantiate a new notification
- struct Params : public LLInitParam::Block<Params>
- {
- friend class LLNotification;
-
- Mandatory<std::string> name;
-
- // optional
- Optional<LLSD> substitutions;
- Optional<LLSD> payload;
- Optional<ENotificationPriority> priority;
- Optional<LLSD> form_elements;
- Optional<LLDate> timestamp;
- Optional<LLNotificationContext*> context;
-
- struct Functor : public LLInitParam::Choice<Functor>
- {
- Alternative<std::string> name;
- Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
-
- Functor()
- : name("functor_name"),
- function("functor")
- {}
- };
- Optional<Functor> functor;
-
- Params()
- : name("name"),
- priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- timestamp("time_stamp")
- {
- timestamp = LLDate::now();
- }
-
- Params(const std::string& _name)
- : name("name"),
- priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- timestamp("time_stamp")
- {
- functor.name = _name;
- name = _name;
- timestamp = LLDate::now();
- }
- };
-
-private:
-
- LLUUID mId;
- LLSD mPayload;
- LLSD mSubstitutions;
- LLDate mTimestamp;
- LLDate mExpiresAt;
- bool mCancelled;
- bool mRespondedTo; // once the notification has been responded to, this becomes true
- bool mIgnored;
- ENotificationPriority mPriority;
- LLNotificationFormPtr mForm;
-
- // a reference to the template
- LLNotificationTemplatePtr mTemplatep;
-
- /*
- We want to be able to store and reload notifications so that they can survive
- a shutdown/restart of the client. So we can't simply pass in callbacks;
- we have to specify a callback mechanism that can be used by name rather than
- by some arbitrary pointer -- and then people have to initialize callbacks
- in some useful location. So we use LLNotificationFunctorRegistry to manage them.
- */
- std::string mResponseFunctorName;
-
- /*
- In cases where we want to specify an explict, non-persisted callback,
- we store that in the callback registry under a dynamically generated
- key, and store the key in the notification, so we can still look it up
- using the same mechanism.
- */
- bool mTemporaryResponder;
-
- void init(const std::string& template_name, const LLSD& form_elements);
-
- LLNotification(const Params& p);
-
- // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT
- // for anything real!
- LLNotification(LLUUID uuid) : mId(uuid) {}
-
- void cancel();
-
- bool payloadContainsAll(const std::vector<std::string>& required_fields) const;
-
-public:
-
- // constructor from a saved notification
- LLNotification(const LLSD& sd);
-
- void setResponseFunctor(std::string const &responseFunctorName);
-
- typedef enum e_response_template_type
- {
- WITHOUT_DEFAULT_BUTTON,
- WITH_DEFAULT_BUTTON
- } EResponseTemplateType;
-
- // return response LLSD filled in with default form contents and (optionally) the default button selected
- LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON);
-
- // returns index of first button with value==TRUE
- // usually this the button the user clicked on
- // returns -1 if no button clicked (e.g. form has not been displayed)
- static S32 getSelectedOption(const LLSD& notification, const LLSD& response);
- // returns name of first button with value==TRUE
- static std::string getSelectedOptionName(const LLSD& notification);
-
- // after someone responds to a notification (usually by clicking a button,
- // but sometimes by filling out a little form and THEN clicking a button),
- // the result of the response (the name and value of the button clicked,
- // plus any other data) should be packaged up as LLSD, then passed as a
- // parameter to the notification's respond() method here. This will look up
- // and call the appropriate responder.
- //
- // response is notification serialized as LLSD:
- // ["name"] = notification name
- // ["form"] = LLSD tree that includes form description and any prefilled form data
- // ["response"] = form data filled in by user
- // (including, but not limited to which button they clicked on)
- // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),
- // ["item_id"] (attached inventory item), etc.
- // ["substitutions"] = string substitutions used to generate notification message
- // from the template
- // ["time"] = time at which notification was generated;
- // ["expiry"] = time at which notification expires;
- // ["responseFunctor"] = name of registered functor that handles responses to notification;
- LLSD asLLSD();
-
- void respond(const LLSD& sd);
-
- void setIgnored(bool ignore);
-
- bool isCancelled() const
- {
- return mCancelled;
- }
-
- bool isRespondedTo() const
- {
- return mRespondedTo;
- }
-
- bool isIgnored() const
- {
- return mIgnored;
- }
-
- const std::string& getName() const
- {
- return mTemplatep->mName;
- }
-
- const LLUUID& id() const
- {
- return mId;
- }
-
- const LLSD& getPayload() const
- {
- return mPayload;
- }
-
- const LLSD& getSubstitutions() const
- {
- return mSubstitutions;
- }
-
- const LLDate& getDate() const
- {
- return mTimestamp;
- }
-
- std::string getType() const
- {
- return (mTemplatep ? mTemplatep->mType : "");
- }
-
- std::string getMessage() const;
- std::string getLabel() const;
-
- std::string getURL() const;
-// {
-// return (mTemplatep ? mTemplatep->mURL : "");
-// }
-
- S32 getURLOption() const
- {
- return (mTemplatep ? mTemplatep->mURLOption : -1);
- }
-
- S32 getURLOpenExternally() const
- {
- return(mTemplatep? mTemplatep->mURLOpenExternally : -1);
- }
-
- const LLNotificationFormPtr getForm();
-
- const LLDate getExpiration() const
- {
- return mExpiresAt;
- }
-
- ENotificationPriority getPriority() const
- {
- return mPriority;
- }
-
- const LLUUID getID() const
- {
- return mId;
- }
-
- // comparing two notifications normally means comparing them by UUID (so we can look them
- // up quickly this way)
- bool operator<(const LLNotification& rhs) const
- {
- return mId < rhs.mId;
- }
-
- bool operator==(const LLNotification& rhs) const
- {
- return mId == rhs.mId;
- }
-
- bool operator!=(const LLNotification& rhs) const
- {
- return !operator==(rhs);
- }
-
- bool isSameObjectAs(const LLNotification* rhs) const
- {
- return this == rhs;
- }
-
- // this object has been updated, so tell all our clients
- void update();
-
- void updateFrom(LLNotificationPtr other);
-
- // A fuzzy equals comparator.
- // true only if both notifications have the same template and
- // 1) flagged as unique (there can be only one of these) OR
- // 2) all required payload fields of each also exist in the other.
- bool isEquivalentTo(LLNotificationPtr that) const;
-
- // if the current time is greater than the expiration, the notification is expired
- bool isExpired() const
- {
- if (mExpiresAt.secondsSinceEpoch() == 0)
- {
- return false;
- }
-
- LLDate rightnow = LLDate::now();
- return rightnow > mExpiresAt;
- }
-
- std::string summarize() const;
-
- bool hasUniquenessConstraints() const { return (mTemplatep ? mTemplatep->mUnique : false);}
-
- virtual ~LLNotification() {}
-};
-
-std::ostream& operator<<(std::ostream& s, const LLNotification& notification);
-
-namespace LLNotificationFilters
-{
- // a sample filter
- bool includeEverything(LLNotificationPtr p);
-
- typedef enum e_comparison
- {
- EQUAL,
- LESS,
- GREATER,
- LESS_EQUAL,
- GREATER_EQUAL
- } EComparison;
-
- // generic filter functor that takes method or member variable reference
- template<typename T>
- struct filterBy
- {
- typedef boost::function<T (LLNotificationPtr)> field_t;
- typedef typename boost::remove_reference<T>::type value_t;
-
- filterBy(field_t field, value_t value, EComparison comparison = EQUAL)
- : mField(field),
- mFilterValue(value),
- mComparison(comparison)
- {
- }
-
- bool operator()(LLNotificationPtr p)
- {
- switch(mComparison)
- {
- case EQUAL:
- return mField(p) == mFilterValue;
- case LESS:
- return mField(p) < mFilterValue;
- case GREATER:
- return mField(p) > mFilterValue;
- case LESS_EQUAL:
- return mField(p) <= mFilterValue;
- case GREATER_EQUAL:
- return mField(p) >= mFilterValue;
- default:
- return false;
- }
- }
-
- field_t mField;
- value_t mFilterValue;
- EComparison mComparison;
- };
-};
-
-namespace LLNotificationComparators
-{
- typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection;
-
- // generic order functor that takes method or member variable reference
- template<typename T>
- struct orderBy
- {
- typedef boost::function<T (LLNotificationPtr)> field_t;
- orderBy(field_t field, EDirection = ORDER_INCREASING) : mField(field) {}
- bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs)
- {
- if (mDirection == ORDER_DECREASING)
- {
- return mField(lhs) > mField(rhs);
- }
- else
- {
- return mField(lhs) < mField(rhs);
- }
- }
-
- field_t mField;
- EDirection mDirection;
- };
-
- struct orderByUUID : public orderBy<const LLUUID&>
- {
- orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {}
- };
-
- struct orderByDate : public orderBy<const LLDate&>
- {
- orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {}
- };
-};
-
-typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
-typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator;
-typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet;
-typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
-
-// ========================================================
-// Abstract base class (interface) for a channel; also used for the master container.
-// This lets us arrange channels into a call hierarchy.
-
-// We maintain a heirarchy of notification channels; events are always started at the top
-// and propagated through the hierarchy only if they pass a filter.
-// Any channel can be created with a parent. A null parent (empty string) means it's
-// tied to the root of the tree (the LLNotifications class itself).
-// The default hierarchy looks like this:
-//
-// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History
-// +-- Alerts
-// +-- Notifications
-//
-// In general, new channels that want to only see notifications that pass through
-// all of the built-in tests should attach to the "Visible" channel
-//
-class LLNotificationChannelBase :
- public LLEventTrackable
-{
- LOG_CLASS(LLNotificationChannelBase);
-public:
- LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) :
- mFilter(filter), mItems(comp)
- {}
- virtual ~LLNotificationChannelBase() {}
- // you can also connect to a Channel, so you can be notified of
- // changes to this channel
- template <typename LISTENER>
- LLBoundListener connectChanged(const LISTENER& slot)
- {
- // Examine slot to see if it binds an LLEventTrackable subclass, or a
- // boost::shared_ptr to something, or a boost::weak_ptr to something.
- // Call this->connectChangedImpl() to actually connect it.
- return LLEventDetail::visit_and_connect(slot,
- boost::bind(&LLNotificationChannelBase::connectChangedImpl,
- this,
- _1));
- }
- template <typename LISTENER>
- LLBoundListener connectPassedFilter(const LISTENER& slot)
- {
- // see comments in connectChanged()
- return LLEventDetail::visit_and_connect(slot,
- boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl,
- this,
- _1));
- }
- template <typename LISTENER>
- LLBoundListener connectFailedFilter(const LISTENER& slot)
- {
- // see comments in connectChanged()
- return LLEventDetail::visit_and_connect(slot,
- boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl,
- this,
- _1));
- }
-
- // use this when items change or to add a new one
- bool updateItem(const LLSD& payload);
- const LLNotificationFilter& getFilter() { return mFilter; }
-
-protected:
- LLBoundListener connectChangedImpl(const LLEventListener& slot);
- LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
- LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);
-
- LLNotificationSet mItems;
- LLStandardSignal mChanged;
- LLStandardSignal mPassedFilter;
- LLStandardSignal mFailedFilter;
-
- // these are action methods that subclasses can override to take action
- // on specific types of changes; the management of the mItems list is
- // still handled by the generic handler.
- virtual void onLoad(LLNotificationPtr p) {}
- virtual void onAdd(LLNotificationPtr p) {}
- virtual void onDelete(LLNotificationPtr p) {}
- virtual void onChange(LLNotificationPtr p) {}
-
- bool updateItem(const LLSD& payload, LLNotificationPtr pNotification);
- LLNotificationFilter mFilter;
-};
-
-// The type of the pointers that we're going to manage in the NotificationQueue system
-// Because LLNotifications is a singleton, we don't actually expect to ever
-// destroy it, but if it becomes necessary to do so, the shared_ptr model
-// will ensure that we don't leak resources.
-class LLNotificationChannel;
-typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr;
-
-// manages a list of notifications
-// Note that if this is ever copied around, we might find ourselves with multiple copies
-// of a queue with notifications being added to different nonequivalent copies. So we
-// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it.
-//
-// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to
-// do something like:
-// LLNotificationChannel::buildChannel("name", "parent"...);
-// This returns an LLNotificationChannelPtr, which you can store, or
-// you can then retrieve the channel by using the registry:
-// LLNotifications::instance().getChannel("name")...
-//
-class LLNotificationChannel :
- boost::noncopyable,
- public LLNotificationChannelBase
-{
- LOG_CLASS(LLNotificationChannel);
-
-public:
- virtual ~LLNotificationChannel() {}
- typedef LLNotificationSet::iterator Iterator;
-
- std::string getName() const { return mName; }
- std::string getParentChannelName() { return mParent; }
-
- bool isEmpty() const;
-
- Iterator begin();
- Iterator end();
-
- // Channels have a comparator to control sort order;
- // the default sorts by arrival date
- void setComparator(LLNotificationComparator comparator);
-
- std::string summarize();
-
- // factory method for constructing these channels; since they're self-registering,
- // we want to make sure that you can't use new to make them
- static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter=LLNotificationFilters::includeEverything,
- LLNotificationComparator comparator=LLNotificationComparators::orderByUUID());
-
-protected:
- // Notification Channels have a filter, which determines which notifications
- // will be added to this channel.
- // Channel filters cannot change.
- // Channels have a protected constructor so you can't make smart pointers that don't
- // come from our internal reference; call NotificationChannel::build(args)
- LLNotificationChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter, LLNotificationComparator comparator);
-
-private:
- std::string mName;
- std::string mParent;
- LLNotificationComparator mComparator;
-};
-
-
-class LLNotificationsListener;
-
-class LLNotifications :
- public LLSingleton<LLNotifications>,
- public LLNotificationChannelBase
-{
- LOG_CLASS(LLNotifications);
-
- friend class LLSingleton<LLNotifications>;
-public:
- // load notification descriptions from file;
- // OK to call more than once because it will reload
- bool loadTemplates();
- LLXMLNodePtr checkForXMLTemplate(LLXMLNodePtr item);
-
- // Add a simple notification (from XUI)
- void addFromCallback(const LLSD& name);
-
- // we provide a collection of simple add notification functions so that it's reasonable to create notifications in one line
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions = LLSD(),
- const LLSD& payload = LLSD());
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name);
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- LLNotificationFunctorRegistry::ResponseFunctor functor);
- LLNotificationPtr add(const LLNotification::Params& p);
-
- void add(const LLNotificationPtr pNotif);
- void cancel(LLNotificationPtr pNotif);
- void update(const LLNotificationPtr pNotif);
-
- LLNotificationPtr find(LLUUID uuid);
-
- typedef boost::function<void (LLNotificationPtr)> NotificationProcess;
-
- void forEachNotification(NotificationProcess process);
-
- // This is all stuff for managing the templates
- // take your template out
- LLNotificationTemplatePtr getTemplate(const std::string& name);
-
- // get the whole collection
- typedef std::vector<std::string> TemplateNames;
- TemplateNames getTemplateNames() const; // returns a list of notification names
-
- typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap;
-
- TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); }
- TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); }
-
- // test for existence
- bool templateExists(const std::string& name);
- // useful if you're reloading the file
- void clearTemplates(); // erase all templates
-
- void forceResponse(const LLNotification::Params& params, S32 option);
-
- void createDefaultChannels();
-
- typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap;
- ChannelMap mChannels;
-
- void addChannel(LLNotificationChannelPtr pChan);
- LLNotificationChannelPtr getChannel(const std::string& channelName);
-
- std::string getGlobalString(const std::string& key) const;
-
- void setIgnoreAllNotifications(bool ignore);
- bool getIgnoreAllNotifications();
-
-private:
- // we're a singleton, so we don't have a public constructor
- LLNotifications();
- /*virtual*/ void initSingleton();
-
- void loadPersistentNotifications();
-
- bool expirationFilter(LLNotificationPtr pNotification);
- bool expirationHandler(const LLSD& payload);
- bool uniqueFilter(LLNotificationPtr pNotification);
- bool uniqueHandler(const LLSD& payload);
- bool failedUniquenessTest(const LLSD& payload);
- LLNotificationChannelPtr pHistoryChannel;
- LLNotificationChannelPtr pExpirationChannel;
-
- // put your template in
- bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate);
- TemplateMap mTemplates;
-
- std::string mFileName;
-
- typedef std::map<std::string, LLXMLNodePtr> XMLTemplateMap;
- XMLTemplateMap mXmlTemplates;
-
- LLNotificationMap mUniqueNotifications;
-
- typedef std::map<std::string, std::string> GlobalStringMap;
- GlobalStringMap mGlobalStrings;
-
- bool mIgnoreAllNotifications;
-
- boost::scoped_ptr<LLNotificationsListener> mListener;
-};
-
-
-#endif//LL_LLNOTIFICATIONS_H
-
+/** +* @file llnotifications.h +* @brief Non-UI manager and support for keeping a prioritized list of notifications +* @author Q (with assistance from Richard and Coco) +* +* $LicenseInfo:firstyear=2008&license=viewergpl$ +* +* Copyright (c) 2008-2009, Linden Research, Inc. +* +* Second Life Viewer Source Code +* The source code in this file ("Source Code") is provided by Linden Lab +* to you under the terms of the GNU General Public License, version 2.0 +* ("GPL"), unless you have obtained a separate licensing agreement +* ("Other License"), formally executed by you and Linden Lab. Terms of +* the GPL can be found in doc/GPL-license.txt in this distribution, or +* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 +* +* There are special exceptions to the terms and conditions of the GPL as +* it is applied to this Source Code. View the full text of the exception +* in the file doc/FLOSS-exception.txt in this software distribution, or +* online at +* http://secondlifegrid.net/programs/open_source/licensing/flossexception +* +* By copying, modifying or distributing this software, you acknowledge +* that you have read and understood your obligations described above, +* and agree to abide by those obligations. +* +* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +* COMPLETENESS OR PERFORMANCE. +* $/LicenseInfo$ +*/ + +#ifndef LL_LLNOTIFICATIONS_H +#define LL_LLNOTIFICATIONS_H + +/** + * This system is intended to provide a singleton mechanism for adding + * notifications to one of an arbitrary set of event channels. + * + * Controlling JIRA: DEV-9061 + * + * Every notification has (see code for full list): + * - a textual name, which is used to look up its template in the XML files + * - a payload, which is a block of LLSD + * - a channel, which is normally extracted from the XML files but + * can be overridden. + * - a timestamp, used to order the notifications + * - expiration time -- if nonzero, specifies a time after which the + * notification will no longer be valid. + * - a callback name and a couple of status bits related to callbacks (see below) + * + * There is a management class called LLNotifications, which is an LLSingleton. + * The class maintains a collection of all of the notifications received + * or processed during this session, and also manages the persistence + * of those notifications that must be persisted. + * + * We also have Channels. A channel is a view on a collection of notifications; + * The collection is defined by a filter function that controls which + * notifications are in the channel, and its ordering is controlled by + * a comparator. + * + * There is a hierarchy of channels; notifications flow down from + * the management class (LLNotifications, which itself inherits from + * The channel base class) to the individual channels. + * Any change to notifications (add, delete, modify) is + * automatically propagated through the channel hierarchy. + * + * We provide methods for adding a new notification, for removing + * one, and for managing channels. Channels are relatively cheap to construct + * and maintain, so in general, human interfaces should use channels to + * select and manage their lists of notifications. + * + * We also maintain a collection of templates that are loaded from the + * XML file of template translations. The system supports substitution + * of named variables from the payload into the XML file. + * + * By default, only the "unknown message" template is built into the system. + * It is not an error to add a notification that's not found in the + * template system, but it is logged. + * + */ + +#include <string> +#include <list> +#include <vector> +#include <map> +#include <set> +#include <iomanip> +#include <sstream> + +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/type_traits.hpp> + +// we want to minimize external dependencies, but this one is important +#include "llsd.h" + +// and we need this to manage the notification callbacks +#include "llevents.h" +#include "llfunctorregistry.h" +#include "llui.h" +#include "llmemory.h" +#include "llnotificationslistener.h" + +class LLNotification; +typedef boost::shared_ptr<LLNotification> LLNotificationPtr; + + +typedef enum e_notification_priority +{ + NOTIFICATION_PRIORITY_UNSPECIFIED, + NOTIFICATION_PRIORITY_LOW, + NOTIFICATION_PRIORITY_NORMAL, + NOTIFICATION_PRIORITY_HIGH, + NOTIFICATION_PRIORITY_CRITICAL +} ENotificationPriority; + +typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder; + +typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry; +typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration; + +// context data that can be looked up via a notification's payload by the display logic +// derive from this class to implement specific contexts +class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID> +{ +public: + LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID()) + { + } + + virtual ~LLNotificationContext() {} + + LLSD asLLSD() const + { + return getKey(); + } + +private: + +}; + +// Contains notification form data, such as buttons and text fields along with +// manipulator functions +class LLNotificationForm +{ + LOG_CLASS(LLNotificationForm); + +public: + typedef enum e_ignore_type + { + IGNORE_NO, + IGNORE_WITH_DEFAULT_RESPONSE, + IGNORE_WITH_LAST_RESPONSE, + IGNORE_SHOW_AGAIN + } EIgnoreType; + + LLNotificationForm(); + LLNotificationForm(const LLSD& sd); + LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node); + + LLSD asLLSD() const; + + S32 getNumElements() { return mFormData.size(); } + LLSD getElement(S32 index) { return mFormData.get(index); } + LLSD getElement(const std::string& element_name); + bool hasElement(const std::string& element_name); + void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD()); + void formatElements(const LLSD& substitutions); + // appends form elements from another form serialized as LLSD + void append(const LLSD& sub_form); + std::string getDefaultOption(); + + EIgnoreType getIgnoreType() { return mIgnore; } + std::string getIgnoreMessage() { return mIgnoreMsg; } + +private: + LLSD mFormData; + EIgnoreType mIgnore; + std::string mIgnoreMsg; +}; + +typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr; + +// This is the class of object read from the XML file (notifications.xml, +// from the appropriate local language directory). +struct LLNotificationTemplate +{ + LLNotificationTemplate(); + // the name of the notification -- the key used to identify it + // Ideally, the key should follow variable naming rules + // (no spaces or punctuation). + std::string mName; + // The type of the notification + // used to control which queue it's stored in + std::string mType; + // The text used to display the notification. Replaceable parameters + // are enclosed in square brackets like this []. + std::string mMessage; + // The label for the notification; used for + // certain classes of notification (those with a window and a window title). + // Also used when a notification pops up underneath the current one. + // Replaceable parameters can be used in the label. + std::string mLabel; + // The name of the icon image. This should include an extension. + std::string mIcon; + // This is the Highlander bit -- "There Can Be Only One" + // An outstanding notification with this bit set + // is updated by an incoming notification with the same name, + // rather than creating a new entry in the queue. + // (used for things like progress indications, or repeating warnings + // like "the grid is going down in N minutes") + bool mUnique; + // if we want to be unique only if a certain part of the payload is constant + // specify the field names for the payload. The notification will only be + // combined if all of the fields named in the context are identical in the + // new and the old notification; otherwise, the notification will be + // duplicated. This is to support suppressing duplicate offers from the same + // sender but still differentiating different offers. Example: Invitation to + // conference chat. + std::vector<std::string> mUniqueContext; + // If this notification expires automatically, this value will be + // nonzero, and indicates the number of seconds for which the notification + // will be valid (a teleport offer, for example, might be valid for + // 300 seconds). + U32 mExpireSeconds; + // if the offer expires, one of the options is chosen automatically + // based on its "value" parameter. This controls which one. + // If expireSeconds is specified, expireOption should also be specified. + U32 mExpireOption; + // if the notification contains a url, it's stored here (and replaced + // into the message where [_URL] is found) + std::string mURL; + // if there's a URL in the message, this controls which option visits + // that URL. Obsolete this and eliminate the buttons for affected + // messages when we allow clickable URLs in the UI + U32 mURLOption; + + U32 mURLOpenExternally; + //This is a flag that tells if the url needs to open externally dispite + //what the user setting is. + + // does this notification persist across sessions? if so, it will be + // serialized to disk on first receipt and read on startup + bool mPersist; + // This is the name of the default functor, if present, to be + // used for the notification's callback. It is optional, and used only if + // the notification is constructed without an identified functor. + std::string mDefaultFunctor; + // The form data associated with a given notification (buttons, text boxes, etc) + LLNotificationFormPtr mForm; + // default priority for notifications of this type + ENotificationPriority mPriority; + // UUID of the audio file to be played when this notification arrives + // this is loaded as a name, but looked up to get the UUID upon template load. + // If null, it wasn't specified. + LLUUID mSoundEffect; +}; + +// we want to keep a map of these by name, and it's best to manage them +// with smart pointers +typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr; + +/** + * @class LLNotification + * @brief The object that expresses the details of a notification + * + * We make this noncopyable because + * we want to manage these through LLNotificationPtr, and only + * ever create one instance of any given notification. + * + * The enable_shared_from_this flag ensures that if we construct + * a smart pointer from a notification, we'll always get the same + * shared pointer. + */ +class LLNotification : + boost::noncopyable, + public boost::enable_shared_from_this<LLNotification> +{ +LOG_CLASS(LLNotification); +friend class LLNotifications; + +public: + // parameter object used to instantiate a new notification + struct Params : public LLInitParam::Block<Params> + { + friend class LLNotification; + + Mandatory<std::string> name; + + // optional + Optional<LLSD> substitutions; + Optional<LLSD> payload; + Optional<ENotificationPriority> priority; + Optional<LLSD> form_elements; + Optional<LLDate> timestamp; + Optional<LLNotificationContext*> context; + + struct Functor : public LLInitParam::Choice<Functor> + { + Alternative<std::string> name; + Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function; + + Functor() + : name("functor_name"), + function("functor") + {} + }; + Optional<Functor> functor; + + Params() + : name("name"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + timestamp("time_stamp") + { + timestamp = LLDate::now(); + } + + Params(const std::string& _name) + : name("name"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + timestamp("time_stamp") + { + functor.name = _name; + name = _name; + timestamp = LLDate::now(); + } + }; + +private: + + LLUUID mId; + LLSD mPayload; + LLSD mSubstitutions; + LLDate mTimestamp; + LLDate mExpiresAt; + bool mCancelled; + bool mRespondedTo; // once the notification has been responded to, this becomes true + bool mIgnored; + ENotificationPriority mPriority; + LLNotificationFormPtr mForm; + + // a reference to the template + LLNotificationTemplatePtr mTemplatep; + + /* + We want to be able to store and reload notifications so that they can survive + a shutdown/restart of the client. So we can't simply pass in callbacks; + we have to specify a callback mechanism that can be used by name rather than + by some arbitrary pointer -- and then people have to initialize callbacks + in some useful location. So we use LLNotificationFunctorRegistry to manage them. + */ + std::string mResponseFunctorName; + + /* + In cases where we want to specify an explict, non-persisted callback, + we store that in the callback registry under a dynamically generated + key, and store the key in the notification, so we can still look it up + using the same mechanism. + */ + bool mTemporaryResponder; + + void init(const std::string& template_name, const LLSD& form_elements); + + LLNotification(const Params& p); + + // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT + // for anything real! + LLNotification(LLUUID uuid) : mId(uuid) {} + + void cancel(); + + bool payloadContainsAll(const std::vector<std::string>& required_fields) const; + +public: + + // constructor from a saved notification + LLNotification(const LLSD& sd); + + void setResponseFunctor(std::string const &responseFunctorName); + + typedef enum e_response_template_type + { + WITHOUT_DEFAULT_BUTTON, + WITH_DEFAULT_BUTTON + } EResponseTemplateType; + + // return response LLSD filled in with default form contents and (optionally) the default button selected + LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON); + + // returns index of first button with value==TRUE + // usually this the button the user clicked on + // returns -1 if no button clicked (e.g. form has not been displayed) + static S32 getSelectedOption(const LLSD& notification, const LLSD& response); + // returns name of first button with value==TRUE + static std::string getSelectedOptionName(const LLSD& notification); + + // after someone responds to a notification (usually by clicking a button, + // but sometimes by filling out a little form and THEN clicking a button), + // the result of the response (the name and value of the button clicked, + // plus any other data) should be packaged up as LLSD, then passed as a + // parameter to the notification's respond() method here. This will look up + // and call the appropriate responder. + // + // response is notification serialized as LLSD: + // ["name"] = notification name + // ["form"] = LLSD tree that includes form description and any prefilled form data + // ["response"] = form data filled in by user + // (including, but not limited to which button they clicked on) + // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification), + // ["item_id"] (attached inventory item), etc. + // ["substitutions"] = string substitutions used to generate notification message + // from the template + // ["time"] = time at which notification was generated; + // ["expiry"] = time at which notification expires; + // ["responseFunctor"] = name of registered functor that handles responses to notification; + LLSD asLLSD(); + + void respond(const LLSD& sd); + + void setIgnored(bool ignore); + + bool isCancelled() const + { + return mCancelled; + } + + bool isRespondedTo() const + { + return mRespondedTo; + } + + bool isIgnored() const + { + return mIgnored; + } + + const std::string& getName() const + { + return mTemplatep->mName; + } + + const LLUUID& id() const + { + return mId; + } + + const LLSD& getPayload() const + { + return mPayload; + } + + const LLSD& getSubstitutions() const + { + return mSubstitutions; + } + + const LLDate& getDate() const + { + return mTimestamp; + } + + std::string getType() const + { + return (mTemplatep ? mTemplatep->mType : ""); + } + + std::string getMessage() const; + std::string getLabel() const; + + std::string getURL() const; +// { +// return (mTemplatep ? mTemplatep->mURL : ""); +// } + + S32 getURLOption() const + { + return (mTemplatep ? mTemplatep->mURLOption : -1); + } + + S32 getURLOpenExternally() const + { + return(mTemplatep? mTemplatep->mURLOpenExternally : -1); + } + + const LLNotificationFormPtr getForm(); + + const LLDate getExpiration() const + { + return mExpiresAt; + } + + ENotificationPriority getPriority() const + { + return mPriority; + } + + const LLUUID getID() const + { + return mId; + } + + // comparing two notifications normally means comparing them by UUID (so we can look them + // up quickly this way) + bool operator<(const LLNotification& rhs) const + { + return mId < rhs.mId; + } + + bool operator==(const LLNotification& rhs) const + { + return mId == rhs.mId; + } + + bool operator!=(const LLNotification& rhs) const + { + return !operator==(rhs); + } + + bool isSameObjectAs(const LLNotification* rhs) const + { + return this == rhs; + } + + // this object has been updated, so tell all our clients + void update(); + + void updateFrom(LLNotificationPtr other); + + // A fuzzy equals comparator. + // true only if both notifications have the same template and + // 1) flagged as unique (there can be only one of these) OR + // 2) all required payload fields of each also exist in the other. + bool isEquivalentTo(LLNotificationPtr that) const; + + // if the current time is greater than the expiration, the notification is expired + bool isExpired() const + { + if (mExpiresAt.secondsSinceEpoch() == 0) + { + return false; + } + + LLDate rightnow = LLDate::now(); + return rightnow > mExpiresAt; + } + + std::string summarize() const; + + bool hasUniquenessConstraints() const { return (mTemplatep ? mTemplatep->mUnique : false);} + + virtual ~LLNotification() {} +}; + +std::ostream& operator<<(std::ostream& s, const LLNotification& notification); + +namespace LLNotificationFilters +{ + // a sample filter + bool includeEverything(LLNotificationPtr p); + + typedef enum e_comparison + { + EQUAL, + LESS, + GREATER, + LESS_EQUAL, + GREATER_EQUAL + } EComparison; + + // generic filter functor that takes method or member variable reference + template<typename T> + struct filterBy + { + typedef boost::function<T (LLNotificationPtr)> field_t; + typedef typename boost::remove_reference<T>::type value_t; + + filterBy(field_t field, value_t value, EComparison comparison = EQUAL) + : mField(field), + mFilterValue(value), + mComparison(comparison) + { + } + + bool operator()(LLNotificationPtr p) + { + switch(mComparison) + { + case EQUAL: + return mField(p) == mFilterValue; + case LESS: + return mField(p) < mFilterValue; + case GREATER: + return mField(p) > mFilterValue; + case LESS_EQUAL: + return mField(p) <= mFilterValue; + case GREATER_EQUAL: + return mField(p) >= mFilterValue; + default: + return false; + } + } + + field_t mField; + value_t mFilterValue; + EComparison mComparison; + }; +}; + +namespace LLNotificationComparators +{ + typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection; + + // generic order functor that takes method or member variable reference + template<typename T> + struct orderBy + { + typedef boost::function<T (LLNotificationPtr)> field_t; + orderBy(field_t field, EDirection = ORDER_INCREASING) : mField(field) {} + bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) + { + if (mDirection == ORDER_DECREASING) + { + return mField(lhs) > mField(rhs); + } + else + { + return mField(lhs) < mField(rhs); + } + } + + field_t mField; + EDirection mDirection; + }; + + struct orderByUUID : public orderBy<const LLUUID&> + { + orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {} + }; + + struct orderByDate : public orderBy<const LLDate&> + { + orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {} + }; +}; + +typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter; +typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator; +typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet; +typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap; + +// ======================================================== +// Abstract base class (interface) for a channel; also used for the master container. +// This lets us arrange channels into a call hierarchy. + +// We maintain a heirarchy of notification channels; events are always started at the top +// and propagated through the hierarchy only if they pass a filter. +// Any channel can be created with a parent. A null parent (empty string) means it's +// tied to the root of the tree (the LLNotifications class itself). +// The default hierarchy looks like this: +// +// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History +// +-- Alerts +// +-- Notifications +// +// In general, new channels that want to only see notifications that pass through +// all of the built-in tests should attach to the "Visible" channel +// +class LLNotificationChannelBase : + public LLEventTrackable +{ + LOG_CLASS(LLNotificationChannelBase); +public: + LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) : + mFilter(filter), mItems(comp) + {} + virtual ~LLNotificationChannelBase() {} + // you can also connect to a Channel, so you can be notified of + // changes to this channel + template <typename LISTENER> + LLBoundListener connectChanged(const LISTENER& slot) + { + // Examine slot to see if it binds an LLEventTrackable subclass, or a + // boost::shared_ptr to something, or a boost::weak_ptr to something. + // Call this->connectChangedImpl() to actually connect it. + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectChangedImpl, + this, + _1)); + } + template <typename LISTENER> + LLBoundListener connectPassedFilter(const LISTENER& slot) + { + // see comments in connectChanged() + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, + this, + _1)); + } + template <typename LISTENER> + LLBoundListener connectFailedFilter(const LISTENER& slot) + { + // see comments in connectChanged() + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl, + this, + _1)); + } + + // use this when items change or to add a new one + bool updateItem(const LLSD& payload); + const LLNotificationFilter& getFilter() { return mFilter; } + +protected: + LLBoundListener connectChangedImpl(const LLEventListener& slot); + LLBoundListener connectPassedFilterImpl(const LLEventListener& slot); + LLBoundListener connectFailedFilterImpl(const LLEventListener& slot); + + LLNotificationSet mItems; + LLStandardSignal mChanged; + LLStandardSignal mPassedFilter; + LLStandardSignal mFailedFilter; + + // these are action methods that subclasses can override to take action + // on specific types of changes; the management of the mItems list is + // still handled by the generic handler. + virtual void onLoad(LLNotificationPtr p) {} + virtual void onAdd(LLNotificationPtr p) {} + virtual void onDelete(LLNotificationPtr p) {} + virtual void onChange(LLNotificationPtr p) {} + + bool updateItem(const LLSD& payload, LLNotificationPtr pNotification); + LLNotificationFilter mFilter; +}; + +// The type of the pointers that we're going to manage in the NotificationQueue system +// Because LLNotifications is a singleton, we don't actually expect to ever +// destroy it, but if it becomes necessary to do so, the shared_ptr model +// will ensure that we don't leak resources. +class LLNotificationChannel; +typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr; + +// manages a list of notifications +// Note that if this is ever copied around, we might find ourselves with multiple copies +// of a queue with notifications being added to different nonequivalent copies. So we +// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it. +// +// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to +// do something like: +// LLNotificationChannel::buildChannel("name", "parent"...); +// This returns an LLNotificationChannelPtr, which you can store, or +// you can then retrieve the channel by using the registry: +// LLNotifications::instance().getChannel("name")... +// +class LLNotificationChannel : + boost::noncopyable, + public LLNotificationChannelBase +{ + LOG_CLASS(LLNotificationChannel); + +public: + virtual ~LLNotificationChannel() {} + typedef LLNotificationSet::iterator Iterator; + + std::string getName() const { return mName; } + std::string getParentChannelName() { return mParent; } + + bool isEmpty() const; + + Iterator begin(); + Iterator end(); + + // Channels have a comparator to control sort order; + // the default sorts by arrival date + void setComparator(LLNotificationComparator comparator); + + std::string summarize(); + + // factory method for constructing these channels; since they're self-registering, + // we want to make sure that you can't use new to make them + static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent, + LLNotificationFilter filter=LLNotificationFilters::includeEverything, + LLNotificationComparator comparator=LLNotificationComparators::orderByUUID()); + +protected: + // Notification Channels have a filter, which determines which notifications + // will be added to this channel. + // Channel filters cannot change. + // Channels have a protected constructor so you can't make smart pointers that don't + // come from our internal reference; call NotificationChannel::build(args) + LLNotificationChannel(const std::string& name, const std::string& parent, + LLNotificationFilter filter, LLNotificationComparator comparator); + +private: + std::string mName; + std::string mParent; + LLNotificationComparator mComparator; +}; + + +class LLNotifications : + public LLSingleton<LLNotifications>, + public LLNotificationChannelBase +{ + LOG_CLASS(LLNotifications); + + friend class LLSingleton<LLNotifications>; +public: + // load notification descriptions from file; + // OK to call more than once because it will reload + bool loadTemplates(); + LLXMLNodePtr checkForXMLTemplate(LLXMLNodePtr item); + + // Add a simple notification (from XUI) + void addFromCallback(const LLSD& name); + + // we provide a collection of simple add notification functions so that it's reasonable to create notifications in one line + LLNotificationPtr add(const std::string& name, + const LLSD& substitutions = LLSD(), + const LLSD& payload = LLSD()); + LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + const std::string& functor_name); + LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor); + LLNotificationPtr add(const LLNotification::Params& p); + + void add(const LLNotificationPtr pNotif); + void cancel(LLNotificationPtr pNotif); + void update(const LLNotificationPtr pNotif); + + LLNotificationPtr find(LLUUID uuid); + + typedef boost::function<void (LLNotificationPtr)> NotificationProcess; + + void forEachNotification(NotificationProcess process); + + // This is all stuff for managing the templates + // take your template out + LLNotificationTemplatePtr getTemplate(const std::string& name); + + // get the whole collection + typedef std::vector<std::string> TemplateNames; + TemplateNames getTemplateNames() const; // returns a list of notification names + + typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap; + + TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); } + TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); } + + // test for existence + bool templateExists(const std::string& name); + // useful if you're reloading the file + void clearTemplates(); // erase all templates + + void forceResponse(const LLNotification::Params& params, S32 option); + + void createDefaultChannels(); + + typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap; + ChannelMap mChannels; + + void addChannel(LLNotificationChannelPtr pChan); + LLNotificationChannelPtr getChannel(const std::string& channelName); + + std::string getGlobalString(const std::string& key) const; + + void setIgnoreAllNotifications(bool ignore); + bool getIgnoreAllNotifications(); + +private: + // we're a singleton, so we don't have a public constructor + LLNotifications(); + /*virtual*/ void initSingleton(); + + void loadPersistentNotifications(); + + bool expirationFilter(LLNotificationPtr pNotification); + bool expirationHandler(const LLSD& payload); + bool uniqueFilter(LLNotificationPtr pNotification); + bool uniqueHandler(const LLSD& payload); + bool failedUniquenessTest(const LLSD& payload); + LLNotificationChannelPtr pHistoryChannel; + LLNotificationChannelPtr pExpirationChannel; + + // put your template in + bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate); + TemplateMap mTemplates; + + std::string mFileName; + + typedef std::map<std::string, LLXMLNodePtr> XMLTemplateMap; + XMLTemplateMap mXmlTemplates; + + LLNotificationMap mUniqueNotifications; + + typedef std::map<std::string, std::string> GlobalStringMap; + GlobalStringMap mGlobalStrings; + + bool mIgnoreAllNotifications; + + boost::scoped_ptr<LLNotificationsListener> mListener; +}; + + +#endif//LL_LLNOTIFICATIONS_H + |