From 11a148415e810706f2a1835b6b717a0a062d458f Mon Sep 17 00:00:00 2001
From: Gilbert Gonzales <gilbert@lindenlab.com>
Date: Fri, 28 Sep 2012 18:22:05 -0700
Subject: [PATCH] CHUI-102: Now the participants and one-on-one conversations
 have right-click-menus. These menus are functional as well, but 'chat
 history' does not yet work.

---
 indra/llui/llfolderview.cpp                   |   5 +-
 indra/llui/llfolderview.h                     |   2 +
 indra/newview/llconversationmodel.cpp         |  44 ++++
 indra/newview/llconversationmodel.h           |  18 +-
 indra/newview/llimfloater.h                   |   1 +
 indra/newview/llimfloatercontainer.cpp        | 192 +++++++++++++++++-
 indra/newview/llimfloatercontainer.h          |   5 +
 indra/newview/llinventorypanel.cpp            |   1 +
 .../default/xui/en/menu_conversation.xml      | 109 ++++++++++
 9 files changed, 372 insertions(+), 5 deletions(-)
 create mode 100644 indra/newview/skins/default/xui/en/menu_conversation.xml

diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index ce1bc5914c0..327a0642282 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -138,7 +138,8 @@ LLFolderView::Params::Params()
 	use_label_suffix("use_label_suffix"),
 	allow_multiselect("allow_multiselect", true),
 	show_empty_message("show_empty_message", true),
-	use_ellipses("use_ellipses", false)
+	use_ellipses("use_ellipses", false),
+    options_menu("options_menu", "")
 {
 	folder_indentation = -4;
 }
@@ -228,7 +229,7 @@ LLFolderView::LLFolderView(const Params& p)
 
 
 	// make the popup menu available
-	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
 	if (!menu)
 	{
 		menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 81b0f087e87..260275269df 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -94,6 +94,8 @@ class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler
 								use_ellipses,
 								show_item_link_overlays;
 		Mandatory<LLFolderViewModelInterface*>	view_model;
+        Optional<std::string>   options_menu;
+
 
 		Params();
 	};
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp
index 7d0ffa07889..1c4c7aefae2 100644
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -28,6 +28,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "llconversationmodel.h"
+#include "llmenugl.h"
 
 //
 // Conversation items : common behaviors
@@ -84,6 +85,24 @@ void LLConversationItem::showProperties(void)
 {
 }
 
+void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t&   items)
+{
+    items.push_back(std::string("view_profile"));
+    items.push_back(std::string("im"));
+    items.push_back(std::string("offer_teleport"));
+    items.push_back(std::string("voice_call"));
+    items.push_back(std::string("chat_history"));
+    items.push_back(std::string("separator_chat_history"));
+    items.push_back(std::string("add_friend"));
+    items.push_back(std::string("remove_friend"));
+    items.push_back(std::string("invite_to_group"));
+    items.push_back(std::string("separator_invite_to_group"));
+    items.push_back(std::string("map"));
+    items.push_back(std::string("share"));
+    items.push_back(std::string("pay"));
+    items.push_back(std::string("block_unblock"));
+}
+
 //
 // LLConversationItemSession
 // 
@@ -191,6 +210,22 @@ void LLConversationItemSession::setDistance(const LLUUID& participant_id, F64 di
 	}
 }
 
+void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+    lldebugs << "LLConversationItemParticipant::buildContextMenu()" << llendl;
+    menuentry_vec_t items;
+    menuentry_vec_t disabled_items;
+
+    if(this->getType() == CONV_SESSION_1_ON_1)
+    {
+        items.push_back(std::string("close_conversation"));
+        items.push_back(std::string("separator_disconnect_from_voice"));
+        buildParticipantMenuOptions(items);
+    }
+
+    hide_context_entries(menu, items, disabled_items);
+}
+
 // The time of activity of a session is the time of the most recent activity, session and participants included
 const bool LLConversationItemSession::getTime(F64& time) const
 {
@@ -249,6 +284,15 @@ LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid,
 	mConvType = CONV_PARTICIPANT;
 }
 
+void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+    menuentry_vec_t items;
+    menuentry_vec_t disabled_items;
+
+    buildParticipantMenuOptions(items);
+    hide_context_entries(menu, items, disabled_items);
+}
+
 void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name)
 {
 	mName = av_name.mUsername;
diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h
index 30f94d51aee..43fa66e8e20 100755
--- a/indra/newview/llconversationmodel.h
+++ b/indra/newview/llconversationmodel.h
@@ -41,6 +41,8 @@ class LLConversationItemParticipant;
 typedef std::map<LLUUID, LLConversationItem*> conversations_items_map;
 typedef std::map<LLUUID, LLFolderViewItem*> conversations_widgets_map;
 
+typedef std::vector<std::string> menuentry_vec_t;
+
 // Conversation items: we hold a list of those and create an LLFolderViewItem widget for each  
 // that we tuck into the mConversationsListPanel. 
 class LLConversationItem : public LLFolderViewModelItemCommon
@@ -124,6 +126,8 @@ class LLConversationItem : public LLFolderViewModelItemCommon
 	void resetRefresh() { mNeedsRefresh = false; }
 	bool needsRefresh() { return mNeedsRefresh; }
 	
+    void buildParticipantMenuOptions(menuentry_vec_t&   items);
+
 protected:
 	std::string mName;	// Name of the session or the participant
 	LLUUID mUUID;		// UUID of the session or the participant
@@ -155,6 +159,7 @@ class LLConversationItemSession : public LLConversationItem
 	
 	bool isLoaded() { return mIsLoaded; }
 	
+    void buildContextMenu(LLMenuGL& menu, U32 flags);
 	virtual const bool getTime(F64& time) const;
 
 	void dumpDebugData();
@@ -178,7 +183,8 @@ class LLConversationItemParticipant : public LLConversationItem
 	void setIsModerator(bool is_moderator) { mIsModerator = is_moderator; mNeedsRefresh = true; }
 	void setTimeNow() { mLastActiveTime = LLFrameTimer::getElapsedSeconds(); mNeedsRefresh = true; }
 	void setDistance(F64 dist) { mDistToAgent = dist; mNeedsRefresh = true; }
-	
+
+    void buildContextMenu(LLMenuGL& menu, U32 flags);
 	void onAvatarNameCache(const LLAvatarName& av_name);
 
 	virtual const bool getDistanceToAgent(F64& dist) const { dist = mDistToAgent; return (dist >= 0.0); }
@@ -273,4 +279,14 @@ class LLConversationViewModel
 private:
 };
 
+// Utility function to hide all entries except those in the list
+// Can be called multiple times on the same menu (e.g. if multiple items
+// are selected).  If "append" is false, then only common enabled items
+// are set as enabled.
+
+//(defined in inventorybridge.cpp)
+void hide_context_entries(LLMenuGL& menu, 
+    const menuentry_vec_t &entries_to_show, 
+    const menuentry_vec_t &disabled_entries);
+
 #endif // LL_LLCONVERSATIONMODEL_H
diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h
index e4a67a3d567..fbaf009939d 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llimfloater.h
@@ -128,6 +128,7 @@ class LLIMFloater
 	static void onIMChicletCreated(const LLUUID& session_id);
 
 	bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; }
+    LLUUID getOtherParticipantUUID() {return mOtherParticipantUUID;}
 
 	static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb);
 	static floater_showed_signal_t sIMFloaterShowedSignal;
diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp
index 14d40d46856..b4802c71f9e 100755
--- a/indra/newview/llimfloatercontainer.cpp
+++ b/indra/newview/llimfloatercontainer.cpp
@@ -58,8 +58,12 @@ LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed)
 	mConversationsRoot(NULL),
 	mInitialized(false)
 {
+    mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2));
 	mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLIMFloaterContainer::onCustomAction,  this, _2));
-	mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2));
+	
+    mEnableCallbackRegistrar.add("Avatar.CheckItem",  boost::bind(&LLIMFloaterContainer::checkContextMenuItem,	this, _2));
+    mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLIMFloaterContainer::enableContextMenuItem,	this, _2));
+    mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelected, this, _2));
 
 	// Firstly add our self to IMSession observers, so we catch session events
     LLIMMgr::getInstance()->addSessionObserver(this);
@@ -133,7 +137,9 @@ BOOL LLIMFloaterContainer::postBuild()
     p.listener = base_item;
     p.view_model = &mConversationViewModel;
     p.root = NULL;
+    p.options_menu = "menu_conversation.xml";
 	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
+    mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
 
 	// a scroller for folder view
 	LLRect scroller_view_rect = mConversationsListPanel->getRect();
@@ -341,7 +347,7 @@ void LLIMFloaterContainer::idle(void* user_data)
 	{
 		self->setNearbyDistances();
 	}
-	
+
 	self->mConversationsRoot->update();
 }
 
@@ -661,6 +667,188 @@ void LLIMFloaterContainer::setSortOrder(const LLConversationSort& order)
 	gSavedSettings.setU32("ConversationSortOrder", (U32)order);
 }
 
+void LLIMFloaterContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids)
+{
+    const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList();
+
+    std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin();
+    const std::set<LLFolderViewItem*>::const_iterator it_end = selectedItems.end();
+    LLConversationItem * conversationItem;
+
+    for (; it != it_end; ++it)
+    {
+        conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+        selected_uuids.push_back(conversationItem->getUUID());
+    }
+}
+void LLIMFloaterContainer::doToSelected(const LLSD& userdata)
+{
+    std::string command = userdata.asString();
+    uuid_vec_t selected_uuids;
+    LLUUID currentSelectedUUID;
+    LLIMFloater * conversation;
+
+    getSelectedUUIDs(selected_uuids);
+    conversation = LLIMFloater::findInstance(selected_uuids.front());
+    
+    if(conversation && 
+        static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem())->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+    {
+        currentSelectedUUID = conversation->getOtherParticipantUUID();
+    }
+    else
+    {
+        currentSelectedUUID = selected_uuids.front();
+    }
+
+    //Close the selected conversation
+    if(conversation && "close_conversation" == command)
+    {
+        LLFloater::onClickClose(conversation);
+    }
+    else if ("view_profile" == command)
+    {
+        LLAvatarActions::showProfile(currentSelectedUUID);
+    }
+    else if("im" == command)
+    {
+        LLAvatarActions::startIM(currentSelectedUUID);
+    }
+    else if("offer_teleport" == command)
+    {
+        LLAvatarActions::offerTeleport(selected_uuids);
+    }
+    else if("voice_call" == command)
+    {
+        LLAvatarActions::startCall(currentSelectedUUID);
+    }
+    else if("add_friend" == command)
+    {
+        LLAvatarActions::requestFriendshipDialog(currentSelectedUUID);
+    }
+    else if("remove_friend" == command)
+    {
+        LLAvatarActions::removeFriendDialog(currentSelectedUUID);
+    }
+    else if("invite_to_group" == command)
+    {
+        LLAvatarActions::inviteToGroup(currentSelectedUUID);
+    }
+    else if("map" == command)
+    {
+        LLAvatarActions::showOnMap(currentSelectedUUID);
+    }
+    else if("share" == command)
+    {
+        LLAvatarActions::share(currentSelectedUUID);
+    }
+    else if("pay" == command)
+    {
+        LLAvatarActions::pay(currentSelectedUUID);
+    }
+    else if("block_unblock" == command)
+    {
+        LLAvatarActions::toggleBlock(currentSelectedUUID);
+    }
+}
+
+bool LLIMFloaterContainer::enableContextMenuItem(const LLSD& userdata)
+{
+    std::string item = userdata.asString();
+    uuid_vec_t mUUIDs;
+    getSelectedUUIDs(mUUIDs);
+
+    // Note: can_block and can_delete is used only for one person selected menu
+    // so we don't need to go over all uuids.
+
+    if (item == std::string("can_block"))
+    {
+        const LLUUID& id = mUUIDs.front();
+        return LLAvatarActions::canBlock(id);
+    }
+    else if (item == std::string("can_add"))
+    {
+        // We can add friends if:
+        // - there are selected people
+        // - and there are no friends among selection yet.
+
+        //EXT-7389 - disable for more than 1
+        if(mUUIDs.size() > 1)
+        {
+            return false;
+        }
+
+        bool result = (mUUIDs.size() > 0);
+
+        uuid_vec_t::const_iterator
+            id = mUUIDs.begin(),
+            uuids_end = mUUIDs.end();
+
+        for (;id != uuids_end; ++id)
+        {
+            if ( LLAvatarActions::isFriend(*id) )
+            {
+                result = false;
+                break;
+            }
+        }
+
+        return result;
+    }
+    else if (item == std::string("can_delete"))
+    {
+        // We can remove friends if:
+        // - there are selected people
+        // - and there are only friends among selection.
+
+        bool result = (mUUIDs.size() > 0);
+
+        uuid_vec_t::const_iterator
+            id = mUUIDs.begin(),
+            uuids_end = mUUIDs.end();
+
+        for (;id != uuids_end; ++id)
+        {
+            if ( !LLAvatarActions::isFriend(*id) )
+            {
+                result = false;
+                break;
+            }
+        }
+
+        return result;
+    }
+    else if (item == std::string("can_call"))
+    {
+        return LLAvatarActions::canCall();
+    }
+    else if (item == std::string("can_show_on_map"))
+    {
+        const LLUUID& id = mUUIDs.front();
+
+        return (LLAvatarTracker::instance().isBuddyOnline(id) && is_agent_mappable(id))
+            || gAgent.isGodlike();
+    }
+    else if(item == std::string("can_offer_teleport"))
+    {
+        return LLAvatarActions::canOfferTeleport(mUUIDs);
+    }
+    return false;
+}
+
+bool LLIMFloaterContainer::checkContextMenuItem(const LLSD& userdata)
+{
+    std::string item = userdata.asString();
+    const LLUUID& id = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem())->getUUID();
+
+    if (item == std::string("is_blocked"))
+    {
+        return LLAvatarActions::isBlocked(id);
+    }
+
+    return false;
+}
+
 void LLIMFloaterContainer::repositioningWidgets()
 {
 	if (!mInitialized)
diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h
index e8d185297c3..cc2d0ce6abb 100644
--- a/indra/newview/llimfloatercontainer.h
+++ b/indra/newview/llimfloatercontainer.h
@@ -111,6 +111,11 @@ class LLIMFloaterContainer
 	void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order);
 	void setSortOrder(const LLConversationSort& order);
 
+    void getSelectedUUIDs(uuid_vec_t& selected_uuids);
+    void doToSelected(const LLSD& userdata);
+    bool checkContextMenuItem(const LLSD& userdata);
+    bool enableContextMenuItem(const LLSD& userdata);
+
 	LLButton* mExpandCollapseBtn;
 	LLLayoutPanel* mMessagesPane;
 	LLLayoutPanel* mConversationsPane;
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 2a84616ddfd..6e692adf2a9 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -172,6 +172,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
     p.show_empty_message = mShowEmptyMessage;
     p.show_item_link_overlays = mShowItemLinkOverlays;
     p.root = NULL;
+    p.options_menu = "menu_inventory.xml";
 
     return LLUICtrlFactory::create<LLFolderView>(p);
 }
diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml
new file mode 100644
index 00000000000..94399be61ce
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_conversation.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ bottom="806"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="menu_conversation_participant"
+ visible="false">
+     <menu_item_call
+     label="Close conversation"
+     layout="topleft"
+     name="close_conversation">
+        <on_click function="Avatar.DoToSelected" parameter="close_conversation"/>
+	 </menu_item_call>
+     <menu_item_call
+     label="Open voice conversation"
+     layout="topleft"
+     name="open_voice_conversation">
+        <on_click function="Avatar.DoToSelected" parameter="open_voice_conversation"/>
+     </menu_item_call>	
+     <menu_item_call
+     label="Disconnect from voice"
+     layout="topleft"
+     name="disconnect_from_voice">
+        <on_click function="Avatar.DoToSelected" parameter="disconnect_from_voice"/>
+    </menu_item_call>	
+	<menu_item_separator layout="topleft" name="separator_disconnect_from_voice"/>	
+    <menu_item_call
+     label="View Profile"
+     layout="topleft"
+     name="view_profile">
+        <on_click function="Avatar.DoToSelected" parameter="view_profile"/>
+    </menu_item_call>
+    <menu_item_call
+     label="IM"
+     layout="topleft"
+     name="im">
+        <on_click function="Avatar.DoToSelected" parameter="im"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Offer teleport"
+     layout="topleft"
+     name="offer_teleport">
+        <on_click function="Avatar.DoToSelected" parameter="offer_teleport"/>
+        <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Voice call"
+     layout="topleft"
+     name="voice_call">
+        <on_click function="Avatar.DoToSelected" parameter="voice_call"/>
+        <on_enable function="Avatar.EnableItem" parameter="can_call" />
+    </menu_item_call>
+    <menu_item_call
+     label="Chat history..."
+     layout="topleft"
+     name="chat_history">
+        <on_click function="Avatar.DoToSelected" parameter="chat_history"/>
+    </menu_item_call>	
+    <menu_item_separator layout="topleft" name="separator_chat_history"/>	
+    <menu_item_call
+     label="Add friend"
+     layout="topleft"
+     name="add_friend">
+        <on_click function="Avatar.DoToSelected" parameter="add_friend"/>
+        <on_enable function="Avatar.EnableItem" parameter="can_add" />
+    </menu_item_call>
+    <menu_item_call
+     label="Remove friend"
+     layout="topleft"
+     name="remove_friend">
+        <on_click function="Avatar.DoToSelected" parameter="remove_friend" />
+        <on_enable function="Avatar.EnableItem" parameter="can_delete" />
+    </menu_item_call>	
+    <menu_item_call
+     label="Invite to group..."
+     layout="topleft"
+     name="invite_to_group">
+        <on_click function="Avatar.DoToSelected" parameter="invite_to_group" />
+    </menu_item_call>
+    <menu_item_separator layout="topleft" name="separator_invite_to_group"/>		
+    <menu_item_call
+     label="Map"
+     layout="topleft"
+     name="map">
+        <on_click function="Avatar.DoToSelected" parameter="map" />
+        <on_enable function="Avatar.EnableItem" parameter="can_show_on_map" />
+    </menu_item_call>
+    <menu_item_call
+     label="Share"
+     layout="topleft"
+     name="share">
+        <on_click function="Avatar.DoToSelected" parameter="share" />
+    </menu_item_call>
+    <menu_item_call
+     label="Pay"
+     layout="topleft"
+     name="pay">
+        <on_click function="Avatar.DoToSelected" parameter="pay" />
+    </menu_item_call>
+    <menu_item_check
+     label="Block / unblock"
+     layout="topleft"
+     name="block_unblock">
+        <on_click function="Avatar.DoToSelected" parameter="block_unblock" />
+		<on_check function="Avatar.CheckItem" parameter="is_blocked" />
+		<on_enable  function="Avatar.EnableItem" parameter="can_block" />
+    </menu_item_check>
+</toggleable_menu>
-- 
GitLab