diff options
| -rw-r--r-- | indra/llui/CMakeLists.txt | 378 | ||||
| -rw-r--r-- | indra/llui/llnotifications.cpp | 3005 | ||||
| -rw-r--r-- | indra/llui/llnotifications.h | 1823 | ||||
| -rw-r--r-- | indra/llui/llnotificationslistener.cpp | 28 | ||||
| -rw-r--r-- | indra/llui/llnotificationslistener.h | 31 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 6 | 
6 files changed, 2672 insertions, 2599 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 117e8e28ab..a6a5ef1f92 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -1,188 +1,190 @@ -# -*- cmake -*- - -project(llui) - -include(00-Common) -include(LLAudio) -include(LLCommon) -include(LLImage) -include(LLMath) -include(LLMessage) -include(LLRender) -include(LLWindow) -include(LLVFS) -include(LLXML) - -include_directories( -    ${LLAUDIO_INCLUDE_DIRS} -    ${LLCOMMON_INCLUDE_DIRS} -    ${LLIMAGE_INCLUDE_DIRS} -    ${LLMATH_INCLUDE_DIRS} -    ${LLMESSAGE_INCLUDE_DIRS} -    ${LLRENDER_INCLUDE_DIRS} -    ${LLWINDOW_INCLUDE_DIRS} -    ${LLVFS_INCLUDE_DIRS} -    ${LLXML_INCLUDE_DIRS} -    ) - -set(llui_SOURCE_FILES -    llalertdialog.cpp -    llbutton.cpp -    llcheckboxctrl.cpp -    llclipboard.cpp -    llcombobox.cpp -    llconsole.cpp -    llcontainerview.cpp -    llctrlselectioninterface.cpp -    lldraghandle.cpp -    lleditmenuhandler.cpp -    llf32uictrl.cpp -    llfloater.cpp -    llfloaterreg.cpp -    llflyoutbutton.cpp  -    llfocusmgr.cpp -    llfunctorregistry.cpp -    lliconctrl.cpp -    llinitparam.cpp -    llkeywords.cpp -    lllayoutstack.cpp -    lllineeditor.cpp -    llmenugl.cpp -    llmodaldialog.cpp -    llmultifloater.cpp  -    llmultislider.cpp -    llmultisliderctrl.cpp -    llnotifications.cpp -    llpanel.cpp -    llprogressbar.cpp -    llradiogroup.cpp -    llresizebar.cpp -    llresizehandle.cpp -    llresmgr.cpp -    llscrollbar.cpp -    llscrollcontainer.cpp -    llscrollingpanellist.cpp -    llscrolllistcell.cpp -    llscrolllistcolumn.cpp -    llscrolllistctrl.cpp -    llscrolllistitem.cpp -    llsdparam.cpp -    llsearcheditor.cpp  -    llslider.cpp -    llsliderctrl.cpp -    llspinctrl.cpp -    llstatbar.cpp -    llstatgraph.cpp -    llstatview.cpp -    llstyle.cpp -    lltabcontainer.cpp -    lltextbox.cpp -    lltexteditor.cpp -    lltextparser.cpp -    lltrans.cpp -    llui.cpp -    lluicolortable.cpp -    lluictrl.cpp -    lluictrlfactory.cpp -    lluiimage.cpp -    lluistring.cpp -    llundo.cpp -    llviewborder.cpp -    llviewmodel.cpp -    llview.cpp -    llviewquery.cpp -    ) -     -set(llui_HEADER_FILES -    CMakeLists.txt - -    llalertdialog.h -    llbutton.h -    llcallbackmap.h -    llcheckboxctrl.h -    llclipboard.h -    llcombobox.h -    llconsole.h -    llcontainerview.h -    llctrlselectioninterface.h -    lldraghandle.h -    lleditmenuhandler.h -    llf32uictrl.h -    llfloater.h -    llfloaterreg.h -    llflyoutbutton.h  -    llfocusmgr.h -    llfunctorregistry.h -    llhtmlhelp.h -    lliconctrl.h -    llinitparam.h -    llkeywords.h -    lllayoutstack.h -    lllazyvalue.h -    lllineeditor.h -    llmenugl.h -    llmodaldialog.h -    llmultifloater.h  -    llmultisliderctrl.h -    llmultislider.h -    llnotifications.h -    llpanel.h -    llprogressbar.h -    llradiogroup.h -    llregistry.h -    llresizebar.h -    llresizehandle.h -    llresmgr.h -    llsearcheditor.h  -    llscrollbar.h -    llscrollcontainer.h -    llscrollingpanellist.h -    llscrolllistcell.h -    llscrolllistcolumn.h -    llscrolllistctrl.h -    llscrolllistitem.h -    llsdparam.h -    llsliderctrl.h -    llslider.h -    llspinctrl.h -    llstatbar.h -    llstatgraph.h -    llstatview.h -    llstyle.h -    lltabcontainer.h -    lltextbox.h -    lltexteditor.h -    lltextparser.h -    lltrans.h -    lluicolortable.h -    lluiconstants.h -    lluictrlfactory.h -    lluictrl.h -    lluifwd.h -    llui.h -    lluiimage.h -    lluistring.h -    llundo.h -    llviewborder.h -    llviewmodel.h -    llview.h -    llviewquery.h -    ) - -set_source_files_properties(${llui_HEADER_FILES} -                            PROPERTIES HEADER_FILE_ONLY TRUE) - -list(APPEND llui_SOURCE_FILES ${llui_HEADER_FILES}) - -add_library (llui ${llui_SOURCE_FILES}) -# Libraries on which this library depends, needed for Linux builds -# Sort by high-level to low-level -target_link_libraries(llui -    llrender -    llwindow -    llimage -    llvfs       # ugh, just for LLDir -    llxml -    llcommon    # must be after llimage, llwindow, llrender -    llmath -    ) +# -*- cmake -*-
 +
 +project(llui)
 +
 +include(00-Common)
 +include(LLAudio)
 +include(LLCommon)
 +include(LLImage)
 +include(LLMath)
 +include(LLMessage)
 +include(LLRender)
 +include(LLWindow)
 +include(LLVFS)
 +include(LLXML)
 +
 +include_directories(
 +    ${LLAUDIO_INCLUDE_DIRS}
 +    ${LLCOMMON_INCLUDE_DIRS}
 +    ${LLIMAGE_INCLUDE_DIRS}
 +    ${LLMATH_INCLUDE_DIRS}
 +    ${LLMESSAGE_INCLUDE_DIRS}
 +    ${LLRENDER_INCLUDE_DIRS}
 +    ${LLWINDOW_INCLUDE_DIRS}
 +    ${LLVFS_INCLUDE_DIRS}
 +    ${LLXML_INCLUDE_DIRS}
 +    )
 +
 +set(llui_SOURCE_FILES
 +    llalertdialog.cpp
 +    llbutton.cpp
 +    llcheckboxctrl.cpp
 +    llclipboard.cpp
 +    llcombobox.cpp
 +    llconsole.cpp
 +    llcontainerview.cpp
 +    llctrlselectioninterface.cpp
 +    lldraghandle.cpp
 +    lleditmenuhandler.cpp
 +    llf32uictrl.cpp
 +    llfloater.cpp
 +    llfloaterreg.cpp
 +    llflyoutbutton.cpp 
 +    llfocusmgr.cpp
 +    llfunctorregistry.cpp
 +    lliconctrl.cpp
 +    llinitparam.cpp
 +    llkeywords.cpp
 +    lllayoutstack.cpp
 +    lllineeditor.cpp
 +    llmenugl.cpp
 +    llmodaldialog.cpp
 +    llmultifloater.cpp 
 +    llmultislider.cpp
 +    llmultisliderctrl.cpp
 +    llnotifications.cpp
 +    llnotificationslistener.cpp
 +    llpanel.cpp
 +    llprogressbar.cpp
 +    llradiogroup.cpp
 +    llresizebar.cpp
 +    llresizehandle.cpp
 +    llresmgr.cpp
 +    llscrollbar.cpp
 +    llscrollcontainer.cpp
 +    llscrollingpanellist.cpp
 +    llscrolllistcell.cpp
 +    llscrolllistcolumn.cpp
 +    llscrolllistctrl.cpp
 +    llscrolllistitem.cpp
 +    llsdparam.cpp
 +    llsearcheditor.cpp 
 +    llslider.cpp
 +    llsliderctrl.cpp
 +    llspinctrl.cpp
 +    llstatbar.cpp
 +    llstatgraph.cpp
 +    llstatview.cpp
 +    llstyle.cpp
 +    lltabcontainer.cpp
 +    lltextbox.cpp
 +    lltexteditor.cpp
 +    lltextparser.cpp
 +    lltrans.cpp
 +    llui.cpp
 +    lluicolortable.cpp
 +    lluictrl.cpp
 +    lluictrlfactory.cpp
 +    lluiimage.cpp
 +    lluistring.cpp
 +    llundo.cpp
 +    llviewborder.cpp
 +    llviewmodel.cpp
 +    llview.cpp
 +    llviewquery.cpp
 +    )
 +    
 +set(llui_HEADER_FILES
 +    CMakeLists.txt
 +
 +    llalertdialog.h
 +    llbutton.h
 +    llcallbackmap.h
 +    llcheckboxctrl.h
 +    llclipboard.h
 +    llcombobox.h
 +    llconsole.h
 +    llcontainerview.h
 +    llctrlselectioninterface.h
 +    lldraghandle.h
 +    lleditmenuhandler.h
 +    llf32uictrl.h
 +    llfloater.h
 +    llfloaterreg.h
 +    llflyoutbutton.h 
 +    llfocusmgr.h
 +    llfunctorregistry.h
 +    llhtmlhelp.h
 +    lliconctrl.h
 +    llinitparam.h
 +    llkeywords.h
 +    lllayoutstack.h
 +    lllazyvalue.h
 +    lllineeditor.h
 +    llmenugl.h
 +    llmodaldialog.h
 +    llmultifloater.h 
 +    llmultisliderctrl.h
 +    llmultislider.h
 +    llnotifications.h
 +    llnotificationslistener.h
 +    llpanel.h
 +    llprogressbar.h
 +    llradiogroup.h
 +    llregistry.h
 +    llresizebar.h
 +    llresizehandle.h
 +    llresmgr.h
 +    llsearcheditor.h 
 +    llscrollbar.h
 +    llscrollcontainer.h
 +    llscrollingpanellist.h
 +    llscrolllistcell.h
 +    llscrolllistcolumn.h
 +    llscrolllistctrl.h
 +    llscrolllistitem.h
 +    llsdparam.h
 +    llsliderctrl.h
 +    llslider.h
 +    llspinctrl.h
 +    llstatbar.h
 +    llstatgraph.h
 +    llstatview.h
 +    llstyle.h
 +    lltabcontainer.h
 +    lltextbox.h
 +    lltexteditor.h
 +    lltextparser.h
 +    lltrans.h
 +    lluicolortable.h
 +    lluiconstants.h
 +    lluictrlfactory.h
 +    lluictrl.h
 +    lluifwd.h
 +    llui.h
 +    lluiimage.h
 +    lluistring.h
 +    llundo.h
 +    llviewborder.h
 +    llviewmodel.h
 +    llview.h
 +    llviewquery.h
 +    )
 +
 +set_source_files_properties(${llui_HEADER_FILES}
 +                            PROPERTIES HEADER_FILE_ONLY TRUE)
 +
 +list(APPEND llui_SOURCE_FILES ${llui_HEADER_FILES})
 +
 +add_library (llui ${llui_SOURCE_FILES})
 +# Libraries on which this library depends, needed for Linux builds
 +# Sort by high-level to low-level
 +target_link_libraries(llui
 +    llrender
 +    llwindow
 +    llimage
 +    llvfs       # ugh, just for LLDir
 +    llxml
 +    llcommon    # must be after llimage, llwindow, llrender
 +    llmath
 +    )
 diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 50fee41029..ec92e57b6e 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1,1501 +1,1504 @@ -/** -* @file llnotifications.cpp -* @brief Non-UI queue manager for keeping a prioritized list of notifications -* -* $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$ -*/ - -#include "linden_common.h" - -#include "llnotifications.h" - -#include "lluictrl.h" -#include "lluictrlfactory.h" -#include "lldir.h" -#include "llsdserialize.h" -#include "lltrans.h" - -#include <algorithm> -#include <boost/regex.hpp> - - -const std::string NOTIFICATION_PERSIST_VERSION = "0.93"; - -// local channel for notification history -class LLNotificationHistoryChannel : public LLNotificationChannel -{ -	LOG_CLASS(LLNotificationHistoryChannel); -public: -	LLNotificationHistoryChannel(const std::string& filename) :  -		LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()), -		mFileName(filename) -	{ -		connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1)); -		loadPersistentNotifications(); -	} - -private: -	bool historyHandler(const LLSD& payload) -	{ -		// we ignore "load" messages, but rewrite the persistence file on any other -		std::string sigtype = payload["sigtype"]; -		if (sigtype != "load") -		{ -			savePersistentNotifications(); -		} -		return false; -	} - -	// The history channel gets all notifications except those that have been cancelled -	static bool historyFilter(LLNotificationPtr pNotification) -	{ -		return !pNotification->isCancelled(); -	} - -	void savePersistentNotifications() -	{ -		llinfos << "Saving open notifications to " << mFileName << llendl; - -		llofstream notify_file(mFileName.c_str()); -		if (!notify_file.is_open())  -		{ -			llwarns << "Failed to open " << mFileName << llendl; -			return; -		} - -		LLSD output; -		output["version"] = NOTIFICATION_PERSIST_VERSION; -		LLSD& data = output["data"]; - -		for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) -		{ -			if (!LLNotifications::instance().templateExists((*it)->getName())) continue; - -			// only store notifications flagged as persisting -			LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName()); -			if (!templatep->mPersist) continue; - -			data.append((*it)->asLLSD()); -		} - -		LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); -		formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); -	} - -	void loadPersistentNotifications() -	{ -		llinfos << "Loading open notifications from " << mFileName << llendl; - -		llifstream notify_file(mFileName.c_str()); -		if (!notify_file.is_open())  -		{ -			llwarns << "Failed to open " << mFileName << llendl; -			return; -		} - -		LLSD input; -		LLPointer<LLSDParser> parser = new LLSDXMLParser(); -		if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0) -		{ -			llwarns << "Failed to parse open notifications" << llendl; -			return; -		} - -		if (input.isUndefined()) return; -		std::string version = input["version"]; -		if (version != NOTIFICATION_PERSIST_VERSION) -		{ -			llwarns << "Bad open notifications version: " << version << llendl; -			return; -		} -		LLSD& data = input["data"]; -		if (data.isUndefined()) return; - -		LLNotifications& instance = LLNotifications::instance(); -		for (LLSD::array_const_iterator notification_it = data.beginArray(); -			notification_it != data.endArray(); -			++notification_it) -		{ -			instance.add(LLNotificationPtr(new LLNotification(*notification_it))); -		} -	} - -	//virtual -	void onDelete(LLNotificationPtr pNotification) -	{ -		// we want to keep deleted notifications in our log -		mItems.insert(pNotification); -		 -		return; -	} -	 -private: -	std::string mFileName; -}; - -bool filterIgnoredNotifications(LLNotificationPtr notification) -{ -	// filter everything if we are to ignore ALL -	if(LLNotifications::instance().getIgnoreAllNotifications()) -	{ -		return false; -	} - -	LLNotificationFormPtr form = notification->getForm(); -	// Check to see if the user wants to ignore this alert -	if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO) -	{ -		return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName()); -	} - -	return true; -} - -bool handleIgnoredNotification(const LLSD& payload) -{ -	if (payload["sigtype"].asString() == "add") -	{ -		LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); -		if (!pNotif) return false; - -		LLNotificationFormPtr form = pNotif->getForm(); -		LLSD response; -		switch(form->getIgnoreType()) -		{ -		case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE: -			response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON); -			break; -		case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE: -			response = LLUI::sSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName()); -			break; -		case LLNotificationForm::IGNORE_SHOW_AGAIN: -			break; -		default: -			return false; -		} -		pNotif->setIgnored(true); -		pNotif->respond(response); -		return true; 	// don't process this item any further -	} -	return false; -} - -namespace LLNotificationFilters -{ -	// a sample filter -	bool includeEverything(LLNotificationPtr p) -	{ -		return true; -	} -}; - -LLNotificationForm::LLNotificationForm() -:	mFormData(LLSD::emptyArray()), -	mIgnore(IGNORE_NO) -{ -} - - -LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node)  -:	mFormData(LLSD::emptyArray()), -	mIgnore(IGNORE_NO) -{ -	if (!xml_node->hasName("form")) -	{ -		llwarns << "Bad xml node for form: " << xml_node->getName() << llendl; -	} -	LLXMLNodePtr child = xml_node->getFirstChild(); -	while(child) -	{ -		child = LLNotifications::instance().checkForXMLTemplate(child); - -		LLSD item_entry; -		std::string element_name = child->getName()->mString; - -		if (element_name == "ignore" ) -		{ -			bool save_option = false; -			child->getAttribute_bool("save_option", save_option); -			if (!save_option) -			{ -				mIgnore = IGNORE_WITH_DEFAULT_RESPONSE; -			} -			else -			{ -				// remember last option chosen by user and automatically respond with that in the future -				mIgnore = IGNORE_WITH_LAST_RESPONSE; -				LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name)); -			} -			child->getAttributeString("text", mIgnoreMsg); -			BOOL show_notification = TRUE; -			LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE); -		} -		else -		{ -			// flatten xml form entry into single LLSD map with type==name -			item_entry["type"] = element_name; -			const LLXMLAttribList::iterator attrib_end = child->mAttributes.end(); -			for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin(); -				attrib_it != attrib_end; -				++attrib_it) -			{ -				item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue(); -			} -			item_entry["value"] = child->getTextContents(); -			mFormData.append(item_entry); -		} - -		child = child->getNextSibling(); -	} -} - -LLNotificationForm::LLNotificationForm(const LLSD& sd) -{ -	if (sd.isArray()) -	{ -		mFormData = sd; -	} -	else -	{ -		llwarns << "Invalid form data " << sd << llendl; -		mFormData = LLSD::emptyArray(); -	} -} - -LLSD LLNotificationForm::asLLSD() const -{  -	return mFormData;  -} - -LLSD LLNotificationForm::getElement(const std::string& element_name) -{ -	for (LLSD::array_const_iterator it = mFormData.beginArray(); -		it != mFormData.endArray(); -		++it) -	{ -		if ((*it)["name"].asString() == element_name) return (*it); -	} -	return LLSD(); -} - - -bool LLNotificationForm::hasElement(const std::string& element_name) -{ -	for (LLSD::array_const_iterator it = mFormData.beginArray(); -		it != mFormData.endArray(); -		++it) -	{ -		if ((*it)["name"].asString() == element_name) return true; -	} -	return false; -} - -void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value) -{ -	LLSD element; -	element["type"] = type; -	element["name"] = name; -	element["text"] = name; -	element["value"] = value; -	element["index"] = mFormData.size(); -	mFormData.append(element); -} - -void LLNotificationForm::append(const LLSD& sub_form) -{ -	if (sub_form.isArray()) -	{ -		for (LLSD::array_const_iterator it = sub_form.beginArray(); -			it != sub_form.endArray(); -			++it) -		{ -			mFormData.append(*it); -		} -	} -} - -void LLNotificationForm::formatElements(const LLSD& substitutions) -{ -	for (LLSD::array_iterator it = mFormData.beginArray(); -		it != mFormData.endArray(); -		++it) -	{ -		// format "text" component of each form element -		if ((*it).has("text")) -		{ -			std::string text = (*it)["text"].asString(); -			LLStringUtil::format(text, substitutions); -			(*it)["text"] = text; -		} -		if ((*it)["type"].asString() == "text" && (*it).has("value")) -		{ -			std::string value = (*it)["value"].asString(); -			LLStringUtil::format(value, substitutions); -			(*it)["value"] = value; -		} -	} -} - -std::string LLNotificationForm::getDefaultOption() -{ -	for (LLSD::array_const_iterator it = mFormData.beginArray(); -		it != mFormData.endArray(); -		++it) -	{ -		if ((*it)["default"]) return (*it)["name"].asString(); -	} -	return ""; -} - -LLNotificationTemplate::LLNotificationTemplate() : -	mExpireSeconds(0), -	mExpireOption(-1), -	mURLOption(-1), -    mURLOpenExternally(-1), -	mUnique(false), -	mPriority(NOTIFICATION_PRIORITY_NORMAL) -{ -	mForm = LLNotificationFormPtr(new LLNotificationForm());  -} - -LLNotification::LLNotification(const LLNotification::Params& p) :  -	mTimestamp(p.timestamp),  -	mSubstitutions(p.substitutions), -	mPayload(p.payload), -	mExpiresAt(0), -	mTemporaryResponder(false), -	mRespondedTo(false), -	mPriority(p.priority), -	mCancelled(false), -	mIgnored(false) -{ -	if (p.functor.name.isChosen()) -	{ -		mResponseFunctorName = p.functor.name; -	} -	else if (p.functor.function.isChosen()) -	{ -		mResponseFunctorName = LLUUID::generateNewID().asString(); -		LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function()); - -		mTemporaryResponder = true; -	} - -	mId.generate(); -	init(p.name, p.form_elements); -} - - -LLNotification::LLNotification(const LLSD& sd) : -	mTemporaryResponder(false), -	mRespondedTo(false), -	mCancelled(false), -	mIgnored(false) -{  -	mId.generate(); -	mSubstitutions = sd["substitutions"]; -	mPayload = sd["payload"];  -	mTimestamp = sd["time"];  -	mExpiresAt = sd["expiry"]; -	mPriority = (ENotificationPriority)sd["priority"].asInteger(); -	mResponseFunctorName = sd["responseFunctor"].asString(); -	std::string templatename = sd["name"].asString(); -	init(templatename, LLSD()); -	// replace form with serialized version -	mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"])); -} - - -LLSD LLNotification::asLLSD() -{ -	LLSD output; -	output["name"] = mTemplatep->mName; -	output["form"] = getForm()->asLLSD(); -	output["substitutions"] = mSubstitutions; -	output["payload"] = mPayload; -	output["time"] = mTimestamp; -	output["expiry"] = mExpiresAt; -	output["priority"] = (S32)mPriority; -	output["responseFunctor"] = mResponseFunctorName; -	return output; -} - -void LLNotification::update() -{ -	LLNotifications::instance().update(shared_from_this()); -} - -void LLNotification::updateFrom(LLNotificationPtr other) -{ -	// can only update from the same notification type -	if (mTemplatep != other->mTemplatep) return; - -	// NOTE: do NOT change the ID, since it is the key to -	// this given instance, just update all the metadata -	//mId = other->mId; - -	mPayload = other->mPayload; -	mSubstitutions = other->mSubstitutions; -	mTimestamp = other->mTimestamp; -	mExpiresAt = other->mExpiresAt; -	mCancelled = other->mCancelled; -	mIgnored = other->mIgnored; -	mPriority = other->mPriority; -	mForm = other->mForm; -	mResponseFunctorName = other->mResponseFunctorName; -	mRespondedTo = other->mRespondedTo; -	mTemporaryResponder = other->mTemporaryResponder; - -	update(); -} - -const LLNotificationFormPtr LLNotification::getForm() -{ -	return mForm; -} - -void LLNotification::cancel() -{ -	mCancelled = true; -} - -LLSD LLNotification::getResponseTemplate(EResponseTemplateType type) -{ -	LLSD response = LLSD::emptyMap(); -	for (S32 element_idx = 0; -		element_idx < mForm->getNumElements(); -		++element_idx) -	{ -		LLSD element = mForm->getElement(element_idx); -		if (element.has("name")) -		{ -			response[element["name"].asString()] = element["value"]; -		} - -		if ((type == WITH_DEFAULT_BUTTON)  -			&& element["default"].asBoolean()) -		{ -			response[element["name"].asString()] = true; -		} -	} -	return response; -} - -//static -S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response) -{ -	LLNotificationForm form(notification["form"]); - -	for (S32 element_idx = 0; -		element_idx < form.getNumElements(); -		++element_idx) -	{ -		LLSD element = form.getElement(element_idx); - -		// only look at buttons -		if (element["type"].asString() == "button"  -			&& response[element["name"].asString()].asBoolean()) -		{ -			return element["index"].asInteger(); -		} -	} - -	return -1; -} - -//static -std::string LLNotification::getSelectedOptionName(const LLSD& response) -{ -	for (LLSD::map_const_iterator response_it = response.beginMap(); -		response_it != response.endMap(); -		++response_it) -	{ -		if (response_it->second.isBoolean() && response_it->second.asBoolean()) -		{ -			return response_it->first; -		} -	} -	return ""; -} - - -void LLNotification::respond(const LLSD& response) -{ -	mRespondedTo = true; -	// look up the functor -	LLNotificationFunctorRegistry::ResponseFunctor functor =  -		LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName); -	// and then call it -	functor(asLLSD(), response); -	 -	if (mTemporaryResponder) -	{ -		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); -		mResponseFunctorName = ""; -		mTemporaryResponder = false; -	} - -	if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) -	{ -		BOOL show_notification = mIgnored ? FALSE : TRUE; -		LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification); -		if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) -		{ -			LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response); -		} -	} - -	update(); -} - -void LLNotification::setIgnored(bool ignore) -{ -	mIgnored = ignore; -} - -void LLNotification::setResponseFunctor(std::string const &responseFunctorName) -{ -	if (mTemporaryResponder) -		// get rid of the old one -		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); -	mResponseFunctorName = responseFunctorName; -	mTemporaryResponder = false; -} - -bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const -{ -	for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();  -		required_fields_it != required_fields.end(); -		required_fields_it++) -	{ -		std::string required_field_name = *required_fields_it; -		if( ! getPayload().has(required_field_name)) -		{ -			return false; // a required field was not found -		} -	} -	return true; // all required fields were found -} - -bool LLNotification::isEquivalentTo(LLNotificationPtr that) const -{ -	if (this->mTemplatep->mName != that->mTemplatep->mName)  -	{ -		return false; // must have the same template name or forget it -	} -	if (this->mTemplatep->mUnique) -	{ -		// highlander bit sez there can only be one of these -		return -			this->payloadContainsAll(that->mTemplatep->mUniqueContext) && -			that->payloadContainsAll(this->mTemplatep->mUniqueContext); -	} -	return false;  -} - -void LLNotification::init(const std::string& template_name, const LLSD& form_elements) -{ -	mTemplatep = LLNotifications::instance().getTemplate(template_name); -	if (!mTemplatep) return; - -	// add default substitutions -	const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs(); -	for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin(); -		 iter != default_args.end(); ++iter) -	{ -		mSubstitutions[iter->first] = iter->second; -	} -	mSubstitutions["_URL"] = getURL(); -	mSubstitutions["_NAME"] = template_name; -	// TODO: something like this so that a missing alert is sensible: -	//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions); - -	mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm)); -	mForm->append(form_elements); - -	// apply substitution to form labels -	mForm->formatElements(mSubstitutions); - -	LLDate rightnow = LLDate::now(); -	if (mTemplatep->mExpireSeconds) -	{ -		mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds); -	} - -	if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED) -	{ -		mPriority = mTemplatep->mPriority; -	} -} - -std::string LLNotification::summarize() const -{ -	std::string s = "Notification("; -	s += getName(); -	s += ") : "; -	s += mTemplatep ? mTemplatep->mMessage : ""; -	// should also include timestamp and expiration time (but probably not payload) -	return s; -} - -std::string LLNotification::getMessage() const -{ -	// all our callers cache this result, so it gives us more flexibility -	// to do the substitution at call time rather than attempting to  -	// cache it in the notification -	if (!mTemplatep) -		return std::string(); - -	std::string message = mTemplatep->mMessage; -	LLStringUtil::format(message, mSubstitutions); -	return message; -} - -std::string LLNotification::getLabel() const -{ -	std::string label = mTemplatep->mLabel; -	LLStringUtil::format(label, mSubstitutions); -	return (mTemplatep ? label : ""); -} - -std::string LLNotification::getURL() const -{ -	if (!mTemplatep) -		return std::string(); -	std::string url = mTemplatep->mURL; -	LLStringUtil::format(url, mSubstitutions); -	return (mTemplatep ? url : ""); -} - -// ========================================================= -// LLNotificationChannel implementation -// --- -LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot) -{ -	// when someone wants to connect to a channel, we first throw them -	// all of the notifications that are already in the channel -	// we use a special signal called "load" in case the channel wants to care -	// only about new notifications -	for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it) -	{ -		slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id())); -	} -	// and then connect the signal so that all future notifications will also be -	// forwarded. -	return mChanged.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot) -{ -	// these two filters only fire for notifications added after the current one, because -	// they don't participate in the hierarchy. -	return mPassedFilter.connect(slot); -} - -LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot) -{ -	return mFailedFilter.connect(slot); -} - -// external call, conforms to our standard signature -bool LLNotificationChannelBase::updateItem(const LLSD& payload) -{	 -	// first check to see if it's in the master list -	LLNotificationPtr pNotification	 = LLNotifications::instance().find(payload["id"]); -	if (!pNotification) -		return false;	// not found -	 -	return updateItem(payload, pNotification); -} - - -//FIX QUIT NOT WORKING - - -// internal call, for use in avoiding lookup -bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification) -{	 -	std::string cmd = payload["sigtype"]; -	LLNotificationSet::iterator foundItem = mItems.find(pNotification); -	bool wasFound = (foundItem != mItems.end()); -	bool passesFilter = mFilter(pNotification); -	 -	// first, we offer the result of the filter test to the simple -	// signals for pass/fail. One of these is guaranteed to be called. -	// If either signal returns true, the change processing is NOT performed -	// (so don't return true unless you know what you're doing!) -	bool abortProcessing = false; -	if (passesFilter) -	{ -		abortProcessing = mPassedFilter(payload); -	} -	else -	{ -		abortProcessing = mFailedFilter(payload); -	} -	 -	if (abortProcessing) -	{ -		return true; -	} -	 -	if (cmd == "load") -	{ -		// should be no reason we'd ever get a load if we already have it -		// if passes filter send a load message, else do nothing -		assert(!wasFound); -		if (passesFilter) -		{ -			// not in our list, add it and say so -			mItems.insert(pNotification); -			abortProcessing = mChanged(payload); -			onLoad(pNotification); -		} -	} -	else if (cmd == "change") -	{ -		// if it passes filter now and was found, we just send a change message -		// if it passes filter now and wasn't found, we have to add it -		// if it doesn't pass filter and wasn't found, we do nothing -		// if it doesn't pass filter and was found, we need to delete it -		if (passesFilter) -		{ -			if (wasFound) -			{ -				// it already existed, so this is a change -				// since it changed in place, all we have to do is resend the signal -				abortProcessing = mChanged(payload); -				onChange(pNotification); -			} -			else -			{ -				// not in our list, add it and say so -				mItems.insert(pNotification); -				// our payload is const, so make a copy before changing it -				LLSD newpayload = payload; -				newpayload["sigtype"] = "add"; -				abortProcessing = mChanged(newpayload); -				onChange(pNotification); -			} -		} -		else -		{ -			if (wasFound) -			{ -				// it already existed, so this is a delete -				mItems.erase(pNotification); -				// our payload is const, so make a copy before changing it -				LLSD newpayload = payload; -				newpayload["sigtype"] = "delete"; -				abortProcessing = mChanged(newpayload); -				onChange(pNotification); -			} -			// didn't pass, not on our list, do nothing -		} -	} -	else if (cmd == "add") -	{ -		// should be no reason we'd ever get an add if we already have it -		// if passes filter send an add message, else do nothing -		assert(!wasFound); -		if (passesFilter) -		{ -			// not in our list, add it and say so -			mItems.insert(pNotification); -			abortProcessing = mChanged(payload); -			onAdd(pNotification); -		} -	} -	else if (cmd == "delete") -	{ -		// if we have it in our list, pass on the delete, then delete it, else do nothing -		if (wasFound) -		{ -			abortProcessing = mChanged(payload); -			mItems.erase(pNotification); -			onDelete(pNotification); -		} -	} -	return abortProcessing; -} - -/* static */ -LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,  -															 const std::string& parent, -															 LLNotificationFilter filter,  -															 LLNotificationComparator comparator) -{ -	// note: this is not a leak; notifications are self-registering. -	// This factory helps to prevent excess deletions by making sure all smart -	// pointers to notification channels come from the same source -	new LLNotificationChannel(name, parent, filter, comparator); -	return LLNotifications::instance().getChannel(name); -} - - -LLNotificationChannel::LLNotificationChannel(const std::string& name,  -											 const std::string& parent, -											 LLNotificationFilter filter,  -											 LLNotificationComparator comparator) :  -LLNotificationChannelBase(filter, comparator), -mName(name), -mParent(parent) -{ -	// store myself in the channel map -	LLNotifications::instance().addChannel(LLNotificationChannelPtr(this)); -	// bind to notification broadcast -	if (parent.empty()) -	{ -		LLNotifications::instance().connectChanged( -			boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); -	} -	else -	{ -		LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent); -		p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); -	} -} - - -void LLNotificationChannel::setComparator(LLNotificationComparator comparator)  -{  -	mComparator = comparator;  -	LLNotificationSet s2(mComparator); -	s2.insert(mItems.begin(), mItems.end()); -	mItems.swap(s2); -	 -	// notify clients that we've been resorted -	mChanged(LLSD().insert("sigtype", "sort"));  -} - -bool LLNotificationChannel::isEmpty() const -{ -	return mItems.empty(); -} - -LLNotificationChannel::Iterator LLNotificationChannel::begin() -{ -	return mItems.begin(); -} - -LLNotificationChannel::Iterator LLNotificationChannel::end() -{ -	return mItems.end(); -} - -std::string LLNotificationChannel::summarize() -{ -	std::string s("Channel '"); -	s += mName; -	s += "'\n  "; -	for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it) -	{ -		s += (*it)->summarize(); -		s += "\n  "; -	} -	return s; -} - - -// --- -// END OF LLNotificationChannel implementation -// ========================================================= - - -// ========================================================= -// LLNotifications implementation -// --- -LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, -															   LLNotificationComparators::orderByUUID()), -									mIgnoreAllNotifications(false) -{ -	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); -} - - -// The expiration channel gets all notifications that are cancelled -bool LLNotifications::expirationFilter(LLNotificationPtr pNotification) -{ -	return pNotification->isCancelled() || pNotification->isRespondedTo(); -} - -bool LLNotifications::expirationHandler(const LLSD& payload) -{ -	if (payload["sigtype"].asString() != "delete") -	{ -		// anything added to this channel actually should be deleted from the master -		cancel(find(payload["id"])); -		return true;	// don't process this item any further -	} -	return false; -} - -bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) -{ -	if (!pNotif->hasUniquenessConstraints()) -	{ -		return true; -	} - -	// checks against existing unique notifications -	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); -		existing_it != mUniqueNotifications.end(); -		++existing_it) -	{ -		LLNotificationPtr existing_notification = existing_it->second; -		if (pNotif != existing_notification  -			&& pNotif->isEquivalentTo(existing_notification)) -		{ -			return false; -		} -	} - -	return true; -} - -bool LLNotifications::uniqueHandler(const LLSD& payload) -{ -	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); -	if (pNotif && pNotif->hasUniquenessConstraints())  -	{ -		if (payload["sigtype"].asString() == "add") -		{ -			// not a duplicate according to uniqueness criteria, so we keep it -			// and store it for future uniqueness checks -			mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif)); -		} -		else if (payload["sigtype"].asString() == "delete") -		{ -			mUniqueNotifications.erase(pNotif->getName()); -		} -	} - -	return false; -} - -bool LLNotifications::failedUniquenessTest(const LLSD& payload) -{ -	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); -	 -	if (!pNotif || !pNotif->hasUniquenessConstraints()) -	{ -		return false; -	} - -	// checks against existing unique notifications -	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); -		existing_it != mUniqueNotifications.end(); -		++existing_it) -	{ -		LLNotificationPtr existing_notification = existing_it->second; -		if (pNotif != existing_notification  -			&& pNotif->isEquivalentTo(existing_notification)) -		{ -			// copy notification instance data over to oldest instance -			// of this unique notification and update it -			existing_notification->updateFrom(pNotif); -			// then delete the new one -			pNotif->cancel(); -		} -	} - -	return false; -} - - -void LLNotifications::addChannel(LLNotificationChannelPtr pChan) -{ -	mChannels[pChan->getName()] = pChan; -} - -LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) -{ -	ChannelMap::iterator p = mChannels.find(channelName); -	if(p == mChannels.end()) -	{ -		llerrs << "Did not find channel named " << channelName << llendl; -	} -	return p->second; -} - - -// this function is called once at construction time, after the object is constructed. -void LLNotifications::initSingleton() -{ -	loadTemplates(); -	createDefaultChannels(); -} - -void LLNotifications::createDefaultChannels() -{ -	// now construct the various channels AFTER loading the notifications, -	// because the history channel is going to rewrite the stored notifications file -	LLNotificationChannel::buildChannel("Expiration", "", -		boost::bind(&LLNotifications::expirationFilter, this, _1)); -	LLNotificationChannel::buildChannel("Unexpired", "", -		!boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind -	LLNotificationChannel::buildChannel("Unique", "Unexpired", -		boost::bind(&LLNotifications::uniqueFilter, this, _1)); -	LLNotificationChannel::buildChannel("Ignore", "Unique", -		filterIgnoredNotifications); -	LLNotificationChannel::buildChannel("Visible", "Ignore", -		&LLNotificationFilters::includeEverything); - -	// create special history channel -	//std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" ); -	// use ^^^ when done debugging notifications serialization -	std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" ); -	// this isn't a leak, don't worry about the empty "new" -	new LLNotificationHistoryChannel(notifications_log_file); - -	// connect action methods to these channels -	LLNotifications::instance().getChannel("Expiration")-> -        connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); -	LLNotifications::instance().getChannel("Unique")-> -        connectChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1)); -	LLNotifications::instance().getChannel("Unique")-> -        connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); -	LLNotifications::instance().getChannel("Ignore")-> -		connectFailedFilter(&handleIgnoredNotification); -} - -bool LLNotifications::addTemplate(const std::string &name,  -								  LLNotificationTemplatePtr theTemplate) -{ -	if (mTemplates.count(name)) -	{ -		llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl; -		return false; -	} -	mTemplates[name] = theTemplate; -	return true; -} - -LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name) -{ -	if (mTemplates.count(name)) -	{ -		return mTemplates[name]; -	} -	else -	{ -		return mTemplates["MissingAlert"]; -	} -} - -bool LLNotifications::templateExists(const std::string& name) -{ -	return (mTemplates.count(name) != 0); -} - -void LLNotifications::clearTemplates() -{ -	mTemplates.clear(); -} - -void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option) -{ -	LLNotificationPtr temp_notify(new LLNotification(params)); -	LLSD response = temp_notify->getResponseTemplate(); -	LLSD selected_item = temp_notify->getForm()->getElement(option); -	 -	if (selected_item.isUndefined()) -	{ -		llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl; -		return; -	} -	response[selected_item["name"].asString()] = true; - -	temp_notify->respond(response); -} - -LLNotifications::TemplateNames LLNotifications::getTemplateNames() const -{ -	TemplateNames names; -	for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it) -	{ -		names.push_back(it->first); -	} -	return names; -} - -typedef std::map<std::string, std::string> StringMap; -void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements) -{ -	//llwarns << "replaceSubstitutionStrings" << llendl; -	// walk the list of attributes looking for replacements -	for (LLXMLAttribList::iterator it=node->mAttributes.begin(); -		 it != node->mAttributes.end(); ++it) -	{ -		std::string value = it->second->getValue(); -		if (value[0] == '$') -		{ -			value.erase(0, 1);	// trim off the $ -			std::string replacement; -			StringMap::const_iterator found = replacements.find(value); -			if (found != replacements.end()) -			{ -				replacement = found->second; -				//llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl; - -				it->second->setValue(replacement); -			} -			else -			{ -				llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl; -			} -		} -	} -	 -	// now walk the list of children and call this recursively. -	for (LLXMLNodePtr child = node->getFirstChild();  -		 child.notNull(); child = child->getNextSibling()) -	{ -		replaceSubstitutionStrings(child, replacements); -	} -} - -// private to this file -// returns true if the template request was invalid and there's nothing else we -// can do with this node, false if you should keep processing (it may have -// replaced the contents of the node referred to) -LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item) -{ -	if (item->hasName("usetemplate")) -	{ -		std::string replacementName; -		if (item->getAttributeString("name", replacementName)) -		{ -			StringMap replacements; -			for (LLXMLAttribList::const_iterator it=item->mAttributes.begin();  -				 it != item->mAttributes.end(); ++it) -			{ -				replacements[it->second->getName()->mString] = it->second->getValue(); -			} -			if (mXmlTemplates.count(replacementName)) -			{ -				item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]); -				 -				// walk the nodes looking for $(substitution) here and replace -				replaceSubstitutionStrings(item, replacements); -			} -			else -			{ -				llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl; -			} -		} -	} -	return item; -} - -bool LLNotifications::loadTemplates() -{ -	const std::string xml_filename = "notifications.xml"; -	LLXMLNodePtr root; -	 -	BOOL success  = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); -	 -	if (!success || root.isNull() || !root->hasName( "notifications" )) -	{ -		llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl; -		return false; -	} -	 -	clearTemplates(); -	 -	for (LLXMLNodePtr item = root->getFirstChild(); -		 item.notNull(); item = item->getNextSibling()) -	{ -		// we do this FIRST so that item can be changed if we  -		// encounter a usetemplate -- we just replace the -		// current xml node and keep processing -		item = checkForXMLTemplate(item); -		 -		if (item->hasName("global")) -		{ -			std::string global_name; -			if (item->getAttributeString("name", global_name)) -			{ -				mGlobalStrings[global_name] = item->getTextContents(); -			} -			continue; -		} -		 -		if (item->hasName("template")) -		{ -			// store an xml template; templates must have a single node (can contain -			// other nodes) -			std::string name; -			item->getAttributeString("name", name); -			LLXMLNodePtr ptr = item->getFirstChild(); -			mXmlTemplates[name] = ptr; -			continue; -		} -		 -		if (!item->hasName("notification")) -		{ -            llwarns << "Unexpected entity " << item->getName()->mString <<  -                       " found in " << xml_filename << llendl; -			continue; -		} -		 -		// now we know we have a notification entry, so let's build it -		LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate()); - -		if (!item->getAttributeString("name", pTemplate->mName)) -		{ -			llwarns << "Unable to parse notification with no name" << llendl; -			continue; -		} -		 -		//llinfos << "Parsing " << pTemplate->mName << llendl; -		 -		pTemplate->mMessage = item->getTextContents(); -		pTemplate->mDefaultFunctor = pTemplate->mName; -		item->getAttributeString("type", pTemplate->mType); -		item->getAttributeString("icon", pTemplate->mIcon); -		item->getAttributeString("label", pTemplate->mLabel); -		item->getAttributeU32("duration", pTemplate->mExpireSeconds); -		item->getAttributeU32("expireOption", pTemplate->mExpireOption); - -		std::string priority; -		item->getAttributeString("priority", priority); -		pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL; -		if (!priority.empty()) -		{ -			if (priority == "low")      pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW; -			if (priority == "normal")   pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL; -			if (priority == "high")     pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH; -			if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL; -		} -		 -		item->getAttributeString("functor", pTemplate->mDefaultFunctor); - -		BOOL persist = false; -		item->getAttributeBOOL("persist", persist); -		pTemplate->mPersist = persist; -		 -		std::string sound; -		item->getAttributeString("sound", sound); -		if (!sound.empty()) -		{ -			// test for bad sound effect name / missing effect -			if (LLUI::sSettingGroups["config"]->controlExists(sound)) -			{ -				pTemplate->mSoundEffect =  -					LLUUID(LLUI::sSettingGroups["config"]->getString(sound)); -			} -			else -			{ -				llwarns << "Unknown sound effect control name " << sound -					<< llendl; -			} -		} - -		for (LLXMLNodePtr child = item->getFirstChild(); -			 !child.isNull(); child = child->getNextSibling()) -		{ -			child = checkForXMLTemplate(child); -			 -			// <url> -			if (child->hasName("url")) -			{ -				pTemplate->mURL = child->getTextContents(); -				child->getAttributeU32("option", pTemplate->mURLOption); -				child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally); -			} -			 -            if (child->hasName("unique")) -            { -                pTemplate->mUnique = true; -                for (LLXMLNodePtr formitem = child->getFirstChild(); -                     !formitem.isNull(); formitem = formitem->getNextSibling()) -                { -                    if (formitem->hasName("context")) -                    { -                        std::string key; -                        formitem->getAttributeString("key", key); -                        pTemplate->mUniqueContext.push_back(key); -                        //llwarns << "adding " << key << " to unique context" << llendl; -                    } -                    else -                    { -                        llwarns << "'unique' has unrecognized subelement "  -                        << formitem->getName()->mString << llendl; -                    } -                } -            } -             -			// <form> -			if (child->hasName("form")) -			{ -                pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child)); -			} -		} -		addTemplate(pTemplate->mName, pTemplate); -	} -	 -	//std::ostringstream ostream; -	//root->writeToOstream(ostream, "\n  "); -	//llwarns << ostream.str() << llendl; -	 -	return true; -} - -// Add a simple notification (from XUI) -void LLNotifications::addFromCallback(const LLSD& name) -{ -	add(LLNotification::Params().name(name.asString()));	 -} - -// we provide a couple of simple add notification functions so that it's reasonable to create notifications in one line -LLNotificationPtr LLNotifications::add(const std::string& name,  -										const LLSD& substitutions,  -										const LLSD& payload) -{ -	LLNotification::Params::Functor functor_p; -	functor_p.name = name; -	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	 -} - -LLNotificationPtr LLNotifications::add(const std::string& name,  -										const LLSD& substitutions,  -										const LLSD& payload,  -										const std::string& functor_name) -{ -	LLNotification::Params::Functor functor_p; -	functor_p.name = functor_name; -	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	 -} - -LLNotificationPtr LLNotifications::add(const std::string& name,  -										const LLSD& substitutions,  -										const LLSD& payload,  -										LLNotificationFunctorRegistry::ResponseFunctor functor) -{ -	LLNotification::Params::Functor functor_p; -	functor_p.function = functor; -	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	 -} - -// generalized add function that takes a parameter block object for more complex instantiations -LLNotificationPtr LLNotifications::add(const LLNotification::Params& p) -{ -	LLNotificationPtr pNotif(new LLNotification(p)); -	add(pNotif); -	return pNotif; -} - - -void LLNotifications::add(const LLNotificationPtr pNotif) -{ -	// first see if we already have it -- if so, that's a problem -	LLNotificationSet::iterator it=mItems.find(pNotif); -	if (it != mItems.end()) -	{ -		llerrs << "Notification added a second time to the master notification channel." << llendl; -	} - -	updateItem(LLSD().insert("sigtype", "add").insert("id", pNotif->id()), pNotif); -} - -void LLNotifications::cancel(LLNotificationPtr pNotif) -{ -	LLNotificationSet::iterator it=mItems.find(pNotif); -	if (it == mItems.end()) -	{ -		llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl; -	} -	updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif); -	pNotif->cancel(); -} - -void LLNotifications::update(const LLNotificationPtr pNotif) -{ -	LLNotificationSet::iterator it=mItems.find(pNotif); -	if (it != mItems.end()) -	{ -		updateItem(LLSD().insert("sigtype", "change").insert("id", pNotif->id()), pNotif); -	} -} - - -LLNotificationPtr LLNotifications::find(LLUUID uuid) -{ -	LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid)); -	LLNotificationSet::iterator it=mItems.find(target); -	if (it == mItems.end()) -	{ -		llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl; -		return LLNotificationPtr((LLNotification*)NULL); -	} -	else -	{ -		return *it; -	} -} - -void LLNotifications::forEachNotification(NotificationProcess process) -{ -	std::for_each(mItems.begin(), mItems.end(), process); -} - -std::string LLNotifications::getGlobalString(const std::string& key) const -{ -	GlobalStringMap::const_iterator it = mGlobalStrings.find(key); -	if (it != mGlobalStrings.end()) -	{ -		return it->second; -	} -	else -	{ -		// if we don't have the key as a global, return the key itself so that the error -		// is self-diagnosing. -		return key; -	} -} - -void LLNotifications::setIgnoreAllNotifications(bool setting) -{ -	mIgnoreAllNotifications = setting;  -} -bool LLNotifications::getIgnoreAllNotifications() -{ -	return mIgnoreAllNotifications;  -} -													 -// --- -// END OF LLNotifications implementation -// ========================================================= - -std::ostream& operator<<(std::ostream& s, const LLNotification& notification) -{ -	s << notification.summarize(); -	return s; -} - +/**
 +* @file llnotifications.cpp
 +* @brief Non-UI queue manager for keeping a prioritized list of notifications
 +*
 +* $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$
 +*/
 +
 +#include "linden_common.h"
 +
 +#include "llnotifications.h"
 +
 +#include "lluictrl.h"
 +#include "lluictrlfactory.h"
 +#include "lldir.h"
 +#include "llsdserialize.h"
 +#include "lltrans.h"
 +#include "llnotificationslistener.h"
 +
 +#include <algorithm>
 +#include <boost/regex.hpp>
 +
 +
 +const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
 +
 +// local channel for notification history
 +class LLNotificationHistoryChannel : public LLNotificationChannel
 +{
 +	LOG_CLASS(LLNotificationHistoryChannel);
 +public:
 +	LLNotificationHistoryChannel(const std::string& filename) : 
 +		LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
 +		mFileName(filename)
 +	{
 +		connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
 +		loadPersistentNotifications();
 +	}
 +
 +private:
 +	bool historyHandler(const LLSD& payload)
 +	{
 +		// we ignore "load" messages, but rewrite the persistence file on any other
 +		std::string sigtype = payload["sigtype"];
 +		if (sigtype != "load")
 +		{
 +			savePersistentNotifications();
 +		}
 +		return false;
 +	}
 +
 +	// The history channel gets all notifications except those that have been cancelled
 +	static bool historyFilter(LLNotificationPtr pNotification)
 +	{
 +		return !pNotification->isCancelled();
 +	}
 +
 +	void savePersistentNotifications()
 +	{
 +		llinfos << "Saving open notifications to " << mFileName << llendl;
 +
 +		llofstream notify_file(mFileName.c_str());
 +		if (!notify_file.is_open()) 
 +		{
 +			llwarns << "Failed to open " << mFileName << llendl;
 +			return;
 +		}
 +
 +		LLSD output;
 +		output["version"] = NOTIFICATION_PERSIST_VERSION;
 +		LLSD& data = output["data"];
 +
 +		for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
 +		{
 +			if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
 +
 +			// only store notifications flagged as persisting
 +			LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
 +			if (!templatep->mPersist) continue;
 +
 +			data.append((*it)->asLLSD());
 +		}
 +
 +		LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
 +		formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
 +	}
 +
 +	void loadPersistentNotifications()
 +	{
 +		llinfos << "Loading open notifications from " << mFileName << llendl;
 +
 +		llifstream notify_file(mFileName.c_str());
 +		if (!notify_file.is_open()) 
 +		{
 +			llwarns << "Failed to open " << mFileName << llendl;
 +			return;
 +		}
 +
 +		LLSD input;
 +		LLPointer<LLSDParser> parser = new LLSDXMLParser();
 +		if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
 +		{
 +			llwarns << "Failed to parse open notifications" << llendl;
 +			return;
 +		}
 +
 +		if (input.isUndefined()) return;
 +		std::string version = input["version"];
 +		if (version != NOTIFICATION_PERSIST_VERSION)
 +		{
 +			llwarns << "Bad open notifications version: " << version << llendl;
 +			return;
 +		}
 +		LLSD& data = input["data"];
 +		if (data.isUndefined()) return;
 +
 +		LLNotifications& instance = LLNotifications::instance();
 +		for (LLSD::array_const_iterator notification_it = data.beginArray();
 +			notification_it != data.endArray();
 +			++notification_it)
 +		{
 +			instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
 +		}
 +	}
 +
 +	//virtual
 +	void onDelete(LLNotificationPtr pNotification)
 +	{
 +		// we want to keep deleted notifications in our log
 +		mItems.insert(pNotification);
 +		
 +		return;
 +	}
 +	
 +private:
 +	std::string mFileName;
 +};
 +
 +bool filterIgnoredNotifications(LLNotificationPtr notification)
 +{
 +	// filter everything if we are to ignore ALL
 +	if(LLNotifications::instance().getIgnoreAllNotifications())
 +	{
 +		return false;
 +	}
 +
 +	LLNotificationFormPtr form = notification->getForm();
 +	// Check to see if the user wants to ignore this alert
 +	if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
 +	{
 +		return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName());
 +	}
 +
 +	return true;
 +}
 +
 +bool handleIgnoredNotification(const LLSD& payload)
 +{
 +	if (payload["sigtype"].asString() == "add")
 +	{
 +		LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
 +		if (!pNotif) return false;
 +
 +		LLNotificationFormPtr form = pNotif->getForm();
 +		LLSD response;
 +		switch(form->getIgnoreType())
 +		{
 +		case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
 +			response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
 +			break;
 +		case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
 +			response = LLUI::sSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
 +			break;
 +		case LLNotificationForm::IGNORE_SHOW_AGAIN:
 +			break;
 +		default:
 +			return false;
 +		}
 +		pNotif->setIgnored(true);
 +		pNotif->respond(response);
 +		return true; 	// don't process this item any further
 +	}
 +	return false;
 +}
 +
 +namespace LLNotificationFilters
 +{
 +	// a sample filter
 +	bool includeEverything(LLNotificationPtr p)
 +	{
 +		return true;
 +	}
 +};
 +
 +LLNotificationForm::LLNotificationForm()
 +:	mFormData(LLSD::emptyArray()),
 +	mIgnore(IGNORE_NO)
 +{
 +}
 +
 +
 +LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node) 
 +:	mFormData(LLSD::emptyArray()),
 +	mIgnore(IGNORE_NO)
 +{
 +	if (!xml_node->hasName("form"))
 +	{
 +		llwarns << "Bad xml node for form: " << xml_node->getName() << llendl;
 +	}
 +	LLXMLNodePtr child = xml_node->getFirstChild();
 +	while(child)
 +	{
 +		child = LLNotifications::instance().checkForXMLTemplate(child);
 +
 +		LLSD item_entry;
 +		std::string element_name = child->getName()->mString;
 +
 +		if (element_name == "ignore" )
 +		{
 +			bool save_option = false;
 +			child->getAttribute_bool("save_option", save_option);
 +			if (!save_option)
 +			{
 +				mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
 +			}
 +			else
 +			{
 +				// remember last option chosen by user and automatically respond with that in the future
 +				mIgnore = IGNORE_WITH_LAST_RESPONSE;
 +				LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
 +			}
 +			child->getAttributeString("text", mIgnoreMsg);
 +			BOOL show_notification = TRUE;
 +			LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE);
 +		}
 +		else
 +		{
 +			// flatten xml form entry into single LLSD map with type==name
 +			item_entry["type"] = element_name;
 +			const LLXMLAttribList::iterator attrib_end = child->mAttributes.end();
 +			for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin();
 +				attrib_it != attrib_end;
 +				++attrib_it)
 +			{
 +				item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue();
 +			}
 +			item_entry["value"] = child->getTextContents();
 +			mFormData.append(item_entry);
 +		}
 +
 +		child = child->getNextSibling();
 +	}
 +}
 +
 +LLNotificationForm::LLNotificationForm(const LLSD& sd)
 +{
 +	if (sd.isArray())
 +	{
 +		mFormData = sd;
 +	}
 +	else
 +	{
 +		llwarns << "Invalid form data " << sd << llendl;
 +		mFormData = LLSD::emptyArray();
 +	}
 +}
 +
 +LLSD LLNotificationForm::asLLSD() const
 +{ 
 +	return mFormData; 
 +}
 +
 +LLSD LLNotificationForm::getElement(const std::string& element_name)
 +{
 +	for (LLSD::array_const_iterator it = mFormData.beginArray();
 +		it != mFormData.endArray();
 +		++it)
 +	{
 +		if ((*it)["name"].asString() == element_name) return (*it);
 +	}
 +	return LLSD();
 +}
 +
 +
 +bool LLNotificationForm::hasElement(const std::string& element_name)
 +{
 +	for (LLSD::array_const_iterator it = mFormData.beginArray();
 +		it != mFormData.endArray();
 +		++it)
 +	{
 +		if ((*it)["name"].asString() == element_name) return true;
 +	}
 +	return false;
 +}
 +
 +void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
 +{
 +	LLSD element;
 +	element["type"] = type;
 +	element["name"] = name;
 +	element["text"] = name;
 +	element["value"] = value;
 +	element["index"] = mFormData.size();
 +	mFormData.append(element);
 +}
 +
 +void LLNotificationForm::append(const LLSD& sub_form)
 +{
 +	if (sub_form.isArray())
 +	{
 +		for (LLSD::array_const_iterator it = sub_form.beginArray();
 +			it != sub_form.endArray();
 +			++it)
 +		{
 +			mFormData.append(*it);
 +		}
 +	}
 +}
 +
 +void LLNotificationForm::formatElements(const LLSD& substitutions)
 +{
 +	for (LLSD::array_iterator it = mFormData.beginArray();
 +		it != mFormData.endArray();
 +		++it)
 +	{
 +		// format "text" component of each form element
 +		if ((*it).has("text"))
 +		{
 +			std::string text = (*it)["text"].asString();
 +			LLStringUtil::format(text, substitutions);
 +			(*it)["text"] = text;
 +		}
 +		if ((*it)["type"].asString() == "text" && (*it).has("value"))
 +		{
 +			std::string value = (*it)["value"].asString();
 +			LLStringUtil::format(value, substitutions);
 +			(*it)["value"] = value;
 +		}
 +	}
 +}
 +
 +std::string LLNotificationForm::getDefaultOption()
 +{
 +	for (LLSD::array_const_iterator it = mFormData.beginArray();
 +		it != mFormData.endArray();
 +		++it)
 +	{
 +		if ((*it)["default"]) return (*it)["name"].asString();
 +	}
 +	return "";
 +}
 +
 +LLNotificationTemplate::LLNotificationTemplate() :
 +	mExpireSeconds(0),
 +	mExpireOption(-1),
 +	mURLOption(-1),
 +    mURLOpenExternally(-1),
 +	mUnique(false),
 +	mPriority(NOTIFICATION_PRIORITY_NORMAL)
 +{
 +	mForm = LLNotificationFormPtr(new LLNotificationForm()); 
 +}
 +
 +LLNotification::LLNotification(const LLNotification::Params& p) : 
 +	mTimestamp(p.timestamp), 
 +	mSubstitutions(p.substitutions),
 +	mPayload(p.payload),
 +	mExpiresAt(0),
 +	mTemporaryResponder(false),
 +	mRespondedTo(false),
 +	mPriority(p.priority),
 +	mCancelled(false),
 +	mIgnored(false)
 +{
 +	if (p.functor.name.isChosen())
 +	{
 +		mResponseFunctorName = p.functor.name;
 +	}
 +	else if (p.functor.function.isChosen())
 +	{
 +		mResponseFunctorName = LLUUID::generateNewID().asString();
 +		LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
 +
 +		mTemporaryResponder = true;
 +	}
 +
 +	mId.generate();
 +	init(p.name, p.form_elements);
 +}
 +
 +
 +LLNotification::LLNotification(const LLSD& sd) :
 +	mTemporaryResponder(false),
 +	mRespondedTo(false),
 +	mCancelled(false),
 +	mIgnored(false)
 +{ 
 +	mId.generate();
 +	mSubstitutions = sd["substitutions"];
 +	mPayload = sd["payload"]; 
 +	mTimestamp = sd["time"]; 
 +	mExpiresAt = sd["expiry"];
 +	mPriority = (ENotificationPriority)sd["priority"].asInteger();
 +	mResponseFunctorName = sd["responseFunctor"].asString();
 +	std::string templatename = sd["name"].asString();
 +	init(templatename, LLSD());
 +	// replace form with serialized version
 +	mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
 +}
 +
 +
 +LLSD LLNotification::asLLSD()
 +{
 +	LLSD output;
 +	output["name"] = mTemplatep->mName;
 +	output["form"] = getForm()->asLLSD();
 +	output["substitutions"] = mSubstitutions;
 +	output["payload"] = mPayload;
 +	output["time"] = mTimestamp;
 +	output["expiry"] = mExpiresAt;
 +	output["priority"] = (S32)mPriority;
 +	output["responseFunctor"] = mResponseFunctorName;
 +	return output;
 +}
 +
 +void LLNotification::update()
 +{
 +	LLNotifications::instance().update(shared_from_this());
 +}
 +
 +void LLNotification::updateFrom(LLNotificationPtr other)
 +{
 +	// can only update from the same notification type
 +	if (mTemplatep != other->mTemplatep) return;
 +
 +	// NOTE: do NOT change the ID, since it is the key to
 +	// this given instance, just update all the metadata
 +	//mId = other->mId;
 +
 +	mPayload = other->mPayload;
 +	mSubstitutions = other->mSubstitutions;
 +	mTimestamp = other->mTimestamp;
 +	mExpiresAt = other->mExpiresAt;
 +	mCancelled = other->mCancelled;
 +	mIgnored = other->mIgnored;
 +	mPriority = other->mPriority;
 +	mForm = other->mForm;
 +	mResponseFunctorName = other->mResponseFunctorName;
 +	mRespondedTo = other->mRespondedTo;
 +	mTemporaryResponder = other->mTemporaryResponder;
 +
 +	update();
 +}
 +
 +const LLNotificationFormPtr LLNotification::getForm()
 +{
 +	return mForm;
 +}
 +
 +void LLNotification::cancel()
 +{
 +	mCancelled = true;
 +}
 +
 +LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
 +{
 +	LLSD response = LLSD::emptyMap();
 +	for (S32 element_idx = 0;
 +		element_idx < mForm->getNumElements();
 +		++element_idx)
 +	{
 +		LLSD element = mForm->getElement(element_idx);
 +		if (element.has("name"))
 +		{
 +			response[element["name"].asString()] = element["value"];
 +		}
 +
 +		if ((type == WITH_DEFAULT_BUTTON) 
 +			&& element["default"].asBoolean())
 +		{
 +			response[element["name"].asString()] = true;
 +		}
 +	}
 +	return response;
 +}
 +
 +//static
 +S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
 +{
 +	LLNotificationForm form(notification["form"]);
 +
 +	for (S32 element_idx = 0;
 +		element_idx < form.getNumElements();
 +		++element_idx)
 +	{
 +		LLSD element = form.getElement(element_idx);
 +
 +		// only look at buttons
 +		if (element["type"].asString() == "button" 
 +			&& response[element["name"].asString()].asBoolean())
 +		{
 +			return element["index"].asInteger();
 +		}
 +	}
 +
 +	return -1;
 +}
 +
 +//static
 +std::string LLNotification::getSelectedOptionName(const LLSD& response)
 +{
 +	for (LLSD::map_const_iterator response_it = response.beginMap();
 +		response_it != response.endMap();
 +		++response_it)
 +	{
 +		if (response_it->second.isBoolean() && response_it->second.asBoolean())
 +		{
 +			return response_it->first;
 +		}
 +	}
 +	return "";
 +}
 +
 +
 +void LLNotification::respond(const LLSD& response)
 +{
 +	mRespondedTo = true;
 +	// look up the functor
 +	LLNotificationFunctorRegistry::ResponseFunctor functor = 
 +		LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
 +	// and then call it
 +	functor(asLLSD(), response);
 +	
 +	if (mTemporaryResponder)
 +	{
 +		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
 +		mResponseFunctorName = "";
 +		mTemporaryResponder = false;
 +	}
 +
 +	if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
 +	{
 +		BOOL show_notification = mIgnored ? FALSE : TRUE;
 +		LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification);
 +		if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
 +		{
 +			LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
 +		}
 +	}
 +
 +	update();
 +}
 +
 +void LLNotification::setIgnored(bool ignore)
 +{
 +	mIgnored = ignore;
 +}
 +
 +void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
 +{
 +	if (mTemporaryResponder)
 +		// get rid of the old one
 +		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
 +	mResponseFunctorName = responseFunctorName;
 +	mTemporaryResponder = false;
 +}
 +
 +bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
 +{
 +	for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin(); 
 +		required_fields_it != required_fields.end();
 +		required_fields_it++)
 +	{
 +		std::string required_field_name = *required_fields_it;
 +		if( ! getPayload().has(required_field_name))
 +		{
 +			return false; // a required field was not found
 +		}
 +	}
 +	return true; // all required fields were found
 +}
 +
 +bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
 +{
 +	if (this->mTemplatep->mName != that->mTemplatep->mName) 
 +	{
 +		return false; // must have the same template name or forget it
 +	}
 +	if (this->mTemplatep->mUnique)
 +	{
 +		// highlander bit sez there can only be one of these
 +		return
 +			this->payloadContainsAll(that->mTemplatep->mUniqueContext) &&
 +			that->payloadContainsAll(this->mTemplatep->mUniqueContext);
 +	}
 +	return false; 
 +}
 +
 +void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
 +{
 +	mTemplatep = LLNotifications::instance().getTemplate(template_name);
 +	if (!mTemplatep) return;
 +
 +	// add default substitutions
 +	const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
 +	for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
 +		 iter != default_args.end(); ++iter)
 +	{
 +		mSubstitutions[iter->first] = iter->second;
 +	}
 +	mSubstitutions["_URL"] = getURL();
 +	mSubstitutions["_NAME"] = template_name;
 +	// TODO: something like this so that a missing alert is sensible:
 +	//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
 +
 +	mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
 +	mForm->append(form_elements);
 +
 +	// apply substitution to form labels
 +	mForm->formatElements(mSubstitutions);
 +
 +	LLDate rightnow = LLDate::now();
 +	if (mTemplatep->mExpireSeconds)
 +	{
 +		mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
 +	}
 +
 +	if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
 +	{
 +		mPriority = mTemplatep->mPriority;
 +	}
 +}
 +
 +std::string LLNotification::summarize() const
 +{
 +	std::string s = "Notification(";
 +	s += getName();
 +	s += ") : ";
 +	s += mTemplatep ? mTemplatep->mMessage : "";
 +	// should also include timestamp and expiration time (but probably not payload)
 +	return s;
 +}
 +
 +std::string LLNotification::getMessage() const
 +{
 +	// all our callers cache this result, so it gives us more flexibility
 +	// to do the substitution at call time rather than attempting to 
 +	// cache it in the notification
 +	if (!mTemplatep)
 +		return std::string();
 +
 +	std::string message = mTemplatep->mMessage;
 +	LLStringUtil::format(message, mSubstitutions);
 +	return message;
 +}
 +
 +std::string LLNotification::getLabel() const
 +{
 +	std::string label = mTemplatep->mLabel;
 +	LLStringUtil::format(label, mSubstitutions);
 +	return (mTemplatep ? label : "");
 +}
 +
 +std::string LLNotification::getURL() const
 +{
 +	if (!mTemplatep)
 +		return std::string();
 +	std::string url = mTemplatep->mURL;
 +	LLStringUtil::format(url, mSubstitutions);
 +	return (mTemplatep ? url : "");
 +}
 +
 +// =========================================================
 +// LLNotificationChannel implementation
 +// ---
 +LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
 +{
 +	// when someone wants to connect to a channel, we first throw them
 +	// all of the notifications that are already in the channel
 +	// we use a special signal called "load" in case the channel wants to care
 +	// only about new notifications
 +	for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
 +	{
 +		slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id()));
 +	}
 +	// and then connect the signal so that all future notifications will also be
 +	// forwarded.
 +	return mChanged.connect(slot);
 +}
 +
 +LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
 +{
 +	// these two filters only fire for notifications added after the current one, because
 +	// they don't participate in the hierarchy.
 +	return mPassedFilter.connect(slot);
 +}
 +
 +LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
 +{
 +	return mFailedFilter.connect(slot);
 +}
 +
 +// external call, conforms to our standard signature
 +bool LLNotificationChannelBase::updateItem(const LLSD& payload)
 +{	
 +	// first check to see if it's in the master list
 +	LLNotificationPtr pNotification	 = LLNotifications::instance().find(payload["id"]);
 +	if (!pNotification)
 +		return false;	// not found
 +	
 +	return updateItem(payload, pNotification);
 +}
 +
 +
 +//FIX QUIT NOT WORKING
 +
 +
 +// internal call, for use in avoiding lookup
 +bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
 +{	
 +	std::string cmd = payload["sigtype"];
 +	LLNotificationSet::iterator foundItem = mItems.find(pNotification);
 +	bool wasFound = (foundItem != mItems.end());
 +	bool passesFilter = mFilter(pNotification);
 +	
 +	// first, we offer the result of the filter test to the simple
 +	// signals for pass/fail. One of these is guaranteed to be called.
 +	// If either signal returns true, the change processing is NOT performed
 +	// (so don't return true unless you know what you're doing!)
 +	bool abortProcessing = false;
 +	if (passesFilter)
 +	{
 +		abortProcessing = mPassedFilter(payload);
 +	}
 +	else
 +	{
 +		abortProcessing = mFailedFilter(payload);
 +	}
 +	
 +	if (abortProcessing)
 +	{
 +		return true;
 +	}
 +	
 +	if (cmd == "load")
 +	{
 +		// should be no reason we'd ever get a load if we already have it
 +		// if passes filter send a load message, else do nothing
 +		assert(!wasFound);
 +		if (passesFilter)
 +		{
 +			// not in our list, add it and say so
 +			mItems.insert(pNotification);
 +			abortProcessing = mChanged(payload);
 +			onLoad(pNotification);
 +		}
 +	}
 +	else if (cmd == "change")
 +	{
 +		// if it passes filter now and was found, we just send a change message
 +		// if it passes filter now and wasn't found, we have to add it
 +		// if it doesn't pass filter and wasn't found, we do nothing
 +		// if it doesn't pass filter and was found, we need to delete it
 +		if (passesFilter)
 +		{
 +			if (wasFound)
 +			{
 +				// it already existed, so this is a change
 +				// since it changed in place, all we have to do is resend the signal
 +				abortProcessing = mChanged(payload);
 +				onChange(pNotification);
 +			}
 +			else
 +			{
 +				// not in our list, add it and say so
 +				mItems.insert(pNotification);
 +				// our payload is const, so make a copy before changing it
 +				LLSD newpayload = payload;
 +				newpayload["sigtype"] = "add";
 +				abortProcessing = mChanged(newpayload);
 +				onChange(pNotification);
 +			}
 +		}
 +		else
 +		{
 +			if (wasFound)
 +			{
 +				// it already existed, so this is a delete
 +				mItems.erase(pNotification);
 +				// our payload is const, so make a copy before changing it
 +				LLSD newpayload = payload;
 +				newpayload["sigtype"] = "delete";
 +				abortProcessing = mChanged(newpayload);
 +				onChange(pNotification);
 +			}
 +			// didn't pass, not on our list, do nothing
 +		}
 +	}
 +	else if (cmd == "add")
 +	{
 +		// should be no reason we'd ever get an add if we already have it
 +		// if passes filter send an add message, else do nothing
 +		assert(!wasFound);
 +		if (passesFilter)
 +		{
 +			// not in our list, add it and say so
 +			mItems.insert(pNotification);
 +			abortProcessing = mChanged(payload);
 +			onAdd(pNotification);
 +		}
 +	}
 +	else if (cmd == "delete")
 +	{
 +		// if we have it in our list, pass on the delete, then delete it, else do nothing
 +		if (wasFound)
 +		{
 +			abortProcessing = mChanged(payload);
 +			mItems.erase(pNotification);
 +			onDelete(pNotification);
 +		}
 +	}
 +	return abortProcessing;
 +}
 +
 +/* static */
 +LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name, 
 +															 const std::string& parent,
 +															 LLNotificationFilter filter, 
 +															 LLNotificationComparator comparator)
 +{
 +	// note: this is not a leak; notifications are self-registering.
 +	// This factory helps to prevent excess deletions by making sure all smart
 +	// pointers to notification channels come from the same source
 +	new LLNotificationChannel(name, parent, filter, comparator);
 +	return LLNotifications::instance().getChannel(name);
 +}
 +
 +
 +LLNotificationChannel::LLNotificationChannel(const std::string& name, 
 +											 const std::string& parent,
 +											 LLNotificationFilter filter, 
 +											 LLNotificationComparator comparator) : 
 +LLNotificationChannelBase(filter, comparator),
 +mName(name),
 +mParent(parent)
 +{
 +	// store myself in the channel map
 +	LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
 +	// bind to notification broadcast
 +	if (parent.empty())
 +	{
 +		LLNotifications::instance().connectChanged(
 +			boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
 +	}
 +	else
 +	{
 +		LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
 +		p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
 +	}
 +}
 +
 +
 +void LLNotificationChannel::setComparator(LLNotificationComparator comparator) 
 +{ 
 +	mComparator = comparator; 
 +	LLNotificationSet s2(mComparator);
 +	s2.insert(mItems.begin(), mItems.end());
 +	mItems.swap(s2);
 +	
 +	// notify clients that we've been resorted
 +	mChanged(LLSD().insert("sigtype", "sort")); 
 +}
 +
 +bool LLNotificationChannel::isEmpty() const
 +{
 +	return mItems.empty();
 +}
 +
 +LLNotificationChannel::Iterator LLNotificationChannel::begin()
 +{
 +	return mItems.begin();
 +}
 +
 +LLNotificationChannel::Iterator LLNotificationChannel::end()
 +{
 +	return mItems.end();
 +}
 +
 +std::string LLNotificationChannel::summarize()
 +{
 +	std::string s("Channel '");
 +	s += mName;
 +	s += "'\n  ";
 +	for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it)
 +	{
 +		s += (*it)->summarize();
 +		s += "\n  ";
 +	}
 +	return s;
 +}
 +
 +
 +// ---
 +// END OF LLNotificationChannel implementation
 +// =========================================================
 +
 +
 +// =========================================================
 +// LLNotifications implementation
 +// ---
 +LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
 +															   LLNotificationComparators::orderByUUID()),
 +									mIgnoreAllNotifications(false)
 +{
 +	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
 +
 +    mListener.reset(new LLNotificationsListener(*this));
 +}
 +
 +
 +// The expiration channel gets all notifications that are cancelled
 +bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
 +{
 +	return pNotification->isCancelled() || pNotification->isRespondedTo();
 +}
 +
 +bool LLNotifications::expirationHandler(const LLSD& payload)
 +{
 +	if (payload["sigtype"].asString() != "delete")
 +	{
 +		// anything added to this channel actually should be deleted from the master
 +		cancel(find(payload["id"]));
 +		return true;	// don't process this item any further
 +	}
 +	return false;
 +}
 +
 +bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
 +{
 +	if (!pNotif->hasUniquenessConstraints())
 +	{
 +		return true;
 +	}
 +
 +	// checks against existing unique notifications
 +	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
 +		existing_it != mUniqueNotifications.end();
 +		++existing_it)
 +	{
 +		LLNotificationPtr existing_notification = existing_it->second;
 +		if (pNotif != existing_notification 
 +			&& pNotif->isEquivalentTo(existing_notification))
 +		{
 +			return false;
 +		}
 +	}
 +
 +	return true;
 +}
 +
 +bool LLNotifications::uniqueHandler(const LLSD& payload)
 +{
 +	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
 +	if (pNotif && pNotif->hasUniquenessConstraints()) 
 +	{
 +		if (payload["sigtype"].asString() == "add")
 +		{
 +			// not a duplicate according to uniqueness criteria, so we keep it
 +			// and store it for future uniqueness checks
 +			mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
 +		}
 +		else if (payload["sigtype"].asString() == "delete")
 +		{
 +			mUniqueNotifications.erase(pNotif->getName());
 +		}
 +	}
 +
 +	return false;
 +}
 +
 +bool LLNotifications::failedUniquenessTest(const LLSD& payload)
 +{
 +	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
 +	
 +	if (!pNotif || !pNotif->hasUniquenessConstraints())
 +	{
 +		return false;
 +	}
 +
 +	// checks against existing unique notifications
 +	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
 +		existing_it != mUniqueNotifications.end();
 +		++existing_it)
 +	{
 +		LLNotificationPtr existing_notification = existing_it->second;
 +		if (pNotif != existing_notification 
 +			&& pNotif->isEquivalentTo(existing_notification))
 +		{
 +			// copy notification instance data over to oldest instance
 +			// of this unique notification and update it
 +			existing_notification->updateFrom(pNotif);
 +			// then delete the new one
 +			pNotif->cancel();
 +		}
 +	}
 +
 +	return false;
 +}
 +
 +
 +void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
 +{
 +	mChannels[pChan->getName()] = pChan;
 +}
 +
 +LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
 +{
 +	ChannelMap::iterator p = mChannels.find(channelName);
 +	if(p == mChannels.end())
 +	{
 +		llerrs << "Did not find channel named " << channelName << llendl;
 +	}
 +	return p->second;
 +}
 +
 +
 +// this function is called once at construction time, after the object is constructed.
 +void LLNotifications::initSingleton()
 +{
 +	loadTemplates();
 +	createDefaultChannels();
 +}
 +
 +void LLNotifications::createDefaultChannels()
 +{
 +	// now construct the various channels AFTER loading the notifications,
 +	// because the history channel is going to rewrite the stored notifications file
 +	LLNotificationChannel::buildChannel("Expiration", "",
 +		boost::bind(&LLNotifications::expirationFilter, this, _1));
 +	LLNotificationChannel::buildChannel("Unexpired", "",
 +		!boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
 +	LLNotificationChannel::buildChannel("Unique", "Unexpired",
 +		boost::bind(&LLNotifications::uniqueFilter, this, _1));
 +	LLNotificationChannel::buildChannel("Ignore", "Unique",
 +		filterIgnoredNotifications);
 +	LLNotificationChannel::buildChannel("Visible", "Ignore",
 +		&LLNotificationFilters::includeEverything);
 +
 +	// create special history channel
 +	//std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
 +	// use ^^^ when done debugging notifications serialization
 +	std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
 +	// this isn't a leak, don't worry about the empty "new"
 +	new LLNotificationHistoryChannel(notifications_log_file);
 +
 +	// connect action methods to these channels
 +	LLNotifications::instance().getChannel("Expiration")->
 +        connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
 +	LLNotifications::instance().getChannel("Unique")->
 +        connectChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
 +	LLNotifications::instance().getChannel("Unique")->
 +        connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
 +	LLNotifications::instance().getChannel("Ignore")->
 +		connectFailedFilter(&handleIgnoredNotification);
 +}
 +
 +bool LLNotifications::addTemplate(const std::string &name, 
 +								  LLNotificationTemplatePtr theTemplate)
 +{
 +	if (mTemplates.count(name))
 +	{
 +		llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl;
 +		return false;
 +	}
 +	mTemplates[name] = theTemplate;
 +	return true;
 +}
 +
 +LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
 +{
 +	if (mTemplates.count(name))
 +	{
 +		return mTemplates[name];
 +	}
 +	else
 +	{
 +		return mTemplates["MissingAlert"];
 +	}
 +}
 +
 +bool LLNotifications::templateExists(const std::string& name)
 +{
 +	return (mTemplates.count(name) != 0);
 +}
 +
 +void LLNotifications::clearTemplates()
 +{
 +	mTemplates.clear();
 +}
 +
 +void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
 +{
 +	LLNotificationPtr temp_notify(new LLNotification(params));
 +	LLSD response = temp_notify->getResponseTemplate();
 +	LLSD selected_item = temp_notify->getForm()->getElement(option);
 +	
 +	if (selected_item.isUndefined())
 +	{
 +		llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl;
 +		return;
 +	}
 +	response[selected_item["name"].asString()] = true;
 +
 +	temp_notify->respond(response);
 +}
 +
 +LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
 +{
 +	TemplateNames names;
 +	for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
 +	{
 +		names.push_back(it->first);
 +	}
 +	return names;
 +}
 +
 +typedef std::map<std::string, std::string> StringMap;
 +void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
 +{
 +	//llwarns << "replaceSubstitutionStrings" << llendl;
 +	// walk the list of attributes looking for replacements
 +	for (LLXMLAttribList::iterator it=node->mAttributes.begin();
 +		 it != node->mAttributes.end(); ++it)
 +	{
 +		std::string value = it->second->getValue();
 +		if (value[0] == '$')
 +		{
 +			value.erase(0, 1);	// trim off the $
 +			std::string replacement;
 +			StringMap::const_iterator found = replacements.find(value);
 +			if (found != replacements.end())
 +			{
 +				replacement = found->second;
 +				//llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl;
 +
 +				it->second->setValue(replacement);
 +			}
 +			else
 +			{
 +				llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl;
 +			}
 +		}
 +	}
 +	
 +	// now walk the list of children and call this recursively.
 +	for (LLXMLNodePtr child = node->getFirstChild(); 
 +		 child.notNull(); child = child->getNextSibling())
 +	{
 +		replaceSubstitutionStrings(child, replacements);
 +	}
 +}
 +
 +// private to this file
 +// returns true if the template request was invalid and there's nothing else we
 +// can do with this node, false if you should keep processing (it may have
 +// replaced the contents of the node referred to)
 +LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item)
 +{
 +	if (item->hasName("usetemplate"))
 +	{
 +		std::string replacementName;
 +		if (item->getAttributeString("name", replacementName))
 +		{
 +			StringMap replacements;
 +			for (LLXMLAttribList::const_iterator it=item->mAttributes.begin(); 
 +				 it != item->mAttributes.end(); ++it)
 +			{
 +				replacements[it->second->getName()->mString] = it->second->getValue();
 +			}
 +			if (mXmlTemplates.count(replacementName))
 +			{
 +				item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]);
 +				
 +				// walk the nodes looking for $(substitution) here and replace
 +				replaceSubstitutionStrings(item, replacements);
 +			}
 +			else
 +			{
 +				llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl;
 +			}
 +		}
 +	}
 +	return item;
 +}
 +
 +bool LLNotifications::loadTemplates()
 +{
 +	const std::string xml_filename = "notifications.xml";
 +	LLXMLNodePtr root;
 +	
 +	BOOL success  = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
 +	
 +	if (!success || root.isNull() || !root->hasName( "notifications" ))
 +	{
 +		llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl;
 +		return false;
 +	}
 +	
 +	clearTemplates();
 +	
 +	for (LLXMLNodePtr item = root->getFirstChild();
 +		 item.notNull(); item = item->getNextSibling())
 +	{
 +		// we do this FIRST so that item can be changed if we 
 +		// encounter a usetemplate -- we just replace the
 +		// current xml node and keep processing
 +		item = checkForXMLTemplate(item);
 +		
 +		if (item->hasName("global"))
 +		{
 +			std::string global_name;
 +			if (item->getAttributeString("name", global_name))
 +			{
 +				mGlobalStrings[global_name] = item->getTextContents();
 +			}
 +			continue;
 +		}
 +		
 +		if (item->hasName("template"))
 +		{
 +			// store an xml template; templates must have a single node (can contain
 +			// other nodes)
 +			std::string name;
 +			item->getAttributeString("name", name);
 +			LLXMLNodePtr ptr = item->getFirstChild();
 +			mXmlTemplates[name] = ptr;
 +			continue;
 +		}
 +		
 +		if (!item->hasName("notification"))
 +		{
 +            llwarns << "Unexpected entity " << item->getName()->mString << 
 +                       " found in " << xml_filename << llendl;
 +			continue;
 +		}
 +		
 +		// now we know we have a notification entry, so let's build it
 +		LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate());
 +
 +		if (!item->getAttributeString("name", pTemplate->mName))
 +		{
 +			llwarns << "Unable to parse notification with no name" << llendl;
 +			continue;
 +		}
 +		
 +		//llinfos << "Parsing " << pTemplate->mName << llendl;
 +		
 +		pTemplate->mMessage = item->getTextContents();
 +		pTemplate->mDefaultFunctor = pTemplate->mName;
 +		item->getAttributeString("type", pTemplate->mType);
 +		item->getAttributeString("icon", pTemplate->mIcon);
 +		item->getAttributeString("label", pTemplate->mLabel);
 +		item->getAttributeU32("duration", pTemplate->mExpireSeconds);
 +		item->getAttributeU32("expireOption", pTemplate->mExpireOption);
 +
 +		std::string priority;
 +		item->getAttributeString("priority", priority);
 +		pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
 +		if (!priority.empty())
 +		{
 +			if (priority == "low")      pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW;
 +			if (priority == "normal")   pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
 +			if (priority == "high")     pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH;
 +			if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL;
 +		}
 +		
 +		item->getAttributeString("functor", pTemplate->mDefaultFunctor);
 +
 +		BOOL persist = false;
 +		item->getAttributeBOOL("persist", persist);
 +		pTemplate->mPersist = persist;
 +		
 +		std::string sound;
 +		item->getAttributeString("sound", sound);
 +		if (!sound.empty())
 +		{
 +			// test for bad sound effect name / missing effect
 +			if (LLUI::sSettingGroups["config"]->controlExists(sound))
 +			{
 +				pTemplate->mSoundEffect = 
 +					LLUUID(LLUI::sSettingGroups["config"]->getString(sound));
 +			}
 +			else
 +			{
 +				llwarns << "Unknown sound effect control name " << sound
 +					<< llendl;
 +			}
 +		}
 +
 +		for (LLXMLNodePtr child = item->getFirstChild();
 +			 !child.isNull(); child = child->getNextSibling())
 +		{
 +			child = checkForXMLTemplate(child);
 +			
 +			// <url>
 +			if (child->hasName("url"))
 +			{
 +				pTemplate->mURL = child->getTextContents();
 +				child->getAttributeU32("option", pTemplate->mURLOption);
 +				child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally);
 +			}
 +			
 +            if (child->hasName("unique"))
 +            {
 +                pTemplate->mUnique = true;
 +                for (LLXMLNodePtr formitem = child->getFirstChild();
 +                     !formitem.isNull(); formitem = formitem->getNextSibling())
 +                {
 +                    if (formitem->hasName("context"))
 +                    {
 +                        std::string key;
 +                        formitem->getAttributeString("key", key);
 +                        pTemplate->mUniqueContext.push_back(key);
 +                        //llwarns << "adding " << key << " to unique context" << llendl;
 +                    }
 +                    else
 +                    {
 +                        llwarns << "'unique' has unrecognized subelement " 
 +                        << formitem->getName()->mString << llendl;
 +                    }
 +                }
 +            }
 +            
 +			// <form>
 +			if (child->hasName("form"))
 +			{
 +                pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child));
 +			}
 +		}
 +		addTemplate(pTemplate->mName, pTemplate);
 +	}
 +	
 +	//std::ostringstream ostream;
 +	//root->writeToOstream(ostream, "\n  ");
 +	//llwarns << ostream.str() << llendl;
 +	
 +	return true;
 +}
 +
 +// Add a simple notification (from XUI)
 +void LLNotifications::addFromCallback(const LLSD& name)
 +{
 +	add(LLNotification::Params().name(name.asString()));	
 +}
 +
 +// we provide a couple of simple add notification functions so that it's reasonable to create notifications in one line
 +LLNotificationPtr LLNotifications::add(const std::string& name, 
 +										const LLSD& substitutions, 
 +										const LLSD& payload)
 +{
 +	LLNotification::Params::Functor functor_p;
 +	functor_p.name = name;
 +	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
 +}
 +
 +LLNotificationPtr LLNotifications::add(const std::string& name, 
 +										const LLSD& substitutions, 
 +										const LLSD& payload, 
 +										const std::string& functor_name)
 +{
 +	LLNotification::Params::Functor functor_p;
 +	functor_p.name = functor_name;
 +	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
 +}
 +
 +LLNotificationPtr LLNotifications::add(const std::string& name, 
 +										const LLSD& substitutions, 
 +										const LLSD& payload, 
 +										LLNotificationFunctorRegistry::ResponseFunctor functor)
 +{
 +	LLNotification::Params::Functor functor_p;
 +	functor_p.function = functor;
 +	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
 +}
 +
 +// generalized add function that takes a parameter block object for more complex instantiations
 +LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
 +{
 +	LLNotificationPtr pNotif(new LLNotification(p));
 +	add(pNotif);
 +	return pNotif;
 +}
 +
 +
 +void LLNotifications::add(const LLNotificationPtr pNotif)
 +{
 +	// first see if we already have it -- if so, that's a problem
 +	LLNotificationSet::iterator it=mItems.find(pNotif);
 +	if (it != mItems.end())
 +	{
 +		llerrs << "Notification added a second time to the master notification channel." << llendl;
 +	}
 +
 +	updateItem(LLSD().insert("sigtype", "add").insert("id", pNotif->id()), pNotif);
 +}
 +
 +void LLNotifications::cancel(LLNotificationPtr pNotif)
 +{
 +	LLNotificationSet::iterator it=mItems.find(pNotif);
 +	if (it == mItems.end())
 +	{
 +		llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
 +	}
 +	updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif);
 +	pNotif->cancel();
 +}
 +
 +void LLNotifications::update(const LLNotificationPtr pNotif)
 +{
 +	LLNotificationSet::iterator it=mItems.find(pNotif);
 +	if (it != mItems.end())
 +	{
 +		updateItem(LLSD().insert("sigtype", "change").insert("id", pNotif->id()), pNotif);
 +	}
 +}
 +
 +
 +LLNotificationPtr LLNotifications::find(LLUUID uuid)
 +{
 +	LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
 +	LLNotificationSet::iterator it=mItems.find(target);
 +	if (it == mItems.end())
 +	{
 +		llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl;
 +		return LLNotificationPtr((LLNotification*)NULL);
 +	}
 +	else
 +	{
 +		return *it;
 +	}
 +}
 +
 +void LLNotifications::forEachNotification(NotificationProcess process)
 +{
 +	std::for_each(mItems.begin(), mItems.end(), process);
 +}
 +
 +std::string LLNotifications::getGlobalString(const std::string& key) const
 +{
 +	GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
 +	if (it != mGlobalStrings.end())
 +	{
 +		return it->second;
 +	}
 +	else
 +	{
 +		// if we don't have the key as a global, return the key itself so that the error
 +		// is self-diagnosing.
 +		return key;
 +	}
 +}
 +
 +void LLNotifications::setIgnoreAllNotifications(bool setting)
 +{
 +	mIgnoreAllNotifications = setting; 
 +}
 +bool LLNotifications::getIgnoreAllNotifications()
 +{
 +	return mIgnoreAllNotifications; 
 +}
 +													
 +// ---
 +// END OF LLNotifications implementation
 +// =========================================================
 +
 +std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
 +{
 +	s << notification.summarize();
 +	return s;
 +}
 +
 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 <string> -#include <list> -#include <vector> -#include <map> -#include <set> -#include <iomanip> -#include <sstream> - -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> -#include <boost/type_traits.hpp> - -// we want to minimize external dependencies, but this one is important -#include "llsd.h" - -// and we need this to manage the notification callbacks -#include "llevents.h" -#include "llfunctorregistry.h" -#include "llui.h" -#include "llmemory.h" - -class LLNotification; -typedef boost::shared_ptr<LLNotification> LLNotificationPtr; - -	 -typedef enum e_notification_priority -{ -	NOTIFICATION_PRIORITY_UNSPECIFIED, -	NOTIFICATION_PRIORITY_LOW, -	NOTIFICATION_PRIORITY_NORMAL, -	NOTIFICATION_PRIORITY_HIGH, -	NOTIFICATION_PRIORITY_CRITICAL -} ENotificationPriority; - -typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder; - -typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry; -typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration; - -// context data that can be looked up via a notification's payload by the display logic -// derive from this class to implement specific contexts -class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID> -{ -public: -	LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID()) -	{ -	} - -	virtual ~LLNotificationContext() {} - -	LLSD asLLSD() const -	{ -		return getKey(); -	} - -private: - -}; - -// Contains notification form data, such as buttons and text fields along with -// manipulator functions -class LLNotificationForm -{ -	LOG_CLASS(LLNotificationForm); - -public: -	typedef enum e_ignore_type -	{  -		IGNORE_NO, -		IGNORE_WITH_DEFAULT_RESPONSE,  -		IGNORE_WITH_LAST_RESPONSE,  -		IGNORE_SHOW_AGAIN  -	} EIgnoreType; - -	LLNotificationForm(); -	LLNotificationForm(const LLSD& sd); -	LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node); - -	LLSD asLLSD() const; - -	S32 getNumElements() { return mFormData.size(); } -	LLSD getElement(S32 index) { return mFormData.get(index); } -	LLSD getElement(const std::string& element_name); -	bool hasElement(const std::string& element_name); -	void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD()); -	void formatElements(const LLSD& substitutions); -	// appends form elements from another form serialized as LLSD -	void append(const LLSD& sub_form); -	std::string getDefaultOption(); - -	EIgnoreType getIgnoreType() { return mIgnore; } -	std::string getIgnoreMessage() { return mIgnoreMsg; } - -private: -	LLSD	mFormData; -	EIgnoreType mIgnore; -	std::string mIgnoreMsg; -}; - -typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr; - -// This is the class of object read from the XML file (notifications.xml,  -// from the appropriate local language directory). -struct LLNotificationTemplate -{ -	LLNotificationTemplate(); -    // the name of the notification -- the key used to identify it -    // Ideally, the key should follow variable naming rules  -    // (no spaces or punctuation). -    std::string mName; -    // The type of the notification -    // used to control which queue it's stored in -    std::string mType; -    // The text used to display the notification. Replaceable parameters -    // are enclosed in square brackets like this []. -    std::string mMessage; -	// The label for the notification; used for  -	// certain classes of notification (those with a window and a window title).  -	// Also used when a notification pops up underneath the current one. -	// Replaceable parameters can be used in the label. -	std::string mLabel; -	// The name of the icon image. This should include an extension. -	std::string mIcon; -    // This is the Highlander bit -- "There Can Be Only One" -    // An outstanding notification with this bit set -    // is updated by an incoming notification with the same name, -    // rather than creating a new entry in the queue. -    // (used for things like progress indications, or repeating warnings -    // like "the grid is going down in N minutes") -    bool mUnique; -    // if we want to be unique only if a certain part of the payload is constant -    // specify the field names for the payload. The notification will only be -    // combined if all of the fields named in the context are identical in the -    // new and the old notification; otherwise, the notification will be -    // duplicated. This is to support suppressing duplicate offers from the same -    // sender but still differentiating different offers. Example: Invitation to -    // conference chat. -    std::vector<std::string> mUniqueContext; -    // If this notification expires automatically, this value will be  -    // nonzero, and indicates the number of seconds for which the notification -    // will be valid (a teleport offer, for example, might be valid for  -    // 300 seconds).  -    U32 mExpireSeconds; -    // if the offer expires, one of the options is chosen automatically -    // based on its "value" parameter. This controls which one.  -    // If expireSeconds is specified, expireOption should also be specified. -    U32 mExpireOption; -    // if the notification contains a url, it's stored here (and replaced  -    // into the message where [_URL] is found) -    std::string mURL; -    // if there's a URL in the message, this controls which option visits -    // that URL. Obsolete this and eliminate the buttons for affected -    // messages when we allow clickable URLs in the UI -    U32 mURLOption; -	 -	U32 mURLOpenExternally; -	//This is a flag that tells if the url needs to open externally dispite  -	//what the user setting is. -	 -	// does this notification persist across sessions? if so, it will be -	// serialized to disk on first receipt and read on startup -	bool mPersist; -	// This is the name of the default functor, if present, to be -	// used for the notification's callback. It is optional, and used only if  -	// the notification is constructed without an identified functor. -	std::string mDefaultFunctor; -	// The form data associated with a given notification (buttons, text boxes, etc) -    LLNotificationFormPtr mForm; -	// default priority for notifications of this type -	ENotificationPriority mPriority; -	// UUID of the audio file to be played when this notification arrives -	// this is loaded as a name, but looked up to get the UUID upon template load. -	// If null, it wasn't specified. -	LLUUID mSoundEffect; -}; - -// we want to keep a map of these by name, and it's best to manage them -// with smart pointers -typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr; - -/** - * @class LLNotification - * @brief The object that expresses the details of a notification - *  - * We make this noncopyable because - * we want to manage these through LLNotificationPtr, and only - * ever create one instance of any given notification. - *  - * The enable_shared_from_this flag ensures that if we construct - * a smart pointer from a notification, we'll always get the same - * shared pointer. - */ -class LLNotification  :  -	boost::noncopyable, -	public boost::enable_shared_from_this<LLNotification> -{ -LOG_CLASS(LLNotification); -friend class LLNotifications; - -public: -	// parameter object used to instantiate a new notification -	struct Params : public LLInitParam::Block<Params> -	{ -		friend class LLNotification; -	 -		Mandatory<std::string>					name; - -		// optional -		Optional<LLSD>							substitutions; -		Optional<LLSD>							payload; -		Optional<ENotificationPriority>			priority; -		Optional<LLSD>							form_elements; -		Optional<LLDate>						timestamp; -		Optional<LLNotificationContext*>		context; - -		struct Functor : public LLInitParam::Choice<Functor> -		{ -			Alternative<std::string>										name; -			Alternative<LLNotificationFunctorRegistry::ResponseFunctor>	function; - -			Functor() -			:	name("functor_name"), -				function("functor") -			{} -		}; -		Optional<Functor>						functor; - -		Params() -		:	name("name"), -			priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), -			timestamp("time_stamp") -		{ -			timestamp = LLDate::now(); -		} - -		Params(const std::string& _name)  -			:	name("name"), -				priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), -				timestamp("time_stamp") -		{ -			functor.name = _name; -			name = _name; -			timestamp = LLDate::now(); -		} -	}; - -private: -	 -	LLUUID mId; -	LLSD mPayload; -	LLSD mSubstitutions; -	LLDate mTimestamp; -	LLDate mExpiresAt; -	bool mCancelled; -	bool mRespondedTo; 	// once the notification has been responded to, this becomes true -	bool mIgnored; -	ENotificationPriority mPriority; -	LLNotificationFormPtr mForm; -	 -	// a reference to the template -	LLNotificationTemplatePtr mTemplatep; -	 -	/* -	 We want to be able to store and reload notifications so that they can survive -	 a shutdown/restart of the client. So we can't simply pass in callbacks; -	 we have to specify a callback mechanism that can be used by name rather than  -	 by some arbitrary pointer -- and then people have to initialize callbacks  -	 in some useful location. So we use LLNotificationFunctorRegistry to manage them. -	 */ -	 std::string mResponseFunctorName; -	 -	/* -	 In cases where we want to specify an explict, non-persisted callback,  -	 we store that in the callback registry under a dynamically generated -	 key, and store the key in the notification, so we can still look it up -	 using the same mechanism. -	 */ -	bool mTemporaryResponder; - -	void init(const std::string& template_name, const LLSD& form_elements); - -	LLNotification(const Params& p); - -	// this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT -	// for anything real! -	LLNotification(LLUUID uuid) : mId(uuid) {} - -	void cancel(); - -	bool payloadContainsAll(const std::vector<std::string>& required_fields) const; - -public: - -	// constructor from a saved notification -	LLNotification(const LLSD& sd); - -	void setResponseFunctor(std::string const &responseFunctorName); - -	typedef enum e_response_template_type -	{ -		WITHOUT_DEFAULT_BUTTON, -		WITH_DEFAULT_BUTTON -	} EResponseTemplateType; - -	// return response LLSD filled in with default form contents and (optionally) the default button selected -	LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON); - -	// returns index of first button with value==TRUE -	// usually this the button the user clicked on -	// returns -1 if no button clicked (e.g. form has not been displayed) -	static S32 getSelectedOption(const LLSD& notification, const LLSD& response); -	// returns name of first button with value==TRUE -	static std::string getSelectedOptionName(const LLSD& notification); - -	// after someone responds to a notification (usually by clicking a button, -	// but sometimes by filling out a little form and THEN clicking a button), -    // the result of the response (the name and value of the button clicked, -	// plus any other data) should be packaged up as LLSD, then passed as a -	// parameter to the notification's respond() method here. This will look up -	// and call the appropriate responder. -	// -	// response is notification serialized as LLSD: -	// ["name"] = notification name -	// ["form"] = LLSD tree that includes form description and any prefilled form data -	// ["response"] = form data filled in by user -	// (including, but not limited to which button they clicked on) -	// ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),   -	//				["item_id"] (attached inventory item), etc. -	// ["substitutions"] = string substitutions used to generate notification message -    // from the template -	// ["time"] = time at which notification was generated; -	// ["expiry"] = time at which notification expires; -	// ["responseFunctor"] = name of registered functor that handles responses to notification; -	LLSD asLLSD(); - -	void respond(const LLSD& sd); - -	void setIgnored(bool ignore); - -	bool isCancelled() const -	{ -		return mCancelled; -	} - -	bool isRespondedTo() const -	{ -		return mRespondedTo; -	} - -	bool isIgnored() const -	{ -		return mIgnored; -	} - -	const std::string& getName() const -	{ -		return mTemplatep->mName; -	} -	 -	const LLUUID& id() const -	{ -		return mId; -	} -	 -	const LLSD& getPayload() const -	{ -		return mPayload; -	} - -	const LLSD& getSubstitutions() const -	{ -		return mSubstitutions; -	} - -	const LLDate& getDate() const -	{ -		return mTimestamp; -	} - -	std::string getType() const -	{ -		return (mTemplatep ? mTemplatep->mType : ""); -	} - -	std::string getMessage() const; -	std::string getLabel() const; - -	std::string getURL() const; -//	{ -//		return (mTemplatep ? mTemplatep->mURL : ""); -//	} - -	S32 getURLOption() const -	{ -		return (mTemplatep ? mTemplatep->mURLOption : -1); -	} -     -	S32 getURLOpenExternally() const -	{ -		return(mTemplatep? mTemplatep->mURLOpenExternally : -1); -	} -	 -	const LLNotificationFormPtr getForm(); - -	const LLDate getExpiration() const -	{ -		return mExpiresAt; -	} - -	ENotificationPriority getPriority() const -	{ -		return mPriority; -	} - -	const LLUUID getID() const -	{ -		return mId; -	} -	 -	// comparing two notifications normally means comparing them by UUID (so we can look them -	// up quickly this way) -	bool operator<(const LLNotification& rhs) const -	{ -		return mId < rhs.mId; -	} - -	bool operator==(const LLNotification& rhs) const -	{ -		return mId == rhs.mId; -	} - -	bool operator!=(const LLNotification& rhs) const -	{ -		return !operator==(rhs); -	} - -	bool isSameObjectAs(const LLNotification* rhs) const -	{ -		return this == rhs; -	} -	 -	// this object has been updated, so tell all our clients -	void update(); - -	void updateFrom(LLNotificationPtr other); -	 -	// A fuzzy equals comparator. -	// true only if both notifications have the same template and  -	//     1) flagged as unique (there can be only one of these) OR  -	//     2) all required payload fields of each also exist in the other. -	bool isEquivalentTo(LLNotificationPtr that) const; -	 -	// if the current time is greater than the expiration, the notification is expired -	bool isExpired() const -	{ -		if (mExpiresAt.secondsSinceEpoch() == 0) -		{ -			return false; -		} -		 -		LLDate rightnow = LLDate::now(); -		return rightnow > mExpiresAt; -	} -	 -	std::string summarize() const; - -	bool hasUniquenessConstraints() const { return (mTemplatep ? mTemplatep->mUnique : false);} - -	virtual ~LLNotification() {} -}; - -std::ostream& operator<<(std::ostream& s, const LLNotification& notification); - -namespace LLNotificationFilters -{ -	// a sample filter -	bool includeEverything(LLNotificationPtr p); - -	typedef enum e_comparison  -	{  -		EQUAL,  -		LESS,  -		GREATER,  -		LESS_EQUAL,  -		GREATER_EQUAL  -	} EComparison; - -	// generic filter functor that takes method or member variable reference -	template<typename T> -	struct filterBy -	{ -		typedef boost::function<T (LLNotificationPtr)>	field_t; -		typedef typename boost::remove_reference<T>::type		value_t; -		 -		filterBy(field_t field, value_t value, EComparison comparison = EQUAL)  -			:	mField(field),  -				mFilterValue(value), -				mComparison(comparison) -		{ -		}		 -		 -		bool operator()(LLNotificationPtr p) -		{ -			switch(mComparison) -			{ -			case EQUAL: -				return mField(p) == mFilterValue; -			case LESS: -				return mField(p) < mFilterValue; -			case GREATER: -				return mField(p) > mFilterValue; -			case LESS_EQUAL: -				return mField(p) <= mFilterValue; -			case GREATER_EQUAL: -				return mField(p) >= mFilterValue; -			default: -				return false; -			} -		} - -		field_t mField; -		value_t	mFilterValue; -		EComparison mComparison; -	}; -}; - -namespace LLNotificationComparators -{ -	typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection; - -	// generic order functor that takes method or member variable reference -	template<typename T> -	struct orderBy -	{ -		typedef boost::function<T (LLNotificationPtr)> field_t; -		orderBy(field_t field, EDirection = ORDER_INCREASING) : mField(field) {} -		bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) -		{ -			if (mDirection == ORDER_DECREASING) -			{ -				return mField(lhs) > mField(rhs); -			} -			else -			{ -				return mField(lhs) < mField(rhs); -			} -		} - -		field_t mField; -		EDirection mDirection; -	}; - -	struct orderByUUID : public orderBy<const LLUUID&> -	{ -		orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {} -	}; - -	struct orderByDate : public orderBy<const LLDate&> -	{ -		orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {} -	}; -}; - -typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter; -typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator; -typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet; -typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap; - -// ======================================================== -// Abstract base class (interface) for a channel; also used for the master container. -// This lets us arrange channels into a call hierarchy. - -// We maintain a heirarchy of notification channels; events are always started at the top -// and propagated through the hierarchy only if they pass a filter. -// Any channel can be created with a parent. A null parent (empty string) means it's -// tied to the root of the tree (the LLNotifications class itself). -// The default hierarchy looks like this: -// -// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History -//                                                                          +-- Alerts -//                                                                          +-- Notifications -// -// In general, new channels that want to only see notifications that pass through  -// all of the built-in tests should attach to the "Visible" channel -// -class LLNotificationChannelBase : -	public LLEventTrackable -{ -	LOG_CLASS(LLNotificationChannelBase); -public: -	LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) :  -		mFilter(filter), mItems(comp)  -	{} -	virtual ~LLNotificationChannelBase() {} -	// you can also connect to a Channel, so you can be notified of -	// changes to this channel -	template <typename LISTENER> -    LLBoundListener connectChanged(const LISTENER& slot) -    { -        // Examine slot to see if it binds an LLEventTrackable subclass, or a -        // boost::shared_ptr to something, or a boost::weak_ptr to something. -        // Call this->connectChangedImpl() to actually connect it. -        return LLEventDetail::visit_and_connect(slot, -                                  boost::bind(&LLNotificationChannelBase::connectChangedImpl, -                                              this, -                                              _1)); -    } -    template <typename LISTENER> -	LLBoundListener connectPassedFilter(const LISTENER& slot) -    { -        // see comments in connectChanged() -        return LLEventDetail::visit_and_connect(slot, -                                  boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, -                                              this, -                                              _1)); -    } -    template <typename LISTENER> -	LLBoundListener connectFailedFilter(const LISTENER& slot) -    { -        // see comments in connectChanged() -        return LLEventDetail::visit_and_connect(slot, -                                  boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl, -                                              this, -                                              _1)); -    } - -	// use this when items change or to add a new one -	bool updateItem(const LLSD& payload); -	const LLNotificationFilter& getFilter() { return mFilter; } - -protected: -    LLBoundListener connectChangedImpl(const LLEventListener& slot); -    LLBoundListener connectPassedFilterImpl(const LLEventListener& slot); -    LLBoundListener connectFailedFilterImpl(const LLEventListener& slot); - -	LLNotificationSet mItems; -	LLStandardSignal mChanged; -	LLStandardSignal mPassedFilter; -	LLStandardSignal mFailedFilter; -	 -	// these are action methods that subclasses can override to take action  -	// on specific types of changes; the management of the mItems list is -	// still handled by the generic handler. -	virtual void onLoad(LLNotificationPtr p) {} -	virtual void onAdd(LLNotificationPtr p) {} -	virtual void onDelete(LLNotificationPtr p) {} -	virtual void onChange(LLNotificationPtr p) {} - -	bool updateItem(const LLSD& payload, LLNotificationPtr pNotification); -	LLNotificationFilter mFilter; -}; - -// The type of the pointers that we're going to manage in the NotificationQueue system -// Because LLNotifications is a singleton, we don't actually expect to ever  -// destroy it, but if it becomes necessary to do so, the shared_ptr model -// will ensure that we don't leak resources. -class LLNotificationChannel; -typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr; - -// manages a list of notifications -// Note that if this is ever copied around, we might find ourselves with multiple copies -// of a queue with notifications being added to different nonequivalent copies. So we  -// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it. -//  -// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to  -// do something like: -//		LLNotificationChannel::buildChannel("name", "parent"...); -// This returns an LLNotificationChannelPtr, which you can store, or -// you can then retrieve the channel by using the registry: -//		LLNotifications::instance().getChannel("name")... -// -class LLNotificationChannel :  -	boost::noncopyable,  -	public LLNotificationChannelBase -{ -	LOG_CLASS(LLNotificationChannel); - -public:   -	virtual ~LLNotificationChannel() {} -	typedef LLNotificationSet::iterator Iterator; -     -	std::string getName() const { return mName; } -	std::string getParentChannelName() { return mParent; } -     -    bool isEmpty() const; -     -    Iterator begin(); -    Iterator end(); - -    // Channels have a comparator to control sort order; -	// the default sorts by arrival date -    void setComparator(LLNotificationComparator comparator); -	 -	std::string summarize(); - -	// factory method for constructing these channels; since they're self-registering, -	// we want to make sure that you can't use new to make them -	static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent, -						LLNotificationFilter filter=LLNotificationFilters::includeEverything,  -						LLNotificationComparator comparator=LLNotificationComparators::orderByUUID()); -	 -protected: -    // Notification Channels have a filter, which determines which notifications -	// will be added to this channel.  -	// Channel filters cannot change. -	// Channels have a protected constructor so you can't make smart pointers that don't  -	// come from our internal reference; call NotificationChannel::build(args) -	LLNotificationChannel(const std::string& name, const std::string& parent, -						  LLNotificationFilter filter, LLNotificationComparator comparator); - -private: -	std::string mName; -	std::string mParent; -	LLNotificationComparator mComparator; -}; - - - -class LLNotifications :  -	public LLSingleton<LLNotifications>,  -	public LLNotificationChannelBase -{ -	LOG_CLASS(LLNotifications); - -	friend class LLSingleton<LLNotifications>; -public: -	// load notification descriptions from file;  -	// OK to call more than once because it will reload -	bool loadTemplates();   -	LLXMLNodePtr checkForXMLTemplate(LLXMLNodePtr item); -	 -	// Add a simple notification (from XUI) -	void addFromCallback(const LLSD& name); -	 -	// we provide a collection of simple add notification functions so that it's reasonable to create notifications in one line -	LLNotificationPtr add(const std::string& name,  -						const LLSD& substitutions = LLSD(),  -						const LLSD& payload = LLSD()); -	LLNotificationPtr add(const std::string& name,  -						const LLSD& substitutions,  -						const LLSD& payload,  -						const std::string& functor_name); -	LLNotificationPtr add(const std::string& name,  -						const LLSD& substitutions,  -						const LLSD& payload,  -						LLNotificationFunctorRegistry::ResponseFunctor functor); -	LLNotificationPtr add(const LLNotification::Params& p); - -	void add(const LLNotificationPtr pNotif); -	void cancel(LLNotificationPtr pNotif); -	void update(const LLNotificationPtr pNotif); - -	LLNotificationPtr find(LLUUID uuid); -	 -	typedef boost::function<void (LLNotificationPtr)> NotificationProcess; -	 -	void forEachNotification(NotificationProcess process); - -	// This is all stuff for managing the templates -	// take your template out -	LLNotificationTemplatePtr getTemplate(const std::string& name); -	 -	// get the whole collection -	typedef std::vector<std::string> TemplateNames; -	TemplateNames getTemplateNames() const;  // returns a list of notification names -	 -	typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap; - -	TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); } -	TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); } - -	// test for existence -	bool templateExists(const std::string& name); -	// useful if you're reloading the file -	void clearTemplates();   // erase all templates - -	void forceResponse(const LLNotification::Params& params, S32 option); - -	void createDefaultChannels(); - -	typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap; -	ChannelMap mChannels; - -	void addChannel(LLNotificationChannelPtr pChan); -	LLNotificationChannelPtr getChannel(const std::string& channelName); -	 -	std::string getGlobalString(const std::string& key) const; - -	void setIgnoreAllNotifications(bool ignore); -	bool getIgnoreAllNotifications(); - -private: -	// we're a singleton, so we don't have a public constructor -	LLNotifications(); -	/*virtual*/ void initSingleton(); -	 -	void loadPersistentNotifications(); - -	bool expirationFilter(LLNotificationPtr pNotification); -	bool expirationHandler(const LLSD& payload); -	bool uniqueFilter(LLNotificationPtr pNotification); -	bool uniqueHandler(const LLSD& payload); -	bool failedUniquenessTest(const LLSD& payload); -	LLNotificationChannelPtr pHistoryChannel; -	LLNotificationChannelPtr pExpirationChannel; -	 -	// put your template in -	bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate); -	TemplateMap mTemplates; - -	std::string mFileName; -	 -	typedef std::map<std::string, LLXMLNodePtr> XMLTemplateMap; -	XMLTemplateMap mXmlTemplates; - -	LLNotificationMap mUniqueNotifications; -	 -	typedef std::map<std::string, std::string> GlobalStringMap; -	GlobalStringMap mGlobalStrings; - -	bool mIgnoreAllNotifications; -}; - - -#endif//LL_LLNOTIFICATIONS_H - +/**
 +* @file llnotifications.h
 +* @brief Non-UI manager and support for keeping a prioritized list of notifications
 +* @author Q (with assistance from Richard and Coco)
 +*
 +* $LicenseInfo:firstyear=2008&license=viewergpl$
 +* 
 +* Copyright (c) 2008-2009, Linden Research, Inc.
 +* 
 +* Second Life Viewer Source Code
 +* The source code in this file ("Source Code") is provided by Linden Lab
 +* to you under the terms of the GNU General Public License, version 2.0
 +* ("GPL"), unless you have obtained a separate licensing agreement
 +* ("Other License"), formally executed by you and Linden Lab.  Terms of
 +* the GPL can be found in doc/GPL-license.txt in this distribution, or
 +* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 +* 
 +* There are special exceptions to the terms and conditions of the GPL as
 +* it is applied to this Source Code. View the full text of the exception
 +* in the file doc/FLOSS-exception.txt in this software distribution, or
 +* online at
 +* http://secondlifegrid.net/programs/open_source/licensing/flossexception
 +* 
 +* By copying, modifying or distributing this software, you acknowledge
 +* that you have read and understood your obligations described above,
 +* and agree to abide by those obligations.
 +* 
 +* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 +* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 +* COMPLETENESS OR PERFORMANCE.
 +* $/LicenseInfo$
 +*/
 +
 +#ifndef LL_LLNOTIFICATIONS_H
 +#define LL_LLNOTIFICATIONS_H
 +
 +/**
 + * This system is intended to provide a singleton mechanism for adding
 + * notifications to one of an arbitrary set of event channels.
 + * 
 + * Controlling JIRA: DEV-9061
 + *
 + * Every notification has (see code for full list):
 + *  - a textual name, which is used to look up its template in the XML files
 + *  - a payload, which is a block of LLSD
 + *  - a channel, which is normally extracted from the XML files but
 + *	  can be overridden.
 + *  - a timestamp, used to order the notifications
 + *  - expiration time -- if nonzero, specifies a time after which the
 + *    notification will no longer be valid.
 + *  - a callback name and a couple of status bits related to callbacks (see below)
 + * 
 + * There is a management class called LLNotifications, which is an LLSingleton.
 + * The class maintains a collection of all of the notifications received
 + * or processed during this session, and also manages the persistence
 + * of those notifications that must be persisted.
 + * 
 + * We also have Channels. A channel is a view on a collection of notifications;
 + * The collection is defined by a filter function that controls which
 + * notifications are in the channel, and its ordering is controlled by 
 + * a comparator. 
 + *
 + * There is a hierarchy of channels; notifications flow down from
 + * the management class (LLNotifications, which itself inherits from
 + * The channel base class) to the individual channels.
 + * Any change to notifications (add, delete, modify) is 
 + * automatically propagated through the channel hierarchy.
 + * 
 + * We provide methods for adding a new notification, for removing
 + * one, and for managing channels. Channels are relatively cheap to construct
 + * and maintain, so in general, human interfaces should use channels to
 + * select and manage their lists of notifications.
 + * 
 + * We also maintain a collection of templates that are loaded from the 
 + * XML file of template translations. The system supports substitution
 + * of named variables from the payload into the XML file.
 + * 
 + * By default, only the "unknown message" template is built into the system.
 + * It is not an error to add a notification that's not found in the 
 + * template system, but it is logged.
 + *
 + */
 +
 +#include <string>
 +#include <list>
 +#include <vector>
 +#include <map>
 +#include <set>
 +#include <iomanip>
 +#include <sstream>
 +
 +#include <boost/utility.hpp>
 +#include <boost/shared_ptr.hpp>
 +#include <boost/enable_shared_from_this.hpp>
 +#include <boost/type_traits.hpp>
 +
 +// we want to minimize external dependencies, but this one is important
 +#include "llsd.h"
 +
 +// and we need this to manage the notification callbacks
 +#include "llevents.h"
 +#include "llfunctorregistry.h"
 +#include "llui.h"
 +#include "llmemory.h"
 +
 +class LLNotification;
 +typedef boost::shared_ptr<LLNotification> LLNotificationPtr;
 +
 +	
 +typedef enum e_notification_priority
 +{
 +	NOTIFICATION_PRIORITY_UNSPECIFIED,
 +	NOTIFICATION_PRIORITY_LOW,
 +	NOTIFICATION_PRIORITY_NORMAL,
 +	NOTIFICATION_PRIORITY_HIGH,
 +	NOTIFICATION_PRIORITY_CRITICAL
 +} ENotificationPriority;
 +
 +typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
 +
 +typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
 +typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
 +
 +// context data that can be looked up via a notification's payload by the display logic
 +// derive from this class to implement specific contexts
 +class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
 +{
 +public:
 +	LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
 +	{
 +	}
 +
 +	virtual ~LLNotificationContext() {}
 +
 +	LLSD asLLSD() const
 +	{
 +		return getKey();
 +	}
 +
 +private:
 +
 +};
 +
 +// Contains notification form data, such as buttons and text fields along with
 +// manipulator functions
 +class LLNotificationForm
 +{
 +	LOG_CLASS(LLNotificationForm);
 +
 +public:
 +	typedef enum e_ignore_type
 +	{ 
 +		IGNORE_NO,
 +		IGNORE_WITH_DEFAULT_RESPONSE, 
 +		IGNORE_WITH_LAST_RESPONSE, 
 +		IGNORE_SHOW_AGAIN 
 +	} EIgnoreType;
 +
 +	LLNotificationForm();
 +	LLNotificationForm(const LLSD& sd);
 +	LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node);
 +
 +	LLSD asLLSD() const;
 +
 +	S32 getNumElements() { return mFormData.size(); }
 +	LLSD getElement(S32 index) { return mFormData.get(index); }
 +	LLSD getElement(const std::string& element_name);
 +	bool hasElement(const std::string& element_name);
 +	void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD());
 +	void formatElements(const LLSD& substitutions);
 +	// appends form elements from another form serialized as LLSD
 +	void append(const LLSD& sub_form);
 +	std::string getDefaultOption();
 +
 +	EIgnoreType getIgnoreType() { return mIgnore; }
 +	std::string getIgnoreMessage() { return mIgnoreMsg; }
 +
 +private:
 +	LLSD	mFormData;
 +	EIgnoreType mIgnore;
 +	std::string mIgnoreMsg;
 +};
 +
 +typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
 +
 +// This is the class of object read from the XML file (notifications.xml, 
 +// from the appropriate local language directory).
 +struct LLNotificationTemplate
 +{
 +	LLNotificationTemplate();
 +    // the name of the notification -- the key used to identify it
 +    // Ideally, the key should follow variable naming rules 
 +    // (no spaces or punctuation).
 +    std::string mName;
 +    // The type of the notification
 +    // used to control which queue it's stored in
 +    std::string mType;
 +    // The text used to display the notification. Replaceable parameters
 +    // are enclosed in square brackets like this [].
 +    std::string mMessage;
 +	// The label for the notification; used for 
 +	// certain classes of notification (those with a window and a window title). 
 +	// Also used when a notification pops up underneath the current one.
 +	// Replaceable parameters can be used in the label.
 +	std::string mLabel;
 +	// The name of the icon image. This should include an extension.
 +	std::string mIcon;
 +    // This is the Highlander bit -- "There Can Be Only One"
 +    // An outstanding notification with this bit set
 +    // is updated by an incoming notification with the same name,
 +    // rather than creating a new entry in the queue.
 +    // (used for things like progress indications, or repeating warnings
 +    // like "the grid is going down in N minutes")
 +    bool mUnique;
 +    // if we want to be unique only if a certain part of the payload is constant
 +    // specify the field names for the payload. The notification will only be
 +    // combined if all of the fields named in the context are identical in the
 +    // new and the old notification; otherwise, the notification will be
 +    // duplicated. This is to support suppressing duplicate offers from the same
 +    // sender but still differentiating different offers. Example: Invitation to
 +    // conference chat.
 +    std::vector<std::string> mUniqueContext;
 +    // If this notification expires automatically, this value will be 
 +    // nonzero, and indicates the number of seconds for which the notification
 +    // will be valid (a teleport offer, for example, might be valid for 
 +    // 300 seconds). 
 +    U32 mExpireSeconds;
 +    // if the offer expires, one of the options is chosen automatically
 +    // based on its "value" parameter. This controls which one. 
 +    // If expireSeconds is specified, expireOption should also be specified.
 +    U32 mExpireOption;
 +    // if the notification contains a url, it's stored here (and replaced 
 +    // into the message where [_URL] is found)
 +    std::string mURL;
 +    // if there's a URL in the message, this controls which option visits
 +    // that URL. Obsolete this and eliminate the buttons for affected
 +    // messages when we allow clickable URLs in the UI
 +    U32 mURLOption;
 +	
 +	U32 mURLOpenExternally;
 +	//This is a flag that tells if the url needs to open externally dispite 
 +	//what the user setting is.
 +	
 +	// does this notification persist across sessions? if so, it will be
 +	// serialized to disk on first receipt and read on startup
 +	bool mPersist;
 +	// This is the name of the default functor, if present, to be
 +	// used for the notification's callback. It is optional, and used only if 
 +	// the notification is constructed without an identified functor.
 +	std::string mDefaultFunctor;
 +	// The form data associated with a given notification (buttons, text boxes, etc)
 +    LLNotificationFormPtr mForm;
 +	// default priority for notifications of this type
 +	ENotificationPriority mPriority;
 +	// UUID of the audio file to be played when this notification arrives
 +	// this is loaded as a name, but looked up to get the UUID upon template load.
 +	// If null, it wasn't specified.
 +	LLUUID mSoundEffect;
 +};
 +
 +// we want to keep a map of these by name, and it's best to manage them
 +// with smart pointers
 +typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;
 +
 +/**
 + * @class LLNotification
 + * @brief The object that expresses the details of a notification
 + * 
 + * We make this noncopyable because
 + * we want to manage these through LLNotificationPtr, and only
 + * ever create one instance of any given notification.
 + * 
 + * The enable_shared_from_this flag ensures that if we construct
 + * a smart pointer from a notification, we'll always get the same
 + * shared pointer.
 + */
 +class LLNotification  : 
 +	boost::noncopyable,
 +	public boost::enable_shared_from_this<LLNotification>
 +{
 +LOG_CLASS(LLNotification);
 +friend class LLNotifications;
 +
 +public:
 +	// parameter object used to instantiate a new notification
 +	struct Params : public LLInitParam::Block<Params>
 +	{
 +		friend class LLNotification;
 +	
 +		Mandatory<std::string>					name;
 +
 +		// optional
 +		Optional<LLSD>							substitutions;
 +		Optional<LLSD>							payload;
 +		Optional<ENotificationPriority>			priority;
 +		Optional<LLSD>							form_elements;
 +		Optional<LLDate>						timestamp;
 +		Optional<LLNotificationContext*>		context;
 +
 +		struct Functor : public LLInitParam::Choice<Functor>
 +		{
 +			Alternative<std::string>										name;
 +			Alternative<LLNotificationFunctorRegistry::ResponseFunctor>	function;
 +
 +			Functor()
 +			:	name("functor_name"),
 +				function("functor")
 +			{}
 +		};
 +		Optional<Functor>						functor;
 +
 +		Params()
 +		:	name("name"),
 +			priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
 +			timestamp("time_stamp")
 +		{
 +			timestamp = LLDate::now();
 +		}
 +
 +		Params(const std::string& _name) 
 +			:	name("name"),
 +				priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
 +				timestamp("time_stamp")
 +		{
 +			functor.name = _name;
 +			name = _name;
 +			timestamp = LLDate::now();
 +		}
 +	};
 +
 +private:
 +	
 +	LLUUID mId;
 +	LLSD mPayload;
 +	LLSD mSubstitutions;
 +	LLDate mTimestamp;
 +	LLDate mExpiresAt;
 +	bool mCancelled;
 +	bool mRespondedTo; 	// once the notification has been responded to, this becomes true
 +	bool mIgnored;
 +	ENotificationPriority mPriority;
 +	LLNotificationFormPtr mForm;
 +	
 +	// a reference to the template
 +	LLNotificationTemplatePtr mTemplatep;
 +	
 +	/*
 +	 We want to be able to store and reload notifications so that they can survive
 +	 a shutdown/restart of the client. So we can't simply pass in callbacks;
 +	 we have to specify a callback mechanism that can be used by name rather than 
 +	 by some arbitrary pointer -- and then people have to initialize callbacks 
 +	 in some useful location. So we use LLNotificationFunctorRegistry to manage them.
 +	 */
 +	 std::string mResponseFunctorName;
 +	
 +	/*
 +	 In cases where we want to specify an explict, non-persisted callback, 
 +	 we store that in the callback registry under a dynamically generated
 +	 key, and store the key in the notification, so we can still look it up
 +	 using the same mechanism.
 +	 */
 +	bool mTemporaryResponder;
 +
 +	void init(const std::string& template_name, const LLSD& form_elements);
 +
 +	LLNotification(const Params& p);
 +
 +	// this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT
 +	// for anything real!
 +	LLNotification(LLUUID uuid) : mId(uuid) {}
 +
 +	void cancel();
 +
 +	bool payloadContainsAll(const std::vector<std::string>& required_fields) const;
 +
 +public:
 +
 +	// constructor from a saved notification
 +	LLNotification(const LLSD& sd);
 +
 +	void setResponseFunctor(std::string const &responseFunctorName);
 +
 +	typedef enum e_response_template_type
 +	{
 +		WITHOUT_DEFAULT_BUTTON,
 +		WITH_DEFAULT_BUTTON
 +	} EResponseTemplateType;
 +
 +	// return response LLSD filled in with default form contents and (optionally) the default button selected
 +	LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON);
 +
 +	// returns index of first button with value==TRUE
 +	// usually this the button the user clicked on
 +	// returns -1 if no button clicked (e.g. form has not been displayed)
 +	static S32 getSelectedOption(const LLSD& notification, const LLSD& response);
 +	// returns name of first button with value==TRUE
 +	static std::string getSelectedOptionName(const LLSD& notification);
 +
 +	// after someone responds to a notification (usually by clicking a button,
 +	// but sometimes by filling out a little form and THEN clicking a button),
 +    // the result of the response (the name and value of the button clicked,
 +	// plus any other data) should be packaged up as LLSD, then passed as a
 +	// parameter to the notification's respond() method here. This will look up
 +	// and call the appropriate responder.
 +	//
 +	// response is notification serialized as LLSD:
 +	// ["name"] = notification name
 +	// ["form"] = LLSD tree that includes form description and any prefilled form data
 +	// ["response"] = form data filled in by user
 +	// (including, but not limited to which button they clicked on)
 +	// ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),  
 +	//				["item_id"] (attached inventory item), etc.
 +	// ["substitutions"] = string substitutions used to generate notification message
 +    // from the template
 +	// ["time"] = time at which notification was generated;
 +	// ["expiry"] = time at which notification expires;
 +	// ["responseFunctor"] = name of registered functor that handles responses to notification;
 +	LLSD asLLSD();
 +
 +	void respond(const LLSD& sd);
 +
 +	void setIgnored(bool ignore);
 +
 +	bool isCancelled() const
 +	{
 +		return mCancelled;
 +	}
 +
 +	bool isRespondedTo() const
 +	{
 +		return mRespondedTo;
 +	}
 +
 +	bool isIgnored() const
 +	{
 +		return mIgnored;
 +	}
 +
 +	const std::string& getName() const
 +	{
 +		return mTemplatep->mName;
 +	}
 +	
 +	const LLUUID& id() const
 +	{
 +		return mId;
 +	}
 +	
 +	const LLSD& getPayload() const
 +	{
 +		return mPayload;
 +	}
 +
 +	const LLSD& getSubstitutions() const
 +	{
 +		return mSubstitutions;
 +	}
 +
 +	const LLDate& getDate() const
 +	{
 +		return mTimestamp;
 +	}
 +
 +	std::string getType() const
 +	{
 +		return (mTemplatep ? mTemplatep->mType : "");
 +	}
 +
 +	std::string getMessage() const;
 +	std::string getLabel() const;
 +
 +	std::string getURL() const;
 +//	{
 +//		return (mTemplatep ? mTemplatep->mURL : "");
 +//	}
 +
 +	S32 getURLOption() const
 +	{
 +		return (mTemplatep ? mTemplatep->mURLOption : -1);
 +	}
 +    
 +	S32 getURLOpenExternally() const
 +	{
 +		return(mTemplatep? mTemplatep->mURLOpenExternally : -1);
 +	}
 +	
 +	const LLNotificationFormPtr getForm();
 +
 +	const LLDate getExpiration() const
 +	{
 +		return mExpiresAt;
 +	}
 +
 +	ENotificationPriority getPriority() const
 +	{
 +		return mPriority;
 +	}
 +
 +	const LLUUID getID() const
 +	{
 +		return mId;
 +	}
 +	
 +	// comparing two notifications normally means comparing them by UUID (so we can look them
 +	// up quickly this way)
 +	bool operator<(const LLNotification& rhs) const
 +	{
 +		return mId < rhs.mId;
 +	}
 +
 +	bool operator==(const LLNotification& rhs) const
 +	{
 +		return mId == rhs.mId;
 +	}
 +
 +	bool operator!=(const LLNotification& rhs) const
 +	{
 +		return !operator==(rhs);
 +	}
 +
 +	bool isSameObjectAs(const LLNotification* rhs) const
 +	{
 +		return this == rhs;
 +	}
 +	
 +	// this object has been updated, so tell all our clients
 +	void update();
 +
 +	void updateFrom(LLNotificationPtr other);
 +	
 +	// A fuzzy equals comparator.
 +	// true only if both notifications have the same template and 
 +	//     1) flagged as unique (there can be only one of these) OR 
 +	//     2) all required payload fields of each also exist in the other.
 +	bool isEquivalentTo(LLNotificationPtr that) const;
 +	
 +	// if the current time is greater than the expiration, the notification is expired
 +	bool isExpired() const
 +	{
 +		if (mExpiresAt.secondsSinceEpoch() == 0)
 +		{
 +			return false;
 +		}
 +		
 +		LLDate rightnow = LLDate::now();
 +		return rightnow > mExpiresAt;
 +	}
 +	
 +	std::string summarize() const;
 +
 +	bool hasUniquenessConstraints() const { return (mTemplatep ? mTemplatep->mUnique : false);}
 +
 +	virtual ~LLNotification() {}
 +};
 +
 +std::ostream& operator<<(std::ostream& s, const LLNotification& notification);
 +
 +namespace LLNotificationFilters
 +{
 +	// a sample filter
 +	bool includeEverything(LLNotificationPtr p);
 +
 +	typedef enum e_comparison 
 +	{ 
 +		EQUAL, 
 +		LESS, 
 +		GREATER, 
 +		LESS_EQUAL, 
 +		GREATER_EQUAL 
 +	} EComparison;
 +
 +	// generic filter functor that takes method or member variable reference
 +	template<typename T>
 +	struct filterBy
 +	{
 +		typedef boost::function<T (LLNotificationPtr)>	field_t;
 +		typedef typename boost::remove_reference<T>::type		value_t;
 +		
 +		filterBy(field_t field, value_t value, EComparison comparison = EQUAL) 
 +			:	mField(field), 
 +				mFilterValue(value),
 +				mComparison(comparison)
 +		{
 +		}		
 +		
 +		bool operator()(LLNotificationPtr p)
 +		{
 +			switch(mComparison)
 +			{
 +			case EQUAL:
 +				return mField(p) == mFilterValue;
 +			case LESS:
 +				return mField(p) < mFilterValue;
 +			case GREATER:
 +				return mField(p) > mFilterValue;
 +			case LESS_EQUAL:
 +				return mField(p) <= mFilterValue;
 +			case GREATER_EQUAL:
 +				return mField(p) >= mFilterValue;
 +			default:
 +				return false;
 +			}
 +		}
 +
 +		field_t mField;
 +		value_t	mFilterValue;
 +		EComparison mComparison;
 +	};
 +};
 +
 +namespace LLNotificationComparators
 +{
 +	typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection;
 +
 +	// generic order functor that takes method or member variable reference
 +	template<typename T>
 +	struct orderBy
 +	{
 +		typedef boost::function<T (LLNotificationPtr)> field_t;
 +		orderBy(field_t field, EDirection = ORDER_INCREASING) : mField(field) {}
 +		bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs)
 +		{
 +			if (mDirection == ORDER_DECREASING)
 +			{
 +				return mField(lhs) > mField(rhs);
 +			}
 +			else
 +			{
 +				return mField(lhs) < mField(rhs);
 +			}
 +		}
 +
 +		field_t mField;
 +		EDirection mDirection;
 +	};
 +
 +	struct orderByUUID : public orderBy<const LLUUID&>
 +	{
 +		orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {}
 +	};
 +
 +	struct orderByDate : public orderBy<const LLDate&>
 +	{
 +		orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {}
 +	};
 +};
 +
 +typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
 +typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator;
 +typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet;
 +typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
 +
 +// ========================================================
 +// Abstract base class (interface) for a channel; also used for the master container.
 +// This lets us arrange channels into a call hierarchy.
 +
 +// We maintain a heirarchy of notification channels; events are always started at the top
 +// and propagated through the hierarchy only if they pass a filter.
 +// Any channel can be created with a parent. A null parent (empty string) means it's
 +// tied to the root of the tree (the LLNotifications class itself).
 +// The default hierarchy looks like this:
 +//
 +// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History
 +//                                                                          +-- Alerts
 +//                                                                          +-- Notifications
 +//
 +// In general, new channels that want to only see notifications that pass through 
 +// all of the built-in tests should attach to the "Visible" channel
 +//
 +class LLNotificationChannelBase :
 +	public LLEventTrackable
 +{
 +	LOG_CLASS(LLNotificationChannelBase);
 +public:
 +	LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) : 
 +		mFilter(filter), mItems(comp) 
 +	{}
 +	virtual ~LLNotificationChannelBase() {}
 +	// you can also connect to a Channel, so you can be notified of
 +	// changes to this channel
 +	template <typename LISTENER>
 +    LLBoundListener connectChanged(const LISTENER& slot)
 +    {
 +        // Examine slot to see if it binds an LLEventTrackable subclass, or a
 +        // boost::shared_ptr to something, or a boost::weak_ptr to something.
 +        // Call this->connectChangedImpl() to actually connect it.
 +        return LLEventDetail::visit_and_connect(slot,
 +                                  boost::bind(&LLNotificationChannelBase::connectChangedImpl,
 +                                              this,
 +                                              _1));
 +    }
 +    template <typename LISTENER>
 +	LLBoundListener connectPassedFilter(const LISTENER& slot)
 +    {
 +        // see comments in connectChanged()
 +        return LLEventDetail::visit_and_connect(slot,
 +                                  boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl,
 +                                              this,
 +                                              _1));
 +    }
 +    template <typename LISTENER>
 +	LLBoundListener connectFailedFilter(const LISTENER& slot)
 +    {
 +        // see comments in connectChanged()
 +        return LLEventDetail::visit_and_connect(slot,
 +                                  boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl,
 +                                              this,
 +                                              _1));
 +    }
 +
 +	// use this when items change or to add a new one
 +	bool updateItem(const LLSD& payload);
 +	const LLNotificationFilter& getFilter() { return mFilter; }
 +
 +protected:
 +    LLBoundListener connectChangedImpl(const LLEventListener& slot);
 +    LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
 +    LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);
 +
 +	LLNotificationSet mItems;
 +	LLStandardSignal mChanged;
 +	LLStandardSignal mPassedFilter;
 +	LLStandardSignal mFailedFilter;
 +	
 +	// these are action methods that subclasses can override to take action 
 +	// on specific types of changes; the management of the mItems list is
 +	// still handled by the generic handler.
 +	virtual void onLoad(LLNotificationPtr p) {}
 +	virtual void onAdd(LLNotificationPtr p) {}
 +	virtual void onDelete(LLNotificationPtr p) {}
 +	virtual void onChange(LLNotificationPtr p) {}
 +
 +	bool updateItem(const LLSD& payload, LLNotificationPtr pNotification);
 +	LLNotificationFilter mFilter;
 +};
 +
 +// The type of the pointers that we're going to manage in the NotificationQueue system
 +// Because LLNotifications is a singleton, we don't actually expect to ever 
 +// destroy it, but if it becomes necessary to do so, the shared_ptr model
 +// will ensure that we don't leak resources.
 +class LLNotificationChannel;
 +typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr;
 +
 +// manages a list of notifications
 +// Note that if this is ever copied around, we might find ourselves with multiple copies
 +// of a queue with notifications being added to different nonequivalent copies. So we 
 +// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it.
 +// 
 +// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to 
 +// do something like:
 +//		LLNotificationChannel::buildChannel("name", "parent"...);
 +// This returns an LLNotificationChannelPtr, which you can store, or
 +// you can then retrieve the channel by using the registry:
 +//		LLNotifications::instance().getChannel("name")...
 +//
 +class LLNotificationChannel : 
 +	boost::noncopyable, 
 +	public LLNotificationChannelBase
 +{
 +	LOG_CLASS(LLNotificationChannel);
 +
 +public:  
 +	virtual ~LLNotificationChannel() {}
 +	typedef LLNotificationSet::iterator Iterator;
 +    
 +	std::string getName() const { return mName; }
 +	std::string getParentChannelName() { return mParent; }
 +    
 +    bool isEmpty() const;
 +    
 +    Iterator begin();
 +    Iterator end();
 +
 +    // Channels have a comparator to control sort order;
 +	// the default sorts by arrival date
 +    void setComparator(LLNotificationComparator comparator);
 +	
 +	std::string summarize();
 +
 +	// factory method for constructing these channels; since they're self-registering,
 +	// we want to make sure that you can't use new to make them
 +	static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent,
 +						LLNotificationFilter filter=LLNotificationFilters::includeEverything, 
 +						LLNotificationComparator comparator=LLNotificationComparators::orderByUUID());
 +	
 +protected:
 +    // Notification Channels have a filter, which determines which notifications
 +	// will be added to this channel. 
 +	// Channel filters cannot change.
 +	// Channels have a protected constructor so you can't make smart pointers that don't 
 +	// come from our internal reference; call NotificationChannel::build(args)
 +	LLNotificationChannel(const std::string& name, const std::string& parent,
 +						  LLNotificationFilter filter, LLNotificationComparator comparator);
 +
 +private:
 +	std::string mName;
 +	std::string mParent;
 +	LLNotificationComparator mComparator;
 +};
 +
 +
 +class LLNotificationsListener;
 +
 +class LLNotifications : 
 +	public LLSingleton<LLNotifications>, 
 +	public LLNotificationChannelBase
 +{
 +	LOG_CLASS(LLNotifications);
 +
 +	friend class LLSingleton<LLNotifications>;
 +public:
 +	// load notification descriptions from file; 
 +	// OK to call more than once because it will reload
 +	bool loadTemplates();  
 +	LLXMLNodePtr checkForXMLTemplate(LLXMLNodePtr item);
 +	
 +	// Add a simple notification (from XUI)
 +	void addFromCallback(const LLSD& name);
 +	
 +	// we provide a collection of simple add notification functions so that it's reasonable to create notifications in one line
 +	LLNotificationPtr add(const std::string& name, 
 +						const LLSD& substitutions = LLSD(), 
 +						const LLSD& payload = LLSD());
 +	LLNotificationPtr add(const std::string& name, 
 +						const LLSD& substitutions, 
 +						const LLSD& payload, 
 +						const std::string& functor_name);
 +	LLNotificationPtr add(const std::string& name, 
 +						const LLSD& substitutions, 
 +						const LLSD& payload, 
 +						LLNotificationFunctorRegistry::ResponseFunctor functor);
 +	LLNotificationPtr add(const LLNotification::Params& p);
 +
 +	void add(const LLNotificationPtr pNotif);
 +	void cancel(LLNotificationPtr pNotif);
 +	void update(const LLNotificationPtr pNotif);
 +
 +	LLNotificationPtr find(LLUUID uuid);
 +	
 +	typedef boost::function<void (LLNotificationPtr)> NotificationProcess;
 +	
 +	void forEachNotification(NotificationProcess process);
 +
 +	// This is all stuff for managing the templates
 +	// take your template out
 +	LLNotificationTemplatePtr getTemplate(const std::string& name);
 +	
 +	// get the whole collection
 +	typedef std::vector<std::string> TemplateNames;
 +	TemplateNames getTemplateNames() const;  // returns a list of notification names
 +	
 +	typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap;
 +
 +	TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); }
 +	TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); }
 +
 +	// test for existence
 +	bool templateExists(const std::string& name);
 +	// useful if you're reloading the file
 +	void clearTemplates();   // erase all templates
 +
 +	void forceResponse(const LLNotification::Params& params, S32 option);
 +
 +	void createDefaultChannels();
 +
 +	typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap;
 +	ChannelMap mChannels;
 +
 +	void addChannel(LLNotificationChannelPtr pChan);
 +	LLNotificationChannelPtr getChannel(const std::string& channelName);
 +	
 +	std::string getGlobalString(const std::string& key) const;
 +
 +	void setIgnoreAllNotifications(bool ignore);
 +	bool getIgnoreAllNotifications();
 +
 +private:
 +	// we're a singleton, so we don't have a public constructor
 +	LLNotifications();
 +	/*virtual*/ void initSingleton();
 +	
 +	void loadPersistentNotifications();
 +
 +	bool expirationFilter(LLNotificationPtr pNotification);
 +	bool expirationHandler(const LLSD& payload);
 +	bool uniqueFilter(LLNotificationPtr pNotification);
 +	bool uniqueHandler(const LLSD& payload);
 +	bool failedUniquenessTest(const LLSD& payload);
 +	LLNotificationChannelPtr pHistoryChannel;
 +	LLNotificationChannelPtr pExpirationChannel;
 +	
 +	// put your template in
 +	bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate);
 +	TemplateMap mTemplates;
 +
 +	std::string mFileName;
 +	
 +	typedef std::map<std::string, LLXMLNodePtr> XMLTemplateMap;
 +	XMLTemplateMap mXmlTemplates;
 +
 +	LLNotificationMap mUniqueNotifications;
 +	
 +	typedef std::map<std::string, std::string> GlobalStringMap;
 +	GlobalStringMap mGlobalStrings;
 +
 +	bool mIgnoreAllNotifications;
 +
 +    boost::scoped_ptr<LLNotificationsListener> mListener;
 +};
 +
 +
 +#endif//LL_LLNOTIFICATIONS_H
 +
 diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp new file mode 100644 index 0000000000..d2d83bd6e3 --- /dev/null +++ b/indra/llui/llnotificationslistener.cpp @@ -0,0 +1,28 @@ +/** + * @file   llnotificationslistener.cpp + * @author Brad Kittenbrink + * @date   2009-07-08 + * @brief  Implementation for llnotificationslistener. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llnotificationslistener.h" + +#include "llnotifications.h" +
 +LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
 +    LLDispatchListener("LLNotifications", "op"),
 +    mNotifications(notifications)
 +{
 +    add("requestAdd", &LLNotificationsListener::requestAdd);
 +} + +void LLNotificationsListener::requestAdd(const LLSD& event_data) const +{ +    mNotifications.add(event_data["name"], event_data["substitutions"], event_data["payload"]); +} diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h new file mode 100644 index 0000000000..a163b00550 --- /dev/null +++ b/indra/llui/llnotificationslistener.h @@ -0,0 +1,31 @@ +/** + * @file   llnotificationslistener.h + * @author Brad Kittenbrink + * @date   2009-07-08 + * @brief  Wrap subset of LLNotifications API in event API for test scripts. + *  + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#ifndef LL_LLNOTIFICATIONSLISTENER_H +#define LL_LLNOTIFICATIONSLISTENER_H + +#include "lleventdispatcher.h" +
 +class LLNotifications;
 +class LLSD;
 +
 +class LLNotificationsListener : public LLDispatchListener
 +{
 +public:
 +    LLNotificationsListener(LLNotifications & notifications);
 +
 +    void requestAdd(LLSD const & event_data) const;
 +
 +private:
 +    LLNotifications & mNotifications;
 +};
 +
 +#endif // LL_LLNOTIFICATIONSLISTENER_H
 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b14853777d..947f5bdb20 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1154,6 +1154,8 @@ bool LLAppViewer::mainLoop()  bool LLAppViewer::cleanup()  { +    // *TODO - unload event host module here -brad +  	//----------------------------------------------  	//this test code will be removed after the test  	//test manual call stack tracer @@ -4132,4 +4134,8 @@ void LLAppViewer::loadEventHostModule(S32 listen_port) const  	args["listen_port"] = listen_port;  	ll_plugin_start_func(args); + +    args = LLSD(); +    args["MESSAGE"] = "EventHost module loaded successfully"; +    LLNotifications::instance().add("SystemMessageTip", args);  }  | 
