From 09b750483a2cde7ea3c80a0238f3224a2cf1cb70 Mon Sep 17 00:00:00 2001
From: AndreyL ProductEngine <alihatskiy@productengine.com>
Date: Thu, 3 Jan 2019 22:59:49 +0200
Subject: [PATCH] SL-10293 Firestorm PR: preferences and menu search

---
 doc/contributions.txt                         |   1 +
 indra/llui/CMakeLists.txt                     |   1 +
 indra/llui/llbutton.cpp                       |   4 +
 indra/llui/llbutton.h                         |   7 +
 indra/llui/llcheckboxctrl.h                   |  13 ++
 indra/llui/llmenugl.cpp                       |   4 +
 indra/llui/llmenugl.h                         |   9 +-
 indra/llui/llsearchablecontrol.h              |  71 ++++++++
 indra/llui/llsliderctrl.h                     |  15 +-
 indra/llui/lltabcontainer.cpp                 |  50 +++++-
 indra/llui/lltabcontainer.h                   |   2 +
 indra/llui/lltextbase.cpp                     |  11 ++
 indra/llui/lltextbase.h                       |   8 +-
 indra/llui/lluictrl.h                         |   1 +
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llfloaterpreference.cpp         | 121 +++++++++++++-
 indra/newview/llfloaterpreference.h           |  15 ++
 indra/newview/llsearchableui.cpp              | 154 ++++++++++++++++++
 indra/newview/llsearchableui.h                | 121 ++++++++++++++
 indra/newview/llstatusbar.cpp                 |  92 ++++++++++-
 indra/newview/llstatusbar.h                   |  17 ++
 .../default/xui/en/floater_preferences.xml    |  42 ++++-
 .../skins/default/xui/en/panel_status_bar.xml |  40 +++++
 23 files changed, 789 insertions(+), 12 deletions(-)
 create mode 100644 indra/llui/llsearchablecontrol.h
 create mode 100644 indra/newview/llsearchableui.cpp
 create mode 100644 indra/newview/llsearchableui.h

diff --git a/doc/contributions.txt b/doc/contributions.txt
index bb910aa8384..66323a38c62 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1070,6 +1070,7 @@ Nicky Dasmijn
     STORM-2010
 	STORM-2082
 	MAINT-6665
+	SL-10293
 Nicky Perian
 	OPEN-1
 	STORM-1087
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 8054eb36199..e44f57fa9f4 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -200,6 +200,7 @@ set(llui_HEADER_FILES
     llresizehandle.h
     llresmgr.h
     llrngwriter.h
+    llsearchablecontrol.h
     llsearcheditor.h 
     llscrollbar.h
     llscrollcontainer.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 510a2537b9d..6b7a8a8b860 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -769,6 +769,10 @@ void LLButton::draw()
 		}
 	}
 
+	// Highlight if needed
+	if( ll::ui::SearchableControl::getHighlighted() )
+		label_color = ll::ui::SearchableControl::getHighlightColor();
+
 	// Unselected label assignments
 	LLWString label = getCurrentLabel();
 
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 7b4719866d5..7629ed1fea8 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -62,6 +62,7 @@ class LLUICtrlFactory;
 
 class LLButton
 : public LLUICtrl, public LLBadgeOwner
+, public ll::ui::SearchableControl
 {
 public:
 	struct Params 
@@ -380,6 +381,12 @@ class LLButton
 	LLFlashTimer *				mFlashingTimer;
 	bool                        mForceFlashing; // Stick flashing color even if button is pressed
 	bool						mHandleRightMouse;
+
+protected:
+	virtual std::string _getSearchText() const
+	{
+		return getLabelUnselected() + getToolTip();
+	}
 };
 
 // Build time optimization, generate once in .cpp file
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index 71bdc32e66b..07ae9c3b186 100644
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -47,6 +47,7 @@ class LLViewBorder;
 
 class LLCheckBoxCtrl
 : public LLUICtrl
+, public ll::ui::SearchableControl
 {
 public:
 	struct Params 
@@ -108,6 +109,18 @@ class LLCheckBoxCtrl
 	virtual BOOL		isDirty()	const;		// Returns TRUE if the user has modified this control.
 	virtual void		resetDirty();			// Clear dirty state
 
+protected:
+	virtual std::string _getSearchText() const
+	{
+		return getLabel() + getToolTip();
+	}
+
+	virtual void onSetHighlight() const // When highlight, really do highlight the label
+	{
+		if( mLabel )
+			mLabel->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
+	}
+
 protected:
 	// note: value is stored in toggle state of button
 	LLButton*		mButton;
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 0d42f726fab..92543b952e8 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -504,6 +504,10 @@ void LLMenuItemGL::draw( void )
 		color = mDisabledColor.get();
 	}
 
+	// Highlight if needed
+	if( ll::ui::SearchableControl::getHighlighted() )
+		color = ll::ui::SearchableControl::getHighlightColor();
+
 	// Draw the text on top.
 	if (mBriefItem)
 	{
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 69f7d215134..78f688642ea 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -48,7 +48,7 @@ extern S32 MENU_BAR_WIDTH;
 // The LLMenuItemGL represents a single menu item in a menu. 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-class LLMenuItemGL : public LLUICtrl
+class LLMenuItemGL: public LLUICtrl, public ll::ui::SearchableControl
 {
 public:
 	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
@@ -175,7 +175,12 @@ class LLMenuItemGL : public LLUICtrl
 	// This function appends the character string representation of
 	// the current accelerator key and mask to the provided string.
 	void appendAcceleratorString( std::string& st ) const;
-		
+
+	virtual std::string _getSearchText() const
+	{
+		return mLabel.getString();
+	}
+
 protected:
 	KEY mAcceleratorKey;
 	MASK mAcceleratorMask;
diff --git a/indra/llui/llsearchablecontrol.h b/indra/llui/llsearchablecontrol.h
new file mode 100644
index 00000000000..f7f1ffa0a5f
--- /dev/null
+++ b/indra/llui/llsearchablecontrol.h
@@ -0,0 +1,71 @@
+/**
+* @file llsearchablecontrol.h
+*
+* $LicenseInfo:firstyear=2019&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2019, Linden Research, Inc.
+*
+* 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.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_SEARCHABLE_CONTROL_H
+#define LL_SEARCHABLE_CONTROL_H
+
+#include "lluicolortable.h"
+#include "lluicolor.h"
+
+namespace ll
+{
+	namespace ui
+	{
+		class SearchableControl
+		{
+			mutable bool mIsHighlighed;
+		public:
+			SearchableControl()
+				: mIsHighlighed( false )
+			{ }
+			virtual ~SearchableControl()
+			{ }
+
+			LLColor4 getHighlightColor( ) const
+			{
+				static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red);
+				return highlight_color.get();
+			}
+
+			void setHighlighted( bool aVal ) const
+			{
+				mIsHighlighed = aVal;
+				onSetHighlight( );
+			}
+			bool getHighlighted( ) const
+			{ return mIsHighlighed; }
+
+			std::string getSearchText() const
+			{ return _getSearchText(); } 
+		protected:
+			virtual std::string _getSearchText() const = 0;
+			virtual void onSetHighlight( ) const
+			{ }
+		};
+	}
+}
+
+
+#endif
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 67cca9ef04e..2bb8668b90a 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -35,7 +35,7 @@
 #include "lllineeditor.h"
 
 
-class LLSliderCtrl : public LLF32UICtrl
+class LLSliderCtrl: public LLF32UICtrl, public ll::ui::SearchableControl
 {
 public:
 	struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
@@ -131,6 +131,19 @@ class LLSliderCtrl : public LLF32UICtrl
 	static void		onEditorGainFocus(LLFocusableElement* caller, void *userdata);
 	static void		onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
 
+protected:
+	virtual std::string _getSearchText() const
+	{
+		std::string strLabel;
+		if( mLabelBox )
+			strLabel = mLabelBox->getLabel();
+		return strLabel + getToolTip();
+	}
+	virtual void onSetHighlight() const  // When highlight, really do highlight the label
+	{
+		if( mLabelBox )
+			mLabelBox->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
+	}
 private:
 	void			updateText();
 	void			reportInvalidData();
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 1b2f09cff59..9c8636f936c 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -76,7 +76,8 @@ class LLTabTuple
 		mButton(b),
 		mOldState(FALSE),
 		mPlaceholderText(placeholder),
-		mPadding(0)
+		mPadding(0),
+		mVisible(true)
 	{}
 
 	LLTabContainer*  mTabContainer;
@@ -85,6 +86,8 @@ class LLTabTuple
 	BOOL			 mOldState;
 	LLTextBox*		 mPlaceholderText;
 	S32				 mPadding;
+
+	mutable bool mVisible;
 };
 
 //----------------------------------------------------------------------------
@@ -398,7 +401,10 @@ void LLTabContainer::draw()
 				{
 					break;
 				}
-				target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
+
+				if( (*iter)->mVisible )
+					target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
+
 				cur_scroll_pos--;
 			}
 
@@ -467,6 +473,12 @@ void LLTabContainer::draw()
 		{
 			LLTabTuple* tuple = *iter;
 
+			if( !tuple->mVisible )
+			{
+				tuple->mButton->setVisible( false );
+				continue;
+			}
+
 			tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
 									   top ? top - tuple->mButton->getRect().mTop : 0 );
 			if (top) top -= BTN_HEIGHT + tabcntrv_pad;
@@ -1505,7 +1517,7 @@ BOOL LLTabContainer::setTab(S32 which)
 	}
 
 	BOOL is_visible = FALSE;
-	if (selected_tuple->mButton->getEnabled())
+	if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible )
 	{
 		setCurrentPanelIndex(which);
 
@@ -2121,3 +2133,35 @@ S32 LLTabContainer::getTotalTabWidth() const
 {
     return mTotalTabWidth;
 }
+
+void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible )
+{
+	for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
+	{
+		LLTabTuple const *pTT = *itr;
+		if( pTT->mTabPanel == aPanel )
+		{
+			pTT->mVisible = aVisible;
+			break;
+		}
+	}
+
+	bool foundTab( false );
+	for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
+	{
+		LLTabTuple const *pTT = *itr;
+		if( pTT->mVisible )
+		{
+			this->selectTab( itr - mTabList.begin() );
+			foundTab = true;
+			break;
+		}
+	}
+
+	if( foundTab )
+		this->setVisible( TRUE );
+	else
+		this->setVisible( FALSE );
+
+	updateMaxScrollPos();
+}
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 4a5f08f5d3c..6bf963313c9 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -216,6 +216,8 @@ class LLTabContainer : public LLPanel
 	S32			getMinTabWidth() const { return mMinTabWidth; }
 	S32			getMaxTabWidth() const { return mMaxTabWidth; }
 
+	void setTabVisibility( LLPanel const *aPanel, bool );
+
 	void		startDragAndDropDelayTimer() { mDragAndDropDelayTimer.start(); }
 	
 	void onTabBtn( const LLSD& data, LLPanel* panel );
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index c570285856d..a23741b6dd6 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1222,6 +1222,17 @@ void LLTextBase::draw()
 		gl_rect_2d(text_rect, bg_color % alpha, TRUE);
 	}
 
+	// Draw highlighted if needed
+	if( ll::ui::SearchableControl::getHighlighted() )
+	{
+		LLColor4 bg_color = ll::ui::SearchableControl::getHighlightColor();
+		LLRect bg_rect = mVisibleTextRect;
+		if( mScroller )
+			bg_rect.intersectWith( text_rect );
+
+		gl_rect_2d( text_rect, bg_color, TRUE );
+	}
+	
 	bool should_clip = mClip || mScroller != NULL;
 	{ LLLocalClipRect clip(text_rect, should_clip);
  
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 5fdde445ef5..9831c35858f 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -275,7 +275,8 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
 class LLTextBase 
 :	public LLUICtrl,
 	protected LLEditMenuHandler,
-	public LLSpellCheckMenuHandler
+	public LLSpellCheckMenuHandler,
+	public ll::ui::SearchableControl
 {
 public:
 	friend class LLTextSegment;
@@ -617,6 +618,11 @@ class LLTextBase
 	void							appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false);
 	S32 normalizeUri(std::string& uri);
 	
+protected:
+	virtual std::string _getSearchText() const
+	{
+		return mLabel.getString() + getToolTip();
+	}
 
 protected:
 	// text segmentation and flow
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 550bee5c709..63baed67934 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -37,6 +37,7 @@
 #include "llinitparam.h"
 #include "llview.h"
 #include "llviewmodel.h"		// *TODO move dependency to .cpp file
+#include "llsearchablecontrol.h"
 
 const BOOL TAKE_FOCUS_YES = TRUE;
 const BOOL TAKE_FOCUS_NO  = FALSE;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 33886acb71f..a8019ee168d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -532,6 +532,7 @@ set(viewer_SOURCE_FILES
     llscrollingpanelparam.cpp
     llscrollingpanelparambase.cpp
     llsculptidsize.cpp
+    llsearchableui.cpp
     llsearchcombobox.cpp
     llsearchhistory.cpp
     llsecapi.cpp
@@ -1148,6 +1149,7 @@ set(viewer_HEADER_FILES
     llscrollingpanelparam.h
     llscrollingpanelparambase.h
     llsculptidsize.h
+    llsearchableui.h
     llsearchcombobox.h
     llsearchhistory.h
     llsecapi.h
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index ac751a785dc..c3dea73c057 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -117,6 +117,8 @@
 #include "llfeaturemanager.h"
 #include "llviewertexturelist.h"
 
+#include "llsearchableui.h"
+
 const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f;
 char const* const VISIBILITY_DEFAULT = "default";
 char const* const VISIBILITY_HIDDEN = "hidden";
@@ -393,6 +395,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
 
 	mCommitCallbackRegistrar.add("Pref.ClearLog",				boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
 	mCommitCallbackRegistrar.add("Pref.DeleteTranscripts",      boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
+	mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // <FS:ND/> Hook up for filtering
 }
 
 void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type )
@@ -506,7 +509,10 @@ BOOL LLFloaterPreference::postBuild()
 	LLSliderCtrl* fov_slider = getChild<LLSliderCtrl>("camera_fov");
 	fov_slider->setMinValue(LLViewerCamera::getInstance()->getMinView());
 	fov_slider->setMaxValue(LLViewerCamera::getInstance()->getMaxView());
-
+	
+	// Hook up and init for filtering
+	mFilterEdit = getChild<LLSearchEditor>("search_prefs_edit");
+	mFilterEdit->setKeystrokeCallback(boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false));
 
 	return TRUE;
 }
@@ -786,6 +792,13 @@ void LLFloaterPreference::onOpen(const LLSD& key)
 	save_btn->setEnabled(started);
 	delete_btn->setEnabled(started);
 	exceptions_btn->setEnabled(started);
+
+	collectSearchableItems();
+	if (!mFilterEdit->getText().empty())
+	{
+		mFilterEdit->setText(LLStringExplicit(""));
+		onUpdateFilterTerm(true);
+	}
 }
 
 void LLFloaterPreference::onVertexShaderEnable()
@@ -2985,3 +2998,109 @@ void LLFloaterPreferenceProxy::onChangeSocksSettings()
 
 }
 
+void LLFloaterPreference::onUpdateFilterTerm(bool force)
+{
+	LLWString seachValue = utf8str_to_wstring( mFilterEdit->getValue() );
+	LLWStringUtil::toLower( seachValue );
+
+	if( !mSearchData || (mSearchData->mLastFilter == seachValue && !force))
+		return;
+
+	mSearchData->mLastFilter = seachValue;
+
+	if( !mSearchData->mRootTab )
+		return;
+
+	mSearchData->mRootTab->hightlightAndHide( seachValue );
+	LLTabContainer *pRoot = getChild< LLTabContainer >( "pref core" );
+	if( pRoot )
+		pRoot->selectFirstTab();
+}
+
+void collectChildren( LLView const *aView, ll::prefs::PanelDataPtr aParentPanel, ll::prefs::TabContainerDataPtr aParentTabContainer )
+{
+	if( !aView )
+		return;
+
+	llassert_always( aParentPanel || aParentTabContainer );
+
+	LLView::child_list_const_iter_t itr = aView->beginChild();
+	LLView::child_list_const_iter_t itrEnd = aView->endChild();
+
+	while( itr != itrEnd )
+	{
+		LLView *pView = *itr;
+		ll::prefs::PanelDataPtr pCurPanelData = aParentPanel;
+		ll::prefs::TabContainerDataPtr pCurTabContainer = aParentTabContainer;
+		if( !pView )
+			continue;
+		LLPanel const *pPanel = dynamic_cast< LLPanel const *>( pView );
+		LLTabContainer const *pTabContainer = dynamic_cast< LLTabContainer const *>( pView );
+		ll::ui::SearchableControl const *pSCtrl = dynamic_cast< ll::ui::SearchableControl const *>( pView );
+
+		if( pTabContainer )
+		{
+			pCurPanelData.reset();
+
+			pCurTabContainer = ll::prefs::TabContainerDataPtr( new ll::prefs::TabContainerData );
+			pCurTabContainer->mTabContainer = const_cast< LLTabContainer *>( pTabContainer );
+			pCurTabContainer->mLabel = pTabContainer->getLabel();
+			pCurTabContainer->mPanel = 0;
+
+			if( aParentPanel )
+				aParentPanel->mChildPanel.push_back( pCurTabContainer );
+			if( aParentTabContainer )
+				aParentTabContainer->mChildPanel.push_back( pCurTabContainer );
+		}
+		else if( pPanel )
+		{
+			pCurTabContainer.reset();
+
+			pCurPanelData = ll::prefs::PanelDataPtr( new ll::prefs::PanelData );
+			pCurPanelData->mPanel = pPanel;
+			pCurPanelData->mLabel = pPanel->getLabel();
+
+			llassert_always( aParentPanel || aParentTabContainer );
+
+			if( aParentTabContainer )
+				aParentTabContainer->mChildPanel.push_back( pCurPanelData );
+			else if( aParentPanel )
+				aParentPanel->mChildPanel.push_back( pCurPanelData );
+		}
+		else if( pSCtrl && pSCtrl->getSearchText().size() )
+		{
+			ll::prefs::SearchableItemPtr item = ll::prefs::SearchableItemPtr( new ll::prefs::SearchableItem() );
+			item->mView = pView;
+			item->mCtrl = pSCtrl;
+
+			item->mLabel = utf8str_to_wstring( pSCtrl->getSearchText() );
+			LLWStringUtil::toLower( item->mLabel );
+
+			llassert_always( aParentPanel || aParentTabContainer );
+
+			if( aParentPanel )
+				aParentPanel->mChildren.push_back( item );
+			if( aParentTabContainer )
+				aParentTabContainer->mChildren.push_back( item );
+		}
+		collectChildren( pView, pCurPanelData, pCurTabContainer );
+		++itr;
+	}
+}
+
+void LLFloaterPreference::collectSearchableItems()
+{
+	mSearchData.reset( nullptr );
+	LLTabContainer *pRoot = getChild< LLTabContainer >( "pref core" );
+	if( mFilterEdit && pRoot )
+	{
+		mSearchData.reset(new ll::prefs::SearchData() );
+
+		ll::prefs::TabContainerDataPtr pRootTabcontainer = ll::prefs::TabContainerDataPtr( new ll::prefs::TabContainerData );
+		pRootTabcontainer->mTabContainer = pRoot;
+		pRootTabcontainer->mLabel = pRoot->getLabel();
+		mSearchData->mRootTab = pRootTabcontainer;
+
+		collectChildren( this, ll::prefs::PanelDataPtr(), pRootTabcontainer );
+	}
+}
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index 4e51137df56..d609c42ebe7 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -36,6 +36,7 @@
 #include "llfloater.h"
 #include "llavatarpropertiesprocessor.h"
 #include "llconversationlog.h"
+#include "llsearcheditor.h"
 
 class LLConversationLogObserver;
 class LLPanelPreference;
@@ -47,6 +48,14 @@ class LLSliderCtrl;
 class LLSD;
 class LLTextBox;
 
+namespace ll
+{
+	namespace prefs
+	{
+		struct SearchData;
+	}
+}
+
 typedef std::map<std::string, std::string> notifications_map;
 
 typedef enum
@@ -205,6 +214,12 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver,
 	LLAvatarData mAvatarProperties;
 	std::string mSavedGraphicsPreset;
 	LOG_CLASS(LLFloaterPreference);
+
+	LLSearchEditor *mFilterEdit;
+	std::unique_ptr< ll::prefs::SearchData > mSearchData;
+
+	void onUpdateFilterTerm( bool force = false );
+	void collectSearchableItems();
 };
 
 class LLPanelPreference : public LLPanel
diff --git a/indra/newview/llsearchableui.cpp b/indra/newview/llsearchableui.cpp
new file mode 100644
index 00000000000..6058079ae46
--- /dev/null
+++ b/indra/newview/llsearchableui.cpp
@@ -0,0 +1,154 @@
+/**
+* @file llsearchableui.cpp
+*
+* $LicenseInfo:firstyear=2019&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2019, Linden Research, Inc.
+*
+* 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.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+#include "llsearchableui.h"
+
+#include "llview.h"
+#include "lltabcontainer.h"
+#include "llmenugl.h"
+
+ll::prefs::SearchableItem::~SearchableItem()
+{}
+
+void ll::prefs::SearchableItem::setNotHighlighted()
+{
+	mCtrl->setHighlighted( false );
+}
+
+bool ll::prefs::SearchableItem::hightlightAndHide( LLWString const &aFilter )
+{
+	if( mCtrl->getHighlighted() )
+		return true;
+
+	LLView const *pView = dynamic_cast< LLView const* >( mCtrl );
+	if( pView && !pView->getVisible() )
+		return false;
+
+	if( aFilter.empty() )
+	{
+		mCtrl->setHighlighted( false );
+		return true;
+	}
+
+	if( mLabel.find( aFilter ) != LLWString::npos )
+	{
+		mCtrl->setHighlighted( true );
+		return true;
+	}
+
+	return false;
+}
+
+ll::prefs::PanelData::~PanelData()
+{}
+
+bool ll::prefs::PanelData::hightlightAndHide( LLWString const &aFilter )
+{
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		(*itr)->setNotHighlighted( );
+
+	bool bVisible(false);
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		bVisible |= (*itr)->hightlightAndHide( aFilter );
+
+	for( tPanelDataList::iterator itr = mChildPanel.begin(); itr  != mChildPanel.end(); ++itr )
+		bVisible |= (*itr)->hightlightAndHide( aFilter );
+
+	return bVisible;
+}
+
+bool ll::prefs::TabContainerData::hightlightAndHide( LLWString const &aFilter )
+{
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		(*itr)->setNotHighlighted( );
+
+	bool bVisible(false);
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		bVisible |= (*itr)->hightlightAndHide( aFilter );
+
+	for( tPanelDataList::iterator itr = mChildPanel.begin(); itr  != mChildPanel.end(); ++itr )
+	{
+		bool bPanelVisible = (*itr)->hightlightAndHide( aFilter );
+		if( (*itr)->mPanel )
+			mTabContainer->setTabVisibility( (*itr)->mPanel, bPanelVisible );
+		bVisible |= bPanelVisible;
+	}
+
+	return bVisible;
+}
+
+ll::statusbar::SearchableItem::SearchableItem()
+	: mMenu(0)
+	, mCtrl(0)
+	, mWasHiddenBySearch( false )
+{ }
+
+void ll::statusbar::SearchableItem::setNotHighlighted( )
+{
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		(*itr)->setNotHighlighted( );
+
+	if( mCtrl )
+	{
+		mCtrl->setHighlighted( false );
+
+		if( mWasHiddenBySearch )
+			mMenu->setVisible( TRUE );
+	}
+}
+
+bool ll::statusbar::SearchableItem::hightlightAndHide( LLWString const &aFilter )
+{
+	if( mMenu && !mMenu->getVisible() && !mWasHiddenBySearch )
+		return false;
+
+	setNotHighlighted( );
+
+	bool bVisible(false);
+	for( tSearchableItemList::iterator itr = mChildren.begin(); itr  != mChildren.end(); ++itr )
+		bVisible |= (*itr)->hightlightAndHide( aFilter );
+
+	if( aFilter.empty() )
+	{
+		if( mCtrl )
+			mCtrl->setHighlighted( false );
+		return true;
+	}
+
+	if( mLabel.find( aFilter ) != LLWString::npos )
+	{
+		if( mCtrl )
+			mCtrl->setHighlighted( true );
+		return true;
+	}
+
+	if( mCtrl && !bVisible )
+	{
+		mWasHiddenBySearch = true;
+		mMenu->setVisible(FALSE);
+	}
+	return bVisible;
+}
diff --git a/indra/newview/llsearchableui.h b/indra/newview/llsearchableui.h
new file mode 100644
index 00000000000..42b2866fb6a
--- /dev/null
+++ b/indra/newview/llsearchableui.h
@@ -0,0 +1,121 @@
+/**
+* @file llsearchableui.h
+*
+* $LicenseInfo:firstyear=2019&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2019, Linden Research, Inc.
+*
+* 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.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_SEARCHABLE_UI_H
+#define LL_SEARCHABLE_UI_H
+
+class LLMenuItemGL;
+class LLView;
+class LLPanel;
+class LLTabContainer;
+
+#include "llsearchablecontrol.h"
+
+namespace ll
+{
+	namespace prefs
+	{
+		struct SearchableItem;
+		struct PanelData;
+		struct TabContainerData;
+
+		typedef boost::shared_ptr< SearchableItem > SearchableItemPtr;
+		typedef boost::shared_ptr< PanelData > PanelDataPtr;
+		typedef boost::shared_ptr< TabContainerData > TabContainerDataPtr;
+
+		typedef std::vector< TabContainerData > tTabContainerDataList;
+		typedef std::vector< SearchableItemPtr > tSearchableItemList;
+		typedef std::vector< PanelDataPtr > tPanelDataList;
+
+		struct SearchableItem
+		{
+			LLWString mLabel;
+			LLView const *mView;
+			ll::ui::SearchableControl const *mCtrl;
+
+			std::vector< boost::shared_ptr< SearchableItem >  > mChildren;
+
+			virtual ~SearchableItem();
+
+			void setNotHighlighted();
+			virtual bool hightlightAndHide( LLWString const &aFilter );
+		};
+
+		struct PanelData
+		{
+			LLPanel const *mPanel;
+			std::string mLabel;
+
+			std::vector< boost::shared_ptr< SearchableItem > > mChildren;
+			std::vector< boost::shared_ptr< PanelData > > mChildPanel;
+
+			virtual ~PanelData();
+
+			virtual bool hightlightAndHide( LLWString const &aFilter );
+		};
+
+		struct TabContainerData: public PanelData
+		{
+			LLTabContainer *mTabContainer;
+			virtual bool hightlightAndHide( LLWString const &aFilter );
+		};
+
+		struct SearchData
+		{
+			TabContainerDataPtr mRootTab;
+			LLWString mLastFilter;
+		};
+	}
+	namespace statusbar
+	{
+		struct SearchableItem;
+
+		typedef boost::shared_ptr< SearchableItem > SearchableItemPtr;
+
+		typedef std::vector< SearchableItemPtr > tSearchableItemList;
+
+		struct SearchableItem
+		{
+			LLWString mLabel;
+			LLMenuItemGL *mMenu;
+			tSearchableItemList mChildren;
+			ll::ui::SearchableControl const *mCtrl;
+			bool mWasHiddenBySearch;
+
+			SearchableItem();
+
+			void setNotHighlighted( );
+			bool hightlightAndHide( LLWString const &aFilter );
+		};
+
+		struct SearchData
+		{
+			SearchableItemPtr mRootMenu;
+			LLWString mLastFilter;
+		};
+	}
+}
+
+#endif
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index 43c0fbd53ac..b893e4a058b 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -81,6 +81,8 @@
 #include "llparcel.h"
 #include "llstring.h"
 #include "message.h"
+#include "llsearchableui.h"
+#include "llsearcheditor.h"
 
 // system includes
 #include <iomanip>
@@ -113,7 +115,9 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
 	mBalance(0),
 	mHealth(100),
 	mSquareMetersCredit(0),
-	mSquareMetersCommitted(0)
+	mSquareMetersCommitted(0),
+	mFilterEdit(NULL),			// Edit for filtering
+	mSearchPanel(NULL)			// Panel for filtering
 {
 	setRect(rect);
 	
@@ -239,6 +243,16 @@ BOOL LLStatusBar::postBuild()
 	mPanelNearByMedia->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
 	mPanelNearByMedia->setVisible(FALSE);
 
+	// Hook up and init for filtering
+	mFilterEdit = getChild<LLSearchEditor>( "search_menu_edit" );
+	mSearchPanel = getChild<LLPanel>( "menu_search_panel" );
+
+	//mSearchPanel->setVisible(gSavedSettings.getBOOL("MenuSearch"));
+	mFilterEdit->setKeystrokeCallback(boost::bind(&LLStatusBar::onUpdateFilterTerm, this));
+	mFilterEdit->setCommitCallback(boost::bind(&LLStatusBar::onUpdateFilterTerm, this));
+	collectSearchableItems();
+	//gSavedSettings.getControl("MenuSearch")->getCommitSignal()->connect(boost::bind(&LLStatusBar::updateMenuSearchVisibility, this, _2));
+
 	return TRUE;
 }
 
@@ -318,6 +332,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
 	mMediaToggle->setVisible(visible);
 	mSGBandwidth->setVisible(visible);
 	mSGPacketLoss->setVisible(visible);
+	mSearchPanel->setVisible(visible && gSavedSettings.getBOOL("MenuSearch"));
 	setBackgroundVisible(visible);
 	mIconPresets->setVisible(visible);
 }
@@ -358,6 +373,12 @@ void LLStatusBar::setBalance(S32 balance)
 		balance_bg_view->setShape(balance_bg_rect);
 	}
 
+	// If the search panel is shown, move this according to the new balance width. Parcel text will reshape itself in setParcelInfoText
+	if (mSearchPanel && mSearchPanel->getVisible())
+	{
+		updateMenuSearchPosition();
+	}
+
 	if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold")))
 	{
 		if (mBalance > balance)
@@ -570,6 +591,75 @@ void LLStatusBar::onVolumeChanged(const LLSD& newvalue)
 	refresh();
 }
 
+void LLStatusBar::onUpdateFilterTerm()
+{
+	LLWString searchValue = utf8str_to_wstring( mFilterEdit->getValue() );
+	LLWStringUtil::toLower( searchValue );
+
+	if( !mSearchData || mSearchData->mLastFilter == searchValue )
+		return;
+
+	mSearchData->mLastFilter = searchValue;
+
+	mSearchData->mRootMenu->hightlightAndHide( searchValue );
+	gMenuBarView->needsArrange();
+}
+
+void collectChildren( LLMenuGL *aMenu, ll::statusbar::SearchableItemPtr aParentMenu )
+{
+	for( U32 i = 0; i < aMenu->getItemCount(); ++i )
+	{
+		LLMenuItemGL *pMenu = aMenu->getItem( i );
+
+		ll::statusbar::SearchableItemPtr pItem( new ll::statusbar::SearchableItem );
+		pItem->mCtrl = pMenu;
+		pItem->mMenu = pMenu;
+		pItem->mLabel = utf8str_to_wstring( pMenu->ll::ui::SearchableControl::getSearchText() );
+		LLWStringUtil::toLower( pItem->mLabel );
+		aParentMenu->mChildren.push_back( pItem );
+
+		LLMenuItemBranchGL *pBranch = dynamic_cast< LLMenuItemBranchGL* >( pMenu );
+		if( pBranch )
+			collectChildren( pBranch->getBranch(), pItem );
+	}
+
+}
+
+void LLStatusBar::collectSearchableItems()
+{
+	mSearchData.reset( new ll::statusbar::SearchData );
+	ll::statusbar::SearchableItemPtr pItem( new ll::statusbar::SearchableItem );
+	mSearchData->mRootMenu = pItem;
+	collectChildren( gMenuBarView, pItem );
+}
+
+void LLStatusBar::updateMenuSearchVisibility(const LLSD& data)
+{
+	bool visible = data.asBoolean();
+	mSearchPanel->setVisible(visible);
+	if (!visible)
+	{
+		mFilterEdit->setText(LLStringUtil::null);
+		onUpdateFilterTerm();
+	}
+	else
+	{
+		updateMenuSearchPosition();
+	}
+}
+
+void LLStatusBar::updateMenuSearchPosition()
+{
+	const S32 HPAD = 12;
+	LLRect balanceRect = getChildView("balance_bg")->getRect();
+	LLRect searchRect = mSearchPanel->getRect();
+	S32 w = searchRect.getWidth();
+	searchRect.mLeft = balanceRect.mLeft - w - HPAD;
+	searchRect.mRight = searchRect.mLeft + w;
+	mSearchPanel->setShape( searchRect );
+}
+
+
 // Implements secondlife:///app/balance/request to request a L$ balance
 // update via UDP message system. JC
 class LLBalanceHandler : public LLCommandHandler
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index a3326e752a9..403d590acac 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -45,7 +45,15 @@ class LLPanelPresetsPulldown;
 class LLPanelVolumePulldown;
 class LLPanelNearByMedia;
 class LLIconCtrl;
+class LLSearchEditor;
 
+namespace ll
+{
+	namespace statusbar
+	{
+		struct SearchData;
+	}
+}
 class LLStatusBar
 :	public LLPanel
 {
@@ -99,6 +107,15 @@ class LLStatusBar
 	static void onClickMediaToggle(void* data);
 	static void onClickBalance(void* data);
 
+	LLSearchEditor *mFilterEdit;
+	LLPanel *mSearchPanel;
+	void onUpdateFilterTerm();
+
+	std::unique_ptr< ll::statusbar::SearchData > mSearchData;
+	void collectSearchableItems();
+	void updateMenuSearchVisibility( const LLSD& data );
+	void updateMenuSearchPosition();
+
 private:
 	LLTextBox	*mTextTime;
 
diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml
index 845c1efe4d2..0e62d50072e 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences.xml
@@ -3,7 +3,7 @@
  legacy_header_height="18"
  positioning="centered"
  default_tab_group="1"
- height="512"
+ height="530"
  layout="topleft"
  name="Preferences"
  help_topic="preferences"
@@ -25,7 +25,7 @@ https://accounts.secondlife.com/change_email/
      layout="topleft"
      right="-105"
      name="OK"
-     top="473"
+     top="492"
      width="90">
         <button.commit_callback
          function="Pref.OK" />
@@ -43,6 +43,42 @@ https://accounts.secondlife.com/change_email/
         <button.commit_callback
          function="Pref.Cancel" />
     </button>
+
+    <panel
+     name="search_panel"
+     layout="topleft"
+     follows="left|top|right"
+     left="4"
+     right="-4"
+     top="21"
+     height="18"
+     tab_group="2">
+        <search_editor
+         clear_button_visible="true"
+         follows="left|top|right"
+         height="18"
+         label="Search Settings"
+         layout="topleft"
+         left="0"
+         max_length_bytes="255"
+         name="search_prefs_edit"
+         right="-1"
+         text_pad_left="6"
+         tool_tip="Type the search term you are interested in here. Results will be displayed for partial fulltext matches within the setting's name or comment."
+         top="0">
+         <search_editor.commit_callback
+          function="UpdateFilter" />
+         <search_editor.clear_button
+          rect.height="18"
+          rect.width="18"
+          rect.bottom="-1" />
+         <search_editor.search_button
+          rect.height="12"
+          rect.width="12"
+          rect.bottom="-1" />
+        </search_editor>
+    </panel>
+
     <tab_container
      follows="all"
      halign="left"
@@ -54,7 +90,7 @@ https://accounts.secondlife.com/change_email/
      tab_position="left"
      tab_width="140"
      tab_padding_right="0"
-     top="21"
+     top="40"
      width="658">
         <panel
 	 class="panel_preference"
diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml
index 998f1ce5997..52bcce01f7d 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -33,6 +33,46 @@
      name="buycurrencylabel">
         L$ [AMT]
     </panel.string>
+  <panel
+	 height="18"
+	 left="-458"
+	 top="0"
+	 width="120"
+	 follows="right|top"
+	 name="menu_search_panel"
+	 background_opaque="true"
+	 background_visible="true"
+	 bg_opaque_color="MouseGray">
+		<search_editor
+		 clear_button_visible="true"
+		 follows="left|top"
+		 height="18"
+		 label="Search Menus"
+		 layout="topleft"
+		 left="0"
+		 max_length_bytes="255"
+		 name="search_menu_edit"
+		 right="-1"
+		 text_pad_left="6"
+		 tool_tip="Type the search term you are interested in here. Results will be displayed for partial fulltext matches within the menu."
+		 top="0">
+			<search_button 
+			 top_pad="4"
+			 left_pad="4" 
+			 width="12"
+			 height="12" 
+			 image_unselected="Search"
+			 image_selected="Search"/>
+			<clear_button
+			 bottom="2"
+			 height="12"
+			 image_unselected="Icon_Close_Foreground"
+			 image_selected="Icon_Close_Press"
+			 pad_right="4"
+			 pad_left="4"
+			 width="12"/>
+		</search_editor>
+  </panel>
   <panel
     height="18"
     left="-416"
-- 
GitLab