From 6c2e7946f5abfa25694de7c30af16547a0f8ea5e Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sat, 5 Mar 2022 00:35:44 -0500
Subject: [PATCH] Add grid manager panel back

---
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/alfloatergenerictext.cpp        |  61 ++++++++
 indra/newview/alfloatergenerictext.h          |  51 +++++++
 indra/newview/llfloaterpreference.cpp         | 116 ++++++++++++++
 indra/newview/llfloaterpreference.h           |  10 ++
 indra/newview/llviewerfloaterreg.cpp          |   2 +
 .../default/xui/en/floater_generic_text.xml   |  52 +++++++
 .../default/xui/en/floater_preferences.xml    |   5 +
 .../skins/default/xui/en/notifications.xml    |  76 +++++++++
 .../xui/en/panel_preferences_grids.xml        | 144 ++++++++++++++++++
 .../newview/skins/default/xui/en/strings.xml  |   1 +
 11 files changed, 520 insertions(+)
 create mode 100644 indra/newview/alfloatergenerictext.cpp
 create mode 100644 indra/newview/alfloatergenerictext.h
 create mode 100644 indra/newview/skins/default/xui/en/floater_generic_text.xml
 create mode 100644 indra/newview/skins/default/xui/en/panel_preferences_grids.xml

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 9119408fd1b..d3d888cc5d0 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -127,6 +127,7 @@ set(viewer_SOURCE_FILES
     alcontrolcache.cpp
     alfloaterao.cpp
     alfloaterexploresounds.cpp
+    alfloatergenerictext.cpp
     alfloaterparticleeditor.cpp
     alfloaterregiontracker.cpp
     alpanelaomini.cpp
@@ -800,6 +801,7 @@ set(viewer_HEADER_FILES
     alcontrolcache.h
     alfloaterao.h
     alfloaterexploresounds.h
+    alfloatergenerictext.h
     alfloaterparticleeditor.h
     alfloaterregiontracker.h
     alpanelaomini.h
diff --git a/indra/newview/alfloatergenerictext.cpp b/indra/newview/alfloatergenerictext.cpp
new file mode 100644
index 00000000000..cde167dbba1
--- /dev/null
+++ b/indra/newview/alfloatergenerictext.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file llfloatergenerictext.cpp
+ * @brief A generic text floater for dumping info (usually debug info)
+ *
+ * Copyright (c) 2015, Cinder Roxley <cinder@sdf.org>
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "alfloatergenerictext.h"
+#include "llclipboard.h"
+#include "lltexteditor.h"
+
+LLFloaterGenericText::LLFloaterGenericText(const LLSD& key)
+:	LLFloater(key)
+{
+	mTitle = key["title"].asString();
+	mContents = key["data"].asString();
+	
+	mCommitCallbackRegistrar.add("GenericText.Close", boost::bind(&LLFloaterGenericText::onClickClose, this));
+	mCommitCallbackRegistrar.add("GenericText.Copy", boost::bind(&LLFloaterGenericText::onClickCopy, this));
+}
+
+BOOL LLFloaterGenericText::postBuild()
+{
+	setTitle(mTitle);
+	getChild<LLTextEditor>("payload")->setText(mContents);
+	return TRUE;
+}
+
+void LLFloaterGenericText::onClickClose()
+{
+	closeFloater();
+}
+
+void LLFloaterGenericText::onClickCopy()
+{
+	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(mContents, mContents.length()), 0, mContents.length());
+}
diff --git a/indra/newview/alfloatergenerictext.h b/indra/newview/alfloatergenerictext.h
new file mode 100644
index 00000000000..43f35802e96
--- /dev/null
+++ b/indra/newview/alfloatergenerictext.h
@@ -0,0 +1,51 @@
+/**
+ * @file llfloatergenerictext.h
+ * @brief A generic text floater for dumping info (usually debug info)
+ *
+ * Copyright (c) 2015, Cinder Roxley <cinder@sdf.org>
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef LL_FLOATERGENERICTEXT_H
+#define LL_FLOATERGENERICTEXT_H
+
+#include "llfloater.h"
+
+class LLFloaterGenericText final : public LLFloater
+{
+public:
+	LLFloaterGenericText(const LLSD& key);
+	BOOL postBuild() override;
+private:
+	~LLFloaterGenericText() = default;
+	void onClickClose();
+	void onClickCopy();
+	
+	std::string mTitle;
+	std::string mContents;
+	
+};
+
+#endif // LL_FLOATERGENERICTEXT_H
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 36388ec2462..2a55134f616 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -101,6 +101,7 @@
 #include "lltextbox.h"
 #include "llui.h"
 #include "llversioninfo.h"
+#include "llviewernetwork.h"
 #include "llviewerobjectlist.h"
 #include "llvoavatar.h"
 #include "llvovolume.h"
@@ -353,6 +354,12 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
 	mCommitCallbackRegistrar.add("Pref.DeleteTranscripts",      boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
 	mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false));
 
+	mCommitCallbackRegistrar.add("Pref.AddGrid", boost::bind(&LLFloaterPreference::onClickAddGrid, this));
+    mCommitCallbackRegistrar.add("Pref.ActivateGrid", boost::bind(&LLFloaterPreference::onClickActivateGrid, this));
+	mCommitCallbackRegistrar.add("Pref.RemoveGrid", boost::bind(&LLFloaterPreference::onClickRemoveGrid, this));
+	mCommitCallbackRegistrar.add("Pref.RefreshGrid", boost::bind(&LLFloaterPreference::onClickRefreshGrid, this));
+	mCommitCallbackRegistrar.add("Pref.DebugGrid", boost::bind(&LLFloaterPreference::onClickDebugGrid, this));
+	mCommitCallbackRegistrar.add("Pref.SelectGrid", boost::bind(&LLFloaterPreference::onSelectGrid, this, _2));
 	mCommitCallbackRegistrar.add("Pref.AddSkin", boost::bind(&LLFloaterPreference::onAddSkin, this));
 	mCommitCallbackRegistrar.add("Pref.RemoveSkin", boost::bind(&LLFloaterPreference::onRemoveSkin, this));
 	mCommitCallbackRegistrar.add("Pref.ApplySkin", boost::bind(&LLFloaterPreference::onApplySkin, this));
@@ -463,6 +470,9 @@ BOOL LLFloaterPreference::postBuild()
 
 	LLLogChat::getInstance()->setSaveHistorySignal(boost::bind(&LLFloaterPreference::onLogChatHistorySaved, this));
 	
+	refreshGridList();
+	mGridListChangedConnection = LLGridManager::getInstance()->addGridListChangedCallback(boost::bind(&LLFloaterPreference::refreshGridList, this));
+	
 	loadUserSkins();
 	
 
@@ -517,6 +527,109 @@ void LLFloaterPreference::onDoNotDisturbResponseChanged()
 	gSavedPerAccountSettings.setBOOL("DoNotDisturbResponseChanged", response_changed_flag );
 }
 
+////////////////////////////////////////////////////
+// Grid panel
+
+void LLFloaterPreference::refreshGridList()
+{
+	LLScrollListCtrl* grid_list = getChild<LLScrollListCtrl>("grid_list");
+	grid_list->clearRows();
+	std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();
+	for (auto& known_grid : known_grids)
+    {
+		if (!known_grid.first.empty() && !known_grid.second.empty())
+		{
+			bool connected_grid = LLGridManager::getInstance()->getGrid() == known_grid.first;
+			std::vector<std::string> uris;
+			LLGridManager::getInstance()->getLoginURIs(known_grid.first, uris);
+			LLURI login_uri = LLURI(uris.at(0));
+			
+			LLSD row;
+			row["id"] = known_grid.first;
+			row["columns"][0]["column"] = "grid_label";
+			row["columns"][0]["value"] = known_grid.second;
+			row["columns"][0]["font"]["style"] = connected_grid ? "BOLD" : "NORMAL";
+			row["columns"][1]["column"] = "login_uri";
+			row["columns"][1]["value"] = login_uri.authority();
+			row["columns"][1]["font"]["style"] = connected_grid ? "BOLD" : "NORMAL";
+			
+			grid_list->addElement(row);
+		}
+	}
+}
+
+void LLFloaterPreference::onClickAddGrid()
+{
+	std::string login_uri = getChild<LLLineEditor>("add_grid")->getValue().asString();
+	LLGridManager::getInstance()->addRemoteGrid(login_uri, LLGridManager::ADD_MANUAL);
+}
+
+void LLFloaterPreference::onClickActivateGrid()
+{
+    std::string grid = getChild<LLScrollListCtrl>("grid_list")->getSelectedValue().asString();
+    LLGridManager::getInstance()->setGridChoice(grid);
+}
+
+void LLFloaterPreference::onClickRemoveGrid()
+{
+	std::string grid = getChild<LLScrollListCtrl>("grid_list")->getSelectedValue().asString();
+	if (LLGridManager::getInstance()->getGrid() == grid)
+	{
+		LLNotificationsUtil::add("CannotRemoveConnectedGrid",
+								 LLSD().with("GRID", LLGridManager::getInstance()->getGridLabel()));
+	}
+	else
+	{
+		LLNotificationsUtil::add("ConfirmRemoveGrid",
+								 LLSD().with("GRID", LLGridManager::getInstance()->getGridLabel(grid)),
+								 LLSD(grid), boost::bind(&LLFloaterPreference::handleRemoveGridCB, this, _1, _2));
+	}
+}
+
+void LLFloaterPreference::onClickRefreshGrid()
+{
+	std::string grid = getChild<LLScrollListCtrl>("grid_list")->getSelectedValue().asString();
+	// So I'm a little paranoid, no big deal...
+	if (!LLGridManager::getInstance()->isSystemGrid(grid))
+	{
+		LLGridManager::getInstance()->addRemoteGrid(grid, LLGridManager::ADD_MANUAL);
+	}
+}
+
+void LLFloaterPreference::onClickDebugGrid()
+{
+	LLSD args;
+	std::stringstream data_str;
+	const std::string& grid = getChild<LLScrollListCtrl>("grid_list")->getSelectedValue().asString().c_str();
+	LLSD gridInfo = LLGridManager::getInstance()->getGridInfo(grid);
+	LLSDSerialize::toPrettyXML(gridInfo, data_str);
+	args["title"] = llformat("%s - %s", LLTrans::getString("GridInfoTitle").c_str(), grid.c_str());
+	args["data"] = data_str.str();
+	LLFloaterReg::showInstance("generic_text", args);
+}
+
+void LLFloaterPreference::onSelectGrid(const LLSD& data)
+{
+    getChild<LLUICtrl>("activate_grid")->setEnabled(LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP
+                                                    && LLGridManager::getInstance()->getGrid() != data.asString());
+	getChild<LLUICtrl>("remove_grid")->setEnabled(LLGridManager::getInstance()->getGrid() != data.asString()
+												  && !LLGridManager::getInstance()->isSystemGrid(data.asString()));
+	getChild<LLUICtrl>("refresh_grid")->setEnabled(!LLGridManager::getInstance()->isSystemGrid(data.asString()));
+	getChild<LLUICtrl>("debug_grid")->setEnabled(!data.asString().empty());
+}
+
+bool LLFloaterPreference::handleRemoveGridCB(const LLSD& notification, const LLSD& response)
+{
+	const S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+	if (0 == option)
+	{
+		const std::string& grid = notification["payload"].asString();
+		if (!LLGridManager::getInstance()->removeGrid(grid))
+			LLNotificationsUtil::add("RemoveGridFailure",
+									 LLSD().with("GRID", notification["substitutions"]["GRID"].asString()));
+	}
+	return false;
+}
 
 ////////////////////////////////////////////////////
 // Skins panel
@@ -777,6 +890,9 @@ void LLFloaterPreference::refreshSkinInfo(const skin_t& skin)
 
 LLFloaterPreference::~LLFloaterPreference()
 {
+	
+	if (mGridListChangedConnection.connected())
+		mGridListChangedConnection.disconnect();
 	LLConversationLog::instance().removeObserver(this);
 }
 
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index 5641d5db56f..a9b95acf2cd 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -202,6 +202,14 @@ class LLFloaterPreference final : public LLFloater, public LLAvatarPropertiesObs
 	void updateMaxComplexity();
 	static bool loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map);
 
+	void refreshGridList();
+	void onClickAddGrid();
+    void onClickActivateGrid();
+	void onClickRemoveGrid();
+	void onClickRefreshGrid();
+	void onClickDebugGrid();
+	void onSelectGrid(const LLSD& data);
+	bool handleRemoveGridCB(const LLSD& notification, const LLSD& response);
 	
 	void loadUserSkins();
 	void reloadSkinList();
@@ -230,6 +238,8 @@ class LLFloaterPreference final : public LLFloater, public LLAvatarPropertiesObs
 	typedef std::map<std::string, skin_t> skinmap_t;
 	skinmap_t mUserSkins;
 	
+	boost::signals2::connection mGridListChangedConnection;
+
 	LOG_CLASS(LLFloaterPreference);
 
 	LLSearchEditor *mFilterEdit;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index e4078ac8166..04f78d0b4ab 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -33,6 +33,7 @@
 
 #include "alfloaterao.h"
 #include "alfloaterexploresounds.h"
+#include "alfloatergenerictext.h"
 #include "alfloaterparticleeditor.h"
 #include "alfloaterregiontracker.h"
 #include "llcommandhandler.h"
@@ -417,6 +418,7 @@ void LLViewerFloaterReg::registerFloaters()
 
 	LLFloaterReg::add("ao", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterAO>);
 	LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeleteQueue>);
+	LLFloaterReg::add("generic_text", "floater_generic_text.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGenericText>);
 	LLFloaterReg::add("music_ticker", "floater_music_ticker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("particle_editor", "floater_particle_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterParticleEditor>);
 	LLFloaterReg::add("quick_settings", "floater_quick_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
diff --git a/indra/newview/skins/default/xui/en/floater_generic_text.xml b/indra/newview/skins/default/xui/en/floater_generic_text.xml
new file mode 100644
index 00000000000..c0f6ed0b045
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_generic_text.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater can_close="true"
+ single_instance="false"
+ can_drag_on_left="false"
+ can_minimize="true"
+ can_resize="true"
+ height="258"
+ width="258"
+ min_width="214"
+ min_height="100"
+ name="floater_generic_text"
+ title="INFO">
+  <text_editor
+   type="string"
+   length="1"
+   enabled="false"
+   follows="left|top|right|bottom"
+   font="SansSerif"
+   height="200"
+   layout="topleft"
+   top="4"
+   bottom="-32"
+   left="4"
+   right="-4"
+   max_length="65536"
+   name="payload"
+   word_wrap="true">
+It doesn't matter what comes, fresh goes better in life, with Mentos fresh and full of Life! Nothing gets to you, stayin' fresh, stayin' cool, with Mentos fresh and full of life! Fresh goes better! Mentos freshness! Fresh goes better with Mentos, fresh and full of life! Mentos! The Freshmaker!
+  </text_editor>
+  <button
+   follows="right|bottom"
+   label="Copy"
+   name="copy_btn"
+   left="49"
+   top_pad="2"
+   height="26"
+   width="100">
+    <button.commit_callback
+     function="GenericText.Copy" />
+  </button>
+  <button
+   follows="right|bottom"
+   label="Close"
+   name="close_btn"
+   left_pad="4"
+   top_delta="0"
+   height="26"
+   width="100">
+    <button.commit_callback
+     function="GenericText.Close" />
+  </button>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml
index 7200fe09c77..dd64f60a53e 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences.xml
@@ -180,6 +180,11 @@
          label="Interface"
          layout="topleft"
          name="interface" />
+        <panel
+         filename="panel_preferences_grids.xml"
+         label="Grids"
+         layout="topleft"
+         name="grids" />
     </tab_container>
 
 </floater>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 5d891ea24c3..3659e16c0de 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -11994,6 +11994,82 @@ Unable to load the track from [TRACK1] into [TRACK2].
     Dice Roll: [RESULT]
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="InvalidGrid"
+   type="alertmodal">
+    <tag>fail</tag>
+    <tag>opensim</tag>
+    '[GRID]' is not a valid grid identifier.
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="MalformedGridInfo"
+   type="alertmodal">
+    <tag>fail</tag>
+    <tag>opensim</tag>
+    [GRID] provided malformed grid info.
+
+    Please contact them for support.
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="CannotRemoveConnectedGrid"
+   type="alertmodal">
+    <tag>fail</tag>
+    <tag>opensim</tag>
+    [GRID] cannot be removed while you are connected to it.
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="ConfirmRemoveGrid"
+   type="alertmodal">
+    <tag>opensim</tag>
+    <tag>confirm</tag>
+    Are you sure you want to remove [GRID] from the grid list?
+    <usetemplate
+     ignoretext="Confirm removing grids"
+     name="okcancelignore"
+     notext="Cancel"
+     yestext="OK"/>
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="RemoveGridFailure"
+   type="alertmodal">
+    <tag>fail</tag>
+    <tag>opensim</tag>
+    Failed to remove [GRID] from the list.
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="CantAddGrid"
+   type="alertmodal">
+    <tag>fail</tag>
+    <tag>opensim</tag>
+Could not get grid info for [GRID].
+Error: [STATUS] [REASON]
+  </notification>
+  <notification
+   icon="alertmodal.tga"
+   name="AddGridSuccess"
+   type="alertmodal">
+    <form name="form">
+      <ignore name="ignore"
+       text="A grid was added to the grid list"/>
+    </form>
+    <tag>fail</tag>
+    <tag>opensim</tag>
+[GRID] has been added to your grid list.
+  </notification>
+
+  <notification
+   icon="notify.tga"
+   name="SecondLifePasswordTooLong"
+   type="notify">
+    Due to a bug in the Second Life signup process, your password is longer than 16 characters and had been truncated to allow logging in.
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="ConfirmRemoveSkin"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_grids.xml b/indra/newview/skins/default/xui/en/panel_preferences_grids.xml
new file mode 100644
index 00000000000..6c35c446ace
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_preferences_grids.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ border="true"
+ follows="left|top|right|bottom"
+ height="408"
+ label="Grids"
+ layout="topleft"
+ left="102"
+ name="grids"
+ top="1"
+ width="517">
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="12"
+   layout="topleft"
+   left="30"
+   name="add_grid_text"
+   top="12"
+   width="400">
+   Add new grid:
+  </text>
+  <line_editor
+   border_style="line"
+   border_thickness="1"
+   follows="left|top"
+   font="SansSerif"
+   height="19"
+   layout="topleft"
+   left="50"
+   max_length_chars="4096"
+   name="add_grid"
+   top_pad="8"
+   label="Enter a LoginURI"
+   width="320" />
+  <button
+   layout="topleft"
+   follows="left|top"
+   height="19"
+   label="Add"
+   left_pad="2"
+   name="add_grid_commit"
+   top_delta="0"
+   width="50">
+    <button.commit_callback
+     function="Pref.AddGrid" />
+  </button>
+  <text
+   type="string"
+   length="1"
+   layout="topleft"
+   follows="left|top"
+   height="12"
+   left="30"
+   name="manage_grid_text"
+   top_pad="4"
+   width="400">
+   Manage grids:
+  </text>
+  <scroll_list
+   layout="topleft"
+   follows="left|top"
+   height="225"
+   left="50"
+   name="grid_list"
+   draw_heading="true"
+   top_pad="8"
+   width="370">
+    <scroll_list.columns
+     label="Grid name"
+     name="grid_label"
+     width="150" />
+    <scroll_list.columns
+     dynamic_width="true"
+     label="Login URI"
+     name="login_uri" />
+    <scroll_list.commit_callback
+     function="Pref.SelectGrid" />
+  </scroll_list>
+  <button
+   enabled="false"
+   layout="topleft"
+   follows="left|top"
+   height="19"
+   label="Activate"
+   left_pad="5"
+   name="activate_grid"
+   top_delta="5"
+   width="75">
+    <button.commit_callback
+     function="Pref.ActivateGrid" />
+  </button>
+  <button
+    enabled="false"
+    layout="topleft"
+    follows="left|top"
+    height="19"
+    label="Refresh"
+    left_delta="0"
+    name="refresh_grid"
+    top_pad="5"
+    width="75">
+     <button.commit_callback
+      function="Pref.RefreshGrid" />
+  </button>
+  <button
+   enabled="false"
+   layout="topleft"
+   follows="left|top"
+   height="19"
+   label="Remove"
+   left_delta="0"
+   name="remove_grid"
+   top_pad="5"
+   width="75">
+    <button.commit_callback
+     function="Pref.RemoveGrid" />
+  </button>
+  <button
+   enabled="false"
+   layout="topleft"
+   follows="left|top"
+   height="19"
+   label="Debug"
+   left_delta="0"
+   name="debug_grid"
+   top_pad="5"
+   width="75">
+    <button.commit_callback
+     function="Pref.DebugGrid" />
+  </button>
+  <check_box
+   control_name="ForceShowGrid"
+   height="20"
+   layout="topleft"
+   follows="left|top"
+   label="Show Grid Selection at Login"
+   left="30"
+   name="show_grid_selection_check"
+   tool_tip="Show grid selection on login screen to login to other worlds"
+   width="280"
+   top="310" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index db96d5bf3a1..a2ed595b26b 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -4370,5 +4370,6 @@ and report the problem.
   <string name="AvatarTyping">Typing</string>
   <string name="UnknownAvatar">Unknown Avatar</string>
   <string name="NotAvailableOnPlatform">Not availalbe on this platform</string>
+  <string name="GridInfoTitle">GRID INFO</string>
   
 </strings>
-- 
GitLab