From 393f68c1a9785b29adc210124e342af24937d5f5 Mon Sep 17 00:00:00 2001
From: Lynx Linden <lynx@lindenlab.com>
Date: Thu, 12 Nov 2009 12:36:30 +0000
Subject: [PATCH] DEV-2925: Added a new remote object inspector.

This is used to display details about objects that may not be in the
current scene (the existing object inspector is tied to selection and
only works for objects in the scene). The remote inspector lets you see
the name and owner of the object. You can also teleport to the object's
location, view the location on the map, or mute the object.

I've also added more information to the notification you receive when
an object sends you an IM via llInstantMessage(). This notification now
has an "Inspect" button that brings up the remote object inspector.
---
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llinspectremoteobject.cpp       | 200 ++++++++++++++++++
 indra/newview/llinspectremoteobject.h         |  40 ++++
 indra/newview/llviewerfloaterreg.cpp          |   2 +
 indra/newview/llviewermessage.cpp             |  28 ++-
 .../default/xui/en/inspect_remote_object.xml  |  99 +++++++++
 .../skins/default/xui/en/notifications.xml    |   5 +
 7 files changed, 375 insertions(+), 1 deletion(-)
 create mode 100644 indra/newview/llinspectremoteobject.cpp
 create mode 100644 indra/newview/llinspectremoteobject.h
 create mode 100644 indra/newview/skins/default/xui/en/inspect_remote_object.xml

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 13c381edaec..3b18b7d7007 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -246,6 +246,7 @@ set(viewer_SOURCE_FILES
     llinspectavatar.cpp
     llinspectgroup.cpp
     llinspectobject.cpp
+    llinspectremoteobject.cpp
     llinventorybridge.cpp
     llinventoryclipboard.cpp
     llinventoryfilter.cpp
@@ -741,6 +742,7 @@ set(viewer_HEADER_FILES
     llinspectavatar.h
     llinspectgroup.h
     llinspectobject.h
+    llinspectremoteobject.h
     llinventorybridge.h
     llinventoryclipboard.h
     llinventoryfilter.h
diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp
new file mode 100644
index 00000000000..e4d2eec242b
--- /dev/null
+++ b/indra/newview/llinspectremoteobject.cpp
@@ -0,0 +1,200 @@
+/** 
+ * @file llinspectremoteobject.cpp
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * 
+ * Copyright (c) 2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinspectremoteobject.h"
+#include "llinspect.h"
+#include "llslurl.h"
+#include "llmutelist.h"
+#include "llurlaction.h"
+#include "llpanelblockedlist.h"
+#include "llfloaterreg.h"
+#include "llui.h"
+#include "lluictrl.h"
+
+class LLViewerObject;
+
+//////////////////////////////////////////////////////////////////////////////
+// LLInspectRemoteObject
+//////////////////////////////////////////////////////////////////////////////
+
+// Remote Object Inspector, a small information window used to
+// display information about potentially-remote objects. Used
+// to display details about objects sending messages to the user.
+class LLInspectRemoteObject : public LLInspect
+{
+	friend class LLFloaterReg;
+	
+public:
+	LLInspectRemoteObject(const LLSD& object_id);
+	virtual ~LLInspectRemoteObject() {};
+
+	/*virtual*/ BOOL postBuild(void);
+	/*virtual*/ void onOpen(const LLSD& avatar_id);
+
+	void onClickMap();
+	void onClickBlock();
+	void onClickClose();
+	
+private:
+	void update();
+	static void nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data);
+	
+private:
+	LLUUID		 mObjectID;
+	LLUUID		 mOwnerID;
+	std::string  mOwner;
+	std::string  mSLurl;
+	std::string  mName;
+	bool         mGroupOwned;
+};
+
+LLInspectRemoteObject::LLInspectRemoteObject(const LLSD& sd) :
+	LLInspect(LLSD()),
+	mObjectID(NULL),
+	mOwnerID(NULL),
+	mOwner(""),
+	mSLurl(""),
+	mName(""),
+	mGroupOwned(false)
+{
+}
+
+/*virtual*/
+BOOL LLInspectRemoteObject::postBuild(void)
+{
+	// hook up the inspector's buttons
+	getChild<LLUICtrl>("map_btn")->setCommitCallback(
+		boost::bind(&LLInspectRemoteObject::onClickMap, this));
+	getChild<LLUICtrl>("block_btn")->setCommitCallback(
+		boost::bind(&LLInspectRemoteObject::onClickBlock, this));
+	getChild<LLUICtrl>("close_btn")->setCommitCallback(
+		boost::bind(&LLInspectRemoteObject::onClickClose, this));
+
+	return TRUE;
+}
+
+/*virtual*/
+void LLInspectRemoteObject::onOpen(const LLSD& data)
+{
+	// Start animation
+	LLInspect::onOpen(data);
+
+	// Extract appropriate object information from input LLSD
+	// (Eventually, it might be nice to query server for details
+	// rather than require caller to pass in the information.)
+	mObjectID   = data["object_id"].asUUID();
+	mName       = data["name"].asString();
+	mOwnerID    = data["owner_id"].asUUID();
+	mGroupOwned = data["group_owned"].asBoolean();
+	mSLurl      = data["slurl"].asString();
+
+	// work out the owner's name
+	mOwner = "";
+	if (gCacheName)
+	{
+		gCacheName->get(mOwnerID, mGroupOwned, nameCallback, this);
+	}
+
+	// update the inspector with the current object state
+	update();
+
+	// Position the inspector relative to the mouse cursor
+	LLUI::positionViewNearMouse(this);
+}
+
+void LLInspectRemoteObject::onClickMap()
+{
+	std::string url = "secondlife://" + mSLurl;
+	LLUrlAction::showLocationOnMap(url);
+	closeFloater();
+}
+
+void LLInspectRemoteObject::onClickBlock()
+{
+	LLMute::EType mute_type = mGroupOwned ? LLMute::GROUP : LLMute::AGENT;
+	LLMute mute(mOwnerID, mOwner, mute_type);
+	LLMuteList::getInstance()->add(mute);
+	LLPanelBlockedList::showPanelAndSelect(mute.mID);
+	closeFloater();
+}
+
+void LLInspectRemoteObject::onClickClose()
+{
+	closeFloater();
+}
+
+//static 
+void LLInspectRemoteObject::nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data)
+{
+	LLInspectRemoteObject *self = (LLInspectRemoteObject*)data;
+	self->mOwner = first;
+	if (!last.empty())
+	{
+		self->mOwner += " " + last;
+	}
+	self->update();
+}
+
+void LLInspectRemoteObject::update()
+{
+	// show the object name as the inspector's title
+	getChild<LLUICtrl>("object_name")->setValue(mName);
+
+	// show the object's owner - click it to show profile
+	std::string owner = mOwner;
+	if (! mOwnerID.isNull())
+	{
+		if (mGroupOwned)
+		{
+			owner = LLSLURL::buildCommand("group", mOwnerID, "about");
+		}
+		else
+		{
+			owner = LLSLURL::buildCommand("agent", mOwnerID, "about");
+		}
+	}
+	getChild<LLUICtrl>("object_owner")->setValue(owner);
+
+	// display the object's SLurl - click it to teleport
+	std::string url = "secondlife:///app/teleport/" + mSLurl;
+	getChild<LLUICtrl>("object_slurl")->setValue(url);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// LLInspectRemoteObjectUtil
+//////////////////////////////////////////////////////////////////////////////
+void LLInspectRemoteObjectUtil::registerFloater()
+{
+	LLFloaterReg::add("inspect_remote_object", "inspect_remote_object.xml",
+					  &LLFloaterReg::build<LLInspectRemoteObject>);
+}
diff --git a/indra/newview/llinspectremoteobject.h b/indra/newview/llinspectremoteobject.h
new file mode 100644
index 00000000000..e756f1caf4e
--- /dev/null
+++ b/indra/newview/llinspectremoteobject.h
@@ -0,0 +1,40 @@
+/** 
+ * @file llinspectremoteobject.h
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * 
+ * Copyright (c) 2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLINSPECTREMOTEOBJECT_H
+#define LLINSPECTREMOTEOBJECT_H
+
+namespace LLInspectRemoteObjectUtil
+{
+	void registerFloater();
+}
+
+#endif
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index edbac69e1b8..964d3bc2fa5 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -112,6 +112,7 @@
 #include "llinspectavatar.h"
 #include "llinspectgroup.h"
 #include "llinspectobject.h"
+#include "llinspectremoteobject.h"
 #include "llmediaremotectrl.h"
 #include "llmoveview.h"
 #include "llnearbychat.h"
@@ -176,6 +177,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLInspectAvatarUtil::registerFloater();
 	LLInspectGroupUtil::registerFloater();
 	LLInspectObjectUtil::registerFloater();
+	LLInspectRemoteObjectUtil::registerFloater();
 	
 	LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>);
 	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>);
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index a90790c59a8..6d6fc5f49f2 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -1431,6 +1431,17 @@ bool goto_url_callback(const LLSD& notification, const LLSD& response)
 }
 static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback);
 
+bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response)
+{
+	S32 option = LLNotification::getSelectedOption(notification, response);
+	if (0 == option)
+	{
+		LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]);
+	}
+	return false;
+}
+static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback);
+
 void process_improved_im(LLMessageSystem *msg, void **user_data)
 {
 	if (gNoRender)
@@ -1952,9 +1963,24 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 				return;
 			}
 
+			// Build a link to open the object IM info window.
+			std::string location = ll_safe_string((char*)binary_bucket,binary_bucket_size);
+			LLStringUtil::trim(location);
+
 			LLSD substitutions;
+			substitutions["NAME"] = name;
 			substitutions["MSG"] = message.substr(message_offset);
-			LLNotifications::instance().add("ServerObjectMessage", substitutions);
+
+			LLSD payload;
+			payload["object_id"] = session_id;
+			payload["owner_id"] = from_id;
+			payload["slurl"] = location;
+			payload["name"] = name;
+			if (from_group)
+			{
+				payload["groupowned"] = "true";
+			}
+			LLNotifications::instance().add("ServerObjectMessage", substitutions, payload);
 		}
 		break;
 	case IM_FROM_TASK_AS_ALERT:
diff --git a/indra/newview/skins/default/xui/en/inspect_remote_object.xml b/indra/newview/skins/default/xui/en/inspect_remote_object.xml
new file mode 100644
index 00000000000..07c684d9047
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/inspect_remote_object.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<!--
+  Not can_close / no title to avoid window chrome
+  Single instance - only have one at a time, recycle it each spawn
+-->
+<floater
+ legacy_header_height="18"
+ bevel_style="in"
+ bg_opaque_image="Inspector_Background" 
+ can_close="false"
+ can_minimize="false"
+ height="145"
+ layout="topleft"
+ name="inspect_remote_object"
+ single_instance="true"
+ sound_flags="0"
+ visible="true"
+ width="300">
+  <text
+   follows="all"
+   font="SansSerifLargeBold"
+   height="16"
+   left="8"
+   name="object_name"
+   text_color="White"
+   top="5"
+   use_ellipses="true"
+   width="290">
+     Test Object Name That Is Really Long
+  </text>
+  <text
+   follows="all"
+   font="SansSerif"
+   height="20"
+   left="8"
+   name="object_owner_label"
+   width="55"
+   top_pad="20">
+     Owner:
+  </text>
+  <text
+   follows="top|left"
+   font="SansSerif"
+   height="20"
+   left_pad="10"
+   name="object_owner"
+   use_ellipses="true"
+   width="200"
+   word_wrap="false">
+     Longavatarname Johnsonlongstonnammer
+  </text>
+  <text
+   follows="top|left"
+   font="SansSerif"
+   height="20"
+   left="8"
+   name="object_slurl_label"
+   top_pad="10"
+   width="55">
+     Location:
+  </text>
+  <text
+   follows="top|left"
+   height="20"
+   left_pad="10"
+   name="object_slurl"
+   width="240"
+   use_ellipses="true"
+   word_wrap="false">
+     http://slurl.com/Ahern/50/50/50
+  </text>
+  <button
+   follows="top|left"
+   font="SansSerif"
+   height="20"
+   label="Map"
+   left="10"
+   name="map_btn"
+   top="114"
+   width="75" />
+  <button
+   follows="top|left"
+   font="SansSerif"
+   height="20"
+   label="Block"
+   left_pad="5"
+   name="block_btn"
+   top_delta="0"
+   width="75" />
+  <button
+   follows="top|left"
+   font="SansSerif"
+   height="20"
+   label="Close"
+   right="-10"
+   name="close_btn"
+   top_delta="0"
+   width="75" />
+</floater>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index ccd8bc569e7..a44de51cb05 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4696,7 +4696,12 @@ The objects on the selected parcel that are NOT owned by you have been returned
    icon="notify.tga"
    name="ServerObjectMessage"
    type="notify">
+Message from [NAME]:
 [MSG]
+    <usetemplate
+     name="okcancelbuttons"
+     notext="OK"
+     yestext="Inspect"/>
   </notification>
 
   <notification
-- 
GitLab