diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 117e8e28ab3fe328cab6e15966aa0c04ba8bdaac..a6a5ef1f920f7aa163c5ef723fb23588b878a738 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 50fee41029437007732ea11a8931125553d93d5f..ec92e57b6e44305619eecb8c320c2a914499a887 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 512886790cab1f42a9fa0045465e243b4df00853..971d11db9719fb6a351e4fe73d5781c96bb1b6df 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 0000000000000000000000000000000000000000..d2d83bd6e3d051f8d545478e8855ec403c00f1eb
--- /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 0000000000000000000000000000000000000000..a163b00550d50b1395d2f15e26b3c0a18cc033d3
--- /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 b14853777d7ffb4226c2eaba7508937a4f349f36..947f5bdb20f3b0f305084796f901080fc945d1c2 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);
 }