From ecd482d24b63d9658ac71d2bf4155e3ed9175bb9 Mon Sep 17 00:00:00 2001
From: Dmitry Zaporozhan <dzaporozhan@productengine.com>
Date: Thu, 5 Nov 2009 14:08:31 +0200
Subject: [PATCH] Implemented major sub-task EXT-2250 - Implement Classifieds
 list in Picks panel.

--HG--
branch : product-engine
---
 indra/newview/llavatarpropertiesprocessor.cpp |  90 ++++-
 indra/newview/llavatarpropertiesprocessor.h   |  50 ++-
 indra/newview/llpanelpicks.cpp                | 344 +++++++++++++++++-
 indra/newview/llpanelpicks.h                  |  66 +++-
 indra/newview/llstartup.cpp                   |   5 +-
 .../skins/default/xui/en/menu_picks_plus.xml  |  22 ++
 .../skins/default/xui/en/notifications.xml    |  12 +
 .../xui/en/panel_classifieds_list_item.xml    |  83 +++++
 .../skins/default/xui/en/panel_picks.xml      |  43 ++-
 9 files changed, 685 insertions(+), 30 deletions(-)
 create mode 100644 indra/newview/skins/default/xui/en/menu_picks_plus.xml
 create mode 100644 indra/newview/skins/default/xui/en/panel_classifieds_list_item.xml

diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp
index 73e24ca8e76..98a6a4b92ad 100644
--- a/indra/newview/llavatarpropertiesprocessor.cpp
+++ b/indra/newview/llavatarpropertiesprocessor.cpp
@@ -158,6 +158,11 @@ void LLAvatarPropertiesProcessor::sendAvatarTexturesRequest(const LLUUID& avatar
 	removePendingRequest(avatar_id, APT_TEXTURES);
 }
 
+void LLAvatarPropertiesProcessor::sendAvatarClassifiedsRequest(const LLUUID& avatar_id)
+{
+	sendGenericRequest(avatar_id, APT_CLASSIFIEDS, "avatarclassifiedsrequest");
+}
+
 void LLAvatarPropertiesProcessor::sendAvatarPropertiesUpdate(const LLAvatarData* avatar_props)
 {
 	llinfos << "Sending avatarinfo update" << llendl;
@@ -284,12 +289,60 @@ void LLAvatarPropertiesProcessor::processAvatarInterestsReply(LLMessageSystem* m
 */
 }
 
-void LLAvatarPropertiesProcessor::processAvatarClassifiedReply(LLMessageSystem* msg, void**)
+void LLAvatarPropertiesProcessor::processAvatarClassifiedsReply(LLMessageSystem* msg, void**)
 {
-	// avatarclassifiedsrequest is not sent according to new UI design but
-	// keep this method according to resolved issues. 
+	LLAvatarClassifieds classifieds;
+
+	msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, classifieds.agent_id);
+	msg->getUUID(_PREHASH_AgentData, _PREHASH_TargetID, classifieds.target_id);
+
+	S32 block_count = msg->getNumberOfBlocks(_PREHASH_Data);
+
+	for(int n = 0; n < block_count; ++n)
+	{
+		LLAvatarClassifieds::classified_data data;
+
+		msg->getUUID(_PREHASH_Data, _PREHASH_ClassifiedID, data.classified_id, n);
+		msg->getString(_PREHASH_Data, _PREHASH_Name, data.name, n);
+
+		classifieds.classifieds_list.push_back(data);
+	}
+
+	LLAvatarPropertiesProcessor* self = getInstance();
+	// Request processed, no longer pending
+	self->removePendingRequest(classifieds.target_id, APT_CLASSIFIEDS);
+	self->notifyObservers(classifieds.target_id,&classifieds,APT_CLASSIFIEDS);
 }
 
+void LLAvatarPropertiesProcessor::processClassifiedInfoReply(LLMessageSystem* msg, void**)
+{
+	LLAvatarClassifiedInfo c_info;
+
+	msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, c_info.agent_id);
+
+	msg->getUUID(_PREHASH_Data, _PREHASH_ClassifiedID, c_info.classified_id);
+	msg->getUUID(_PREHASH_Data, _PREHASH_CreatorID, c_info.creator_id);
+	msg->getU32(_PREHASH_Data, _PREHASH_CreationDate, c_info.creation_date);
+	msg->getU32(_PREHASH_Data, _PREHASH_ExpirationDate, c_info.expiration_date);
+	msg->getU32(_PREHASH_Data, _PREHASH_Category, c_info.category);
+	msg->getString(_PREHASH_Data, _PREHASH_Name, c_info.name);
+	msg->getString(_PREHASH_Data, _PREHASH_Desc, c_info.description);
+	msg->getUUID(_PREHASH_Data, _PREHASH_ParcelID, c_info.parcel_id);
+	msg->getU32(_PREHASH_Data, _PREHASH_ParentEstate, c_info.parent_estate);
+	msg->getUUID(_PREHASH_Data, _PREHASH_SnapshotID, c_info.snapshot_id);
+	msg->getString(_PREHASH_Data, _PREHASH_SimName, c_info.sim_name);
+	msg->getVector3d(_PREHASH_Data, _PREHASH_PosGlobal, c_info.pos_global);
+	msg->getString(_PREHASH_Data, _PREHASH_ParcelName, c_info.parcel_name);
+	msg->getU8(_PREHASH_Data, _PREHASH_ClassifiedFlags, c_info.classified_flags);
+	msg->getS32(_PREHASH_Data, _PREHASH_PriceForListing, c_info.price_for_listing);
+
+	LLAvatarPropertiesProcessor* self = getInstance();
+	// Request processed, no longer pending
+	self->removePendingRequest(c_info.agent_id, APT_CLASSIFIED_INFO);
+	self->notifyObservers(c_info.agent_id, &c_info, APT_CLASSIFIED_INFO);
+}
+
+
 void LLAvatarPropertiesProcessor::processAvatarNotesReply(LLMessageSystem* msg, void**)
 {
 	LLAvatarNotes avatar_notes;
@@ -451,6 +504,22 @@ void LLAvatarPropertiesProcessor::sendPickDelete( const LLUUID& pick_id )
 	LLAgentPicksInfo::getInstance()->decrementNumberOfPicks();
 }
 
+void LLAvatarPropertiesProcessor::sendClassifiedDelete(const LLUUID& classified_id)
+{
+	LLMessageSystem* msg = gMessageSystem; 
+
+	msg->newMessage(_PREHASH_ClassifiedDelete);
+
+	msg->nextBlock(_PREHASH_AgentData);
+	msg->addUUID(_PREHASH_AgentID, gAgent.getID());
+	msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+
+	msg->nextBlock(_PREHASH_Data);
+	msg->addUUID(_PREHASH_ClassifiedID, classified_id);
+
+	gAgent.sendReliableMessage();
+}
+
 void LLAvatarPropertiesProcessor::sendPickInfoUpdate(const LLPickData* new_pick)
 {
 	if (!new_pick) return;
@@ -495,6 +564,21 @@ void LLAvatarPropertiesProcessor::sendPickInfoRequest(const LLUUID& creator_id,
 	send_generic_message("pickinforequest", request_params);
 }
 
+void LLAvatarPropertiesProcessor::sendClassifiedInfoRequest(const LLUUID& avatar_id, const LLUUID& classified_id)
+{
+	LLMessageSystem* msg = gMessageSystem;
+
+	msg->newMessage(_PREHASH_ClassifiedInfoRequest);
+	msg->nextBlock(_PREHASH_AgentData);
+	
+	msg->addUUID(_PREHASH_AgentID, gAgent.getID());
+	msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+
+	msg->nextBlock(_PREHASH_Data);
+	msg->addUUID(_PREHASH_ClassifiedID, classified_id);
+
+	gAgent.sendReliableMessage();
+}
 
 bool LLAvatarPropertiesProcessor::isPendingRequest(const LLUUID& avatar_id, EAvatarProcessorType type)
 {
diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h
index e6563024b28..3c6b4e17d9d 100644
--- a/indra/newview/llavatarpropertiesprocessor.h
+++ b/indra/newview/llavatarpropertiesprocessor.h
@@ -53,7 +53,9 @@ enum EAvatarProcessorType
 	APT_GROUPS,
 	APT_PICKS,
 	APT_PICK_INFO,
-	APT_TEXTURES
+	APT_TEXTURES,
+	APT_CLASSIFIEDS,
+	APT_CLASSIFIED_INFO
 };
 
 struct LLAvatarData
@@ -136,6 +138,43 @@ struct LLAvatarGroups
 	};
 };
 
+struct LLAvatarClassifieds
+{
+	LLUUID agent_id;
+	LLUUID target_id;
+
+	struct classified_data;
+	typedef std::list<classified_data> classifieds_list_t;
+
+	classifieds_list_t classifieds_list;
+
+	struct classified_data
+	{
+		LLUUID classified_id;
+		std::string name;
+	};
+};
+
+struct LLAvatarClassifiedInfo
+{
+	LLUUID agent_id;
+	LLUUID classified_id;
+	LLUUID creator_id;
+	U32 creation_date;
+	U32 expiration_date;
+	U32 category;
+	std::string name;
+	std::string description;
+	LLUUID parcel_id;
+	U32 parent_estate;
+	LLUUID snapshot_id;
+	std::string sim_name;
+	LLVector3d pos_global;
+	std::string parcel_name;
+	U8 classified_flags;
+	S32 price_for_listing;
+};
+
 class LLAvatarPropertiesObserver
 {
 public:
@@ -162,10 +201,13 @@ class LLAvatarPropertiesProcessor
 	void sendAvatarNotesRequest(const LLUUID& avatar_id);
 	void sendAvatarGroupsRequest(const LLUUID& avatar_id);
 	void sendAvatarTexturesRequest(const LLUUID& avatar_id);
+	void sendAvatarClassifiedsRequest(const LLUUID& avatar_id);
 
 	// Duplicate pick info requests are not suppressed.
 	void sendPickInfoRequest(const LLUUID& creator_id, const LLUUID& pick_id);
 
+	void sendClassifiedInfoRequest(const LLUUID& avatar_id, const LLUUID& classified_id);
+
 	void sendAvatarPropertiesUpdate(const LLAvatarData* avatar_props);
 
 	void sendPickInfoUpdate(const LLPickData* new_pick);
@@ -176,6 +218,8 @@ class LLAvatarPropertiesProcessor
 
 	void sendPickDelete(const LLUUID& pick_id);
 
+	void sendClassifiedDelete(const LLUUID& classified_id);
+
 	// Returns translated, human readable string for account type, such
 	// as "Resident" or "Linden Employee".  Used for profiles, inspectors.
 	static std::string accountType(const LLAvatarData* avatar_data);
@@ -189,7 +233,9 @@ class LLAvatarPropertiesProcessor
 
 	static void processAvatarInterestsReply(LLMessageSystem* msg, void**);
 
-	static void processAvatarClassifiedReply(LLMessageSystem* msg, void**);
+	static void processAvatarClassifiedsReply(LLMessageSystem* msg, void**);
+
+	static void processClassifiedInfoReply(LLMessageSystem* msg, void**);
 
 	static void processAvatarGroupsReply(LLMessageSystem* msg, void**);
 
diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp
index 6181531f82e..526c604bb44 100644
--- a/indra/newview/llpanelpicks.cpp
+++ b/indra/newview/llpanelpicks.cpp
@@ -45,6 +45,8 @@
 #include "llviewermenu.h"
 #include "llregistry.h"
 
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
 #include "llpanelpicks.h"
 #include "llavatarpropertiesprocessor.h"
 #include "llpanelavatar.h"
@@ -62,6 +64,9 @@ static const std::string PICK_ID("pick_id");
 static const std::string PICK_CREATOR_ID("pick_creator_id");
 static const std::string PICK_NAME("pick_name");
 
+static const std::string CLASSIFIED_ID("classified_id");
+static const std::string CLASSIFIED_NAME("classified_name");
+
 
 static LLRegisterPanelClassWrapper<LLPanelPicks> t_panel_picks("panel_picks");
 
@@ -74,9 +79,13 @@ LLPanelPicks::LLPanelPicks()
 	mProfilePanel(NULL),
 	mPickPanel(NULL),
 	mPicksList(NULL),
+	mClassifiedsList(NULL),
 	mPanelPickInfo(NULL),
 	mPanelPickEdit(NULL),
-	mOverflowMenu(NULL)
+	mOverflowMenu(NULL),
+	mPlusMenu(NULL),
+	mPicksAccTab(NULL),
+	mClassifiedsAccTab(NULL)
 {
 }
 
@@ -100,6 +109,9 @@ void LLPanelPicks::updateData()
 	{
 		mPicksList->clear();
 		LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId());
+
+		mClassifiedsList->clear();
+		LLAvatarPropertiesProcessor::getInstance()->sendAvatarClassifiedsRequest(getAvatarId());
 	}
 }
 
@@ -138,13 +150,47 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type)
 
 				mPicksList->addItem(picture, pick_value);
 
-				picture->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickItem, this, _1));
+				picture->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickPickItem, this, _1));
 				picture->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4));
 				picture->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this));
 			}
 
+			showAccordion("tab_picks", mPicksList->size());
+
+			resetDirty();
+			updateButtons();
+		}
+	}
+	else if(APT_CLASSIFIEDS == type)
+	{
+		LLAvatarClassifieds* c_info = static_cast<LLAvatarClassifieds*>(data);
+		if(c_info && getAvatarId() == c_info->target_id)
+		{
+			mClassifiedsList->clear();
+
+			LLAvatarClassifieds::classifieds_list_t::const_iterator it = c_info->classifieds_list.begin();
+			for(; c_info->classifieds_list.end() != it; ++it)
+			{
+				LLAvatarClassifieds::classified_data c_data = *it;
+
+				LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), c_data.classified_id);
+				c_item->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this));
+				c_item->setName(c_data.name);
+
+				LLSD pick_value = LLSD();
+				pick_value.insert(CLASSIFIED_ID, c_data.classified_id);
+				pick_value.insert(CLASSIFIED_NAME, c_data.name);
+
+				mClassifiedsList->addItem(c_item, pick_value);
+
+				c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1));
+				c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4));
+				c_item->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this));
+			}
+
+			showAccordion("tab_classifieds", mClassifiedsList->size());
+
 			resetDirty();
-			LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this);
 			updateButtons();
 		}
 	}
@@ -158,16 +204,44 @@ LLPickItem* LLPanelPicks::getSelectedPickItem()
 	return dynamic_cast<LLPickItem*>(selected_item);
 }
 
+LLClassifiedItem* LLPanelPicks::getSelectedClassifiedItem()
+{
+	LLPanel* selected_item = mClassifiedsList->getSelectedItem();
+	if (!selected_item) 
+	{
+		return NULL;
+	}
+	return dynamic_cast<LLClassifiedItem*>(selected_item);
+}
+
 BOOL LLPanelPicks::postBuild()
 {
 	mPicksList = getChild<LLFlatListView>("picks_list");
+	mClassifiedsList = getChild<LLFlatListView>("classifieds_list");
+
+	mPicksList->setCommitOnSelectionChange(true);
+	mClassifiedsList->setCommitOnSelectionChange(true);
+
+	mPicksList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mPicksList));
+	mClassifiedsList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mClassifiedsList));
 
-	childSetAction(XML_BTN_NEW, boost::bind(&LLPanelPicks::onClickNew, this));
+	mPicksList->setNoItemsCommentText(getString("no_picks"));
+	mClassifiedsList->setNoItemsCommentText(getString("no_classifieds"));
+
+	childSetAction(XML_BTN_NEW, boost::bind(&LLPanelPicks::onClickPlusBtn, this));
 	childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this));
 	childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this));
 	childSetAction(XML_BTN_SHOW_ON_MAP, boost::bind(&LLPanelPicks::onClickMap, this));
 	childSetAction(XML_BTN_INFO, boost::bind(&LLPanelPicks::onClickInfo, this));
 	childSetAction(XML_BTN_OVERFLOW, boost::bind(&LLPanelPicks::onOverflowButtonClicked, this));
+
+	mPicksAccTab = getChild<LLAccordionCtrlTab>("tab_picks");
+	mPicksAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mPicksAccTab));
+	mPicksAccTab->setDisplayChildren(true);
+
+	mClassifiedsAccTab = getChild<LLAccordionCtrlTab>("tab_classifieds");
+	mClassifiedsAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mClassifiedsAccTab));
+	mClassifiedsAccTab->setDisplayChildren(false);
 	
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registar;
 	registar.add("Pick.Info", boost::bind(&LLPanelPicks::onClickInfo, this));
@@ -180,6 +254,10 @@ BOOL LLPanelPicks::postBuild()
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar overflow_registar;
 	overflow_registar.add("PicksList.Overflow", boost::bind(&LLPanelPicks::onOverflowMenuItemClicked, this, _2));
 	mOverflowMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_overflow.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+
+	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar;
+	plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2));
+	mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 	
 	return TRUE;
 }
@@ -202,6 +280,50 @@ void LLPanelPicks::onOverflowMenuItemClicked(const LLSD& param)
 	}
 }
 
+void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param)
+{
+	std::string value = param.asString();
+
+	if("new_pick" == value)
+	{
+		createNewPick();
+	}
+	else if("new_classified" == value)
+	{
+		createNewClassified();
+	}
+}
+
+void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab)
+{
+	if(acc_tab->getDisplayChildren())
+	{
+		if(acc_tab != mPicksAccTab && mPicksAccTab->getDisplayChildren())
+		{
+			mPicksAccTab->setDisplayChildren(false);
+		}
+
+		if(acc_tab != mClassifiedsAccTab && mClassifiedsAccTab->getDisplayChildren())
+		{
+			mClassifiedsAccTab->setDisplayChildren(false);
+		}
+
+		LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("accordion");
+		accordion->arrange();
+	}
+
+	if(!mPicksAccTab->getDisplayChildren())
+	{
+		mPicksList->resetSelection(true);
+	}
+	if(!mClassifiedsAccTab->getDisplayChildren())
+	{
+		mClassifiedsList->resetSelection(true);
+	}
+
+	updateButtons();
+}
+
 void LLPanelPicks::onOverflowButtonClicked()
 {
 	LLRect rect;
@@ -250,21 +372,50 @@ void LLPanelPicks::onOpen(const LLSD& key)
 	LLPanelProfileTab::onOpen(key);
 }
 
+void LLPanelPicks::onListCommit(const LLFlatListView* f_list)
+{
+	// Make sure only one of the lists has selection.
+	if(f_list == mPicksList)
+	{
+		mClassifiedsList->resetSelection(true);
+	}
+	else if(f_list == mClassifiedsList)
+	{
+		mPicksList->resetSelection(true);
+	}
+	else
+	{
+		llwarns << "Unknown list" << llendl;
+	}
+
+	updateButtons();
+}
+
 //static
 void LLPanelPicks::onClickDelete()
 {
-	LLSD pick_value = mPicksList->getSelectedValue();
-	if (pick_value.isUndefined()) return;
+	LLSD value = mPicksList->getSelectedValue();
+	if (value.isDefined())
+	{
+		LLSD args; 
+		args["PICK"] = value[PICK_NAME]; 
+		LLNotifications::instance().add("DeleteAvatarPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeletePick, this, _1, _2)); 
+		return;
+	}
 
-	LLSD args; 
-	args["PICK"] = pick_value[PICK_NAME]; 
-	LLNotifications::instance().add("DeleteAvatarPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackDelete, this, _1, _2)); 
+	value = mClassifiedsList->getSelectedValue();
+	if(value.isDefined())
+	{
+		LLSD args; 
+		args["NAME"] = value[CLASSIFIED_NAME]; 
+		LLNotifications::instance().add("DeleteClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeleteClassified, this, _1, _2)); 
+		return;
+	}
 }
 
-bool LLPanelPicks::callbackDelete(const LLSD& notification, const LLSD& response) 
+bool LLPanelPicks::callbackDeletePick(const LLSD& notification, const LLSD& response) 
 {
 	S32 option = LLNotification::getSelectedOption(notification, response);
-
 	LLSD pick_value = mPicksList->getSelectedValue();
 
 	if (0 == option)
@@ -276,6 +427,20 @@ bool LLPanelPicks::callbackDelete(const LLSD& notification, const LLSD& response
 	return false;
 }
 
+bool LLPanelPicks::callbackDeleteClassified(const LLSD& notification, const LLSD& response) 
+{
+	S32 option = LLNotification::getSelectedOption(notification, response);
+	LLSD value = mClassifiedsList->getSelectedValue();
+
+	if (0 == option)
+	{
+		LLAvatarPropertiesProcessor::instance().sendClassifiedDelete(value[CLASSIFIED_ID]);
+		mClassifiedsList->removeItemByValue(value);
+	}
+	updateButtons();
+	return false;
+}
+
 bool LLPanelPicks::callbackTeleport( const LLSD& notification, const LLSD& response )
 {
 	S32 option = LLNotification::getSelectedOption(notification, response);
@@ -291,9 +456,14 @@ bool LLPanelPicks::callbackTeleport( const LLSD& notification, const LLSD& respo
 void LLPanelPicks::onClickTeleport()
 {
 	LLPickItem* pick_item = getSelectedPickItem();
-	if (!pick_item) return;
+	LLClassifiedItem* c_item = getSelectedClassifiedItem();
+
+	LLVector3d pos;
+	if(pick_item)
+		pos = pick_item->getPosGlobal();
+	else if(c_item)
+		pos = c_item->getPosGlobal();
 
-	LLVector3d pos = pick_item->getPosGlobal();
 	if (!pos.isExactlyZero())
 	{
 		gAgent.teleportViaLocation(pos);
@@ -305,9 +475,15 @@ void LLPanelPicks::onClickTeleport()
 void LLPanelPicks::onClickMap()
 {
 	LLPickItem* pick_item = getSelectedPickItem();
-	if (!pick_item) return;
+	LLClassifiedItem* c_item = getSelectedClassifiedItem();
+
+	LLVector3d pos;
+	if (pick_item)
+		pos = pick_item->getPosGlobal();
+	else if(c_item)
+		pos = c_item->getPosGlobal();
 
-	LLFloaterWorldMap::getInstance()->trackLocation(pick_item->getPosGlobal());
+	LLFloaterWorldMap::getInstance()->trackLocation(pos);
 	LLFloaterReg::showInstance("world_map", "center");
 }
 
@@ -325,7 +501,7 @@ void LLPanelPicks::onRightMouseUpItem(LLUICtrl* item, S32 x, S32 y, MASK mask)
 	}
 }
 
-void LLPanelPicks::onDoubleClickItem(LLUICtrl* item)
+void LLPanelPicks::onDoubleClickPickItem(LLUICtrl* item)
 {
 	LLSD pick_value = mPicksList->getSelectedValue();
 	if (pick_value.isUndefined()) return;
@@ -335,9 +511,19 @@ void LLPanelPicks::onDoubleClickItem(LLUICtrl* item)
 	LLNotifications::instance().add("TeleportToPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); 
 }
 
+void LLPanelPicks::onDoubleClickClassifiedItem(LLUICtrl* item)
+{
+	LLSD value = mClassifiedsList->getSelectedValue();
+	if (value.isUndefined()) return;
+
+	LLSD args; 
+	args["CLASSIFIED"] = value[CLASSIFIED_NAME]; 
+	LLNotifications::instance().add("TeleportToClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); 
+}
+
 void LLPanelPicks::updateButtons()
 {
-	bool has_selected = mPicksList->numSelected();
+	bool has_selected = mPicksList->numSelected() > 0 || mClassifiedsList->numSelected() > 0;
 
 	if (getAvatarId() == gAgentID)
 	{
@@ -366,14 +552,41 @@ void LLPanelPicks::buildPickPanel()
 // 	}
 }
 
-void LLPanelPicks::onClickNew()
+void LLPanelPicks::onClickPlusBtn()
+{
+	LLRect rect;
+	childGetRect(XML_BTN_NEW, rect);
+
+	mPlusMenu->updateParent(LLMenuGL::sMenuContainer);
+	mPlusMenu->setButtonRect(rect, this);
+	LLMenuGL::showPopup(this, mPlusMenu, rect.mLeft, rect.mTop);
+}
+
+void LLPanelPicks::createNewPick()
 {
 	createPickEditPanel();
 
 	getProfilePanel()->openPanel(mPanelPickEdit, LLSD());
 }
 
+void LLPanelPicks::createNewClassified()
+{
+	LLNotifications::instance().add("ClickUnimplemented");
+}
+
 void LLPanelPicks::onClickInfo()
+{
+	if(mPicksList->numSelected() > 0)
+	{
+		openPickInfo();
+	}
+	else if(mClassifiedsList->numSelected() > 0)
+	{
+		openClassifiedInfo();
+	}
+}
+
+void LLPanelPicks::openPickInfo()
 {
 	LLSD selected_value = mPicksList->getSelectedValue();
 	if (selected_value.isUndefined()) return;
@@ -392,6 +605,19 @@ void LLPanelPicks::onClickInfo()
 	getProfilePanel()->openPanel(mPanelPickInfo, params);
 }
 
+void LLPanelPicks::openClassifiedInfo()
+{
+	LLNotifications::instance().add("ClickUnimplemented");
+}
+
+void LLPanelPicks::showAccordion(const std::string& name, bool show)
+{
+	LLAccordionCtrlTab* tab = getChild<LLAccordionCtrlTab>(name);
+	tab->setVisible(show);
+	LLAccordionCtrl* acc = getChild<LLAccordionCtrl>("accordion");
+	acc->arrange();
+}
+
 void LLPanelPicks::onPanelPickClose(LLPanel* panel)
 {
 	panel->setVisible(FALSE);
@@ -475,7 +701,14 @@ void LLPanelPicks::onPanelPickEdit()
 
 void LLPanelPicks::onClickMenuEdit()
 {
-	onPanelPickEdit();
+	if(getSelectedPickItem())
+	{
+		onPanelPickEdit();
+	}
+	else if(getSelectedClassifiedItem())
+	{
+		LLNotifications::instance().add("ClickUnimplemented");
+	}
 }
 
 inline LLPanelProfile* LLPanelPicks::getProfilePanel()
@@ -610,3 +843,76 @@ void LLPickItem::setValue(const LLSD& value)
 	if (!value.has("selected")) return;
 	childSetVisible("selected_icon", value["selected"]);
 }
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLClassifiedItem::LLClassifiedItem(const LLUUID& avatar_id, const LLUUID& classified_id)
+ : LLPanel()
+ , mAvatarId(avatar_id)
+ , mClassifiedId(classified_id)
+{
+	LLUICtrlFactory::getInstance()->buildPanel(this,"panel_classifieds_list_item.xml");
+
+	LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this);
+	LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getAvatarId(), getClassifiedId());
+}
+
+LLClassifiedItem::~LLClassifiedItem()
+{
+	LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this);
+}
+
+void LLClassifiedItem::processProperties(void* data, EAvatarProcessorType type)
+{
+	if(APT_CLASSIFIED_INFO != type)
+	{
+		return;
+	}
+
+	LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data);
+	if( !(c_info && c_info->agent_id == getAvatarId() 
+		&& c_info->classified_id == getClassifiedId()) )
+	{
+		return;
+	}
+
+	setName(c_info->name);
+	setDescription(c_info->description);
+	setSnapshotId(c_info->snapshot_id);
+	setPosGlobal(c_info->pos_global);
+
+	LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this);
+}
+
+BOOL LLClassifiedItem::postBuild()
+{
+	setMouseEnterCallback(boost::bind(&LLPanelPickInfo::childSetVisible, this, "hovered_icon", true));
+	setMouseLeaveCallback(boost::bind(&LLPanelPickInfo::childSetVisible, this, "hovered_icon", false));
+	return TRUE;
+}
+
+void LLClassifiedItem::setValue(const LLSD& value)
+{
+	if (!value.isMap()) return;;
+	if (!value.has("selected")) return;
+	childSetVisible("selected_icon", value["selected"]);
+}
+
+void LLClassifiedItem::setName(const std::string& name)
+{
+	childSetValue("name", name);
+}
+
+void LLClassifiedItem::setDescription(const std::string& desc)
+{
+	childSetValue("description", desc);
+}
+
+void LLClassifiedItem::setSnapshotId(const LLUUID& snapshot_id)
+{
+	childSetValue("picture", snapshot_id);
+}
+
+//EOF
diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h
index 06a0f0a0fdb..d6321a475ca 100644
--- a/indra/newview/llpanelpicks.h
+++ b/indra/newview/llpanelpicks.h
@@ -40,6 +40,7 @@
 #include "llpanelavatar.h"
 #include "llregistry.h"
 
+class LLAccordionCtrlTab;
 class LLPanelProfile;
 class LLMessageSystem;
 class LLVector3d;
@@ -47,6 +48,7 @@ class LLPanelProfileTab;
 class LLAgent;
 class LLMenuGL;
 class LLPickItem;
+class LLClassifiedItem;
 class LLFlatListView;
 class LLPanelPickInfo;
 class LLPanelPickEdit;
@@ -71,6 +73,7 @@ class LLPanelPicks
 
 	// returns the selected pick item
 	LLPickItem* getSelectedPickItem();
+	LLClassifiedItem* getSelectedClassifiedItem();
 
 	//*NOTE top down approch when panel toggling is done only by 
 	// parent panels failed to work (picks related code was in me profile panel)
@@ -83,25 +86,39 @@ class LLPanelPicks
 
 	void onOverflowMenuItemClicked(const LLSD& param);
 	void onOverflowButtonClicked();
+	void onPlusMenuItemClicked(const LLSD& param);
+
+	void onListCommit(const LLFlatListView* f_list);
+	void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab);
 
 	//------------------------------------------------
 	// Callbacks which require panel toggling
 	//------------------------------------------------
-	void onClickNew();
+	void onClickPlusBtn();
 	void onClickInfo();
 	void onPanelPickClose(LLPanel* panel);
 	void onPanelPickSave(LLPanel* panel);
 	void onPanelPickEdit();
 	void onClickMenuEdit();
 
+	void createNewPick();
+	void createNewClassified();
+
+	void openPickInfo();
+	void openClassifiedInfo();
+
+	void showAccordion(const std::string& name, bool show);
+
 	void buildPickPanel();
 
-	bool callbackDelete(const LLSD& notification, const LLSD& response);
+	bool callbackDeletePick(const LLSD& notification, const LLSD& response);
+	bool callbackDeleteClassified(const LLSD& notification, const LLSD& response);
 	bool callbackTeleport(const LLSD& notification, const LLSD& response);
 
 	void updateButtons();
 
-	virtual void onDoubleClickItem(LLUICtrl* item);
+	virtual void onDoubleClickPickItem(LLUICtrl* item);
+	virtual void onDoubleClickClassifiedItem(LLUICtrl* item);
 	virtual void onRightMouseUpItem(LLUICtrl* item, S32 x, S32 y, MASK mask);
 
 	LLPanelProfile* getProfilePanel();
@@ -115,9 +132,14 @@ class LLPanelPicks
 	LLPanelProfile* mProfilePanel;
 	LLPanelPickInfo* mPickPanel;
 	LLFlatListView* mPicksList;
+	LLFlatListView* mClassifiedsList;
 	LLPanelPickInfo* mPanelPickInfo;
 	LLPanelPickEdit* mPanelPickEdit;
 	LLToggleableMenu* mOverflowMenu;
+	LLToggleableMenu* mPlusMenu;
+
+	LLAccordionCtrlTab* mPicksAccTab;
+	LLAccordionCtrlTab* mClassifiedsAccTab;
 };
 
 class LLPickItem : public LLPanel, public LLAvatarPropertiesObserver
@@ -189,4 +211,42 @@ class LLPickItem : public LLPanel, public LLAvatarPropertiesObserver
 	std::string mSimName;
 };
 
+class LLClassifiedItem : public LLPanel, public LLAvatarPropertiesObserver
+{
+public:
+
+	LLClassifiedItem(const LLUUID& avatar_id, const LLUUID& classified_id);
+	
+	virtual ~LLClassifiedItem();
+
+	/*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
+
+	/*virtual*/ BOOL postBuild();
+
+	/*virtual*/ void setValue(const LLSD& value);
+
+	LLUUID getAvatarId() {return mAvatarId;}
+	
+	void setAvatarId(const LLUUID& avatar_id) {mAvatarId = avatar_id;}
+
+	LLUUID getClassifiedId() {return mClassifiedId;}
+
+	void setClassifiedId(const LLUUID& classified_id) {mClassifiedId = classified_id;}
+
+	void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; }
+
+	const LLVector3d& getPosGlobal() { return mPosGlobal; }
+
+	void setName (const std::string& name);
+
+	void setDescription(const std::string& desc);
+
+	void setSnapshotId(const LLUUID& snapshot_id);
+
+private:
+	LLUUID mAvatarId;
+	LLUUID mClassifiedId;
+	LLVector3d mPosGlobal;
+};
+
 #endif // LL_LLPANELPICKS_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 9aa74e8b9f9..0ffea6d600b 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2469,7 +2469,7 @@ void register_viewer_callbacks(LLMessageSystem* msg)
 	msg->setHandlerFunc("AvatarPicksReply",
 						&LLAvatarPropertiesProcessor::processAvatarPicksReply);
  	msg->setHandlerFunc("AvatarClassifiedReply",
- 						&LLAvatarPropertiesProcessor::processAvatarClassifiedReply);
+ 						&LLAvatarPropertiesProcessor::processAvatarClassifiedsReply);
 
 	msg->setHandlerFuncFast(_PREHASH_CreateGroupReply,
 						LLGroupMgr::processCreateGroupReply);
@@ -2535,7 +2535,8 @@ void register_viewer_callbacks(LLMessageSystem* msg)
 
 	msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply);
 	msg->setHandlerFunc("PickInfoReply", &LLAvatarPropertiesProcessor::processPickInfoReply);
-	msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply);
+//	msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply);
+	msg->setHandlerFunc("ClassifiedInfoReply", LLAvatarPropertiesProcessor::processClassifiedInfoReply);
 	msg->setHandlerFunc("ParcelInfoReply", LLRemoteParcelInfoProcessor::processParcelInfoReply);
 	msg->setHandlerFunc("ScriptDialog", process_script_dialog);
 	msg->setHandlerFunc("LoadURL", process_load_url);
diff --git a/indra/newview/skins/default/xui/en/menu_picks_plus.xml b/indra/newview/skins/default/xui/en/menu_picks_plus.xml
new file mode 100644
index 00000000000..3065239615c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_picks_plus.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ height="201"
+ layout="topleft"
+ mouse_opaque="false"
+ name="picks_plus_menu"
+ width="120">
+    <menu_item_call 
+     name="create_pick" 
+     label="New Pick">
+        <menu_item_call.on_click 
+         function="Picks.Plus.Action" 
+         userdata="new_pick" />
+        </menu_item_call>
+    <menu_item_call 
+     name="create_classified" 
+     label="New Classified">
+        <menu_item_call.on_click 
+         function="Picks.Plus.Action" 
+         userdata="new_classified" />
+    </menu_item_call>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index babed28f10e..d479acb20cc 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3050,6 +3050,18 @@ Teleport to [PICK]?
      yestext="Teleport"/>
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="TeleportToClassified"
+   type="alertmodal">
+    Teleport to [CLASSIFIED]?
+    <usetemplate
+     ignoretext="Confirm that I want to teleport to a location in Classifieds"
+     name="okcancelignore"
+     notext="Cancel"
+     yestext="Teleport"/>
+  </notification>
+
   <notification
    icon="alert.tga"
    label="Message everyone in your Estate"
diff --git a/indra/newview/skins/default/xui/en/panel_classifieds_list_item.xml b/indra/newview/skins/default/xui/en/panel_classifieds_list_item.xml
new file mode 100644
index 00000000000..ee333be0cb0
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_classifieds_list_item.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ bevel_style="none"
+ follows="top|left|right"
+ height="85"
+ layout="topleft"
+ left="0"
+ name="classified_item"
+ top="0"
+ width="313">
+    <icon
+     follows="all"
+     height="85"
+     image_name="ListItem_Over"
+     right="-3"
+     mouse_opaque="false"
+     name="hovered_icon"
+     top="1"
+     scale_image="true"
+     visible="false"
+     width="307"/>
+    <icon
+     follows="all"
+     height="85"
+     image_name="ListItem_Select"
+     right="-3"
+     mouse_opaque="false"
+     name="selected_icon"
+     top="1"
+     scale_image="true"
+     visible="false"
+     width="307"/>
+    <texture_picker
+     allow_no_texture="true"
+     border_enabled="true"
+     default_image_name="TabIcon_Places_Large"
+     enabled="false"
+     follows="left|top"
+     height="80"
+     layout="topleft"
+     left="10"
+     mouse_opaque="false"
+     name="picture"
+     tab_stop="false"
+     top="10"
+     top_pad="10"
+     width="90" />
+    <text
+     follows="top|left|right"
+     font="SansSerifSmallBold"
+     height="16"
+     layout="topleft"
+     left="110"
+     name="name"
+     text_color="white"
+     top="9"
+     use_ellipses="false"
+     width="197"
+     word_wrap="false" />
+    <expandable_text
+     follows="top|left|right"
+     font="SansSerifSmall"
+     height="40"
+     layout="topleft"
+     left="110"
+     name="description"
+     top_pad="3"
+     width="178"
+     word_wrap="true" />
+    <button
+     follows="top|right"
+     height="16"
+     image_selected="BuyArrow_Press"
+     image_pressed="BuyArrow_Press"
+     image_unselected="BuyArrow_Press"
+     layout="topleft"
+     name="info_chevron"
+     picture_style="true"
+     right="-7"
+     tab_stop="false"
+     top="27"
+     width="16" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml
index cbe1f11e3d7..a2296edc2ff 100644
--- a/indra/newview/skins/default/xui/en/panel_picks.xml
+++ b/indra/newview/skins/default/xui/en/panel_picks.xml
@@ -8,16 +8,57 @@
  name="panel_picks"
  top="0"
  width="313">
+ <string
+  name="no_picks"
+  value="No Picks" />
+ <string
+  name="no_classifieds"
+  value="No Classifieds" />
+     
+ <accordion
+  follows="all"
+  height="465"
+  layout="topleft"
+  left="0"
+  name="accordion"
+  top="0"
+  width="313">
+    <accordion_tab
+     can_resize="false"
+     layout="topleft"
+     height="235"
+     min_height="150"
+     name="tab_picks"
+     title="Picks"
+     visible="false">
  <flat_list_view
          color="DkGray2"
          follows="all"
-         height="465"
          layout="topleft"
          left="0"
          name="picks_list"
          opaque="true"
          top="0"
          width="313" />
+    </accordion_tab>
+    <accordion_tab
+     can_resize="false"
+     layout="topleft"
+     height="235"
+     name="tab_classifieds"
+     title="Classified"
+     visible="false">
+            <flat_list_view
+             color="DkGray2"
+             follows="all"
+             layout="topleft"
+             left="0"
+             name="classifieds_list"
+             opaque="true"
+             top="0"
+             width="313" />
+    </accordion_tab>
+ </accordion>
    <panel
          background_visible="true"
          bevel_style="none"
-- 
GitLab