Skip to content

Instantly share code, notes, and snippets.

@Katharine
Created August 11, 2016 10:38
Show Gist options
  • Save Katharine/f87f4ce4a0779254ceabd012f725f0a1 to your computer and use it in GitHub Desktop.
Save Katharine/f87f4ce4a0779254ceabd012f725f0a1 to your computer and use it in GitHub Desktop.
diff -r c005a3294457 indra/newview/CMakeLists.txt
--- a/indra/newview/CMakeLists.txt Thu Aug 23 17:45:25 2012 -0400
+++ b/indra/newview/CMakeLists.txt Thu Aug 11 03:37:35 2016 -0700
@@ -111,6 +111,8 @@
exoflickrauth.cpp
exogroupmutelist.cpp
exonotificationmanager.cpp
+ exoscriptedui.cpp
+ exosuisource.cpp
llaccountingcostmanager.cpp
llagent.cpp
llagentaccess.cpp
@@ -719,6 +721,9 @@
exogroupmutelist.h
exonotificationmanager.h
exonotifier.h
+ exoscriptedui.h
+ exosuifloater.h
+ exosuisource.h
groupchatlistener.h
llaccountingcostmanager.h
llagent.h
diff -r c005a3294457 indra/newview/exoscriptedui.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/exoscriptedui.cpp Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,155 @@
+/**
+ * @file exoscriptedui.cpp
+ * @brief Allows for LSL control of some user interface elements
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (C) 2012 Katharine Berry
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "exoscriptedui.h"
+
+#include "llnotificationsutil.h"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+#include "exosuisource.h"
+
+typedef exoSUIMessage::args_t args_t; // because meh, typing.
+
+bool exoScriptedUI::receiveChat(const std::string& text, LLUUID from_id)
+{
+ exoSUIMessage message;
+ if(!parseMessage(text, from_id, message))
+ {
+ return false;
+ }
+ return processMessage(message);
+}
+
+// This method parses out messages from scripts.
+// Such messages are formatted like IRC client->server messages (without origin):
+// COMMAND arg1 arg2 :an extended argument permitting any unicode character.
+// There must always be a command; all other parts are optional. It is legal to have
+// an empty extended argument - it will be interpreted as an empty string.
+// This format was chosen because it should be flexible enough and is easily generated
+// and parsed using LSL.
+bool exoScriptedUI::parseMessage(const std::string& text, LLUUID from_id, exoSUIMessage& message)
+{
+ if(text.length() < 2) return false;
+ if(text[0] != '!') return false;
+ // Set up the message.
+ message.origin = from_id;
+ // First break out the command
+ size_t space = text.find(' ');
+ if(space == std::string::npos)
+ {
+ // It's all the command!
+ message.command = text.substr(1);
+ return true;
+ }
+ message.command = text.substr(1, space - 1);
+
+ // If we have a colon, break that out
+ size_t colon = text.find(':');
+ bool has_final_arg = (colon != std::string::npos);
+ std::string final_arg;
+ if(has_final_arg)
+ {
+ final_arg = text.substr(colon + 1);
+ }
+
+ // Now produce the argument list.
+ std::string argument_text = text.substr(space+1, colon - space - 2);
+ boost::split(message.arguments, argument_text, boost::is_space(), boost::token_compress_on);
+ if(has_final_arg)
+ {
+ message.arguments.push_back(final_arg);
+ }
+
+
+ return true;
+}
+
+bool exoScriptedUI::processMessage(const exoSUIMessage& message)
+{
+ // !suinit [channel] Some sort of explanatory text.
+ // Registers the object, provides a confirmation response, and prompts the
+ // user for permission
+ if(message.command == "suinit")
+ {
+ if(message.arguments.size() < 1)
+ {
+ // We can't really do anything here.
+ return true;
+ }
+ int channel;
+ try
+ {
+ channel = boost::lexical_cast<int>(message.arguments[0]);
+ }
+ catch(const boost::bad_lexical_cast& e)
+ {
+ // Can't do anything here either.
+ return true;
+ }
+ if(mSources.count(message.origin))
+ {
+ mSources.erase(message.origin);
+ }
+ boost::shared_ptr<exoSUISource> source(new exoSUISource(channel, message.origin));
+ mSources.insert(source_map_t::value_type(message.origin, source));
+
+ // Send confirmation message
+ source->send("ready", "1", "0");
+
+ // Show permission prompt
+ LLNotificationsUtil::add("ExodusScriptedUIPermission", LLSD(), LLSD(), boost::bind(&exoScriptedUI::permissionCallback, this, boost::ref(*source), _1, _2));
+ return true;
+ }
+
+ source_map_t::iterator it = mSources.find(message.origin);
+ if(it == mSources.end() || !it->second->mPermitted)
+ {
+ it->second->send("forbidden");
+ return true;
+ }
+
+ if(message.command == "deregister")
+ {
+ it->second->send("deregistered");
+ mSources.erase(it);
+ return true;
+ }
+
+ // Now that we've dealt with permissions checks, etc. hand off to the source for further processing.
+ return it->second->processMessage(message);
+}
+
+void exoScriptedUI::permissionCallback(exoSUISource& source, const LLSD& notification, const LLSD& response)
+{
+ int option = LLNotificationsUtil::getSelectedOption(notification, response);
+
+ source.send("permission", option ? "0" : "1");
+
+ if(option == 1) // Not OK
+ {
+ mSources.erase(source.mUUID);
+ }
+ else
+ {
+ source.mPermitted = true;
+ }
+}
+
diff -r c005a3294457 indra/newview/exoscriptedui.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/exoscriptedui.h Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,49 @@
+/**
+ * @file exoscriptedui.h
+ * @brief Allows for LSL control of some user interface elements
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (C) 2012 Katharine Berry
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ * $/LicenseInfo$
+ */
+
+#ifndef EXOSCRIPTEDUI_H
+#define EXOSCRIPTEDUI_H
+
+#include <boost/shared_ptr.hpp>
+
+class exoSUISource;
+
+struct exoSUIMessage {
+ typedef std::vector<std::string> args_t;
+
+ std::string command;
+ args_t arguments;
+ LLUUID origin;
+};
+
+class exoScriptedUI : public LLSingleton<exoScriptedUI>
+{
+public:
+ bool receiveChat(const std::string& text, LLUUID from_id);
+
+private:
+ bool parseMessage(const std::string& text, LLUUID from_id, exoSUIMessage& message);
+ bool processMessage(const exoSUIMessage& message);
+ void permissionCallback(exoSUISource& source, const LLSD& notification, const LLSD& response);
+
+ typedef std::map<LLUUID, boost::shared_ptr<exoSUISource>, lluuid_less> source_map_t;
+ source_map_t mSources;
+};
+
+#endif
diff -r c005a3294457 indra/newview/exosuifloater.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/exosuifloater.h Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,32 @@
+/**
+ * @file exosuifloater.h
+ * @brief The ScriptedUI floater class, because LLUI made puppy eyes.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (C) 2012 Katharine Berry
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ * $/LicenseInfo$
+ */
+
+#ifndef EXOSUIFLOATER_H
+#define EXOSUIFLOATER_H
+
+#include "llfloater.h"
+
+class exoSUIFloater : public LLFloater
+{
+public:
+ exoSUIFloater(std::string key) : LLFloater(key) { }
+ static const int TITLE_BAR_HEIGHT = 25;
+};
+
+#endif
diff -r c005a3294457 indra/newview/exosuisource.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/exosuisource.cpp Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,527 @@
+/**
+ * @file exosuisource.cpp
+ * @brief Manages UI sources. Ultimately, the bulk of the work is here.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (C) 2012 Katharine Berry
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "exosuisource.h"
+
+#include "llchat.h"
+#include "llviewermessage.h"
+#include "llagent.h"
+#include "llnotificationsutil.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "lluictrlfactory.h"
+#include "stringize.h"
+
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "lllineeditor.h"
+#include "llpanel.h"
+#include "llsliderctrl.h"
+#include "llcolorswatch.h"
+
+#include <typeinfo>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "exoscriptedui.h"
+#include "exosuifloater.h"
+
+typedef exoSUIMessage::args_t args_t;
+
+template<typename T>
+static void parseLayoutParams(T& p, LLView* parent, args_t::const_iterator begin, args_t::const_iterator end);
+
+exoSUISource::exoSUISource(int channel, LLUUID uuid) :
+ mChannel(channel),
+ mUUID(uuid),
+ mPermitted(false),
+ mShutdown(false)
+{
+ prepareHandlers();
+}
+
+exoSUISource::~exoSUISource()
+{
+ mShutdown = true;
+ BOOST_FOREACH(floater_map_t::value_type floater, mFloaters)
+ {
+ floater.second->closeFloater();
+ }
+}
+
+void exoSUISource::prepareHandlers()
+{
+ addHandler("floater", &exoSUISource::handleFloater);
+ addHandler("button", &exoSUISource::handleButton);
+ addHandler("text", &exoSUISource::handleText);
+ addHandler("settext", &exoSUISource::handleSetText);
+ addHandler("input", &exoSUISource::handleInput);
+ addHandler("panel", &exoSUISource::handlePanel);
+ addHandler("slider", &exoSUISource::handleSlider);
+ addHandler("swatch", &exoSUISource::handleSwatch);
+}
+
+inline void exoSUISource::addHandler(const std::string& command, void (exoSUISource::*fn)(const args_t&))
+{
+ mHandlers.insert(handler_map_t::value_type(command, boost::bind(fn, this, _1)));
+}
+
+bool exoSUISource::processMessage(const exoSUIMessage& message)
+{
+ handler_map_t::iterator it = mHandlers.find(message.command);
+ if(it != mHandlers.end())
+ {
+ try
+ {
+ it->second(message.arguments);
+ }
+ catch(const boost::bad_lexical_cast& e)
+ {
+ send("badsyntax", message.command, e.what());
+ }
+ catch(const exo_duplicate_control& e)
+ {
+ send("duplicate", message.command, e.name());
+ }
+ catch(const exo_missing_parent& e)
+ {
+ send("notfound", message.command, e.name());
+ }
+ return true;
+ }
+ return false;
+}
+
+// !floater name width height resizable :FLOATER NAME
+void exoSUISource::handleFloater(const args_t& args)
+{
+ llinfos << "new floater '" << args[0] << "'" << llendl;
+ if(args.size() != 5)
+ {
+ send("badsyntax", "floater", "Usage: [name] [width] [height] [resizable] :[floater name]");
+ return;
+ }
+ const std::string& name = args[0];
+ // Catch duplicates across both floaters and controls to prevent ambiguity.
+ floater_map_t::iterator it = mFloaters.lower_bound(name);
+ if((it != mFloaters.end() && it->first == name) || mControls.count(name))
+ {
+ throw exo_duplicate_control(name);
+ }
+ LLFloater* floater = LLFloaterReg::getInstance("exo_scripted_ui", STRINGIZE(mUUID << name));
+
+ floater->setTitle(args[4]);
+ bool can_resize = boost::lexical_cast<bool>(args[3]);
+ int width = boost::lexical_cast<int>(args[1]);
+ int height = boost::lexical_cast<int>(args[2]) + exoSUIFloater::TITLE_BAR_HEIGHT; // + 25 for header.
+ floater->reshape(width, height);
+ floater->setResizeLimits(width, height);
+ floater->setCanResize(can_resize);
+ floater->setCloseCallback(boost::bind(&exoSUISource::onFloaterClosed, this, floater, name));
+ floater->openFloater(floater->getKey());
+ floater->setFocus(TRUE);
+ mFloaters.insert(it, floater_map_t::value_type(name, floater));
+}
+
+// !button name parent (layout) :label
+void exoSUISource::handleButton(const args_t& args)
+{
+ if(args.size() < 3 || args.size() % 2 == 0)
+ {
+ send("badsyntax", "button", "Usage: [name] [parent] (layout) :[button label]");
+ return;
+ }
+ const std::string& name = args[0];
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+
+ LLButton::Params p = LLUICtrlFactory::getDefaultParams<LLButton>();
+ LLView *parent = findParent(args[1]);
+ p.name = name;
+ p.label = args.back();
+ parseLayoutParams<LLButton::Params>(p, parent, args.begin() + 2, args.end() - 1);
+ llinfos << "adjusted rect: " << p.rect << llendl;
+ LLButton *button = LLUICtrlFactory::create<LLButton>(p, parent);
+ // Some callbacks
+ button->setClickedCallback(boost::bind(&exoSUISource::onGenericCommit, this, name, _2));
+ mControls.insert(it, ctrl_map_t::value_type(name, button));
+ return;
+}
+
+// !text name parent wrap (layout) :text
+void exoSUISource::handleText(const args_t& args)
+{
+ if(args.size() < 4 || args.size() % 2 == 1)
+ {
+ send("badsyntax", "text", "Usage: [name] [parent] [wrap] (layout) :text");
+ return;
+ }
+ const std::string& name = args[0];
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+
+ LLTextBox::Params p = LLUICtrlFactory::getDefaultParams<LLTextBox>();
+ bool word_wrap = boost::lexical_cast<bool>(args[2]);
+ p.wrap = word_wrap;
+ p.use_ellipses = !word_wrap;
+ LLView *parent = findParent(args[1]);
+ parseLayoutParams<LLTextBox::Params>(p, parent, args.begin() + 3, args.end() - 1);
+
+ LLTextBox *text = LLUICtrlFactory::create<LLTextBox>(p, parent);
+ text->setText(args.back());
+ mControls.insert(it, ctrl_map_t::value_type(name, text));
+}
+
+// !input name parent max_length revert_on_esc spellcheck commit_on_focus_lost (layout) :placeholder
+void exoSUISource::handleInput(const args_t& args)
+{
+ if(args.size() < 7 || args.size() % 2 == 0)
+ {
+ send("badsyntax", "text", "Usage: name parent max_length revert_on_esc spellcheck commit_on_focus_lost (layout) :placeholder");
+ return;
+ }
+ const std::string& name = args[0];
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+
+ LLLineEditor::Params p = LLUICtrlFactory::getDefaultParams<LLLineEditor>();
+ p.max_length.bytes = llmin(boost::lexical_cast<int>(args[2]), 512);
+ p.revert_on_esc = boost::lexical_cast<bool>(args[3]);
+ p.spellcheck = boost::lexical_cast<bool>(args[4]);
+ p.commit_on_focus_lost = boost::lexical_cast<bool>(args[5]);
+ p.default_text = args.back();
+
+ LLView *parent = findParent(args[1]);
+ parseLayoutParams<LLLineEditor::Params>(p, parent, args.begin() + 6, args.end() - 1);
+
+ LLLineEditor *input = LLUICtrlFactory::create<LLLineEditor>(p, parent);
+ input->setCommitCallback(boost::bind(&exoSUISource::onGenericCommit, this, name, _2));
+ mControls.insert(it, ctrl_map_t::value_type(name, input));
+}
+
+
+// !settext element :new text
+void exoSUISource::handleSetText(const args_t& args)
+{
+ if(args.size() != 2)
+ {
+ send("badsyntax", "settext", "Usage: [element] :new text");
+ return;
+ }
+ const std::string& name = args[0];
+ const std::string& text = args[1];
+ ctrl_map_t::iterator it = mControls.find(name);
+ if(it == mControls.end())
+ {
+ send("notfound", "settext", name);
+ return;
+ }
+ // Now we have to do whatever is appropriate to the control we actually have.
+ // It would be nice if there was some common API to do this, but there is not.
+ // Note that derived classes will fail here. We currently don't care.
+ // If/when we do a potentially viable alternative is to simply try the casts and
+ // act if they succeed. Slower, though.
+ LLUICtrl* control = it->second;
+ const std::type_info& type = typeid(*control);
+ if(type == typeid(LLTextBox))
+ {
+ dynamic_cast<LLTextBox*>(control)->setText(text);
+ }
+ else if(type == typeid(LLButton))
+ {
+ dynamic_cast<LLButton*>(control)->setLabel(text);
+ }
+ else if(type == typeid(LLLineEditor))
+ {
+ dynamic_cast<LLLineEditor*>(control)->setText(text);
+ }
+ else
+ {
+ // Send what we actually have if this fails.
+ args_t args;
+ args.reserve(3);
+ args.push_back("settext");
+ args.push_back(name);
+ args.push_back(type.name());
+ send("invalid", args);
+ }
+}
+
+// !panel name parent border_width bevel (layout)
+void exoSUISource::handlePanel(const args_t& args)
+{
+ if(args.size() < 4 || args.size() % 2 == 1)
+ {
+ send("badsyntax", "panel", "Usage: name parent border_width bevel (layout)");
+ return;
+ }
+ const std::string& name = args[0];
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+
+ LLPanel::Params p = LLUICtrlFactory::getDefaultParams<LLPanel>();
+ p.name = name;
+ int border_width = boost::lexical_cast<int>(args[2]);
+ if(border_width > 0)
+ {
+ p.has_border = true;
+ p.border.border_thickness = llclamp(border_width, 0, 2);
+ p.border.bevel_style = args[3];
+ }
+ LLView *parent = findParent(args[1]);
+ parseLayoutParams<LLPanel::Params>(p, parent, args.begin() + 4, args.end());
+ LLPanel *panel = LLUICtrlFactory::create<LLPanel>(p, parent);
+ mControls.insert(it, ctrl_map_t::value_type(name, panel));
+}
+
+// !slider name parent orientation label_width text_width show_text can_edit_text decimal_digits min_value max_value increment (layout) :label
+void exoSUISource::handleSlider(const args_t& args)
+{
+ if(args.size() < 13 || args.size() % 2 == 0)
+ {
+ send("badsyntax", "sider", "Usage: name parent label_width text_width show_text can_edit_text decimal_digits default_value min_value max_value increment (layout) :label");
+ return;
+ }
+ const std::string& name = args[0];
+ int label_width = boost::lexical_cast<int>(args[3]);
+ int text_width = boost::lexical_cast<int>(args[4]);
+
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+
+ LLSliderCtrl::Params p = LLUICtrlFactory::getDefaultParams<LLSliderCtrl>();
+ p.name = name;
+ p.orientation = args[2];
+ if(label_width)
+ p.label_width = label_width;
+ if(text_width)
+ p.text_width = text_width;
+ p.show_text = boost::lexical_cast<bool>(args[5]);
+ p.can_edit_text = boost::lexical_cast<bool>(args[6]);
+ p.decimal_digits = boost::lexical_cast<int>(args[7]);
+ p.initial_value = boost::lexical_cast<float>(args[8]);
+ p.min_value = boost::lexical_cast<float>(args[9]);
+ p.max_value = boost::lexical_cast<float>(args[10]);
+ p.increment = boost::lexical_cast<float>(args[11]);
+ p.label = args.back();
+
+ LLView *parent = findParent(args[1]);
+ parseLayoutParams<LLSliderCtrl::Params>(p, parent, args.begin() + 12, args.end() - 1);
+ LLSliderCtrl *slider = LLUICtrlFactory::create<LLSliderCtrl>(p, parent);
+ slider->setCommitCallback(boost::bind(&exoSUISource::onGenericCommit, this, name, _2));
+ mControls.insert(it, ctrl_map_t::value_type(name, slider));
+}
+
+// !swatch name parent immediate red green blue (layout) :caption
+// <red, green, blue> is also acceptable but <red,green,blue> is not.
+void exoSUISource::handleSwatch(const args_t& args)
+{
+ if(args.size() < 7 || args.size() % 2 == 0)
+ {
+ send("badsyntax", "swatch", "Usage: name parent immediate red green blue (layout) :caption");
+ return;
+ }
+ const std::string& name = args[0];
+ ctrl_map_t::iterator it = findControlInsertionPoint(name);
+ // This seems to be the appropriate sequence of casts to get the required
+ // LLUIColor out of LSL's "<0, 0.5, 1>"-style string.
+ LLUIColor colour(LLColor4(LLColor3(parseLSLVector(args.begin() + 3).mV)));
+
+ LLColorSwatchCtrl::Params p = LLUICtrlFactory::getDefaultParams<LLColorSwatchCtrl>();
+ p.color = colour;
+ p.label = args.back();
+ p.can_apply_immediately = boost::lexical_cast<bool>(args[2]);
+
+ LLView *parent = findParent(args[1]);
+ parseLayoutParams<LLColorSwatchCtrl::Params>(p, parent, args.begin() + 6, args.end() - 1);
+ LLColorSwatchCtrl *swatch = LLUICtrlFactory::create<LLColorSwatchCtrl>(p, parent);
+ swatch->setCommitCallback(boost::bind(&exoSUISource::onSwatchCommit, this, name, _1));
+ mControls.insert(it, ctrl_map_t::value_type(name, swatch));
+}
+
+
+exoSUISource::ctrl_map_t::iterator exoSUISource::findControlInsertionPoint(const std::string &name)
+{
+ // Check for duplication in both mFloaters and mControls to avoid
+ // ambiguity when appending (which only specifies a name and not type)
+ if(mFloaters.count(name))
+ {
+ throw exo_duplicate_control(name);
+ }
+ ctrl_map_t::iterator it = mControls.lower_bound(name);
+ if(it != mControls.end() && it->first == name)
+ {
+ throw exo_duplicate_control(name);
+ }
+ return it;
+}
+
+LLView* exoSUISource::findParent(const std::string& name) const
+{
+ // Check floaters first because it's probably more likely and almost
+ // certainly smaller.
+ floater_map_t::const_iterator floater_it = mFloaters.find(name);
+ if(floater_it != mFloaters.end())
+ return floater_it->second;
+
+ ctrl_map_t::const_iterator control_it = mControls.find(name);
+ if(control_it != mControls.end())
+ return control_it->second;
+
+ throw exo_missing_parent(name);
+ return NULL;
+}
+
+void exoSUISource::onFloaterClosed(LLFloater* floater, const std::string& name)
+{
+ // The floater closing should cause it to destroy itself and its contents
+ // without our help. Instead just clean up our references to it.
+ for(LLView::tree_iterator_t it = floater->beginTreeDFS(), end = floater->endTreeDFS(); it != end; ++it)
+ {
+ mControls.erase((*it)->getName());
+ }
+ // If we're shutting down we don't need to send the close notices;
+ // shutdown implies loss of permissions or client exit.
+ // Furthermore, don't erase ourselves from mFloaters - it crashes the
+ // cleanup iteration.
+ if(!mShutdown)
+ {
+ mFloaters.erase(name);
+ // Notify our client.
+ send("floaterclosed", name);
+ }
+}
+
+void exoSUISource::onGenericCommit(const std::string& name, const LLSD& value)
+{
+ send("commit", name, value);
+}
+
+void exoSUISource::onSwatchCommit(const std::string& name, LLUICtrl* ctrl)
+{
+ LLColorSwatchCtrl *swatch = dynamic_cast<LLColorSwatchCtrl*>(ctrl);
+ if(swatch)
+ {
+ const LLColor4& colour = swatch->get();
+ send("commit", name, STRINGIZE("<" << colour.mV[VX] << ", " << colour.mV[VY] << ", " << colour.mV[VZ] << ">"));
+ }
+}
+
+void exoSUISource::send(const std::string& command, const args_t& args) const
+{
+ // Build up the message
+ std::ostringstream text;
+ text << mUUID << " ";
+ text << "!" << command << " ";
+ if(args.size() > 0)
+ {
+ std::copy(args.begin(), args.end() - 1, std::ostream_iterator<std::string>(text, " "));
+ text << ":" << args.back();
+ }
+
+ sendRaw(text.str());
+}
+
+void exoSUISource::send(const std::string& command) const
+{
+ sendRaw(STRINGIZE(mUUID << " !" << command));
+}
+
+void exoSUISource::send(const std::string& command, const std::string& arg) const
+{
+ args_t args;
+ args.push_back(arg);
+ send(command, args);
+}
+
+void exoSUISource::send(const std::string& command, const std::string& arg1, const std::string& arg2) const
+{
+ args_t args;
+ args.reserve(2);
+ args.push_back(arg1);
+ args.push_back(arg2);
+ send(command, args);
+}
+
+void exoSUISource::sendRaw(const std::string& text) const
+{
+ // Uses ScriptDialogReply to let us send on negative channels.
+ gMessageSystem->newMessage("ScriptDialogReply");
+ gMessageSystem->nextBlock("AgentData");
+ gMessageSystem->addUUID("AgentID", gAgent.getID());
+ gMessageSystem->addUUID("SessionID", gAgent.getSessionID());
+ gMessageSystem->nextBlock("Data");
+ gMessageSystem->addUUID("ObjectID", mUUID);
+ gMessageSystem->addS32("ChatChannel", mChannel);
+ gMessageSystem->addS32("ButtonIndex", 1);
+ gMessageSystem->addString("ButtonLabel", text);
+ gAgent.sendReliableMessage();
+}
+
+
+template <typename T>
+static void parseLayoutParams(T& p, LLView* parent, args_t::const_iterator begin, args_t::const_iterator end)
+{
+ // Provide some defaults to save on providing them from LSL repeatedly.
+ p.follows.flags = FOLLOWS_TOP | FOLLOWS_LEFT;
+ p.font.name = "SansSerif"; // this is implied but must be explicitly set to enable font_size.
+
+ for(args_t::const_iterator it = begin; it != end; it += 2)
+ {
+ const std::string& arg = *it;
+ const std::string& value = *(it+1);
+ llinfos << arg << "=" << value << llendl;
+
+ if(arg == "width") p.rect.width = boost::lexical_cast<int>(value);
+ else if(arg == "height") p.rect.height = boost::lexical_cast<int>(value);
+ else if(arg == "left") p.rect.left = boost::lexical_cast<int>(value);
+ else if(arg == "top") p.rect.top = boost::lexical_cast<int>(value);
+ else if(arg == "bottom") p.rect.bottom = boost::lexical_cast<int>(value);
+ else if(arg == "right") p.rect.left = boost::lexical_cast<int>(value);
+ else if(arg == "follows") p.follows.flags = boost::lexical_cast<int>(value);
+ else if(arg == "bottom_delta") p.bottom_delta = boost::lexical_cast<int>(value);
+ else if(arg == "top_pad") p.top_pad = boost::lexical_cast<int>(value);
+ else if(arg == "top_delta") p.top_delta = boost::lexical_cast<int>(value);
+ else if(arg == "left_pad") p.left_pad = boost::lexical_cast<int>(value);
+ else if(arg == "left_delta") p.left_delta = boost::lexical_cast<int>(value);
+ else if(arg == "font") p.font.name = value;
+ else if(arg == "font_size") p.font.size = value;
+ else if(arg == "font_style") p.font.style = value;
+ }
+ p.layout = "topleft";
+ p.from_xui = true;
+ LLView::applyXUILayout(p, parent);
+ // We have to translate down 25 pixels to account for the title bar.
+ // This is done in the LLFloater XUI loader, which we can't use.
+ // We must only do this for floaters, however - any other parent has no
+ // necessary adjustment.
+ if(typeid(*parent) == typeid(exoSUIFloater))
+ {
+ LLRect rect = p.rect;
+ rect.translate(0, -exoSUIFloater::TITLE_BAR_HEIGHT);
+ p.rect = rect;
+ }
+}
+
+LLVector3 exoSUISource::parseLSLVector(args_t::const_iterator it)
+{
+ LLVector3 v;
+ for(int i = 0; i < 3; ++i, ++it)
+ {
+ v.mV[i] = boost::lexical_cast<F32>(boost::trim_copy_if(*it, boost::is_any_of("<,>")));
+ }
+ return v;
+}
\ No newline at end of file
diff -r c005a3294457 indra/newview/exosuisource.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/exosuisource.h Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,121 @@
+/**
+ * @file exosuisource.h
+ * @brief Manages UI sources. Ultimately, the bulk of the work is here.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (C) 2012 Katharine Berry
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ * $/LicenseInfo$
+ */
+
+#ifndef EXOSUISOURCE_H
+#define EXOSUISOURCE_H
+
+#include <exception>
+
+class LLFloater;
+class LLUICtrl;
+class LLView;
+
+struct exoSUIMessage;
+class exo_duplicate_control;
+class exo_missing_parent;
+
+class exoSUISource
+{
+public:
+ typedef std::vector<std::string> args_t;
+
+ exoSUISource(int channel, LLUUID uuid);
+ ~exoSUISource();
+
+ int mChannel;
+ LLUUID mUUID;
+ bool mPermitted;
+
+ void send(const std::string& command, const args_t& args) const;
+
+ // Zero, one and two arguments are the most common cases; provide shortcuts
+ void send(const std::string& command) const;
+ void send(const std::string& command, const std::string& arg) const;
+ void send(const std::string& command, const std::string& arg1, const std::string& args2) const;
+ bool processMessage(const exoSUIMessage& message);
+
+private:
+ // There is no reason to copy this class so don't let it happen.
+ // Beyond that copying breaks the bound references -> crash.
+ // If you actually *need* this, implement it and make it public.
+ // Be careful.
+ exoSUISource(const exoSUISource& other);
+
+ typedef std::map<std::string, LLFloater*> floater_map_t;
+ typedef std::map<std::string, LLUICtrl*> ctrl_map_t;
+ typedef std::map<std::string, boost::function<void(const args_t&)> > handler_map_t;
+ bool mShutdown;
+ floater_map_t mFloaters;
+ ctrl_map_t mControls;
+ handler_map_t mHandlers;
+
+ void sendRaw(const std::string& args) const;
+
+ void prepareHandlers();
+ inline void addHandler(const std::string& command, void (exoSUISource::*fn)(const args_t&));
+ // All of these may throw boost::bad_lexical_cast
+ void handleFloater(const args_t& args);
+ void handleButton(const args_t& args);
+ void handleText(const args_t& args);
+ void handleInput(const args_t& args);
+ void handleSetText(const args_t& args);
+ void handlePanel(const args_t& args);
+ void handleSlider(const args_t& args);
+ void handleSwatch(const args_t& args);
+
+ // Throws exo_duplicate_control if the control already exists
+ ctrl_map_t::iterator findControlInsertionPoint(const std::string& name);
+ // Throws exo_missing_parent if no such control exists
+ LLView* findParent(const std::string& name) const;
+
+ void onFloaterClosed(LLFloater* floater, const std::string& name);
+ void onGenericCommit(const std::string& name, const LLSD& values);
+ // Because LLSD doesn't like casting vectors to strings
+ void onSwatchCommit(const std::string& name, LLUICtrl* ctrl);
+
+ static LLVector3 parseLSLVector(args_t::const_iterator it);
+};
+
+
+// FIXME: These exceptions actually can throw exceptions (specifically std::bad_alloc)
+// despite the empty throw() annotation.
+// This is unlikely but not impossible. It should probably be fixed.
+class exo_duplicate_control : public std::exception
+{
+public:
+ exo_duplicate_control(const std::string& name) throw() { mName = name; }
+ /*virtual*/ const char* what() const throw() { return ("duplicate control " + mName).c_str(); }
+ const std::string& name() const throw() { return mName; }
+ ~exo_duplicate_control() throw() {}
+private:
+ std::string mName;
+};
+
+class exo_missing_parent : public std::exception
+{
+public:
+ exo_missing_parent(const std::string& name) throw() { mName = name; }
+ /*virtual*/ const char* what() const throw() { return ("no such parent " + mName).c_str(); }
+ const std::string& name() const throw() { return mName; }
+ ~exo_missing_parent() throw() {}
+private:
+ std::string mName;
+};
+
+#endif
diff -r c005a3294457 indra/newview/llviewerfloaterreg.cpp
--- a/indra/newview/llviewerfloaterreg.cpp Thu Aug 23 17:45:25 2012 -0400
+++ b/indra/newview/llviewerfloaterreg.cpp Thu Aug 11 03:37:35 2016 -0700
@@ -155,6 +155,7 @@
#include "exofloaterquicktools.h"
#include "exoinventoryhotkeys.h"
#include "exopanelpreferences.h"
+#include "exosuifloater.h"
// </exodus>
// *NOTE: Please add files in alphabetical order to keep merges easy.
@@ -370,6 +371,7 @@
LLFloaterReg::add("animation_overrider", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FloaterAO>); // Zi's Animation Overrider
LLFloaterReg::add("search_replace", "floater_search_replace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearchReplace>); // <exodus/>
LLFloaterReg::add("remove_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<exoFloaterRemoveQueue>);
+ LLFloaterReg::add("exo_scripted_ui", "exo_floater_scripted_ui.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<exoSUIFloater>);
// </exodus>
LLFloaterReg::registerControlVariables(); // Make sure visibility and rect controls get preserved when saving
diff -r c005a3294457 indra/newview/llviewermessage.cpp
--- a/indra/newview/llviewermessage.cpp Thu Aug 23 17:45:25 2012 -0400
+++ b/indra/newview/llviewermessage.cpp Thu Aug 11 03:37:35 2016 -0700
@@ -120,6 +120,7 @@
#include "exofloaterteleportrequest.h"
#include "exosystems.h"
#include "llnetmap.h"
+#include "exoscriptedui.h"
// </exodus>
#include "llviewerregion.h"
@@ -4001,6 +4002,10 @@
{
return;
}
+ if(exoScriptedUI::instance().receiveChat(mesg, from_id))
+ {
+ return;
+ }
// </exodus>
// [RLVa:KB] - Checked: 2010-02-XX (RLVa-1.2.0a) | Modified: RLVa-1.1.0f
// TODO-RLVa: [RLVa-1.2.0] consider rewriting this before a RLVa-1.2.0 release
diff -r c005a3294457 indra/newview/llworld.cpp
--- a/indra/newview/llworld.cpp Thu Aug 23 17:45:25 2012 -0400
+++ b/indra/newview/llworld.cpp Thu Aug 11 03:37:35 2016 -0700
@@ -179,11 +179,11 @@
LLViewerRegion* LLWorld::addRegion(const U64 &region_handle, const LLHost &host)
{
LLMemType mt(LLMemType::MTYPE_REGIONS);
- llinfos << "Add region with handle: " << region_handle << " on host " << host << llendl;
+ //llinfos << "Add region with handle: " << region_handle << " on host " << host << llendl;
LLViewerRegion *regionp = getRegionFromHandle(region_handle);
if (regionp)
{
- llinfos << "Region exists, removing it " << llendl;
+ //llinfos << "Region exists, removing it " << llendl;
LLHost old_host = regionp->getHost();
// region already exists!
if (host == old_host && regionp->isAlive())
@@ -1073,7 +1073,7 @@
LLWorld::getInstance()->addRegion(handle, sim);
// give the simulator a message it can use to get ip and port
- llinfos << "simulator_enable() Enabling " << sim << " with code " << msg->getOurCircuitCode() << llendl;
+ //llinfos << "simulator_enable() Enabling " << sim << " with code " << msg->getOurCircuitCode() << llendl;
msg->newMessageFast(_PREHASH_UseCircuitCode);
msg->nextBlockFast(_PREHASH_CircuitCode);
msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode());
diff -r c005a3294457 indra/newview/skins/default/xui/en/exo_floater_scripted_ui.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/indra/newview/skins/default/xui/en/exo_floater_scripted_ui.xml Thu Aug 11 03:37:35 2016 -0700
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ can_minimize="true" can_resize="true" can_close="true"
+ single_instance="false" bevel_style="in" positioning="centered"
+ width="360" height="35" layout="topleft"
+ name="exo_scripted_ui" header_height="25" />
+
+<!-- Unfortunately, several of these properties are ignored because
+ we addcomponents in code instead of XUI. -->
diff -r c005a3294457 indra/newview/skins/default/xui/en/notifications.xml
--- a/indra/newview/skins/default/xui/en/notifications.xml Thu Aug 23 17:45:25 2012 -0400
+++ b/indra/newview/skins/default/xui/en/notifications.xml Thu Aug 11 03:37:35 2016 -0700
@@ -8793,6 +8793,19 @@
yestext="OK"/>
</notification>
+ <notification
+ icon="notifytip.tga"
+ name="ExodusScriptedUIPermission"
+ type="notify">
+The object '[NAME]' wants to access advanced UI functionality.
+
+Would you like to allow this?
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelbuttons"
+ notext="No"
+ yestext="Yes"/>
+ </notification>
<!-- </exodus> -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment