Created
August 11, 2016 10:38
-
-
Save Katharine/f87f4ce4a0779254ceabd012f725f0a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ®ion_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