From a4000c3744e42fcbb638e742f3b63fa31a0dee15 Mon Sep 17 00:00:00 2001 From: Steven Bennetts Date: Fri, 8 May 2009 07:43:08 +0000 Subject: merge trunk@116587 skinning-7@119389 -> viewer-2.0.0-skinning-7 --- indra/llui/llnotifications.h | 97 ++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 40 deletions(-) (limited to 'indra/llui/llnotifications.h') diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index bb379121cc..de86f5daa2 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -100,6 +100,7 @@ // and we need this to manage the notification callbacks #include "llfunctorregistry.h" #include "llui.h" +#include "llmemory.h" class LLNotification; typedef boost::shared_ptr LLNotificationPtr; @@ -280,6 +281,11 @@ struct LLNotificationTemplate // 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; @@ -322,42 +328,49 @@ friend class LLNotifications; public: // parameter object used to instantiate a new notification - class Params : public LLParamBlock + struct Params : public LLInitParam::Block { friend class LLNotification; - public: - Params(const std::string& _name) - : name(_name), - mTemporaryResponder(false), - functor_name(_name), - priority(NOTIFICATION_PRIORITY_UNSPECIFIED), - timestamp(LLDate::now()) + + Mandatory name; + + // optional + Optional substitutions; + Optional payload; + Optional priority; + Optional form_elements; + Optional timestamp; + Optional context; + + struct Functor : public LLInitParam::Choice { + Option name; + Option function; + + Functor() + : name("functor_name"), + function("functor") + {} + }; + Optional functor; + + Params() + : name("name"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + timestamp("time_stamp") + { + timestamp = LLDate::now(); } - // pseudo-param - Params& functor(LLNotificationFunctorRegistry::ResponseFunctor f) - { - functor_name = LLUUID::generateNewID().asString(); - LLNotificationFunctorRegistry::instance().registerFunctor(functor_name, f); - - mTemporaryResponder = true; - return *this; + Params(const std::string& _name) + : name("name"), + priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), + timestamp("time_stamp") + { + functor.name = _name; + name = _name; + timestamp = LLDate::now(); } - - LLMandatoryParam name; - - // optional - LLOptionalParam substitutions; - LLOptionalParam payload; - LLOptionalParam priority; - LLOptionalParam form_elements; - LLOptionalParam timestamp; - LLOptionalParam context; - LLOptionalParam functor_name; - - private: - bool mTemporaryResponder; }; private: @@ -410,10 +423,6 @@ public: // constructor from a saved notification LLNotification(const LLSD& sd); - // This is a string formatter for substituting into the message directly - // from LLSD without going through the hopefully-to-be-obsoleted LLString - static std::string format(const std::string& text, const LLSD& substitutions); - void setResponseFunctor(std::string const &responseFunctorName); typedef enum e_response_template_type @@ -505,16 +514,21 @@ public: std::string getMessage() const; std::string getLabel() const; - std::string getURL() const - { - return (mTemplatep ? mTemplatep->mURL : ""); - } + 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 @@ -813,7 +827,10 @@ public: // 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(), -- cgit v1.2.3 From 3800c0df910c83e987184d541b868168fc2b5bec Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 8 May 2009 21:08:08 +0000 Subject: svn merge -r114679:114681 svn+ssh://svn.lindenlab.com/svn/linden/branches/event-system/event-system-7 svn+ssh://svn.lindenlab.com/svn/linden/branches/event-system/event-system-8 --- indra/llui/llnotifications.h | 87 ++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) (limited to 'indra/llui/llnotifications.h') diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index de86f5daa2..5c8d146e0c 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -91,13 +91,14 @@ #include #include -#include +#include #include // 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" @@ -105,36 +106,6 @@ class LLNotification; typedef boost::shared_ptr LLNotificationPtr; -/***************************************************************************** -* Signal and handler declarations -* Using a single handler signature means that we can have a common handler -* type, rather than needing a distinct one for each different handler. -*****************************************************************************/ - -/** - * A boost::signals Combiner that stops the first time a handler returns true - * We need this because we want to have our handlers return bool, so that - * we have the option to cause a handler to stop further processing. The - * default handler fails when the signal returns a value but has no slots. - */ -struct LLStopWhenHandled -{ - typedef bool result_type; - - template - result_type operator()(InputIterator first, InputIterator last) const - { - for (InputIterator si = first; si != last; ++si) - { - if (*si) - { - return true; - } - } - return false; - } -}; - typedef enum e_notification_priority { @@ -145,27 +116,11 @@ typedef enum e_notification_priority NOTIFICATION_PRIORITY_CRITICAL } ENotificationPriority; -/** - * We want to have a standard signature for all signals; this way, - * we can easily document a protocol for communicating across - * dlls and into scripting languages someday. - * we want to return a bool to indicate whether the signal has been - * handled and should NOT be passed on to other listeners. - * Return true to stop further handling of the signal, and false - * to continue. - * We take an LLSD because this way the contents of the signal - * are independent of the API used to communicate it. - * It is const ref because then there's low cost to pass it; - * if you only need to inspect it, it's very cheap. - */ - typedef boost::function LLNotificationResponder; typedef LLFunctorRegistry LLNotificationFunctorRegistry; typedef LLFunctorRegistration LLNotificationFunctorRegistration; -typedef boost::signal LLStandardSignal; - // 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 @@ -713,7 +668,7 @@ typedef std::multimap LLNotificationMap; // all of the built-in tests should attach to the "Visible" channel // class LLNotificationChannelBase : - public boost::signals::trackable + public LLEventTrackable { LOG_CLASS(LLNotificationChannelBase); public: @@ -723,15 +678,45 @@ public: virtual ~LLNotificationChannelBase() {} // you can also connect to a Channel, so you can be notified of // changes to this channel - virtual void connectChanged(const LLStandardSignal::slot_type& slot); - virtual void connectPassedFilter(const LLStandardSignal::slot_type& slot); - virtual void connectFailedFilter(const LLStandardSignal::slot_type& slot); + template + 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 + LLBoundListener connectPassedFilter(const LISTENER& slot) + { + // see comments in connectChanged() + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, + this, + _1)); + } + template + 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; -- cgit v1.2.3 From 429bd9b55c54164d133276ed5b1fd54e565eb1b4 Mon Sep 17 00:00:00 2001 From: brad kittenbrink Date: Wed, 8 Jul 2009 12:07:31 -0700 Subject: Added LLNotificationsListener to hook LLNotifications to the event system according to https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API reviewed by palmer. --- indra/llui/llnotifications.h | 1823 +++++++++++++++++++++--------------------- 1 file changed, 913 insertions(+), 910 deletions(-) (limited to 'indra/llui/llnotifications.h') diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 512886790c..971d11db97 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -1,910 +1,913 @@ -/** -* @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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -// 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 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 LLNotificationResponder; - -typedef LLFunctorRegistry LLNotificationFunctorRegistry; -typedef LLFunctorRegistration 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 -{ -public: - LLNotificationContext() : LLInstanceTracker(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 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 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 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 -{ -LOG_CLASS(LLNotification); -friend class LLNotifications; - -public: - // parameter object used to instantiate a new notification - struct Params : public LLInitParam::Block - { - friend class LLNotification; - - Mandatory name; - - // optional - Optional substitutions; - Optional payload; - Optional priority; - Optional form_elements; - Optional timestamp; - Optional context; - - struct Functor : public LLInitParam::Choice - { - Alternative name; - Alternative function; - - Functor() - : name("functor_name"), - function("functor") - {} - }; - Optional 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& 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 - struct filterBy - { - typedef boost::function field_t; - typedef typename boost::remove_reference::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 - struct orderBy - { - typedef boost::function 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 - { - orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::id, direction) {} - }; - - struct orderByDate : public orderBy - { - orderByDate(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::getDate, direction) {} - }; -}; - -typedef boost::function LLNotificationFilter; -typedef boost::function LLNotificationComparator; -typedef std::set LLNotificationSet; -typedef std::multimap 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 - 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 - LLBoundListener connectPassedFilter(const LISTENER& slot) - { - // see comments in connectChanged() - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, - this, - _1)); - } - template - 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 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, - public LLNotificationChannelBase -{ - LOG_CLASS(LLNotifications); - - friend class LLSingleton; -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 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 TemplateNames; - TemplateNames getTemplateNames() const; // returns a list of notification names - - typedef std::map 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 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 XMLTemplateMap; - XMLTemplateMap mXmlTemplates; - - LLNotificationMap mUniqueNotifications; - - typedef std::map GlobalStringMap; - GlobalStringMap mGlobalStrings; - - bool mIgnoreAllNotifications; -}; - - -#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// 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 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 LLNotificationResponder; + +typedef LLFunctorRegistry LLNotificationFunctorRegistry; +typedef LLFunctorRegistration 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 +{ +public: + LLNotificationContext() : LLInstanceTracker(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 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 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 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 +{ +LOG_CLASS(LLNotification); +friend class LLNotifications; + +public: + // parameter object used to instantiate a new notification + struct Params : public LLInitParam::Block + { + friend class LLNotification; + + Mandatory name; + + // optional + Optional substitutions; + Optional payload; + Optional priority; + Optional form_elements; + Optional timestamp; + Optional context; + + struct Functor : public LLInitParam::Choice + { + Alternative name; + Alternative function; + + Functor() + : name("functor_name"), + function("functor") + {} + }; + Optional 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& 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 + struct filterBy + { + typedef boost::function field_t; + typedef typename boost::remove_reference::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 + struct orderBy + { + typedef boost::function 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 + { + orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::id, direction) {} + }; + + struct orderByDate : public orderBy + { + orderByDate(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::getDate, direction) {} + }; +}; + +typedef boost::function LLNotificationFilter; +typedef boost::function LLNotificationComparator; +typedef std::set LLNotificationSet; +typedef std::multimap 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 + 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 + LLBoundListener connectPassedFilter(const LISTENER& slot) + { + // see comments in connectChanged() + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, + this, + _1)); + } + template + 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 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, + public LLNotificationChannelBase +{ + LOG_CLASS(LLNotifications); + + friend class LLSingleton; +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 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 TemplateNames; + TemplateNames getTemplateNames() const; // returns a list of notification names + + typedef std::map 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 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 XMLTemplateMap; + XMLTemplateMap mXmlTemplates; + + LLNotificationMap mUniqueNotifications; + + typedef std::map GlobalStringMap; + GlobalStringMap mGlobalStrings; + + bool mIgnoreAllNotifications; + + boost::scoped_ptr mListener; +}; + + +#endif//LL_LLNOTIFICATIONS_H + -- cgit v1.2.3 From 61c27f2663aee60ba6eaeed57341915c8b8b5097 Mon Sep 17 00:00:00 2001 From: brad kittenbrink Date: Wed, 15 Jul 2009 17:54:11 -0700 Subject: Oops, LLNotificationsListener can't just be forward declared under gcc. --- indra/llui/llnotifications.h | 1825 +++++++++++++++++++++--------------------- 1 file changed, 912 insertions(+), 913 deletions(-) (limited to 'indra/llui/llnotifications.h') 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -// 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 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 LLNotificationResponder; - -typedef LLFunctorRegistry LLNotificationFunctorRegistry; -typedef LLFunctorRegistration 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 -{ -public: - LLNotificationContext() : LLInstanceTracker(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 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 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 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 -{ -LOG_CLASS(LLNotification); -friend class LLNotifications; - -public: - // parameter object used to instantiate a new notification - struct Params : public LLInitParam::Block - { - friend class LLNotification; - - Mandatory name; - - // optional - Optional substitutions; - Optional payload; - Optional priority; - Optional form_elements; - Optional timestamp; - Optional context; - - struct Functor : public LLInitParam::Choice - { - Alternative name; - Alternative function; - - Functor() - : name("functor_name"), - function("functor") - {} - }; - Optional 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& 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 - struct filterBy - { - typedef boost::function field_t; - typedef typename boost::remove_reference::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 - struct orderBy - { - typedef boost::function 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 - { - orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::id, direction) {} - }; - - struct orderByDate : public orderBy - { - orderByDate(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::getDate, direction) {} - }; -}; - -typedef boost::function LLNotificationFilter; -typedef boost::function LLNotificationComparator; -typedef std::set LLNotificationSet; -typedef std::multimap 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 - 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 - LLBoundListener connectPassedFilter(const LISTENER& slot) - { - // see comments in connectChanged() - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, - this, - _1)); - } - template - 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 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, - public LLNotificationChannelBase -{ - LOG_CLASS(LLNotifications); - - friend class LLSingleton; -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 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 TemplateNames; - TemplateNames getTemplateNames() const; // returns a list of notification names - - typedef std::map 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 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 XMLTemplateMap; - XMLTemplateMap mXmlTemplates; - - LLNotificationMap mUniqueNotifications; - - typedef std::map GlobalStringMap; - GlobalStringMap mGlobalStrings; - - bool mIgnoreAllNotifications; - - boost::scoped_ptr 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// 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 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 LLNotificationResponder; + +typedef LLFunctorRegistry LLNotificationFunctorRegistry; +typedef LLFunctorRegistration 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 +{ +public: + LLNotificationContext() : LLInstanceTracker(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 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 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 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 +{ +LOG_CLASS(LLNotification); +friend class LLNotifications; + +public: + // parameter object used to instantiate a new notification + struct Params : public LLInitParam::Block + { + friend class LLNotification; + + Mandatory name; + + // optional + Optional substitutions; + Optional payload; + Optional priority; + Optional form_elements; + Optional timestamp; + Optional context; + + struct Functor : public LLInitParam::Choice + { + Alternative name; + Alternative function; + + Functor() + : name("functor_name"), + function("functor") + {} + }; + Optional 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& 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 + struct filterBy + { + typedef boost::function field_t; + typedef typename boost::remove_reference::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 + struct orderBy + { + typedef boost::function 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 + { + orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::id, direction) {} + }; + + struct orderByDate : public orderBy + { + orderByDate(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::getDate, direction) {} + }; +}; + +typedef boost::function LLNotificationFilter; +typedef boost::function LLNotificationComparator; +typedef std::set LLNotificationSet; +typedef std::multimap 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 + 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 + LLBoundListener connectPassedFilter(const LISTENER& slot) + { + // see comments in connectChanged() + return LLEventDetail::visit_and_connect(slot, + boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, + this, + _1)); + } + template + 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 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, + public LLNotificationChannelBase +{ + LOG_CLASS(LLNotifications); + + friend class LLSingleton; +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 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 TemplateNames; + TemplateNames getTemplateNames() const; // returns a list of notification names + + typedef std::map 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 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 XMLTemplateMap; + XMLTemplateMap mXmlTemplates; + + LLNotificationMap mUniqueNotifications; + + typedef std::map GlobalStringMap; + GlobalStringMap mGlobalStrings; + + bool mIgnoreAllNotifications; + + boost::scoped_ptr mListener; +}; + + +#endif//LL_LLNOTIFICATIONS_H + -- cgit v1.2.3 From 9538328d5f7cf0f0be5dd77b8e27032e09104fff Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Fri, 24 Jul 2009 15:08:16 -0700 Subject: Adding LLLoginInstance unit test. - Added LLNotificationsInterface class. - Removed LLLoginInstance use of LLNotifications EventAPI --- indra/llui/llnotifications.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'indra/llui/llnotifications.h') diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 93cdcbeefd..c534267fca 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -799,8 +799,19 @@ private: LLNotificationComparator mComparator; }; +// An interface class to provide a clean linker seam to the LLNotifications class. +// Extend this interface as needed for your use of LLNotifications. +class LLNotificationsInterface +{ +public: + virtual LLNotificationPtr add(const std::string& name, + const LLSD& substitutions, + const LLSD& payload, + LLNotificationFunctorRegistry::ResponseFunctor functor) = 0; +}; class LLNotifications : + public LLNotificationsInterface, public LLSingleton, public LLNotificationChannelBase { @@ -824,7 +835,7 @@ public: const LLSD& substitutions, const LLSD& payload, const std::string& functor_name); - LLNotificationPtr add(const std::string& name, + /* virtual */ LLNotificationPtr add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor); -- cgit v1.2.3