diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 2b4c37be0..f2ad6229c 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -17,6 +17,7 @@ include_directories(
)
set(llcommon_SOURCE_FILES
+ aialert.cpp
aiframetimer.cpp
aithreadid.cpp
imageids.cpp
@@ -106,6 +107,7 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
+ aialert.h
aiframetimer.h
airecursive.h
aithreadid.h
diff --git a/indra/llcommon/aialert.cpp b/indra/llcommon/aialert.cpp
new file mode 100644
index 000000000..44d2c3230
--- /dev/null
+++ b/indra/llcommon/aialert.cpp
@@ -0,0 +1,67 @@
+/**
+ * @file aialert.cpp
+ *
+ * Copyright (c) 2013, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * 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.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 02/11/2013
+ * - Initial version, written by Aleric Inglewood @ SL
+ */
+
+#include "aialert.h"
+
+AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type,
+ std::string const& xml_desc, AIArgs const& args) : mModal(type)
+{
+ if (prefix) mLines.push_back(AIAlertLine(prefix));
+ mLines.push_back(AIAlertLine(xml_desc, args));
+}
+
+AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type,
+ AIAlert const& alert,
+ std::string const& xml_desc, AIArgs const& args) : mLines(alert.mLines), mModal(type)
+{
+ if (alert.mModal == modal) mModal = modal;
+ if (prefix) mLines.push_back(AIAlertLine(prefix, !mLines.empty()));
+ mLines.push_back(AIAlertLine(xml_desc, args));
+}
+
+AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type,
+ std::string const& xml_desc,
+ AIAlert const& alert) : mLines(alert.mLines), mModal(type)
+{
+ if (alert.mModal == modal) mModal = modal;
+ if (!mLines.empty()) { mLines.front().set_newline(); }
+ mLines.push_front(AIAlertLine(xml_desc));
+ if (prefix) mLines.push_front(AIAlertLine(prefix));
+}
+
+AIAlert::AIAlert(AIAlertPrefix const& prefix, modal_nt type,
+ std::string const& xml_desc, AIArgs const& args,
+ AIAlert const& alert) : mLines(alert.mLines), mModal(type)
+{
+ if (alert.mModal == modal) mModal = modal;
+ if (!mLines.empty()) { mLines.front().set_newline(); }
+ mLines.push_front(AIAlertLine(xml_desc, args));
+ if (prefix) mLines.push_front(AIAlertLine(prefix));
+}
+
diff --git a/indra/llcommon/aialert.h b/indra/llcommon/aialert.h
new file mode 100644
index 000000000..655a4890d
--- /dev/null
+++ b/indra/llcommon/aialert.h
@@ -0,0 +1,281 @@
+/**
+ * @file aialert.h
+ * @brief Declaration of AIArgs, AIAlertPrefix, AIAlertLine, AIAlert and AIAlertCode
+ *
+ * Copyright (c) 2013, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * 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.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 02/11/2013
+ * Initial version, written by Aleric Inglewood @ SL
+ */
+
+#ifndef AI_ALERT
+#define AI_ALERT
+
+#include "llpreprocessor.h"
+#include "llstring.h"
+#include
+#include
+
+//===================================================================================================================================
+// Facility to throw errors that can easily be converted to an informative pop-up floater for the user.
+
+// Throw arbitrary class.
+#define THROW_ALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(), AIAlert::not_modal, __VA_ARGS__)
+#define THROW_MALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(), AIAlert::modal, __VA_ARGS__)
+#define THROW_FALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::not_modal, __VA_ARGS__)
+#define THROW_FMALERT_CLASS(Alert, ...) throw Alert(AIAlertPrefix(__PRETTY_FUNCTION__, alert_line_pretty_function_prefix), AIAlert::modal, __VA_ARGS__)
+
+// Shortcut to throw AIAlert.
+#define THROW_ALERT(...) THROW_ALERT_CLASS(AIAlert, __VA_ARGS__)
+#define THROW_MALERT(...) THROW_MALERT_CLASS(AIAlert, __VA_ARGS__)
+#define THROW_FALERT(...) THROW_FALERT_CLASS(AIAlert, __VA_ARGS__)
+#define THROW_FMALERT(...) THROW_FMALERT_CLASS(AIAlert, __VA_ARGS__)
+
+// Shortcut to throw AIAlertCode.
+#define THROW_ALERTC(...) THROW_ALERT_CLASS(AIAlertCode, __VA_ARGS__)
+#define THROW_MALERTC(...) THROW_MALERT_CLASS(AIAlertCode, __VA_ARGS__)
+#define THROW_FALERTC(...) THROW_FALERT_CLASS(AIAlertCode, __VA_ARGS__)
+#define THROW_FMALERTC(...) THROW_FMALERT_CLASS(AIAlertCode, __VA_ARGS__)
+
+// Shortcut to throw AIAlertCode with errno as code.
+#define THROW_ALERTE(...) do { int errn = errno; THROW_ALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0)
+#define THROW_MALERTE(...) do { int errn = errno; THROW_MALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0)
+#define THROW_FALERTE(...) do { int errn = errno; THROW_FALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0)
+#define THROW_FMALERTE(...) do { int errn = errno; THROW_FMALERT_CLASS(AIAlertCode, errn, __VA_ARGS__); } while(0)
+
+// Examples
+
+#ifdef EXAMPLE_CODE
+
+ //----------------------------------------------------------
+ // To show the alert box:
+
+ catch (AIAlert const& alert)
+ {
+ LLNotificationsUtil::add(alert); // Optionally pass alert_line_pretty_function_prefix as second parameter to *suppress* that output.
+ }
+
+ // or, for example
+
+ catch (AIAlertCode const& alert)
+ {
+ if (alert.getCode() != EEXIST)
+ {
+ LLNotificationsUtil::add(alert, alert_line_pretty_function_prefix);
+ }
+ }
+ //----------------------------------------------------------
+ // To throw alerts:
+
+ THROW_ALERT("ExampleKey"); // A) Lookup "ExampleKey" in strings.xml and show translation.
+ THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)(...etc...)); // B) Same as A, but replace [FIRST] with first, [SECOND] with second, etc.
+ THROW_ALERT("ExampleKey", alert); // C) As A, but followed by a colon and a newline, and then the text of 'alert'.
+ THROW_ALERT(alert, "ExampleKey"); // D) The text of 'alert', followed by a colon and a newline and then as A.
+ THROW_ALERT("ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second), alert); // E) As B, but followed by a colon and a newline, and then the text of 'alert'.
+ THROW_ALERT(alert, "ExampleKey", AIArgs("[FIRST]", first)("[SECOND]", second)); // F) The text of 'alert', followed by a colon and a newline and then as B.
+ // where 'alert' is a caught AIAlert object (as above) in a rethrow.
+ // Prepend ALERT with M and/or F to make the alert box Modal and/or prepend the text with the current function name.
+ // For example,
+ THROW_MFALERT("ExampleKey", AIArgs("[FIRST]", first)); // Throw a Modal alert box that is prefixed with the current Function name.
+ // Append E after ALERT to throw an AIAlertCode class that contains the current errno.
+ // For example,
+ THROW_FALERTE("ExampleKey", AIArgs("[FIRST]", first)); // Throw an alert box that is prefixed with the current Function name and pass errno to the catcher.
+
+#endif // EXAMPLE_CODE
+
+//
+//===================================================================================================================================
+
+enum alert_line_type_nt
+{
+ alert_line_normal = 0,
+ alert_line_empty_prefix = 1,
+ alert_line_pretty_function_prefix = 2
+ // These must exist of single bits (a mask).
+};
+
+// An AIAlertPrefix currently comes only in two flavors:
+//
+// alert_line_empty_prefix : An empty prefix.
+// alert_line_pretty_function_prefix : A function name prefix, this is the function from which the alert was thrown.
+
+class LL_COMMON_API AIAlertPrefix
+{
+ public:
+ AIAlertPrefix(void) : mType(alert_line_empty_prefix) { }
+ AIAlertPrefix(char const* str, alert_line_type_nt type) : mStr(str), mType(type) { }
+
+ operator bool(void) const { return mType != alert_line_empty_prefix; }
+ alert_line_type_nt type(void) const { return mType; }
+ std::string const& str(void) const { return mStr; }
+
+ private:
+ std::string mStr; // Literal text. For example a C++ function name.
+ alert_line_type_nt mType; // The type of this prefix.
+};
+
+// A wrapper around LLStringUtil::format_map_t to allow constructing a dictionary
+// on one line by doing:
+//
+// AIArgs("[ARG1]", arg1)("[ARG2]", arg2)("[ARG3]", arg3)...
+
+class LL_COMMON_API AIArgs
+{
+ private:
+ LLStringUtil::format_map_t mArgs; // The underlying replacement map.
+
+ public:
+ // Construct an empty map.
+ AIArgs(void) { }
+ // Construct a map with a single replacement.
+ AIArgs(char const* key, std::string const& replacement) { mArgs[key] = replacement; }
+ // Add another replacement.
+ AIArgs& operator()(char const* key, std::string const& replacement) { mArgs[key] = replacement; return *this; }
+ // The destructor may not throw.
+ ~AIArgs() throw() { }
+
+ // Accessor.
+ LLStringUtil::format_map_t const& operator*() const { return mArgs; }
+};
+
+// A class that represents one line with its replacements.
+// The string mXmlDesc shall be looked up in strings.xml.
+// This is not done as part of this class because LLTrans::getString
+// is not part of llcommon.
+
+class LL_COMMON_API AIAlertLine
+{
+ private:
+ bool mNewline; // Prepend this line with a newline if set.
+ std::string mXmlDesc; // The keyword to look up in string.xml.
+ AIArgs mArgs; // Replacement map.
+ alert_line_type_nt mType; // The type of this line: alert_line_normal for normal lines, other for prefixes.
+
+ public:
+ AIAlertLine(std::string const& xml_desc, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mType(alert_line_normal) { }
+ AIAlertLine(std::string const& xml_desc, AIArgs const& args, bool newline = false) : mNewline(newline), mXmlDesc(xml_desc), mArgs(args), mType(alert_line_normal) { }
+ AIAlertLine(AIAlertPrefix const& prefix, bool newline = false) : mNewline(newline), mXmlDesc("AIPrefix"), mArgs("[PREFIX]", prefix.str()), mType(prefix.type()) { }
+ // The destructor may not throw.
+ ~AIAlertLine() throw() { }
+
+ // Prepend a newline before this line.
+ void set_newline(void) { mNewline = true; }
+
+ // These are to be used like: LLTrans::getString(line.getXmlDesc(), line.args()) and prepend with a \n if prepend_newline() returns true.
+ std::string getXmlDesc(void) const { return mXmlDesc; }
+ LLStringUtil::format_map_t const& args(void) const { return *mArgs; }
+ bool prepend_newline(void) const { return mNewline; }
+
+ // Accessors.
+ bool suppressed(unsigned int suppress_mask) const { return (suppress_mask & mType) != 0; }
+ bool is_prefix(void) const { return mType != alert_line_normal; }
+};
+
+// This class is used to throw an error that will cause
+// an alert box to pop up for the user.
+//
+// An alert box only has text and an OK button.
+// The alert box does not give feed back to the program; it is purely informational.
+
+// The class represents multiple lines, each line is to be translated and catenated,
+// separated by newlines, and then written to an alert box. This is not done as part
+// of this class because LLTrans::getString and LLNotification is not part of llcommon.
+// Instead call LLNotificationUtil::add(AIAlert const&).
+
+class LL_COMMON_API AIAlert : public std::exception
+{
+ public:
+ typedef std::deque lines_type;
+ enum modal_nt { not_modal, modal };
+
+ // The destructor may not throw.
+ ~AIAlert() throw() { }
+
+ // Accessors.
+ lines_type const& lines(void) const { return mLines; }
+ bool is_modal(void) const { return mModal == modal; }
+
+ // A string with zero or more replacements.
+ AIAlert(AIAlertPrefix const& prefix, modal_nt modal,
+ std::string const& xml_desc, AIArgs const& args = AIArgs());
+
+ // Same as above bit prepending the message with the text of another alert.
+ AIAlert(AIAlertPrefix const& prefix, modal_nt modal,
+ AIAlert const& alert,
+ std::string const& xml_desc, AIArgs const& args = AIArgs());
+
+ // Same as above but appending the message with the text of another alert.
+ // (no args)
+ AIAlert(AIAlertPrefix const& prefix, modal_nt modal,
+ std::string const& xml_desc,
+ AIAlert const& alert);
+ // (with args)
+ AIAlert(AIAlertPrefix const& prefix, modal_nt modal,
+ std::string const& xml_desc, AIArgs const& args,
+ AIAlert const& alert);
+
+ private:
+ lines_type mLines; // The lines (or prefixes) of text to be displayed, each consisting on a keyword (to be looked up in strings.xml) and a replacement map.
+ modal_nt mModal; // If true, make the alert box a modal floater.
+};
+
+// Same as AIAlert but allows to pass an additional error code.
+
+class LL_COMMON_API AIAlertCode : public AIAlert
+{
+ private:
+ int mCode;
+
+ public:
+ // The destructor may not throw.
+ ~AIAlertCode() throw() { }
+
+ // Accessor.
+ int getCode(void) const { return mCode; }
+
+ // A string with zero or more replacements.
+ AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code,
+ std::string const& xml_desc, AIArgs const& args = AIArgs()) :
+ AIAlert(prefix, modal, xml_desc, args) { }
+
+ // Same as above bit prepending the message with the text of another alert.
+ AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code,
+ AIAlert const& alert,
+ std::string const& xml_desc, AIArgs const& args = AIArgs()) :
+ AIAlert(prefix, modal, alert, xml_desc, args) { }
+
+ // Same as above but appending the message with the text of another alert.
+ // (no args)
+ AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code,
+ std::string const& xml_desc,
+ AIAlert const& alert) :
+ AIAlert(prefix, modal, xml_desc, alert) { }
+ // (with args)
+ AIAlertCode(AIAlertPrefix const& prefix, modal_nt modal, int code,
+ std::string const& xml_desc, AIArgs const& args,
+ AIAlert const& alert) :
+ AIAlert(prefix, modal, xml_desc, args, alert) { }
+
+};
+
+#endif // AI_ALERT
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 35d20d2ee..112a25e66 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -231,7 +231,9 @@ public:
operator std::string() const { return mString; }
bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; }
std::size_t length() const { return mString.length(); }
-
+ // The destructor may not throw.
+ ~LLFormatMapString() throw() { }
+
private:
std::string mString;
};
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 41254b0bb..2f60b99fa 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -40,6 +40,7 @@
#include "lltrans.h"
#include "llnotifications.h"
+#include "aialert.h"
#include "../newview/hippogridmanager.h"
@@ -1479,6 +1480,32 @@ LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
return pNotif;
}
+LLNotificationPtr LLNotifications::add(AIAlert const& alert, unsigned int suppress_mask)
+{
+ std::string alert_text;
+ bool suppress_newlines = false;
+ bool last_was_prefix = false;
+ for (AIAlert::lines_type::const_iterator line = alert.lines().begin(); line != alert.lines().end(); ++line)
+ {
+ // Even if a line is suppressed, we print its leading newline if requested, but never more than one.
+ if (!suppress_newlines && line->prepend_newline())
+ {
+ alert_text += '\n';
+ suppress_newlines = true;
+ }
+ if (!line->suppressed(suppress_mask))
+ {
+ if (last_was_prefix) alert_text += ' '; // The translation system strips off spaces... add them back.
+ alert_text += LLTrans::getString(line->getXmlDesc(), line->args());
+ suppress_newlines = false;
+ last_was_prefix = line->is_prefix();
+ }
+ }
+ LLSD substitutions = LLSD::emptyMap();
+ substitutions["[PAYLOAD]"] = alert_text;
+ return add(LLNotification::Params(alert.is_modal() ? "AIAlertModal" : "AIAlert").substitutions(substitutions));
+}
+
void LLNotifications::add(const LLNotificationPtr pNotif)
{
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index fe9049ee2..4adfabbcb 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -108,6 +108,8 @@
#include "llnotificationptr.h"
#include "llnotificationcontext.h"
+class AIAlert;
+
typedef enum e_notification_priority
{
NOTIFICATION_PRIORITY_UNSPECIFIED,
@@ -737,6 +739,7 @@ public:
const LLSD& substitutions,
const LLSD& payload,
LLNotificationFunctorRegistry::ResponseFunctor functor);
+ LLNotificationPtr add(AIAlert const& alert, unsigned int suppress_mask);
LLNotificationPtr add(const LLNotification::Params& p);
void forceResponse(const LLNotification::Params& params, S32 option);
diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp
index 4abc9ca69..13e2b897d 100644
--- a/indra/llui/llnotificationsutil.cpp
+++ b/indra/llui/llnotificationsutil.cpp
@@ -30,6 +30,11 @@
#include "llsd.h"
#include "llxmlnode.h" // apparently needed to call LLNotifications::instance()
+LLNotificationPtr LLNotificationsUtil::add(AIAlert const& alert, unsigned int suppress_mask)
+{
+ return LLNotifications::instance().add(alert, suppress_mask);
+}
+
LLNotificationPtr LLNotificationsUtil::add(const std::string& name)
{
return LLNotifications::instance().add(
diff --git a/indra/llui/llnotificationsutil.h b/indra/llui/llnotificationsutil.h
index 4093324d0..586b0d0bc 100644
--- a/indra/llui/llnotificationsutil.h
+++ b/indra/llui/llnotificationsutil.h
@@ -34,9 +34,12 @@
#include
class LLSD;
+class AIAlert;
namespace LLNotificationsUtil
{
+ LLNotificationPtr add(AIAlert const& alert, unsigned int suppress_mask = 0); // Singu extension.
+
LLNotificationPtr add(const std::string& name);
LLNotificationPtr add(const std::string& name,
diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml
index 1bcef0294..919e2595a 100644
--- a/indra/newview/skins/default/xui/en-us/notifications.xml
+++ b/indra/newview/skins/default/xui/en-us/notifications.xml
@@ -133,6 +133,20 @@
+
+[PAYLOAD]
+
+
+
+[PAYLOAD]
+
+
Teleport completed from
+
+ "[PREFIX]: "
+