From eb800ade7eb27038823e12f667901ea32c14160d Mon Sep 17 00:00:00 2001
From: Andrew Dyukov <adyukov@productengine.com>
Date: Thu, 14 Jan 2010 17:42:21 +0200
Subject: [PATCH] At last! Implemented normal task EXT-3477 (Change gesture
 list implementation).

Created a new widget consisting of button and scrollist. List is added to NonSideTrayView
to properly draw it without using topcontrol (because it caused problems).

This commit also fixes following bugs:

EXT-3301 (Opening Gestures on Bottom Tray Disables Move Button)
EXT-3190 (No bottom tray's context menu appears if right mouse click was perform over enabled Gesture button)
EXT-2610 ('Gesture' btn: floater opens on OnMouseDown)

--HG--
branch : product-engine
---
 indra/newview/llfloatergesture.cpp            |   2 +-
 indra/newview/llgesturemgr.cpp                |  10 +
 indra/newview/llgesturemgr.h                  |   2 +
 indra/newview/llnearbychatbar.cpp             | 253 +++++++++++++-----
 indra/newview/llnearbychatbar.h               |  47 +++-
 .../skins/default/xui/en/panel_bottomtray.xml |   6 +-
 .../xui/en/widgets/gesture_combo_box.xml      |  30 ---
 .../xui/en/widgets/gesture_combo_list.xml     |  21 ++
 8 files changed, 259 insertions(+), 112 deletions(-)
 delete mode 100644 indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml
 create mode 100644 indra/newview/skins/default/xui/en/widgets/gesture_combo_list.xml

diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
index 5072bc8c820..6a9c602db20 100644
--- a/indra/newview/llfloatergesture.cpp
+++ b/indra/newview/llfloatergesture.cpp
@@ -415,7 +415,7 @@ void LLFloaterGesture::onClickPlay()
 	LL_DEBUGS("Gesture")<<" Trying to play gesture id: "<< item_id <<LL_ENDL;
 	if(!LLGestureManager::instance().isGestureActive(item_id))
 	{
-		// we need to inform server about gesture activating to be consistent with LLPreviewGesture and  LLGestureComboBox.
+		// we need to inform server about gesture activating to be consistent with LLPreviewGesture and  LLGestureComboList.
 		BOOL inform_server = TRUE;
 		BOOL deactivate_similar = FALSE;
 		LLGestureManager::instance().setGestureLoadedCallback(item_id, boost::bind(&LLFloaterGesture::playGesture, this, item_id));
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index df7aa9eabf3..82293b4aa0b 100644
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -417,6 +417,16 @@ BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id)
 	return gesture->mPlaying;
 }
 
+BOOL LLGestureManager::isGesturePlaying(LLMultiGesture* gesture)
+{
+	if(!gesture)
+	{
+		return FALSE;
+	}
+
+	return gesture->mPlaying;
+}
+
 void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
 {
 	const LLUUID& base_item_id = get_linked_uuid(item_id);
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index e80eea9ae90..c562587c6fd 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -103,6 +103,8 @@ class LLGestureManager : public LLSingleton<LLGestureManager>, public LLInventor
 
 	BOOL isGesturePlaying(const LLUUID& item_id);
 
+	BOOL isGesturePlaying(LLMultiGesture* gesture);
+
 	const item_map_t& getActiveGestures() const { return mActive; }
 	// Force a gesture to be played, for example, if it is being
 	// previewed.
diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp
index 75c2fb07d1c..6cf8bcb4178 100644
--- a/indra/newview/llnearbychatbar.cpp
+++ b/indra/newview/llnearbychatbar.cpp
@@ -48,13 +48,16 @@
 #include "llcommandhandler.h"
 #include "llviewercontrol.h"
 #include "llnavigationbar.h"
+#include "llwindow.h"
+#include "llviewerwindow.h"
+#include "llrootview.h"
 
 S32 LLNearbyChatBar::sLastSpecialChatChannel = 0;
 
 // legacy callback glue
 void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel);
 
-static LLDefaultChildRegistry::Register<LLGestureComboBox> r("gesture_combo_box");
+static LLDefaultChildRegistry::Register<LLGestureComboList> r("gesture_combo_list");
 
 struct LLChatTypeTrigger {
 	std::string name;
@@ -66,13 +69,42 @@ static LLChatTypeTrigger sChatTypeTriggers[] = {
 	{ "/shout"	, CHAT_TYPE_SHOUT}
 };
 
-LLGestureComboBox::LLGestureComboBox(const LLGestureComboBox::Params& p)
-	: LLComboBox(p)
-	, mGestureLabelTimer()
+LLGestureComboList::Params::Params()
+:	combo_button("combo_button"),
+	combo_list("combo_list")
+{
+}
+
+LLGestureComboList::LLGestureComboList(const LLGestureComboList::Params& p)
+:	LLUICtrl(p)
 	, mLabel(p.label)
 	, mViewAllItemIndex(0)
 {
-	setCommitCallback(boost::bind(&LLGestureComboBox::onCommitGesture, this));
+	LLButton::Params button_params = p.combo_button;
+	button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
+
+	mButton = LLUICtrlFactory::create<LLButton>(button_params);
+	mButton->reshape(getRect().getWidth(),getRect().getHeight());
+	mButton->setCommitCallback(boost::bind(&LLGestureComboList::onButtonCommit, this));
+
+	addChild(mButton);
+
+	LLScrollListCtrl::Params params = p.combo_list;
+	params.name("GestureComboList");
+	params.commit_callback.function(boost::bind(&LLGestureComboList::onItemSelected, this, _2));
+	params.visible(false);
+	params.commit_on_keyboard_movement(false);
+
+	mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
+	
+	// *HACK: adding list as a child to NonSideTrayView to make it fully visible without
+	// making it top control (because it would cause problems).
+	gViewerWindow->getNonSideTrayView()->addChild(mList);
+	mList->setVisible(FALSE);
+
+	//****************************Gesture Part********************************/
+
+	setCommitCallback(boost::bind(&LLGestureComboList::onCommitGesture, this));
 
 	// now register us as observer since we have a place to put the results
 	LLGestureManager::instance().addObserver(this);
@@ -80,26 +112,139 @@ LLGestureComboBox::LLGestureComboBox(const LLGestureComboBox::Params& p)
 	// refresh list from current active gestures
 	refreshGestures();
 
-	// This forces using of halign from xml, since LLComboBox
-	// sets it to LLFontGL::LEFT, if text entry is disabled
-	mButton->setHAlign(p.drop_down_button.font_halign);
+	setFocusLostCallback(boost::bind(&LLGestureComboList::hideList, this));
+}
+
+BOOL LLGestureComboList::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+	BOOL handled = FALSE;
+	
+	if (key == KEY_ESCAPE && mask == MASK_NONE )
+	{
+		hideList();
+		handled = TRUE;
+	}
+	else
+	{
+		handled = mList->handleKey(key, mask, called_from_parent);
+	}
 
-	// Pressing Gesture button by SPACE/ENTER key should open gestures list
-	mButton->setCommitCallback(boost::bind(&LLComboBox::onButtonMouseDown, this));
+	return handled; 		
 }
 
-LLGestureComboBox::~LLGestureComboBox()
+void LLGestureComboList::showList()
 {
-	LLGestureManager::instance().removeObserver(this);
+	LLRect rect = mList->getRect();
+	LLRect screen;
+	mButton->localRectToScreen(getRect(), &screen);
+	
+	// Calculating amount of space between the navigation bar and gestures combo
+	LLNavigationBar* nb = LLNavigationBar::getInstance();
+
+	S32 x, nb_bottom;
+	nb->localPointToScreen(0, 0, &x, &nb_bottom);
+
+	S32 max_height = nb_bottom - screen.mTop;
+	mList->calcColumnWidths();
+	rect.setOriginAndSize(screen.mLeft, screen.mTop, llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height);
+
+	mList->setRect(rect);
+	mList->fitContents( llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height);
+
+	gFocusMgr.setKeyboardFocus(this);
+
+	// Show the list and push the button down
+	mButton->setToggleState(TRUE);
+	mList->setVisible(TRUE);
+}
+
+void LLGestureComboList::onButtonCommit()
+{
+	if (!mList->getVisible())
+	{
+		// highlight the last selected item from the original selection before potentially selecting a new item
+		// as visual cue to original value of combo box
+		LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+		if (last_selected_item)
+		{
+			mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
+		}
+
+		if (mList->getItemCount() != 0)
+		{
+			showList();
+		}
+	}
+	else
+	{
+		hideList();
+	} 
+}
+
+void LLGestureComboList::hideList()
+{
+	if (mList->getVisible())
+	{
+		mButton->setToggleState(FALSE);
+		mList->setVisible(FALSE);
+		mList->mouseOverHighlightNthItem(-1);
+		gFocusMgr.setKeyboardFocus(NULL);
+	}
+}
+
+S32 LLGestureComboList::getCurrentIndex() const
+{
+	LLScrollListItem* item = mList->getFirstSelected();
+	if( item )
+	{
+		return mList->getItemIndex( item );
+	}
+	return -1;
 }
 
-void LLGestureComboBox::refreshGestures()
+void LLGestureComboList::onItemSelected(const LLSD& data)
+{
+	const std::string name = mList->getSelectedItemLabel();
+
+	S32 cur_id = getCurrentIndex();
+	mLastSelectedIndex = cur_id;
+	if (cur_id != mList->getItemCount()-1 && cur_id != -1)
+	{
+		mButton->setLabel(name);
+	}
+
+	// hiding the list reasserts the old value stored in the text editor/dropdown button
+	hideList();
+
+	// commit does the reverse, asserting the value in the list
+	onCommit();
+}
+
+void LLGestureComboList::sortByName(bool ascending)
+{
+	mList->sortOnce(0, ascending);
+}
+
+LLSD LLGestureComboList::getValue() const
+{
+	LLScrollListItem* item = mList->getFirstSelected();
+	if( item )
+	{
+		return item->getValue();
+	}
+	else
+	{
+		return LLSD();
+	}
+}
+
+void LLGestureComboList::refreshGestures()
 {
 	//store current selection so we can maintain it
 	LLSD cur_gesture = getValue();
-	selectFirstItem();
-	// clear
-	clearRows();
+	
+	mList->selectFirstItem();
+	mList->clearRows();
 	mGestures.clear();
 
 	LLGestureManager::item_map_t::const_iterator it;
@@ -110,7 +255,7 @@ void LLGestureComboBox::refreshGestures()
 		LLMultiGesture* gesture = (*it).second;
 		if (gesture)
 		{
-			addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx));
+			mList->addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx));
 			mGestures.push_back(gesture);
 			idx++;
 		}
@@ -120,23 +265,42 @@ void LLGestureComboBox::refreshGestures()
 
 	// store index followed by the last added Gesture and add View All item at bottom
 	mViewAllItemIndex = idx;
-	addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex));
+	
+	mList->addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex));
 
 	// Insert label after sorting, at top, with separator below it
-	addSeparator(ADD_TOP);		
-	addSimpleElement(mLabel, ADD_TOP);
+	mList->addSeparator(ADD_TOP);	
+	mList->addSimpleElement(mLabel, ADD_TOP);
 
 	if (cur_gesture.isDefined())
 	{ 
-		selectByValue(cur_gesture);
+		mList->selectByValue(cur_gesture);
+
 	}
 	else
 	{
-		selectFirstItem();
+		mList->selectFirstItem();
+	}
+
+	LLCtrlListInterface* gestures = getListInterface();
+	LLMultiGesture* gesture = NULL;
+	
+	if (gestures)
+	{
+		S32 index = gestures->getSelectedValue().asInteger();
+		if(index > 0)
+			gesture = mGestures.at(index);
 	}
+	
+	if(gesture && LLGestureManager::instance().isGesturePlaying(gesture))
+	{
+		return;
+	}
+	
+	mButton->setLabel(mLabel);
 }
 
-void LLGestureComboBox::onCommitGesture()
+void LLGestureComboList::onCommitGesture()
 {
 	LLCtrlListInterface* gestures = getListInterface();
 	if (gestures)
@@ -167,50 +331,11 @@ void LLGestureComboBox::onCommitGesture()
 			}
 		}
 	}
-
-	mGestureLabelTimer.start();
-	// free focus back to chat bar
-	setFocus(FALSE);
 }
 
-//virtual
-void LLGestureComboBox::draw()
+LLGestureComboList::~LLGestureComboList()
 {
-	// HACK: Leave the name of the gesture in place for a few seconds.
-	const F32 SHOW_GESTURE_NAME_TIME = 2.f;
-	if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
-	{
-		LLCtrlListInterface* gestures = getListInterface();
-		if (gestures) gestures->selectFirstItem();
-		mGestureLabelTimer.stop();
-	}
-
-	LLComboBox::draw();
-}
-
-//virtual
-void LLGestureComboBox::showList()
-{
-	LLComboBox::showList();
-
-	// Calculating amount of space between the navigation bar and gestures combo
-	LLNavigationBar* nb = LLNavigationBar::getInstance();
-	S32 x, nb_bottom;
-	nb->localPointToScreen(0, 0, &x, &nb_bottom);
-	
-	S32 list_bottom;
-	mList->localPointToScreen(0, 0, &x, &list_bottom);
-
-	S32 max_height = nb_bottom - list_bottom;
-
-	LLRect rect = mList->getRect();
-	// List overlapped navigation bar, downsize it
-	if (rect.getHeight() > max_height) 
-	{
-		rect.setOriginAndSize(rect.mLeft, rect.mBottom, rect.getWidth(), max_height);
-		mList->setRect(rect);
-		mList->reshape(rect.getWidth(), rect.getHeight());
-	}
+	LLGestureManager::instance().removeObserver(this);
 }
 
 LLNearbyChatBar::LLNearbyChatBar() 
diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h
index 224118e088d..d9a7403611a 100644
--- a/indra/newview/llnearbychatbar.h
+++ b/indra/newview/llnearbychatbar.h
@@ -42,33 +42,52 @@
 #include "llspeakers.h"
 
 
-class LLGestureComboBox
-	: public LLComboBox
-	, public LLGestureManagerObserver
+class LLGestureComboList
+	: public LLGestureManagerObserver
+	, public LLUICtrl
 {
 public:
-	struct Params : public LLInitParam::Block<Params, LLComboBox::Params> { };
+	struct Params :	public LLInitParam::Block<Params, LLUICtrl::Params>
+	{
+		Optional<LLButton::Params>			combo_button;
+		Optional<LLScrollListCtrl::Params>	combo_list;
+		
+		Params();
+	};
+
 protected:
-	LLGestureComboBox(const Params&);
+	
 	friend class LLUICtrlFactory;
+	LLGestureComboList(const Params&);
+	std::vector<LLMultiGesture*> mGestures;
+	std::string mLabel;
+	LLSD::Integer mViewAllItemIndex;
+
 public:
-	~LLGestureComboBox();
 
+	~LLGestureComboList();
+
+	LLCtrlListInterface* getListInterface()		{ return (LLCtrlListInterface*)mList; };
+	virtual void	showList();
+	virtual void	hideList();
+	virtual BOOL	handleKey(KEY key, MASK mask, BOOL called_from_parent);
+
+	S32				getCurrentIndex() const;
+	void			onItemSelected(const LLSD& data);
+	void			sortByName(bool ascending = true);
 	void refreshGestures();
 	void onCommitGesture();
-	virtual void draw();
+	void onButtonCommit();
+	virtual LLSD	getValue() const;
 
 	// LLGestureManagerObserver trigger
 	virtual void changed() { refreshGestures(); }
 
-protected:
-
-	virtual void showList();
+private:
 
-	LLFrameTimer mGestureLabelTimer;
-	std::vector<LLMultiGesture*> mGestures;
-	std::string mLabel;
-	LLSD::Integer mViewAllItemIndex;
+	LLButton*			mButton;
+	LLScrollListCtrl*	mList;
+	S32                 mLastSelectedIndex;
 };
 
 class LLNearbyChatBar
diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml
index ca547a79e23..4b39210f30c 100644
--- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml
+++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml
@@ -103,7 +103,7 @@
          min_width="52"
          name="gesture_panel"
          user_resize="false">
-         <gesture_combo_box
+         <gesture_combo_list
           follows="left|right"
           height="23"
           label="Gesture"
@@ -113,10 +113,10 @@
           top="5"
           width="82"
           tool_tip="Shows/hides gestures">
-             <gesture_combo_box.drop_down_button
+             <gesture_combo_list.combo_button
               pad_right="10"
               use_ellipses="true" />
-         </gesture_combo_box>
+         </gesture_combo_list>
         </layout_panel>
 		 <icon
          auto_resize="false"
diff --git a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml b/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml
deleted file mode 100644
index 4229f34c09f..00000000000
--- a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<gesture_combo_box
-           label="Gestures" 
-           list_position="below"
-           max_chars="20"
-           follows="right|top">
-  <gesture_combo_box.combo_button name="Combobox Button"
-                          label=""
-                          hover_glow_amount="0.15"
-                          scale_image="true"
-                          image_unselected="ComboButton_Off"
-                          image_selected="ComboButton_Selected"
-                          image_disabled="ComboButton_Disabled"
-                          image_disabled_selected="ComboButton_Disabled_Selected" />
-  <gesture_combo_box.drop_down_button name="Drop Down Button"
-                              label=""
-                              halign="center"
-                              hover_glow_amount="0.15"
-                              scale_image="true"
-                 image_selected="PushButton_Selected_Press"
-                 image_pressed="PushButton_Press"
-		 image_pressed_selected="PushButton_Selected_Press"
-                              image_unselected="PushButton_Off"
-                              image_disabled="PushButton_Disabled"
-                              image_disabled_selected="PushButton_Selected_Disabled" />
-  <gesture_combo_box.combo_list bg_writeable_color="MenuDefaultBgColor"
-                                scroll_bar_bg_visible="false" />
-  <gesture_combo_box.combo_editor name="Combo Text Entry"
-                          select_on_focus="true" />
-</gesture_combo_box>
diff --git a/indra/newview/skins/default/xui/en/widgets/gesture_combo_list.xml b/indra/newview/skins/default/xui/en/widgets/gesture_combo_list.xml
new file mode 100644
index 00000000000..808683864d9
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/gesture_combo_list.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<gesture_combo_list
+    follows="right|top">
+    <gesture_combo_list.combo_button
+     name="Combolist Button"
+     label=""
+     layout="topleft"
+     halign="center"
+     hover_glow_amount="0.15"
+     mouse_opaque="false"
+     scale_image="true"
+     image_selected="PushButton_Selected_Press"
+     image_pressed="PushButton_Press"
+     image_pressed_selected="PushButton_Selected_Press"
+     image_unselected="PushButton_Off"
+     image_disabled="PushButton_Disabled"
+     image_disabled_selected="PushButton_Selected_Disabled" />
+    <gesture_combo_list.combo_list 
+     bg_writeable_color="MenuDefaultBgColor"
+     scroll_bar_bg_visible="false" />
+</gesture_combo_list>
-- 
GitLab