From 5946d84eef30caeae6dee63767fabdcf81613984 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Wed, 16 Feb 2011 12:49:35 -0500
Subject: SWAT-484, SWAT-485: add LLSideTrayListener, a new LLEventAPI. Expand
 XUI-visible LLUICtrl::CommitCallbackRegistry operations to include
 "SideTray.Toggle" and "SideTray.Collapse". Give LLSideTrayListener friend
 access to LLSideTray so it can query the attached and detached tabs.
 Introduce tab_cast template to deal with the unavailability of the
 LLSideTrayTab class outside of llsidetray.cpp.

---
 indra/newview/CMakeLists.txt         |   2 +
 indra/newview/llsidetray.cpp         |  11 +++
 indra/newview/llsidetray.h           |  10 +++
 indra/newview/llsidetraylistener.cpp | 167 +++++++++++++++++++++++++++++++++++
 indra/newview/llsidetraylistener.h   |  36 ++++++++
 5 files changed, 226 insertions(+)
 create mode 100644 indra/newview/llsidetraylistener.cpp
 create mode 100644 indra/newview/llsidetraylistener.h

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index af6beacdfa..11bebf04ef 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -412,6 +412,7 @@ set(viewer_SOURCE_FILES
     llsidepaneliteminfo.cpp
     llsidepaneltaskinfo.cpp
     llsidetray.cpp
+    llsidetraylistener.cpp
     llsidetraypanelcontainer.cpp
     llsky.cpp
     llslurl.cpp
@@ -947,6 +948,7 @@ set(viewer_HEADER_FILES
     llsidepaneliteminfo.h
     llsidepaneltaskinfo.h
     llsidetray.h
+    llsidetraylistener.h
     llsidetraypanelcontainer.h
     llsky.h
     llslurl.h
diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index eb537c7d7b..85efebabfe 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -53,6 +53,8 @@
 
 #include "llsidepanelappearance.h"
 
+#include "llsidetraylistener.h"
+
 //#include "llscrollcontainer.h"
 
 using namespace std;
@@ -71,6 +73,8 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title";
 
 LLSideTray* LLSideTray::sInstance = 0;
 
+static LLSideTrayListener sSideTrayListener(LLSideTray::getInstance);
+
 // static
 LLSideTray* LLSideTray::getInstance()
 {
@@ -417,6 +421,11 @@ LLSideTrayTab*  LLSideTrayTab::createInstance	()
 	return tab;
 }
 
+// Now that we know the definition of LLSideTrayTab, we can implement
+// tab_cast.
+template <>
+LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab) { return tab; }
+
 //////////////////////////////////////////////////////////////////////////////
 // LLSideTrayButton
 // Side Tray tab button with "tear off" handling.
@@ -530,6 +539,8 @@ LLSideTray::LLSideTray(const Params& params)
 	// register handler function to process data from the xml. 
 	// panel_name should be specified via "parameter" attribute.
 	commit.add("SideTray.ShowPanel", boost::bind(&LLSideTray::showPanel, this, _2, LLUUID::null));
+	commit.add("SideTray.Toggle", boost::bind(&LLSideTray::onToggleCollapse, this));
+	commit.add("SideTray.Collapse", boost::bind(&LLSideTray::collapseSideBar, this));
 	LLTransientFloaterMgr::getInstance()->addControlView(this);
 	LLView* side_bar_tabs  = gViewerWindow->getRootView()->getChildView("side_bar_tabs");
 	if (side_bar_tabs != NULL)
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index 184d78845f..c4d3c2626c 100644
--- a/indra/newview/llsidetray.h
+++ b/indra/newview/llsidetray.h
@@ -33,6 +33,13 @@
 class LLAccordionCtrl;
 class LLSideTrayTab;
 
+// Deal with LLSideTrayTab being opaque. Generic do-nothing cast...
+template <class T>
+T tab_cast(LLSideTrayTab* tab) { return tab; }
+// specialized for implementation in presence of LLSideTrayTab definition
+template <>
+LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab);
+
 // added inheritance from LLDestroyClass<LLSideTray> to enable Side Tray perform necessary actions 
 // while disconnecting viewer in LLAppViewer::disconnectViewer().
 // LLDestroyClassList::instance().fireCallbacks() calls destroyClass method. See EXT-245.
@@ -217,6 +224,9 @@ private:
 	}
 	
 private:
+	// Since we provide no public way to query mTabs and mDetachedTabs, give
+	// LLSideTrayListener friend access.
+	friend class LLSideTrayListener;
 	LLPanel*						mButtonsPanel;
 	typedef std::map<std::string,LLButton*> button_map_t;
 	button_map_t					mTabButtons;
diff --git a/indra/newview/llsidetraylistener.cpp b/indra/newview/llsidetraylistener.cpp
new file mode 100644
index 0000000000..185bf1d6a7
--- /dev/null
+++ b/indra/newview/llsidetraylistener.cpp
@@ -0,0 +1,167 @@
+/**
+ * @file   llsidetraylistener.cpp
+ * @author Nat Goodspeed
+ * @date   2011-02-15
+ * @brief  Implementation for llsidetraylistener.
+ * 
+ * $LicenseInfo:firstyear=2011&license=lgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "llviewerprecompiledheaders.h"
+// associated header
+#include "llsidetraylistener.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llsidetray.h"
+#include "llsdutil.h"
+
+LLSideTrayListener::LLSideTrayListener(const Getter& getter):
+    LLEventAPI("LLSideTray",
+               "Operations on side tray (e.g. query state, query tabs)"),
+    mGetter(getter)
+{
+    add("getCollapsed", "Send on [\"reply\"] an [\"open\"] Boolean",
+        &LLSideTrayListener::getCollapsed, LLSDMap("reply", LLSD()));
+    add("getTabs",
+        "Send on [\"reply\"] a map of tab names and info about them",
+        &LLSideTrayListener::getTabs, LLSDMap("reply", LLSD()));
+    add("getPanels",
+        "Send on [\"reply\"] data about panels available with SideTray.ShowPanel",
+        &LLSideTrayListener::getPanels, LLSDMap("reply", LLSD()));
+}
+
+void LLSideTrayListener::getCollapsed(const LLSD& event) const
+{
+    LLReqID reqID(event);
+    LLSD reply(reqID.makeResponse());
+    reply["open"] = ! mGetter()->getCollapsed();
+    LLEventPumps::instance().obtain(event["reply"]).post(reply);
+}
+
+void LLSideTrayListener::getTabs(const LLSD& event) const
+{
+    LLReqID reqID(event);
+    LLSD reply(reqID.makeResponse());
+
+    LLSideTray* tray = mGetter();
+    LLSD::Integer ord(0);
+    for (LLSideTray::child_list_const_iter_t chi(tray->beginChild()), chend(tray->endChild());
+         chi != chend; ++chi, ++ord)
+    {
+        LLView* child = *chi;
+        // How much info is important? Toss in as much as seems reasonable for
+        // each tab. But to me, at least for the moment, the most important
+        // item is the tab name.
+        LLSD info;
+        // I like the idea of returning a map keyed by tab name. But as
+        // compared to an array of maps, that loses sequence information.
+        // Address that by indicating the original order in each map entry.
+        info["ord"] = ord;
+        info["visible"] = bool(child->getVisible());
+        info["enabled"] = bool(child->getEnabled());
+        info["available"] = child->isAvailable();
+        reply[child->getName()] = info;
+    }
+
+    LLEventPumps::instance().obtain(event["reply"]).post(reply);
+}
+
+static LLSD getTabInfo(LLPanel* tab)
+{
+    LLSD panels;
+    for (LLPanel::tree_iterator_t ti(tab->beginTreeDFS()), tend(tab->endTreeDFS());
+         ti != tend; ++ti)
+    {
+        // *ti is actually an LLView*, which had better not be NULL
+        LLView* view(*ti);
+        if (! view)
+        {
+            LL_ERRS("LLSideTrayListener") << "LLSideTrayTab '" << tab->getName()
+                                          << "' has a NULL child LLView*" << LL_ENDL;
+        }
+
+        // The logic we use to decide what "panel" names to return is heavily
+        // based on LLSideTray::showPanel(): the function that actually
+        // implements the "SideTray.ShowPanel" operation. showPanel(), in
+        // turn, depends on LLSideTray::openChildPanel(): when
+        // openChildPanel() returns non-NULL, showPanel() stops searching
+        // attached and detached LLSideTrayTab tabs.
+
+        // For each LLSideTrayTab, openChildPanel() first calls
+        // findChildView(panel_name, true). In other words, panel_name need
+        // not be a direct LLSideTrayTab child, it's sought recursively.
+        // That's why we use (begin|end)TreeDFS() in this loop.
+
+        // But this tree_iterator_t loop will actually traverse every widget
+        // in every panel. Returning all those names will not help our caller:
+        // passing most such names to openChildPanel() would not do what we
+        // want. Even though the code suggests that passing ANY valid
+        // side-panel widget name to openChildPanel() will open the tab
+        // containing that widget, results could get confusing since followup
+        // (onOpen()) logic wouldn't be invoked, and showPanel() wouldn't stop
+        // searching because openChildPanel() would return NULL.
+
+        // We must filter these LLView items, using logic that (sigh!) mirrors
+        // openChildPanel()'s own.
+
+        // openChildPanel() returns a non-NULL LLPanel* when either:
+        // - the LLView is a direct child of an LLSideTrayPanelContainer
+        // - the LLView is itself an LLPanel.
+        // But as LLSideTrayPanelContainer can directly contain LLView items
+        // that are NOT themselves LLPanels (e.g. "sidebar_me" contains an
+        // LLButton called "Jump Right Arrow"), we'd better focus only on
+        // LLSideTrayPanelContainer children that are themselves LLPanel
+        // items. Which means that the second test completely subsumes the
+        // first.
+        LLPanel* panel(dynamic_cast<LLPanel*>(view));
+        if (panel)
+        {
+            // Maybe it's overkill to construct an LLSD::Map for each panel, but
+            // the possibility remains that we might want to deliver more info
+            // about each panel than just its name.
+            panels.append(LLSDMap("name", panel->getName()));
+        }
+    }
+
+    return LLSDMap("panels", panels);
+}
+
+void LLSideTrayListener::getPanels(const LLSD& event) const
+{
+    LLReqID reqID(event);
+    LLSD reply(reqID.makeResponse());
+
+    LLSideTray* tray = mGetter();
+    // Iterate through the attached tabs.
+    LLSD::Integer ord(0);
+    for (LLSideTray::child_vector_t::const_iterator
+             ati(tray->mTabs.begin()), atend(tray->mTabs.end());
+         ati != atend; ++ati)
+    {
+        // We don't have access to LLSideTrayTab: the class definition is
+        // hidden in llsidetray.cpp. But as LLSideTrayTab isa LLPanel, use the
+        // LLPanel API. Unfortunately, without the LLSideTrayTab definition,
+        // the compiler doesn't even know this LLSideTrayTab* is an LLPanel*.
+        // Persuade it.
+        LLPanel* tab(tab_cast<LLPanel*>(*ati));
+        reply[tab->getName()] = getTabInfo(tab).with("attached", true).with("ord", ord);
+    }
+
+    // Now iterate over the detached tabs. These can also be opened via
+    // SideTray.ShowPanel.
+    ord = 0;
+    for (LLSideTray::child_vector_t::const_iterator
+             dti(tray->mDetachedTabs.begin()), dtend(tray->mDetachedTabs.end());
+         dti != dtend; ++dti)
+    {
+        LLPanel* tab(tab_cast<LLPanel*>(*dti));
+        reply[tab->getName()] = getTabInfo(tab).with("attached", false).with("ord", ord);
+    }
+
+    LLEventPumps::instance().obtain(event["reply"]).post(reply);
+}
diff --git a/indra/newview/llsidetraylistener.h b/indra/newview/llsidetraylistener.h
new file mode 100644
index 0000000000..0dd2067433
--- /dev/null
+++ b/indra/newview/llsidetraylistener.h
@@ -0,0 +1,36 @@
+/**
+ * @file   llsidetraylistener.h
+ * @author Nat Goodspeed
+ * @date   2011-02-15
+ * @brief  
+ * 
+ * $LicenseInfo:firstyear=2011&license=lgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLSIDETRAYLISTENER_H)
+#define LL_LLSIDETRAYLISTENER_H
+
+#include "lleventapi.h"
+#include <boost/function.hpp>
+
+class LLSideTray;
+class LLSD;
+
+class LLSideTrayListener: public LLEventAPI
+{
+    typedef boost::function<LLSideTray*()> Getter;
+
+public:
+    LLSideTrayListener(const Getter& getter);
+
+private:
+    void getCollapsed(const LLSD& event) const;
+    void getTabs(const LLSD& event) const;
+    void getPanels(const LLSD& event) const;
+
+    Getter mGetter;
+};
+
+#endif /* ! defined(LL_LLSIDETRAYLISTENER_H) */
-- 
cgit v1.2.3